From e447a91c9ccb0625a2b157576f8826817489c744 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 22 May 2019 11:16:57 -0700 Subject: [PATCH 001/290] Initial stubs for new Material Interface Reconstruction component (mir) --- src/axom/CMakeLists.txt | 1 + src/axom/config.hpp.in | 1 + src/axom/mainpage.md | 2 + src/axom/mir/CMakeLists.txt | 65 ++++++++++++++++++++++++++++++ src/axom/mir/README.md | 8 ++++ src/axom/mir/docs/sphinx/index.rst | 12 ++++++ src/axom/mir/tests/CMakeLists.txt | 34 ++++++++++++++++ src/axom/mir/tests/mir_smoke.cpp | 38 +++++++++++++++++ src/docs/dependencies.dot | 1 + src/docs/doxygen/Doxyfile.in | 1 + src/index.rst | 3 ++ 11 files changed, 166 insertions(+) create mode 100644 src/axom/mir/CMakeLists.txt create mode 100644 src/axom/mir/README.md create mode 100644 src/axom/mir/docs/sphinx/index.rst create mode 100644 src/axom/mir/tests/CMakeLists.txt create mode 100644 src/axom/mir/tests/mir_smoke.cpp diff --git a/src/axom/CMakeLists.txt b/src/axom/CMakeLists.txt index 12827d2c7a..ef43feb31b 100644 --- a/src/axom/CMakeLists.txt +++ b/src/axom/CMakeLists.txt @@ -30,6 +30,7 @@ axom_add_component(COMPONENT_NAME spin DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONE axom_add_component(COMPONENT_NAME sidre DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME mint DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME quest DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) +axom_add_component(COMPONENT_NAME mir DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) # Combine all component object libraries into a unified library blt_add_library(NAME axom diff --git a/src/axom/config.hpp.in b/src/axom/config.hpp.in index b43f33993f..215d966bf3 100644 --- a/src/axom/config.hpp.in +++ b/src/axom/config.hpp.in @@ -91,6 +91,7 @@ * Compiler defines for the toolkit components */ #cmakedefine AXOM_USE_MINT +#cmakedefine AXOM_USE_MIR #cmakedefine AXOM_USE_LUMBERJACK #cmakedefine AXOM_USE_PRIMAL #cmakedefine AXOM_USE_QUEST diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 827988937a..60708dae52 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -8,6 +8,7 @@ Axom provides libraries that address common computer science needs. It grew fro * @subpage coretop provides shared utility functionality to all components. * @subpage lumberjacktop provides logging aggregation and filtering capability. * @subpage minttop provides a comprehensive mesh data model. +* @subpage mirtop provides algorithms for material interface reconstruction on multimaterial meshes. * @subpage primaltop provides an API for geometric primitives and computational geometry tests. * @subpage questtop provides an API to query point distance and position relative to meshes. * @subpage sidretop provides a data store with hierarchical structure. @@ -20,6 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre +- Mir depends on Slic - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt new file mode 100644 index 0000000000..6c7c67635e --- /dev/null +++ b/src/axom/mir/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# MIR -- Material Interface Reconstruction +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Check necessary dependencies +#------------------------------------------------------------------------------ +axom_component_requires(NAME MIR + COMPONENTS SLIC) + +#------------------------------------------------------------------------------ +# Specify all headers/sources +#------------------------------------------------------------------------------ +set(mir_headers + + ) + +set(mir_sources + ../Axom.cpp + ) + +#------------------------------------------------------------------------------ +# Build and install the library +#------------------------------------------------------------------------------ +set(mir_depends_on core slic) + +blt_add_library( + NAME mir + SOURCES ${mir_sources} + HEADERS ${mir_headers} + DEPENDS_ON ${mir_depends_on} + FOLDER axom/mir + OBJECT TRUE ) + +axom_write_unified_header(NAME mir + HEADERS ${mir_headers} ) + +axom_install_component(NAME mir + HEADERS ${mir_headers} ) + +#------------------------------------------------------------------------------ +# Add tests, benchmarks and examples +#------------------------------------------------------------------------------ +if (AXOM_ENABLE_TESTS) + add_subdirectory(tests) + if (ENABLE_BENCHMARKS) + # add_subdirectory(benchmarks) + endif() +endif() + +if (AXOM_ENABLE_EXAMPLES) + #add_subdirectory(examples) +endif() + + +#------------------------------------------------------------------------------ +# Add code checks +#------------------------------------------------------------------------------ +axom_add_code_checks( + PREFIX mir ) + diff --git a/src/axom/mir/README.md b/src/axom/mir/README.md new file mode 100644 index 0000000000..fa6b7fba6a --- /dev/null +++ b/src/axom/mir/README.md @@ -0,0 +1,8 @@ +MIR: Material Interface Reconstruction {#mirtop} +================================================ + +[MIR](@ref axom::mir) provides algorithms for reconstructing interfaces +between materials in a multimaterial mesh. + +The [Mir user documentation](../../../sphinx/axom_docs/html/axom/mir/docs/sphinx/index.html) +describes these algorithms. diff --git a/src/axom/mir/docs/sphinx/index.rst b/src/axom/mir/docs/sphinx/index.rst new file mode 100644 index 0000000000..22f5487fd6 --- /dev/null +++ b/src/axom/mir/docs/sphinx/index.rst @@ -0,0 +1,12 @@ +.. ## Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level COPYRIGHT file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +======================= +Mir User Documentation +======================= + +Axom's Material Interface Reconstruction (MIR) component provides algorithms for +reconstructing the interface surfaces between different materials in multimaterial +meshes. diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt new file mode 100644 index 0000000000..155f8ae486 --- /dev/null +++ b/src/axom/mir/tests/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Mir unit tests +#------------------------------------------------------------------------------ + + +#------------------------------------------------------------------------------ +# Specify list of tests +#------------------------------------------------------------------------------ + +set(gtest_mir_tests + mir_smoke.cpp + ) + + +set(mir_tests_depends_on core slic mir gtest) + +#------------------------------------------------------------------------------ +# Add gtest based tests +#------------------------------------------------------------------------------ +foreach(test ${gtest_mir_tests}) + get_filename_component( test_name ${test} NAME_WE ) + blt_add_executable( NAME ${test_name}_test + SOURCES ${test} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_tests_depends_on} + FOLDER axom/mir/tests ) + blt_add_test( NAME ${test_name} + COMMAND ${test_name}_test ) +endforeach() + diff --git a/src/axom/mir/tests/mir_smoke.cpp b/src/axom/mir/tests/mir_smoke.cpp new file mode 100644 index 0000000000..c71092b54d --- /dev/null +++ b/src/axom/mir/tests/mir_smoke.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_SMOKE_H_ +#define MIR_SMOKE_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + + +TEST(mir,smoke) +{ + SLIC_INFO("Running smoke test for MIR component"); + + EXPECT_TRUE( true ); + EXPECT_EQ( 0, 0 ); +} + + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_SMOKE_H_ diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index 8e747c824c..75ab74554e 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,6 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; + mir -> {slic core}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index d38a7d5aad..d76fccfee4 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -786,6 +786,7 @@ INPUT = @PROJECT_SOURCE_DIR@/axom/mainpage.md \ @PROJECT_SOURCE_DIR@/axom/mint/mesh \ @PROJECT_SOURCE_DIR@/axom/mint/utils \ @PROJECT_SOURCE_DIR@/axom/mint/execution \ + @PROJECT_SOURCE_DIR@/axom/mir/README.md \ @PROJECT_SOURCE_DIR@/axom/primal/README.md \ @PROJECT_SOURCE_DIR@/axom/primal/geometry \ @PROJECT_SOURCE_DIR@/axom/primal/operators \ diff --git a/src/index.rst b/src/index.rst index c4ac47919c..9ac474a4b6 100644 --- a/src/index.rst +++ b/src/index.rst @@ -61,6 +61,7 @@ for Axom software components: Spin (Spatial indexes) Quest (Querying on surface tool) Mint (Mesh data model) + Mir (Material interface reconstruction) Primal (Computational geometry primitives) -------------------------- @@ -71,6 +72,7 @@ Source Code Documentation * `Core <../../../doxygen/axom_doxygen/html/coretop.html>`_ * `Lumberjack <../../../doxygen/axom_doxygen/html/lumberjacktop.html>`_ * `Mint <../../../doxygen/axom_doxygen/html/minttop.html>`_ + * `Mir <../../../doxygen/axom_doxygen/html/mirtop.html>`_ * `Primal <../../../doxygen/axom_doxygen/html/primaltop.html>`_ * `Quest <../../../doxygen/axom_doxygen/html/questtop.html>`_ * `Sidre <../../../doxygen/axom_doxygen/html/sidretop.html>`_ @@ -86,6 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre +- Mir depends on Slic - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From cb2467fd8371b5923ce19aa2505110101c175aa5 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 28 May 2019 14:00:18 -0700 Subject: [PATCH 002/290] Generated config file for MacOS Mojave. --- scripts/uberenv/spack_configs/darwin/compilers.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/scripts/uberenv/spack_configs/darwin/compilers.yaml b/scripts/uberenv/spack_configs/darwin/compilers.yaml index 67d55966b1..264b26da41 100644 --- a/scripts/uberenv/spack_configs/darwin/compilers.yaml +++ b/scripts/uberenv/spack_configs/darwin/compilers.yaml @@ -23,3 +23,15 @@ compilers: f77: /usr/local/bin/gfortran fc: /usr/local/bin/gfortran spec: clang@9.0.0 +- compiler: + environment: {} + extra_rpaths: [] + flags: {} + modules: [] + operating_system: mojave + paths: + cc: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang + cxx: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ + f77: /usr/local/bin/gfortran + fc: /usr/local/bin/gfortran + spec: clang@10.0.0-apple From 4d48abe98ffeffeae5342570961ef045cfe0b5e4 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Mon, 24 Jun 2019 17:15:07 -0700 Subject: [PATCH 003/290] Add uberenv_libs to gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 501c8041a6..5ab2039dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ _axom_build_and_test_* tpl_dirs_summary.json *.swp *.vscode* +uberenv_libs From f8011fa98d40df2067a0449a0e538d39d125b1b9 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Wed, 29 May 2019 08:14:31 -0700 Subject: [PATCH 004/290] Builds a simple mir program. --- src/axom/mir/CMakeLists.txt | 2 +- src/axom/mir/examples/CMakeLists.txt | 33 ++++++++++++++++++++ src/axom/mir/examples/mir_tutorial.cpp | 42 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/axom/mir/examples/CMakeLists.txt create mode 100644 src/axom/mir/examples/mir_tutorial.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 6c7c67635e..8005392b9f 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -53,7 +53,7 @@ if (AXOM_ENABLE_TESTS) endif() if (AXOM_ENABLE_EXAMPLES) - #add_subdirectory(examples) + add_subdirectory(examples) endif() diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt new file mode 100644 index 0000000000..b199beae56 --- /dev/null +++ b/src/axom/mir/examples/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) + +set( mir_examples + mir_tutorial.cpp + ) + +set( mir_example_dependencies + #core + mir + #slic + ) + +blt_list_append( TO mir_example_dependencies ELEMENTS sidre conduit IF AXOM_MIR_USE_SIDRE ) +blt_list_append( TO mir_example_dependencies ELEMENTS raja IF RAJA_FOUND ) +blt_list_append( TO mir_example_dependencies ELEMENTS openmp IF ENABLE_OPENMP ) +blt_list_append( TO mir_example_dependencies ELEMENTS cuda IF ENABLE_CUDA ) + +foreach( example ${mir_examples} ) + + get_filename_component( example_name ${example} NAME_WE ) + + blt_add_executable( + NAME ${example_name}_ex + SOURCES ${example} + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_example_dependencies} + FOLDER axom/mir/examples + ) + +endforeach() diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp new file mode 100644 index 0000000000..7dbcb69a0d --- /dev/null +++ b/src/axom/mir/examples/mir_tutorial.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +#include "axom/mir.hpp" // for Mir classes & functions + + + +// C/C++ includes +#include // for definition of M_PI, exp() + +// namespace aliases +//namespace mir = axom::mir; +namespace numerics = axom::numerics; + +/*! + * \file + * + * \brief Various code snippets/examples used for the Mir tutorial section. + * + * \note These examples are designed to illustrate specific Mir + * concepts and capabilities. Consult the Tutorial section of Mir's + * User Guide for more details. + * + */ + + +/*! + * \brief Tutorial main + */ +int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + + std::cout << "hello from the mir_tutorial!" << std::endl; + + return 0; +} + + + From d9db7fee9b089c4c783d9ae506f7391900d752ae Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 7 Jun 2019 08:20:14 -0700 Subject: [PATCH 005/290] Basic material interface reconstruction implemented for a simple two material quad test mesh. --- src/axom/mir/CMakeLists.txt | 10 +- src/axom/mir/CellData.cpp | 99 ++++ src/axom/mir/CellData.hpp | 51 ++ src/axom/mir/InterfaceReconstructor.cpp | 489 ++++++++++++++++++ src/axom/mir/InterfaceReconstructor.hpp | 64 +++ src/axom/mir/MIRMesh.cpp | 318 ++++++++++++ src/axom/mir/MIRMesh.hpp | 100 ++++ src/axom/mir/MIRMeshTypes.hpp | 74 +++ src/axom/mir/ZooBitMaps.cpp | 47 ++ src/axom/mir/ZooBitMaps.hpp | 17 + src/axom/mir/examples/CMakeLists.txt | 1 + src/axom/mir/examples/mir_tutorial.cpp | 446 +++++++++++++++- src/axom/mir/examples/mir_tutorial_simple.cpp | 122 +++++ 13 files changed, 1833 insertions(+), 5 deletions(-) create mode 100644 src/axom/mir/CellData.cpp create mode 100644 src/axom/mir/CellData.hpp create mode 100644 src/axom/mir/InterfaceReconstructor.cpp create mode 100644 src/axom/mir/InterfaceReconstructor.hpp create mode 100644 src/axom/mir/MIRMesh.cpp create mode 100644 src/axom/mir/MIRMesh.hpp create mode 100644 src/axom/mir/MIRMeshTypes.hpp create mode 100644 src/axom/mir/ZooBitMaps.cpp create mode 100644 src/axom/mir/ZooBitMaps.hpp create mode 100644 src/axom/mir/examples/mir_tutorial_simple.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 8005392b9f..a31de3c473 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -16,11 +16,19 @@ axom_component_requires(NAME MIR # Specify all headers/sources #------------------------------------------------------------------------------ set(mir_headers - + MIRMesh.hpp + MIRMeshTypes.hpp + ZooBitMaps.hpp + InterfaceReconstructor.hpp + CellData.hpp ) set(mir_sources ../Axom.cpp + MIRMesh.cpp + InterfaceReconstructor.cpp + ZooBitMaps.cpp + CellData.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp new file mode 100644 index 0000000000..bfeee02462 --- /dev/null +++ b/src/axom/mir/CellData.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellData.hpp" + +namespace axom +{ +namespace mir +{ + + //-------------------------------------------------------------------------------- + + CellData::CellData() + { + + } + + //-------------------------------------------------------------------------------- + + CellData::CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, + std::vector _vertexPositions, std::vector > _vertexVolumeFractions) + { + numVerts = _numVerts; + numElems = _numElems; + evInds = _evInds; + evBegins = _evBegins; + veInds = _veInds; + veBegins = _veBegins; + vertexPositions = _vertexPositions; + vertexVolumeFractions = _vertexVolumeFractions; + } + + //-------------------------------------------------------------------------------- + + CellData::~CellData() + { + + } + + //-------------------------------------------------------------------------------- + + /// Merges the cell data from the given cell into this cell + void CellData::mergeCell(CellData cellToMerge) + { + // Initialize index offsets + int evBeginsOffset = evInds.size(); + int veBeginsOffset = veInds.size(); + + int vertexIndexOffset = numVerts; + int elementIndexOffset = numElems; + + // Merge the cell topology information + for (auto i = 0; i < cellToMerge.evInds.size(); ++i) + { + evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); + } + + for (auto i = 1; i < cellToMerge.evBegins.size(); ++i) + { + evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); + } + + for (auto i = 0; i < cellToMerge.veInds.size(); ++i) + { + veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); + } + + for (auto i = 1; i < cellToMerge.veBegins.size(); ++i) + { + veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); + } + + // Merge the vertex positions + for (auto i = 0; i < cellToMerge.vertexPositions.size(); ++i) + { + vertexPositions.push_back(cellToMerge.vertexPositions[i]); + } + + // Merge the vertex volume fractions + for (auto matID = 0; matID < vertexVolumeFractions.size(); ++matID) + { + for (auto vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + { + vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); + } + } + + // Merge the total number of verts and elems in the resulting cell + numVerts += cellToMerge.numVerts; + numElems += cellToMerge.numElems; + + } + + //-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp new file mode 100644 index 0000000000..d8a66764f7 --- /dev/null +++ b/src/axom/mir/CellData.hpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __CELL_DATA_H__ +#define __CELL_DATA_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" + +#include "MIRMesh.hpp" + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + + class CellData + { + + public: + CellData(); + CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, + std::vector _vertexPositions, std::vector > _vertexVolumeFractions); + ~CellData(); + + void mergeCell(CellData cellToMerge); + + public: + int numVerts; + int numElems; + + // Cell connectivity/topology + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + + std::vector vertexPositions; + std::vector materialsInCell; // TODO: This is not currently being used. + std::vector > vertexVolumeFractions; + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp new file mode 100644 index 0000000000..1f9f17a9fb --- /dev/null +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -0,0 +1,489 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "InterfaceReconstructor.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /// Default constructor + InterfaceReconstructor::InterfaceReconstructor() + { + + } + +//-------------------------------------------------------------------------------- + + /// Constructor + InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) + { + mesh = _mesh; + } + +//-------------------------------------------------------------------------------- + + /// Destructor + InterfaceReconstructor::~InterfaceReconstructor() + { + + } + +//-------------------------------------------------------------------------------- + + /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. + /// TODO: This method needs to place the computed cell vertices into the intermediate mesh (pass this in as an argument) + void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + { + // Find the vertices associated with each element + auto elementVertices = mesh->bdry[eID]; + + // Check if one of the two currently considered materials is not present at the vertices of the current element + // if (mesh->materialVolumeFractionsElement[matOneID][eID] != 0.0 && mesh->materialVolumeFractionsElement[matTwoID][eID] != 0.0) // TODO: Figure out how to best handle this case + // { + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); // Compute how the cell should be decomposed into new cells + } + // } + // else + // { + // printf("No cuts in element %d.\n", eID); + + // TODO: Add the current cells vertices and data back into the mesh + + // } + } + +//-------------------------------------------------------------------------------- + + /// Computes the points where the triangle element should be clipped based on the volume fractions of mat one and two. + void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + { + printf("Triangle clipping case not yet implemented.\n"); + } + +//-------------------------------------------------------------------------------- + + /// Computes how the given cell should be decomposed into new cells baed on teh vertex volume fractions of matOne and matTwo. + void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, + std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts) + { + printf("Processing element %d: ", eID); + + /**************************************************************** + * DETERMINE THE CLIPPING CASE + ****************************************************************/ + // Get the vertices of the current element to clip + auto elementVertices = tempMesh->bdry[eID]; + + // Determine which vertices correspond to upper left, lower left, lower right, and upper right vertices of the quad. + int upperLeftVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + int upperRightVertex = elementVertices[3]; + + // Determine the dominant color at each vertex + int upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + int lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + int lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + int upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperLeftColor == matOneID) caseIndex |= 8; + if (lowerLeftColor == matOneID) caseIndex |= 4; + if (lowerRightColor == matOneID) caseIndex |= 2; + if (upperRightColor == matOneID) caseIndex |= 1; + + printf("caseIndex: %d\n", caseIndex); + + /**************************************************************** + * GENERATE NEW ELEMENTS + ****************************************************************/ + // Cell information indices + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped + std::map > vertexVolumeFractionsMap; // index: map[vID][matID] = vertexVolumeFractionValues[numMaterials] | Note: maps are ordered sets + + int currentElementIndex = 0; // the next available element index + + // Create the new polygons based on the clipping case + int i = 0; + int numVertices = quadClipTable[caseIndex][i]; + while (numVertices != -1) // for each new element in the current clipping case + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = quadClipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; + + // Find t using "bilinear" interpolation method for any vertex that is not one of the original 4 vertices + if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); // these might be the wrong vertex indices + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 6) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 7) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = quadClipTable[caseIndex][i]; + } + + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_numElements[0] = (int) newElements.size(); + out_numVerts[0] = (int) newVertices.size(); + + // Generate the topology of the new elements (evInds, evBegins, etc) + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_evBegins.push_back(currentEVBeginIndex); + + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } + + // Push the index that occurs after the last vertex + out_evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_veBegins.push_back(currentVEBeginIndex); + + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_veBegins.push_back(currentVEBeginIndex); + + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (verticesPresent[vID] == 1) + { + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 6) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + if (vID == 7) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + } + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_vertexPositions.push_back(itr->second); + } + + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_newVolumeFractionsAtVerts.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + // Ensure that the current vertex being considered is actually present in the current clipping case + if (verticesPresent[vID] == 1) + { + // vertexVolumeFractionsMap[vID].resize(tempMesh->numMaterials); + + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + if (vID == 1) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + if (vID == 4) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 6) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + if (vID == 7) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + } + } + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + int evIndexSubtract[8]; + for (int i = 0; i < 8; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + for (int i = 1; i < 8; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; + + for (unsigned int i = 0; i < out_evInds.size(); ++i) + { + out_evInds[i] -= evIndexSubtract[ out_evInds[i] ]; + } + + + + /**************************************************************** + * DEBUG PRINTING + ****************************************************************/ + { + // TESTING: Print out the values by which the evIndex values need to be substracted + // in order to ensure there are no gaps in the vertex ids in the resulting mesh. + // printf(" evIndexSubtract: { "); + // for (int i = 0; i < 8; i++) + // { + // printf("%d ", evIndexSubtract[i]); + // } + // printf("}\n"); + + // // TESTING: Print out the basic cell information + // printf(" out_numElements: %d\n", out_numElements[0]); + // printf(" out_numVerts: %d\n", out_numVerts[0]); + + // // TESTING: Print out the mesh topology information + // printf(" out_evInds: {"); + // for (int i = 0; i < out_evInds.size(); i++) + // { + // printf("%d ", out_evInds[i]); + // } + // printf("}\n"); + + // printf(" out_evBegins: {"); + // for (int i = 0; i < out_evBegins.size(); i++) + // { + // printf("%d ", out_evBegins[i]); + // } + // printf("}\n"); + + // printf(" out_veInds: {"); + // for (int i = 0; i < out_veInds.size(); i++) + // { + // printf("%d ", out_veInds[i]); + // } + // printf("}\n"); + + // printf(" out_veBegins: {"); + // for (int i = 0; i < out_veBegins.size(); i++) + // { + // printf("%d ", out_veBegins[i]); + // } + // printf("}\n"); + + // // TESTING: Print out the positions of the vertices + // for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + // { + // printf(" Vertex %d: (%f, %f)\n", itr->first, itr->second.m_x, itr->second.m_y); + // } + + // TESTING: Print out the volume fractions at the vertices from the map + // for (auto itr = vertexVolumeFractionsMap.begin(); itr != vertexVolumeFractionsMap.end(); itr++) + // { + // int vID = itr->first; + // printf(" Vertex %d Volume Fractions:\n", vID); + // for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + // { + // printf(" Mat %d: %f\n", matID, vertexVolumeFractionsMap[vID][matID]); + // } + // } + + // TESTING: Print out the volume fractions at the vertices from the vector of materials containing a vector of vertex volume fraction values + // for (int matID = 0; matID < out_newVolumeFractionsAtVerts.size(); ++matID) + // { + // printf(" Material %d:\n", matID); + // for (int vID = 0; vID < out_newVolumeFractionsAtVerts[matID].size(); ++vID) + // { + // printf(" Vertex %d: %f\n", vID, out_newVolumeFractionsAtVerts[matID][vID]); + // } + // } + } + } + +//-------------------------------------------------------------------------------- + +/// Performs linear interpolation between the two given vertex positions. +mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t) +{ + mir::Point2 interpolatedPoint; + interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; + interpolatedPoint.m_y = (1 - t) * vertexOnePos.m_y + t * vertexTwoPos.m_y; + return interpolatedPoint; +} + +//-------------------------------------------------------------------------------- + +/// Performs linear interpolation between the two given float values +axom::float64 InterfaceReconstructor::lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t) +{ + return (1 - t) * f0 + t * f1; +} + +//-------------------------------------------------------------------------------- + +/// Computes the t value as a percent from vertexOne to vertexTwo based on the two materials given. +/// The t value is the place where this edge should be clipped based on the two materials currently being considered. +axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh) +{ + axom::float64 ret = 0.0; + + axom::float64 numerator = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - mesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; + axom::float64 denominator = -mesh->materialVolumeFractionsVertex[matOneID][vertexOneID] + + mesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] + + mesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] + - mesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + + if (denominator != 0.0) + ret = numerator / denominator; + + return ret; +} + +//-------------------------------------------------------------------------------- + +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +{ + // Initialize the final mesh to be the same as the input mesh + mir::MIRMesh finalMesh(mesh); // TODO: This might just copy the reference to the original mesh, and not copy it. Could cause bugs when it gets overwritten. + + // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) + for (int matID = 0; matID < mesh->numMaterials - 1; ++matID) + { + // Copy the mesh to be split + mir::MIRMesh intermediateMesh(&finalMesh); + + // Update the materials upon which the split will occur + int matOne = matID; + int matTwo = matID + 1; + + // Process/split each element + std::vector temp_evInds[intermediateMesh.elems.size()]; // Store the vertex indices adjacent to each element for each cell + std::vector temp_evBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_evBewgins for each element-vertex for each cell + std::vector temp_veInds[intermediateMesh.elems.size()]; // Store the element indices adjacent to each vertex for each cell + std::vector temp_veBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_veInds for each vertex-element for each cell + + std::vector temp_vertexPositions[intermediateMesh.elems.size()]; // Store the positions of the vertices generated for each cell + std::vector materialInCell[intermediateMesh.elems.size()]; // Note: after splitting, the cells should all be clean and thus will have a vf of 1.0 or 0.0 for all materials + + int temp_numVerts[intermediateMesh.elems.size()]; // Store the number of vertices generated for each cell + int temp_numElements[intermediateMesh.elems.size()]; // Store the number of elements generated for each cell + + std::vector > temp_volumeFractionsVertex[intermediateMesh.elems.size()]; + + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + computeClippingPoints(eID, matOne, matTwo, &intermediateMesh, + temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], + materialInCell[eID], &(temp_numVerts[eID]), &(temp_numElements[eID]), temp_volumeFractionsVertex[eID]); + } + + // Copy the generated cell information into CellData structures + std::vector cellSplitInfo; + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + CellData nextCellSplitInfo(temp_numVerts[eID], temp_numElements[eID], temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], temp_volumeFractionsVertex[eID]); + cellSplitInfo.push_back(nextCellSplitInfo); + } + + // Merge each of the cells into the first CellData struct + for (unsigned int eID = 1; eID < cellSplitInfo.size(); ++eID) + cellSplitInfo[0].mergeCell(cellSplitInfo[eID]); + + mir::VertSet combined_verts(cellSplitInfo[0].numVerts); + mir::ElemSet combined_elems(cellSplitInfo[0].numElems); + + // TODO (later): Calculate the element volume fractions (note, they should all be either 0 or 1, since after the splits every cell should be clean) + + // Create the final, processed mesh + mir::MIRMesh processedMesh; + processedMesh.InitializeMesh(cellSplitInfo[0].evInds, cellSplitInfo[0].evBegins, cellSplitInfo[0].veInds, cellSplitInfo[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); + processedMesh.constructMeshRelations(); + processedMesh.constructMeshVolumeFractionsVertex(cellSplitInfo[0].vertexVolumeFractions); + processedMesh.constructVertexPositionMap(cellSplitInfo[0].vertexPositions.data()); + + // Store the current mesh to be passed into the next iteration + finalMesh = processedMesh; + } + + // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon + + return finalMesh; +} + +//-------------------------------------------------------------------------------- + +} +} diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp new file mode 100644 index 0000000000..ddb37b205a --- /dev/null +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -0,0 +1,64 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __INTERFACE_RECONSTRUCTOR_H__ +#define __INTERFACE_RECONSTRUCTOR_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" + +#include "MIRMesh.hpp" +#include "CellData.hpp" +#include "ZooBitMaps.hpp" +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + class InterfaceReconstructor + { + public: + InterfaceReconstructor(); + InterfaceReconstructor(mir::MIRMesh* _mesh); + ~InterfaceReconstructor(); + + mir::MIRMesh computeReconstructedInterface(); + + + // std::vector computeVolumeFractionAverages(mir::MIRMesh* tempMesh); + + private: + mir::MIRMesh* mesh; + + public: + std::vector materialVolumeFractionsVertex; // volume fractions for each material for each vertex + + private: + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); + + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); + + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, + std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts); + + mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); + axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); + axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + + void mergeCellSplitData(); + + }; +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp new file mode 100644 index 0000000000..89da4fc522 --- /dev/null +++ b/src/axom/mir/MIRMesh.cpp @@ -0,0 +1,318 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "MIRMesh.hpp" + +namespace axom +{ +namespace mir +{ + MIRMesh::MIRMesh() + { + + } + + MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor + { + evInds = _mesh->evInds; + evBegins = _mesh->evBegins; + veInds = _mesh->veInds; + veBegins = _mesh->veBegins; + verts = _mesh->verts; + elems = _mesh->elems; + bdry = _mesh->bdry; + cobdry = _mesh->cobdry; + vertexPositions = _mesh->vertexPositions; + materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; + materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + numMaterials = _mesh->numMaterials; + } + + MIRMesh::~MIRMesh() + { + + } + + /// Initializes a mesh with the given topology. + void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) + { + evInds = _evInds; + evBegins = _evBegins; + veInds = _veInds; + veBegins = _veBegins; + verts = _verts; + elems = _elems; + numMaterials = _numMaterials; + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + } + +//-------------------------------------------------------------------------------- + + /// Constructs the mesh boundary and coboundary relations + void MIRMesh::constructMeshRelations() + { + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( evInds.size() ) + .data( evInds.data() ) ); + } + + + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( veInds.size() ) + .data( veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end + } + + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + } + +//-------------------------------------------------------------------------------- + + /// Constructs the volume fraction maps on the elements and vertices given element volume fraction data. + void MIRMesh::constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData) + { + // Initialize the maps for all of the materials with the input volume fraction data for each material + for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + { + // Initialize the map for the current material + materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + + // Copy the data for the current material + for (int eID = 0; eID < elems.size(); ++eID) + { + materialVolumeFractionsElement[matID][eID] = materialVolumeFractionsData[matID][eID]; + } + + SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + } + + // Initialize the maps for all of the vertex volume fractions + for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + { + // Initialize the new map for the volume fractions + materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + + // Calculate the average volume fraction value for the current vertex for the current material + for (int vID = 0; vID < verts.size(); ++vID) + { + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; + + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += materialVolumeFractionsElement[matID][eID]; + } + + materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + } + } + +//-------------------------------------------------------------------------------- + +/// Construct the vertex volume fraction data given vertex volume fraction data +void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > vertexVF) +{ + // Initialize the maps for all of the materials with the input volume fraction data for each vertex + for (int matID = 0; matID < numMaterials; ++matID) + { + // Initialize the map for the current material + materialVolumeFractionsVertex.push_back(ScalarMap( &verts )); + + // Copy the data for the current material + for (int vID = 0; vID < verts.size(); ++vID) + { + materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; + } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + } +} + +//-------------------------------------------------------------------------------- + + /// Constucts the positions map on the vertices + void MIRMesh::constructVertexPositionMap(Point2* data) + { + // construct the position map on the vertices + vertexPositions = PointMap( &verts ); + + for (int vID = 0; vID < verts.size(); ++vID) + vertexPositions[vID] = data[vID]; + + SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + } + +//-------------------------------------------------------------------------------- + + /// Prints out the map values for each element + void MIRMesh::printElementScalarMap(ScalarMap& elements, std::string prefix) + { + std::cout << prefix; + for (int eID = 0; eID < elems.size(); ++eID) + { + printf("Element %d: %f\n", eID, elements[eID]); + } + } + +//-------------------------------------------------------------------------------- + + /// Prints out the map values for each vertex + void MIRMesh::printVertexScalarMap(ScalarMap& vertices, std::string prefix) + { + std::cout << prefix; + for (int vID = 0; vID < verts.size(); ++vID) + { + printf("Vertex %d: %f\n", vID, vertices[vID]); + } + } + +//-------------------------------------------------------------------------------- + + void MIRMesh::print() + { + printf("------------------------Printing Mesh Information:------------------------\n"); + printf("number of vertices: %d\n", verts.size()); + printf("number of elements: %d\n", elems.size()); + printf("number of materials: %d\n", numMaterials); + + printf("evInds: { "); + for (int i = 0; i < evInds.size(); i++) + { + printf("%d ", evInds[i]); + } + printf("}\n"); + + printf("evBegins: { "); + for (int i = 0; i < evBegins.size(); i++) + { + printf("%d ", evBegins[i]); + } + printf("}\n"); + + printf("veInds: { "); + for (int i = 0; i < veInds.size(); i++) + { + printf("%d ", veInds[i]); + } + printf("}\n"); + + printf("veBegins: { "); + for (int i = 0; i < veBegins.size(); i++) + { + printf("%d ", veBegins[i]); + } + printf("}\n"); + + printf("vertexPositions: { "); + for (int i = 0; i < vertexPositions.size(); ++i) + { + printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + } + + printf("}\n"); + printf("--------------------------------------------------------------------------\n"); + } + +//-------------------------------------------------------------------------------- + + /// Reads in a constructs a mesh from the given file + void MIRMesh::readMeshFromFile() + { + printf("Mesh writing functionality not implemented yet."); + + } + +//-------------------------------------------------------------------------------- + + /// Writes out the mesh to a file + void MIRMesh::writeMeshToFile(std::string filename) + { + std::ofstream meshfile; + meshfile.open(filename); + std::ostream_iterator out_it(meshfile, " "); + + // write header + meshfile << "# vtk DataFile Version 3.0\n" + << "vtk output\n" + << "ASCII\n" + << "DATASET UNSTRUCTURED_GRID\n\n" + << "POINTS " << verts.size() << " double\n"; + + // write positions + for (int vID = 0; vID < verts.size(); ++vID) + { + meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + } + + // // write elem-to-vert boundary relation + // meshfile << "\nCELLS " << elems.size() << " " << 5 * elems.size(); // TODO: This will not always be 5 + // for(auto e: elems) + // { + // meshfile<<"\n4 "; // TODO: This will not always be 4 + // std::copy ( bdry.begin(e), bdry.end(e), out_it ); + // } + + // // write element types ( 9 == VTK_QUAD, 5 == VTK_TRIANGLE ) + // meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; + // for(int i=0 ; i< elems.size() ; ++i) + // { + // meshfile << "9 "; // TODO: This will not always be 9, but will be 5 for any triangle elements + // } + + // // write element ids + // meshfile << "\n\nCELL_DATA " << elems.size() + // << "\nSCALARS cellIds int 1" + // << "\nLOOKUP_TABLE default \n"; + // for(int i=0 ; i< elems.size() ; ++i) + // { + // meshfile << elems[i] <<" "; + // } + + // // write vertex ids + // meshfile << "\n\nPOINT_DATA " << verts.size() + // << "\nSCALARS vertIds int 1" + // << "\nLOOKUP_TABLE default \n"; + // for(int i=0 ; i< verts.size() ; ++i) + // { + // meshfile << verts[i] <<" "; + // } + meshfile <<"\n"; + } + +//-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp new file mode 100644 index 0000000000..696967840d --- /dev/null +++ b/src/axom/mir/MIRMesh.hpp @@ -0,0 +1,100 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MIR_MESH_H__ +#define __MIR_MESH_H__ + + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMeshTypes.hpp" + +// C/C++ includes +#include // for definition of M_PI, exp() +#include +#include +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + + +//-------------------------------------------------------------------------------- +namespace axom +{ +namespace mir +{ + struct EdgeClipInfo + { + int vertexOne; + int vertexTwo; + int colorOne; + int colorTwo; + float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. + }; + + //-------------------------------------------------------------------------------- + + class MIRMesh + { + /**************************************************************** + * MESH FUNCTIONS + ****************************************************************/ + public: + MIRMesh(); + MIRMesh(MIRMesh* _mesh); // copy constructor + ~MIRMesh(); + + void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); + + void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations + void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map + void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + + void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element + void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex + + void print(); // Print out a variety of useful information about the mesh + + void readMeshFromFile(); /// Reads in and constructs a mesh from a file + void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file + void attachVertexMap(); /// Adds a map of data to the mesh + + /**************************************************************** + * VARIABLES + ****************************************************************/ + public: + // support data for mesh connectivity + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + + public: + // Mesh Set Definitions + VertSet verts; // the set of vertices in the mesh + ElemSet elems; // the set of elements in the mesh + + public: + // Mesh Relation Definitions + ElemToVertRelation bdry; // Boundary relation from elements to vertices + VertToElemRelation cobdry; // Coboundary relation from vertices to elements + + public: + // Mesh Map Definitions + PointMap vertexPositions; // vertex position + std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element + std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + + public: + int numMaterials; + }; + + //-------------------------------------------------------------------------------- +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp new file mode 100644 index 0000000000..d90ecbeb35 --- /dev/null +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -0,0 +1,74 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MIR_MESH_TYPES_H__ +#define __MIR_MESH_TYPES_H__ + + +#include "axom/core.hpp" // for axom macros +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + /** + * \brief Simple 2D Point class for example + */ + struct Point2 + { + Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} + + Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} + + Point2& operator=(const Point2& other) + { m_x = other.m_x; m_y = other.m_y; return *this; } + + Point2& operator+=(const Point2& other) + { m_x += other.m_x; m_y += other.m_y; return *this; } + + Point2& operator/=(double val) + { m_x /= val; m_y += val; return *this; } + + double& operator[] (int i) { return (i==0) ? m_x : m_y; } + const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } + + friend std::ostream& operator<<(std::ostream& os, const Point2& pt) + { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } + + double m_x, m_y; + }; + + // SET TYPE ALIASES + using PosType = slam::DefaultPositionType; + using ElemType = slam::DefaultElementType; + + using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + + using VertSet = slam::PositionSet< PosType, ElemType >; + using ElemSet = slam::PositionSet< PosType, ElemType >; + + // RELATION TYPE ALIASES + using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; + + // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. + // Note: It is the relation of the elements to the vertices. + + using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; + using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; + + // MAP TYPE ALIASES + using BaseSet = slam::Set< PosType, ElemType >; + using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using PointMap = slam::Map< BaseSet, Point2 >; + +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooBitMaps.cpp new file mode 100644 index 0000000000..d2adf217a3 --- /dev/null +++ b/src/axom/mir/ZooBitMaps.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "ZooBitMaps.hpp" + +namespace axom +{ +namespace mir +{ + + // Quad Vertex Indices + // + // 0 7 3 + // @---------@---------@ + // | | + // | | + // | | + // 4 @ @ 6 + // | | + // | | + // | | + // @---------@---------@ + // 1 5 2 + // + const int quadClipTable[16][19] = + { + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} + }; +} +} \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooBitMaps.hpp new file mode 100644 index 0000000000..09f76ede1e --- /dev/null +++ b/src/axom/mir/ZooBitMaps.hpp @@ -0,0 +1,17 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __ZOO_BIT_MAPS_H__ +#define __ZOO_BIT_MAPS_H__ + + +namespace axom +{ +namespace mir +{ + extern const int quadClipTable[16][19]; +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index b199beae56..605609bf02 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -5,6 +5,7 @@ set( mir_examples mir_tutorial.cpp + mir_tutorial_simple.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp index 7dbcb69a0d..576da4d49d 100644 --- a/src/axom/mir/examples/mir_tutorial.cpp +++ b/src/axom/mir/examples/mir_tutorial.cpp @@ -4,16 +4,437 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros -#include "axom/mir.hpp" // for Mir classes & functions +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" // C/C++ includes #include // for definition of M_PI, exp() +#include // namespace aliases //namespace mir = axom::mir; namespace numerics = axom::numerics; +namespace slam = axom::slam; + +#define EPSILON_ZERO = 0.00000000001f; + +//-------------------------------------------------------------------------------- + +/** + * \brief Simple 2D Point class for example + */ +struct Point2 +{ + Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} + + Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} + + Point2& operator=(const Point2& other) + { m_x = other.m_x; m_y = other.m_y; return *this; } + + Point2& operator+=(const Point2& other) + { m_x += other.m_x; m_y += other.m_y; return *this; } + + Point2& operator/=(double val) + { m_x /= val; m_y += val; return *this; } + + double& operator[] (int i) { return (i==0) ? m_x : m_y; } + const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } + + friend std::ostream& operator<<(std::ostream& os, const Point2& pt) + { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } + + double m_x, m_y; +}; + +//-------------------------------------------------------------------------------- + +struct EdgeClipInfo +{ + int vertexOne; + int vertexTwo; + int colorOne; + int colorTwo; + float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. +}; + +//-------------------------------------------------------------------------------- + +struct MIRMesh +{ + /**************************************************************** + * TYPE ALIASES + ****************************************************************/ + // SET TYPE ALIASES + using PosType = slam::DefaultPositionType; + using ElemType = slam::DefaultElementType; + + using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + + using VertSet = slam::PositionSet< PosType, ElemType >; + using ElemSet = slam::PositionSet< PosType, ElemType >; + + // RELATION TYPE ALIASES + using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; + + // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. + // Note: It is the relation of the elements to the vertices. + using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; + using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; + + // MAP TYPE ALIASES + using BaseSet = slam::Set< PosType, ElemType >; + using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using PointMap = slam::Map< BaseSet, Point2 >; + + enum { GREEN = 0, BLUE = 1 }; + + /**************************************************************** + * MESH FUNCTIONS + ****************************************************************/ + void InitializeMesh() + { + // Create the mesh connectivity information + // data for element-vertex boundary relation + evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + + // data for vertex-element coboundary relation + veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + // Initialize the mesh sets + verts = VertSet(16); // Construct a vertex set with 11 vertices + elems = ElemSet(9); // Construct an element set with 5 elements + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + + printf("Mesh has been initialized.\n"); + } + + /// Constructs the mesh boundary and coboundary relations + void constructMeshRelations() + { + + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( evInds.size() ) + .data( evInds.data() ) ); + } + + + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( veInds.size() ) + .data( veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end + } + + + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + + printf("Finished constructing mesh relations.\n"); + } + + /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionMaps() + { + // TODO: Determine better way to store volume fraction data than 1 map per material? + // Construct the scalar map on the elements for the green material + greenVolumeFractionsElements = ScalarMap( &elems ); + + // Set the green volume fraction for each element + greenVolumeFractionsElements[0] = 1.0; + greenVolumeFractionsElements[1] = 1.0; + greenVolumeFractionsElements[2] = 1.0; + greenVolumeFractionsElements[3] = 1.0; + greenVolumeFractionsElements[4] = 0.5; + greenVolumeFractionsElements[5] = 0.2; + greenVolumeFractionsElements[6] = 0.2; + greenVolumeFractionsElements[7] = 0.0; + greenVolumeFractionsElements[8] = 0.0; + + // Construct the scalar map on the elements for the blue material + blueVolumeFractionsElements = ScalarMap( &elems ); + + blueVolumeFractionsElements[0] = 0.0; + blueVolumeFractionsElements[1] = 0.0; + blueVolumeFractionsElements[2] = 0.0; + blueVolumeFractionsElements[3] = 0.0; + blueVolumeFractionsElements[4] = 0.5; + blueVolumeFractionsElements[5] = 0.8; + blueVolumeFractionsElements[6] = 0.8; + blueVolumeFractionsElements[7] = 1.0; + blueVolumeFractionsElements[8] = 1.0; + + printf("Finished constructing volume fractions for mesh elements.\n"); + } + + /// Constucts the positions map on the vertices + void constructVertexPositionMap() + { + // construct the position map on the vertices + vertexPositions = PointMap( &verts ); + + // first vertex is at origin + vertexPositions[0] = Point2( 0.0, 3.0 ); + vertexPositions[1] = Point2( 1.0, 3.0 ); + vertexPositions[2] = Point2( 2.0, 3.0 ); + vertexPositions[3] = Point2( 3.0, 3.0 ); + + vertexPositions[4] = Point2( 0.0, 2.0 ); + vertexPositions[5] = Point2( 1.0, 2.0 ); + vertexPositions[6] = Point2( 2.0, 2.0 ); + vertexPositions[7] = Point2( 3.0, 2.0 ); + + vertexPositions[8] = Point2( 0.0, 1.0 ); + vertexPositions[9] = Point2( 1.0, 1.0 ); + vertexPositions[10] = Point2( 2.0, 1.0 ); + vertexPositions[11] = Point2( 3.0, 1.0 ); + + vertexPositions[12] = Point2( 0.0, 0.0 ); + vertexPositions[13] = Point2( 1.0, 0.0 ); + vertexPositions[14] = Point2( 2.0, 0.0 ); + vertexPositions[15] = Point2( 3.0, 0.0 ); + + SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + + SLIC_INFO("-- Vertex positions:"); + for(int vID=0 ; vID < verts.size() ; ++vID) + { + SLIC_INFO("Position of vert " << vID << " is " << vertexPositions[vID]); + } + } + + // Computes the average volume fraction at each vertex of the mesh + void computeVolumeFractionAverages() + { + // Initialize the vertex volume fraction maps + greenVolumeFractionsVertices = ScalarMap( &verts ); + blueVolumeFractionsVertices = ScalarMap( &verts ); + + for (int vID = 0; vID < verts.size(); ++vID) + { + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += greenVolumeFractionsElements[eID]; + } + greenVolumeFractionsVertices[vID] = sum / vertexElements.size(); + + // Compute the per vertex volume fractions for the blue material + sum = 0; + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += blueVolumeFractionsElements[eID]; + } + blueVolumeFractionsVertices[vID] = sum / vertexElements.size(); + } + + printf("Finished computing volume fraction averages.\n"); + } + + /// Prints out the map values for each element + void printElementScalarMap(ScalarMap& elements, std::string prefix) + { + std::cout << prefix; + for (int i = 0; i < elems.size(); ++i) + { + printf("Element %d: %f\n", i, elements[i]); + } + } + + /// Prints out the map values for each vertex + void printVertexScalarMap(ScalarMap& vertices, std::string prefix) + { + std::cout << prefix; + for (int i = 0; i < verts.size(); ++i) + { + printf("Vertex %d: %f\n", i, vertices[i]); + } + } + + /// Use bilinear interpolation to compute the points where the given element should be clipped. + /// Returns true if clip should occur, otherwise false. + /// If a clip should occur, the clipping points will be given in p1 and p2. + void computeClippingPoints(const int eID)//, Point2* p1, Point2* p2) + { + // Find the vertices associated with each element + auto elementVertices = bdry[eID]; + + // Check if one of the two currently considered materials is not present at the vertices of the current element + if (blueVolumeFractionsElements[eID] != 0.0 && greenVolumeFractionsElements[eID] != 0.0) + { + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID); + } + } + else + { + printf("No cuts in element %d.\n", eID); + } + } + + /// Computes the points where the given quad element should be clipped + void computeQuadClippingPoints(int eID) + { + auto elementVertices = bdry[eID]; + EdgeClipInfo eci[4]; + + // Initialize the edge clipping info + for (int i = 0; i < 4; ++i) + { + eci[i].vertexOne = elementVertices[ i ]; + eci[i].vertexTwo = elementVertices[ (i + 1) % 4 ]; + + if (blueVolumeFractionsVertices[eci[i].vertexOne] > greenVolumeFractionsVertices[eci[i].vertexOne]) + eci[i].colorOne = BLUE; + else + eci[i].colorOne = GREEN; + + if (blueVolumeFractionsVertices[eci[i].vertexTwo] > greenVolumeFractionsVertices[eci[i].vertexTwo]) + eci[i].colorTwo = BLUE; + else + eci[i].colorTwo = GREEN; + + eci[i].t = -1.0; // default t value means one material dominates the other and no clip should occur on this edge + } + + // Analyze the edge clipping info and determine where to clip the cell + for (int i = 0; i < 4; ++i) + { + if (eci[i].colorOne != eci[i].colorTwo) + { + axom::float64 numerator = blueVolumeFractionsVertices[eci[i].vertexOne] - greenVolumeFractionsVertices[eci[i].vertexOne]; + axom::float64 denominator = -greenVolumeFractionsVertices[eci[i].vertexOne] + + greenVolumeFractionsVertices[eci[i].vertexTwo] + + blueVolumeFractionsVertices[eci[i].vertexOne] + - blueVolumeFractionsVertices[eci[i].vertexTwo]; + + if (denominator != 0.0) + eci[i].t = numerator / denominator; + } + } + + // Print out the cutting information + bool cut = false; + for (int i = 0; i < 4; ++i) + { + if (eci[i].t > 0.0) + { + printf("Cutting element %d between vertices %d and %d. t = %f\n", eID, eci[i].vertexOne, eci[i].vertexTwo, eci[i].t); + cut = true; + } + } + if (!cut) + printf("No cuts in element %d.\n", eID); + + } + + /// Computes the points where the given triangle element should be clipped + void computeTriangleClippingPoints(int eID) + { + printf("Triangle clipping case not yet implemented.\n"); + } + + /**************************************************************** + * VARIABLES + ****************************************************************/ + public: + // Mesh Set Definitions + VertSet verts; // the set of vertices in the mesh + ElemSet elems; // the set of elements in the mesh + + // Mesh Relation Definitions + ElemToVertRelation bdry; // Boundary relation from elements to vertices + VertToElemRelation cobdry; // Coboundary relation from vertices to elements + + // Mesh Map Definitions + PointMap vertexPositions; // vertex position + ScalarMap greenVolumeFractionsElements; // the volume fractions of the green material for each element (NOT each vertex) + ScalarMap blueVolumeFractionsElements; // the volume fractions of the blue material for each element (NOT each vertex) + + ScalarMap greenVolumeFractionsVertices; + ScalarMap blueVolumeFractionsVertices; + + // support data for mesh connectivity + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; +}; + + +//-------------------------------------------------------------------------------- + /*! * \file @@ -32,11 +453,28 @@ namespace numerics = axom::numerics; */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { + printf("Beginning the MIR Tutorial program.\n"); - std::cout << "hello from the mir_tutorial!" << std::endl; + MIRMesh testMesh; + testMesh.InitializeMesh(); + testMesh.constructMeshRelations(); + // testMesh.constructMeshPositionMap(); + testMesh.constructMeshVolumeFractionMaps(); + testMesh.computeVolumeFractionAverages(); - return 0; -} + // Print volume fraction debugging information + // testMesh.printElementScalarMap(testMesh.greenVolumeFractionsElements, "Green Volume Fractions per Element: \n"); + // testMesh.printElementScalarMap(testMesh.blueVolumeFractionsElements, "Blue Volume Fractions per Element: \n"); + // testMesh.printVertexScalarMap(testMesh.greenVolumeFractionsVertices, "Green Volume Fractions per Vertex: \n"); + // testMesh.printVertexScalarMap(testMesh.blueVolumeFractionsVertices, "Blue Volume Fractions per Vertex: \n"); + // Determine where to clip the cells + for (int i = 0; i < 9; ++i) + testMesh.computeClippingPoints(i); + + + return 0; +} +//-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp new file mode 100644 index 0000000000..23bfc09769 --- /dev/null +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + +#include "../MIRMesh.hpp" +#include "../InterfaceReconstructor.hpp" + +// namespace aliases +//namespace mir = axom::mir; +namespace numerics = axom::numerics; +namespace slam = axom::slam; +namespace mir = axom::mir; + +//-------------------------------------------------------------------------------- + +/*! + * \brief Tutorial main + */ +int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 greenVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + materialVolumeFractionsData[GREEN] = greenVolumeFractions; + axom::float64 blueVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + + // Begin material interface reconstruction + mir::InterfaceReconstructor reconstructor(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); + processedMesh.print(); + // processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/markoTestMesh.vtk"); + + return 0; +} + +//-------------------------------------------------------------------------------- + From d1083e01e92f1f9921a3998db495574de4b1c759 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Mon, 10 Jun 2019 15:24:26 -0700 Subject: [PATCH 006/290] Debugged element combination code. Implemented dominant color tracking. Added support for null material. Implemented parent element tracking. Added post-processing to ensure generated elements will always contain an acceptable dominant material. Implemented basic vtk file writing. --- src/axom/mir/CellData.cpp | 12 + src/axom/mir/CellData.hpp | 24 +- src/axom/mir/InterfaceReconstructor.cpp | 571 +++++++++--------- src/axom/mir/InterfaceReconstructor.hpp | 34 +- src/axom/mir/MIRMesh.cpp | 186 ++++-- src/axom/mir/MIRMesh.hpp | 27 +- src/axom/mir/MIRMeshTypes.hpp | 2 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 8 +- 8 files changed, 478 insertions(+), 386 deletions(-) diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index bfeee02462..df4da4d785 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -86,6 +86,18 @@ namespace mir vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); } } + + // Merge the elements' dominant materials + for (auto i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + { + elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); + } + + // Merge the elements' parent ids + for (auto i = 0; i < cellToMerge.elementParents.size(); ++i) + { + elementParents.push_back(cellToMerge.elementParents[i]); + } // Merge the total number of verts and elems in the resulting cell numVerts += cellToMerge.numVerts; diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index d8a66764f7..d91c9761ec 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -9,7 +9,7 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" -#include "MIRMesh.hpp" +#include "MIRMeshTypes.hpp" namespace numerics = axom::numerics; namespace slam = axom::slam; @@ -19,6 +19,8 @@ namespace axom namespace mir { + /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). + /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData { @@ -40,9 +42,23 @@ namespace mir std::vector veInds; std::vector veBegins; - std::vector vertexPositions; - std::vector materialsInCell; // TODO: This is not currently being used. - std::vector > vertexVolumeFractions; + std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + }; + + class CellTopology + { + public: + CellTopology(); + ~CellTopology(); + + public: + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; }; } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 1f9f17a9fb..0160709fde 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -23,7 +23,7 @@ namespace mir /// Constructor InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) { - mesh = _mesh; + originalMesh = _mesh; } //-------------------------------------------------------------------------------- @@ -36,101 +36,111 @@ namespace mir //-------------------------------------------------------------------------------- - /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. - /// TODO: This method needs to place the computed cell vertices into the intermediate mesh (pass this in as an argument) - void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) +/// Reconstructs the material interface and returns the resulting output mesh. +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +{ + // Initialize the final mesh to be the same as the input mesh + mir::MIRMesh finalMesh(originalMesh); + + // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) + for (int matID = 0; matID < originalMesh->numMaterials; ++matID) { - // Find the vertices associated with each element - auto elementVertices = mesh->bdry[eID]; + // Copy the mesh to be split + mir::MIRMesh intermediateMesh(&finalMesh); - // Check if one of the two currently considered materials is not present at the vertices of the current element - // if (mesh->materialVolumeFractionsElement[matOneID][eID] != 0.0 && mesh->materialVolumeFractionsElement[matTwoID][eID] != 0.0) // TODO: Figure out how to best handle this case - // { - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); // Compute how the cell should be decomposed into new cells - } - // } - // else - // { - // printf("No cuts in element %d.\n", eID); + // Update the materials upon which the split will occur + int matOne = matID; + + // Create an array to store the output of each element being split. + CellData temp_cellData[intermediateMesh.elems.size()]; + + // Process/split each element + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) + int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; + printf("Processing materials: %d and %d for Element %d\n", currentDominantMat, matOne, eID); + computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); + } - // TODO: Add the current cells vertices and data back into the mesh + // Merge each of the cells into the first CellData struct + for (unsigned int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + temp_cellData[0].mergeCell(temp_cellData[eID]); + + mir::VertSet combined_verts(temp_cellData[0].numVerts); + mir::ElemSet combined_elems(temp_cellData[0].numElems); - // } + // Create the final, processed mesh + mir::MIRMesh processedMesh; + processedMesh.InitializeMesh(temp_cellData[0].evInds, temp_cellData[0].evBegins, temp_cellData[0].veInds, temp_cellData[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); + processedMesh.constructMeshRelations(); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].vertexVolumeFractions); + processedMesh.constructVertexPositionMap(temp_cellData[0].vertexPositions.data()); + processedMesh.constructElementParentMap(temp_cellData[0].elementParents.data()); + processedMesh.constructElementDominantMaterialMap(temp_cellData[0].elementDominantMaterials); + + // Store the current mesh to be passed into the next iteration + finalMesh = processedMesh; + finalMesh.constructMeshRelations(); // TODO: Figure out why this is necessary here... + + printf("-------------Finished processing material-------------\n"); } + // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon + + return finalMesh; +} + //-------------------------------------------------------------------------------- - /// Computes the points where the triangle element should be clipped based on the volume fractions of mat one and two. - void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. + void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf("Triangle clipping case not yet implemented.\n"); + // Find the vertices associated with each element + auto elementVertices = tempMesh->bdry[eID]; + + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } } //-------------------------------------------------------------------------------- - /// Computes how the given cell should be decomposed into new cells baed on teh vertex volume fractions of matOne and matTwo. - void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, - std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts) + void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf("Processing element %d: ", eID); + printf(" Processing element %d: ", eID); - /**************************************************************** - * DETERMINE THE CLIPPING CASE - ****************************************************************/ - // Get the vertices of the current element to clip + // Determine the clipping case auto elementVertices = tempMesh->bdry[eID]; - // Determine which vertices correspond to upper left, lower left, lower right, and upper right vertices of the quad. int upperLeftVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; int lowerRightVertex = elementVertices[2]; int upperRightVertex = elementVertices[3]; - // Determine the dominant color at each vertex - int upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - int lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - int lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - int upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; - - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperLeftColor == matOneID) caseIndex |= 8; - if (lowerLeftColor == matOneID) caseIndex |= 4; - if (lowerRightColor == matOneID) caseIndex |= 2; - if (upperRightColor == matOneID) caseIndex |= 1; - + unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); printf("caseIndex: %d\n", caseIndex); - /**************************************************************** - * GENERATE NEW ELEMENTS - ****************************************************************/ - // Cell information indices - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped - std::map > vertexVolumeFractionsMap; // index: map[vID][matID] = vertexVolumeFractionValues[numMaterials] | Note: maps are ordered sets - - int currentElementIndex = 0; // the next available element index // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index int i = 0; int numVertices = quadClipTable[caseIndex][i]; - while (numVertices != -1) // for each new element in the current clipping case + + // for each new element in the current clipping case + while (numVertices != -1) { // for each vertex of the new element for (int j = 0; j < numVertices; ++j) @@ -143,10 +153,10 @@ namespace mir newVertices[vID].push_back(currentElementIndex); verticesPresent[vID] = 1; - // Find t using "bilinear" interpolation method for any vertex that is not one of the original 4 vertices + // Find t using linear interpolation for any vertex that is not one of the original 4 vertices if(vID == 4) { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); // these might be the wrong vertex indices + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); } else if(vID == 5) { @@ -174,111 +184,60 @@ namespace mir * CALCULATE THE NEW CELLS' DATA ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_numElements[0] = (int) newElements.size(); - out_numVerts[0] = (int) newVertices.size(); + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); - // Generate the topology of the new elements (evInds, evBegins, etc) - // Store the evInds and evBegins data in the output vectors - int currentEVBeginIndex = 0; + generateTopologyDataFromQuad(newElements, newVertices, out_cellData); + generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + + // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - // Push the start index of the next element - out_evBegins.push_back(currentEVBeginIndex); - - // Push the next element's vertices - for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) { - out_evInds.push_back(itr->second[vIndex]); - ++currentEVBeginIndex; - } - } - - // Push the index that occurs after the last vertex - out_evBegins.push_back(currentEVBeginIndex); + // int temp_vID = newElements[currentElementIndex][it]; + int temp_vID = itr->second[it]; - // Store the veInds and veBegins data in the output vectors - int currentVEBeginIndex = 0; - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - // Push the start index of the vertex's elements - out_veBegins.push_back(currentVEBeginIndex); + // Find the vertex that is one of the four original vertices of the quad element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) + { + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; - // Push the next vertex's elements - for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) - { - out_veInds.push_back(itr->second[eIndex]); - ++currentVEBeginIndex; + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; + } } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); } - - // Push the index that occurs after the last element - out_veBegins.push_back(currentVEBeginIndex); - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - if (verticesPresent[vID] == 1) - { - if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; - if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); - if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); - } - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_vertexPositions.push_back(itr->second); + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_newVolumeFractionsAtVerts.resize(tempMesh->numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int vID = itr->first; - // Ensure that the current vertex being considered is actually present in the current clipping case - if (verticesPresent[vID] == 1) - { - // vertexVolumeFractionsMap[vID].resize(tempMesh->numMaterials); + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) - { - if (vID == 0) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); - if (vID == 1) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); - if (vID == 4) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 6) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); - if (vID == 7) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); - } + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + { + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; + else + out_cellData.elementDominantMaterials[itr->first] = matOneID; } } - + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present int evIndexSubtract[8]; for (int i = 0; i < 8; ++i) @@ -291,87 +250,41 @@ namespace mir for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) { - out_evInds[i] -= evIndexSubtract[ out_evInds[i] ]; + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; } + } +//-------------------------------------------------------------------------------- +/// Finds the bit map representing the clipping case for a quad. +unsigned int InterfaceReconstructor::determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +{ + // Determine the dominant color at each vertex + int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; - /**************************************************************** - * DEBUG PRINTING - ****************************************************************/ + if (matOneID == NULL_MAT) + upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matTwoID; + if (matTwoID == NULL_MAT) + upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; + if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - // TESTING: Print out the values by which the evIndex values need to be substracted - // in order to ensure there are no gaps in the vertex ids in the resulting mesh. - // printf(" evIndexSubtract: { "); - // for (int i = 0; i < 8; i++) - // { - // printf("%d ", evIndexSubtract[i]); - // } - // printf("}\n"); - - // // TESTING: Print out the basic cell information - // printf(" out_numElements: %d\n", out_numElements[0]); - // printf(" out_numVerts: %d\n", out_numVerts[0]); - - // // TESTING: Print out the mesh topology information - // printf(" out_evInds: {"); - // for (int i = 0; i < out_evInds.size(); i++) - // { - // printf("%d ", out_evInds[i]); - // } - // printf("}\n"); - - // printf(" out_evBegins: {"); - // for (int i = 0; i < out_evBegins.size(); i++) - // { - // printf("%d ", out_evBegins[i]); - // } - // printf("}\n"); - - // printf(" out_veInds: {"); - // for (int i = 0; i < out_veInds.size(); i++) - // { - // printf("%d ", out_veInds[i]); - // } - // printf("}\n"); - - // printf(" out_veBegins: {"); - // for (int i = 0; i < out_veBegins.size(); i++) - // { - // printf("%d ", out_veBegins[i]); - // } - // printf("}\n"); - - // // TESTING: Print out the positions of the vertices - // for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - // { - // printf(" Vertex %d: (%f, %f)\n", itr->first, itr->second.m_x, itr->second.m_y); - // } - - // TESTING: Print out the volume fractions at the vertices from the map - // for (auto itr = vertexVolumeFractionsMap.begin(); itr != vertexVolumeFractionsMap.end(); itr++) - // { - // int vID = itr->first; - // printf(" Vertex %d Volume Fractions:\n", vID); - // for (int matID = 0; matID < tempMesh->numMaterials; ++matID) - // { - // printf(" Mat %d: %f\n", matID, vertexVolumeFractionsMap[vID][matID]); - // } - // } - - // TESTING: Print out the volume fractions at the vertices from the vector of materials containing a vector of vertex volume fraction values - // for (int matID = 0; matID < out_newVolumeFractionsAtVerts.size(); ++matID) - // { - // printf(" Material %d:\n", matID); - // for (int vID = 0; vID < out_newVolumeFractionsAtVerts[matID].size(); ++vID) - // { - // printf(" Vertex %d: %f\n", vID, out_newVolumeFractionsAtVerts[matID][vID]); - // } - // } + upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; } - } + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperLeftColor == matOneID) caseIndex |= 8; + if (lowerLeftColor == matOneID) caseIndex |= 4; + if (lowerRightColor == matOneID) caseIndex |= 2; + if (upperRightColor == matOneID) caseIndex |= 1; + + return caseIndex; +} //-------------------------------------------------------------------------------- @@ -400,87 +313,165 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte { axom::float64 ret = 0.0; - axom::float64 numerator = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - mesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; - axom::float64 denominator = -mesh->materialVolumeFractionsVertex[matOneID][vertexOneID] - + mesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] - + mesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - - mesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + axom::float64 vfMatOneVertexOne = tempMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; // Changed these all from originalMesh->materialVolumeFractionsVertex[][] to tempMesh->... + axom::float64 vfMatTwoVertexOne = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID]; + axom::float64 vfMatOneVertexTwo = tempMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID]; + axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + + if (matOneID == NULL_MAT) + { + vfMatOneVertexOne = 0.0; + vfMatOneVertexTwo = 0.0; + } + + if (matTwoID == NULL_MAT) + { + vfMatTwoVertexOne = 0.0; + vfMatTwoVertexTwo = 0.0; + } + + axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; + axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; + + // axom::float64 numerator = originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; + // axom::float64 denominator = -originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID] + // + originalMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] + // + originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] + // - originalMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (denominator != 0.0) ret = numerator / denominator; + if (ret > 1.0 || ret < 0.0) + { + // This shouldn't happen... + printf("OUT OF BOUNDS T VALUE: %f\n", ret); + + // Clamp the t value + ret = fmin(1.0, ret); + ret = fmax(0.0, ret); + } + return ret; } //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from splitting a quad. +void InterfaceReconstructor::generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData) { - // Initialize the final mesh to be the same as the input mesh - mir::MIRMesh finalMesh(mesh); // TODO: This might just copy the reference to the original mesh, and not copy it. Could cause bugs when it gets overwritten. + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_cellData.evBegins.push_back(currentEVBeginIndex); - // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) - for (int matID = 0; matID < mesh->numMaterials - 1; ++matID) - { - // Copy the mesh to be split - mir::MIRMesh intermediateMesh(&finalMesh); + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_cellData.evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } - // Update the materials upon which the split will occur - int matOne = matID; - int matTwo = matID + 1; + // Push the index that occurs after the last vertex + out_cellData.evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_cellData.veBegins.push_back(currentVEBeginIndex); - // Process/split each element - std::vector temp_evInds[intermediateMesh.elems.size()]; // Store the vertex indices adjacent to each element for each cell - std::vector temp_evBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_evBewgins for each element-vertex for each cell - std::vector temp_veInds[intermediateMesh.elems.size()]; // Store the element indices adjacent to each vertex for each cell - std::vector temp_veBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_veInds for each vertex-element for each cell + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_cellData.veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_cellData.veBegins.push_back(currentVEBeginIndex); +} - std::vector temp_vertexPositions[intermediateMesh.elems.size()]; // Store the positions of the vertices generated for each cell - std::vector materialInCell[intermediateMesh.elems.size()]; // Note: after splitting, the cells should all be clean and thus will have a vf of 1.0 or 0.0 for all materials +//-------------------------------------------------------------------------------- - int temp_numVerts[intermediateMesh.elems.size()]; // Store the number of vertices generated for each cell - int temp_numElements[intermediateMesh.elems.size()]; // Store the number of elements generated for each cell +/// Generate the vertex position data for the new elements resulting from spltting a quad. +void InterfaceReconstructor::generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +{ + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets - std::vector > temp_volumeFractionsVertex[intermediateMesh.elems.size()]; + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 6) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + if (vID == 7) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_cellData.vertexPositions.push_back(itr->second); + } +} - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) - { - computeClippingPoints(eID, matOne, matTwo, &intermediateMesh, - temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], - materialInCell[eID], &(temp_numVerts[eID]), &(temp_numElements[eID]), temp_volumeFractionsVertex[eID]); - } +//-------------------------------------------------------------------------------- - // Copy the generated cell information into CellData structures - std::vector cellSplitInfo; - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) +/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. +void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +{ + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { - CellData nextCellSplitInfo(temp_numVerts[eID], temp_numElements[eID], temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], temp_volumeFractionsVertex[eID]); - cellSplitInfo.push_back(nextCellSplitInfo); + int vID = itr->first; + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + if (vID == 1) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + if (vID == 4) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 6) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + if (vID == 7) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + } } +} - // Merge each of the cells into the first CellData struct - for (unsigned int eID = 1; eID < cellSplitInfo.size(); ++eID) - cellSplitInfo[0].mergeCell(cellSplitInfo[eID]); - - mir::VertSet combined_verts(cellSplitInfo[0].numVerts); - mir::ElemSet combined_elems(cellSplitInfo[0].numElems); - - // TODO (later): Calculate the element volume fractions (note, they should all be either 0 or 1, since after the splits every cell should be clean) - - // Create the final, processed mesh - mir::MIRMesh processedMesh; - processedMesh.InitializeMesh(cellSplitInfo[0].evInds, cellSplitInfo[0].evBegins, cellSplitInfo[0].veInds, cellSplitInfo[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); - processedMesh.constructMeshRelations(); - processedMesh.constructMeshVolumeFractionsVertex(cellSplitInfo[0].vertexVolumeFractions); - processedMesh.constructVertexPositionMap(cellSplitInfo[0].vertexPositions.data()); - - // Store the current mesh to be passed into the next iteration - finalMesh = processedMesh; - } - - // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon +//-------------------------------------------------------------------------------- - return finalMesh; +void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + printf("Triangle clipping case not yet implemented.\n"); } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index ddb37b205a..7f620f4405 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -30,34 +30,30 @@ namespace mir mir::MIRMesh computeReconstructedInterface(); - - // std::vector computeVolumeFractionAverages(mir::MIRMesh* tempMesh); - private: - mir::MIRMesh* mesh; - - public: - std::vector materialVolumeFractionsVertex; // volume fractions for each material for each vertex + mir::MIRMesh* originalMesh; private: - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); - - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, - std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts); + // general clipping function + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + + // triangle clipping function + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + // quad clipping functions + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + + // quad clipping interpolation functions mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); - void mergeCellSplitData(); - + // quad clipping points helper functions + unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); + void generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData); + void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 89da4fc522..11dcc49e84 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -16,10 +16,10 @@ namespace mir MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor { - evInds = _mesh->evInds; - evBegins = _mesh->evBegins; - veInds = _mesh->veInds; - veBegins = _mesh->veBegins; + data.evInds = _mesh->data.evInds; + data.evBegins = _mesh->data.evBegins; + data.veInds = _mesh->data.veInds; + data.veBegins = _mesh->data.veBegins; verts = _mesh->verts; elems = _mesh->elems; bdry = _mesh->bdry; @@ -27,6 +27,8 @@ namespace mir vertexPositions = _mesh->vertexPositions; materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + elementParentIDs = _mesh->elementParentIDs; + elementDominantMaterials = _mesh->elementDominantMaterials; numMaterials = _mesh->numMaterials; } @@ -38,10 +40,10 @@ namespace mir /// Initializes a mesh with the given topology. void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) { - evInds = _evInds; - evBegins = _evBegins; - veInds = _veInds; - veBegins = _veBegins; + data.evInds = _evInds; + data.evBegins = _evBegins; + data.veInds = _veInds; + data.veBegins = _veBegins; verts = _verts; elems = _elems; numMaterials = _numMaterials; @@ -64,10 +66,10 @@ namespace mir .toSet( &verts ) .begins( RelationBuilder::BeginsSetBuilder() .size( elems.size() ) - .data( evBegins.data() ) ) + .data( data.evBegins.data() ) ) .indices ( RelationBuilder::IndicesSetBuilder() - .size( evInds.size() ) - .data( evInds.data() ) ); + .size( data.evInds.size() ) + .data( data.evInds.data() ) ); } @@ -80,10 +82,10 @@ namespace mir .toSet( &elems ) .begins( RelationBuilder::BeginsSetBuilder() .size( verts.size() ) - .data( veBegins.data() ) ) + .data( data.veBegins.data() ) ) .indices( RelationBuilder::IndicesSetBuilder() - .size( veInds.size() ) - .data( veInds.data() ) ); + .size( data.veInds.size() ) + .data( data.veInds.data() ) ); // _quadmesh_example_construct_cobdry_relation_end } @@ -175,6 +177,36 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector dominantMaterials) + { + // Initialize the map for the elements' dominant colors + elementDominantMaterials = IntMap( &elems ); + + // Copy the dat for the elements + for (int eID = 0; eID < elems.size(); ++eID) + elementDominantMaterials[eID] = dominantMaterials[eID]; + + SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); + } + //-------------------------------------------------------------------------------- /// Prints out the map values for each element @@ -203,56 +235,91 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector // for definition of M_PI, exp() @@ -21,12 +22,14 @@ namespace numerics = axom::numerics; namespace slam = axom::slam; - //-------------------------------------------------------------------------------- namespace axom { namespace mir { + + #define NULL_MAT -1 + struct EdgeClipInfo { int vertexOne; @@ -54,26 +57,21 @@ namespace mir void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent + void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex void print(); // Print out a variety of useful information about the mesh - void readMeshFromFile(); /// Reads in and constructs a mesh from a file + void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh /**************************************************************** * VARIABLES ****************************************************************/ - public: - // support data for mesh connectivity - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - public: // Mesh Set Definitions VertSet verts; // the set of vertices in the mesh @@ -82,16 +80,23 @@ namespace mir public: // Mesh Relation Definitions ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements + VertToElemRelation cobdry; // Coboundary relation from vertices to elements public: // Mesh Map Definitions - PointMap vertexPositions; // vertex position + PointMap vertexPositions; // vertex position for each vertex std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex public: int numMaterials; + + public: + IntMap elementParentIDs; // the ID of the parent element from the original mesh + IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + + public: + CellData data; // Contains mesh connectivity data, volume fraction data, vertex position data }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index d90ecbeb35..e861385932 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -68,7 +68,7 @@ namespace mir using BaseSet = slam::Set< PosType, ElemType >; using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. using PointMap = slam::Map< BaseSet, Point2 >; - + using IntMap = slam::Map< BaseSet, int >; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 23bfc09769..ae04cbf93f 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -102,18 +102,24 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) points[15] = mir::Point2( 3.0, 0.0 ); } + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + // Build the mesh mir::MIRMesh testMesh; testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); testMesh.constructMeshRelations(); testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); // Begin material interface reconstruction mir::InterfaceReconstructor reconstructor(&testMesh); mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); processedMesh.print(); - // processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/markoTestMesh.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); return 0; } From 001fe8b9cf397d8b4c5353a8e81af4c277408810 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 14 Jun 2019 08:48:20 -0700 Subject: [PATCH 007/290] Implemented triangle clipping functionality. --- src/axom/mir/InterfaceReconstructor.cpp | 545 ++++++++++++------ src/axom/mir/InterfaceReconstructor.hpp | 7 +- src/axom/mir/MIRMesh.cpp | 2 +- src/axom/mir/MIRMeshTypes.hpp | 2 +- src/axom/mir/ZooBitMaps.cpp | 27 +- src/axom/mir/ZooBitMaps.hpp | 1 + src/axom/mir/examples/mir_tutorial_simple.cpp | 208 ++++++- 7 files changed, 619 insertions(+), 173 deletions(-) diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 0160709fde..2984a870d2 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -12,27 +12,27 @@ namespace mir //-------------------------------------------------------------------------------- - /// Default constructor - InterfaceReconstructor::InterfaceReconstructor() - { - - } +/// Default constructor +InterfaceReconstructor::InterfaceReconstructor() +{ + +} //-------------------------------------------------------------------------------- - /// Constructor - InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) - { - originalMesh = _mesh; - } +/// Constructor +InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) +{ + originalMesh = _mesh; +} //-------------------------------------------------------------------------------- - /// Destructor - InterfaceReconstructor::~InterfaceReconstructor() - { +/// Destructor +InterfaceReconstructor::~InterfaceReconstructor() +{ - } +} //-------------------------------------------------------------------------------- @@ -81,9 +81,14 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; - finalMesh.constructMeshRelations(); // TODO: Figure out why this is necessary here... + finalMesh.constructMeshRelations(); printf("-------------Finished processing material-------------\n"); + + // Write out the intermediate meshes that are processed + std::string outFilename = "/Users/sterbentz3/Desktop/intermediateMesh" + std::to_string(matID) + ".vtk"; + finalMesh.writeMeshToFile(outFilename); + // finalMesh.print(); } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon @@ -93,168 +98,168 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() //-------------------------------------------------------------------------------- - /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. - void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) - { - // Find the vertices associated with each element - auto elementVertices = tempMesh->bdry[eID]; +/// Computes the points where the element should be clipped based on the volume fractions of mat one and two. +void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + // Find the vertices associated with each element + auto elementVertices = tempMesh->bdry[eID]; - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); } +} //-------------------------------------------------------------------------------- - void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + printf(" Processing QUAD element %d: ", eID); + + // Determine the clipping case + auto elementVertices = tempMesh->bdry[eID]; + + int upperLeftVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + int upperRightVertex = elementVertices[3]; + + unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); + printf("caseIndex: %d\n", caseIndex); + + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + int verticesPresent[8] = {0,0,0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[8] = {0,0,0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = quadClipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) { - printf(" Processing element %d: ", eID); - - // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; - - int upperLeftVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; - int upperRightVertex = elementVertices[3]; - - unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - printf("caseIndex: %d\n", caseIndex); + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = quadClipTable[caseIndex][i + (j+1)]; - // Generate new elements - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = quadClipTable[caseIndex][i]; - - // for each new element in the current clipping case - while (numVertices != -1) - { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) + // Find t using linear interpolation for any vertex that is not one of the original 4 vertices + if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 6) { - // Find the id of the next vertex of the new element - int vID = quadClipTable[caseIndex][i + (j+1)]; - - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; - - // Find t using linear interpolation for any vertex that is not one of the original 4 vertices - if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 6) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 7) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); - } + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); } + else if(vID == 7) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); + } + } - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = quadClipTable[caseIndex][i]; - } + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = quadClipTable[caseIndex][i]; + } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); - generateTopologyDataFromQuad(newElements, newVertices, out_cellData); - generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateTopologyData(newElements, newVertices, out_cellData); + generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + // Determine and store the dominant material of this element + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) { - int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + // int temp_vID = newElements[currentElementIndex][it]; + int temp_vID = itr->second[it]; + + // Find the vertex that is one of the four original vertices of the quad element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) { - // int temp_vID = newElements[currentElementIndex][it]; - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the four original vertices of the quad element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); + } - // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; - } + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + } - // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) - { - // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; - else - out_cellData.elementDominantMaterials[itr->first] = matOneID; - } - } - - // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present - int evIndexSubtract[8]; - for (int i = 0; i < 8; ++i) + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { - if (verticesPresent[i] == 1) - evIndexSubtract[i] = 0; + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; else - evIndexSubtract[i] = 1; + out_cellData.elementDominantMaterials[itr->first] = matOneID; } - for (int i = 1; i < 8; ++i) - evIndexSubtract[i] += evIndexSubtract[i - 1]; + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + int evIndexSubtract[8]; + for (int i = 0; i < 8; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + for (int i = 1; i < 8; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) - { - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; - } + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + { + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; } +} //-------------------------------------------------------------------------------- @@ -319,16 +324,10 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (matOneID == NULL_MAT) - { - vfMatOneVertexOne = 0.0; - vfMatOneVertexTwo = 0.0; - } + vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; if (matTwoID == NULL_MAT) - { - vfMatTwoVertexOne = 0.0; - vfMatTwoVertexTwo = 0.0; - } + vfMatTwoVertexOne = vfMatTwoVertexTwo = 0.0; axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; @@ -345,7 +344,7 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte if (ret > 1.0 || ret < 0.0) { // This shouldn't happen... - printf("OUT OF BOUNDS T VALUE: %f\n", ret); + printf(" OUT OF BOUNDS T VALUE: %f\n", ret); // Clamp the t value ret = fmin(1.0, ret); @@ -357,8 +356,8 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte //-------------------------------------------------------------------------------- -/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from splitting a quad. -void InterfaceReconstructor::generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData) +/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from a split. +void InterfaceReconstructor::generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData) { // Store the evInds and evBegins data in the output vectors int currentEVBeginIndex = 0; @@ -471,10 +470,232 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapbdry[eID]; + + int upperVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + + unsigned int caseIndex = determineTriangleClippingCase(eID, tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); + printf("caseIndex: %d\n", caseIndex); + + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + int verticesPresent[6] = {0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[6] = {0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = triangleClipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = triangleClipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; + + // Find t using linear interpolation for any vertex that is not one of the original 3 vertices + if(vID == 3) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperVertex, matOneID, matTwoID, tempMesh); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = triangleClipTable[caseIndex][i]; + } + + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); + + generateTopologyData(newElements, newVertices, out_cellData); + generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + generateVertexVolumeFractionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + + // Determine and store the dominant material of this element + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) + { + int temp_vID = itr->second[it]; + + // Find the vertex that is one of the three original vertices of the triangle element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2) + { + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; + } + } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); + } + + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + } + + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + { + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; + else + out_cellData.elementDominantMaterials[itr->first] = matOneID; + } + } + + // Modify the evIndex values to account for the fact that perhaps not all 6 possible vertices are present + int evIndexSubtract[6]; + for (int i = 0; i < 6; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + + for (int i = 1; i < 6; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; + + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + } //-------------------------------------------------------------------------------- +/// Finds the bit map representing the clipping case for a triangle. +unsigned int InterfaceReconstructor::determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +{ + // Determine the dominant color at each vertex + int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; + + if (matOneID == NULL_MAT) + upperColor = lowerLeftColor = lowerRightColor = matTwoID; + if (matTwoID == NULL_MAT) + upperColor = lowerLeftColor = lowerRightColor = matOneID; + if (matOneID != NULL_MAT && matTwoID != NULL_MAT) + { + upperColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + } + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperColor == matOneID) caseIndex |= 4; + if (lowerLeftColor == matOneID) caseIndex |= 2; + if (lowerRightColor == matOneID) caseIndex |= 1; + + return caseIndex; } + +//-------------------------------------------------------------------------------- + +/// Generate the vertex position data for the new elements resulting from spltting a quad. +void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +{ + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperVertex], verticesClippingTValue[vID]); + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_cellData.vertexPositions.push_back(itr->second); + } +} + +//-------------------------------------------------------------------------------- + +/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. +void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +{ + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + if (vID == 1) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 4) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + } + } +} + +//-------------------------------------------------------------------------------- + } +} \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 7f620f4405..b5e80817db 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -51,9 +51,14 @@ namespace mir // quad clipping points helper functions unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData); + void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + + // triangle clipping points helper functions + unsigned int determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); + void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); + void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 11dcc49e84..a9036ba54e 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -276,7 +276,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using ScalarMap = slam::Map< BaseSet, axom::float64 >; using PointMap = slam::Map< BaseSet, Point2 >; using IntMap = slam::Map< BaseSet, int >; } diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooBitMaps.cpp index d2adf217a3..3904fd525c 100644 --- a/src/axom/mir/ZooBitMaps.cpp +++ b/src/axom/mir/ZooBitMaps.cpp @@ -30,18 +30,41 @@ namespace mir {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} }; + + + // Triangle Vertex Indices + // 0 + // @ + // / \ + // / \ + // 3 @ @ 5 + // / \ + // / \ + // 1 @-----@-----@ 2 + // 4 + const int triangleClipTable[8][10] = + { + {3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {3,0,1,2,-1,-1,-1,-1,-1,-1} + }; } } \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooBitMaps.hpp index 09f76ede1e..e765ced325 100644 --- a/src/axom/mir/ZooBitMaps.hpp +++ b/src/axom/mir/ZooBitMaps.hpp @@ -12,6 +12,7 @@ namespace axom namespace mir { extern const int quadClipTable[16][19]; + extern const int triangleClipTable[8][10]; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ae04cbf93f..ab1b17b252 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -16,12 +16,38 @@ namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; +// function templates +mir::MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials +mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials +mir::MIRMesh initTestCaseThree(); // triforce, 2 materials + //-------------------------------------------------------------------------------- /*! * \brief Tutorial main */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + // Intialize a mesh for testing MIR + // mir::MIRMesh testMesh = initTestCaseOne(); + mir::MIRMesh testMesh = initTestCaseTwo(); + // mir::MIRMesh testMesh = initTestCaseThree(); + + // Begin material interface reconstruction + mir::InterfaceReconstructor reconstructor(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); + + // Output results + processedMesh.print(); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + + return 0; +} + +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 1 (from Meredith 2004) +mir::MIRMesh initTestCaseOne() { int numElements = 9; int numVertices = 16; @@ -115,13 +141,183 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) testMesh.constructElementParentMap(elementParents); testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - // Begin material interface reconstruction - mir::InterfaceReconstructor reconstructor(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); - processedMesh.print(); - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + return testMesh; +} - return 0; +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) +mir::MIRMesh initTestCaseTwo() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 3; + enum { BLUE = 2, RED = 0, ORANGE = 1 }; // TODO: the order orange, blue red causes bugs... might be fixed now... was due to incorrect clipping table entries + // TODO: orange, blue, red is fine + // TODO: blue, red, orange is fine + // TODO: red, orange, blue is fine + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 blueVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + axom::float64 redVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + materialVolumeFractionsData[RED] = redVolumeFractions; + axom::float64 orangeVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; + materialVolumeFractionsData[ORANGE] = orangeVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. +mir::MIRMesh initTestCaseThree() +{ + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + // Create the mesh connectivity information + std::vector evInds = { + 0,1,2, // elem 0, card 3, start 0 + 1,3,4, // elem 1, card 3, start 3 + 1,4,2, // elem 2, card 3, start 6 + 2,4,5 // elem 3, card 3, start 9, end 12 + }; + + std::vector evBegins = { + 0,3,6,9,12 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1,2, // vert 1, card 3, start 1 + 0,2,3, // vert 2, card 3, start 4 + 1, // vert 3, card 1, start 7 + 1,2,3, // vert 4, card 3, start 8 + 3 // vert 5, card 1, start 11, end 12 + }; + std::vector veBegins = { + 0,1,4,7,8,11,12 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements + + int numMaterials = 2; + enum { BLUE = 0, RED = 1, }; + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 blueVolumeFractions[] = {0.0, 0.5, 0.8, 0.5}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + axom::float64 redVolumeFractions[] = {1.0, 0.5, 0.2, 0.5}; + materialVolumeFractionsData[RED] = redVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 1.0, 2.0 ); + points[1] = mir::Point2( 0.5, 1.0 ); + points[2] = mir::Point2( 1.5, 1.0 ); + points[3] = mir::Point2( 0.0, 0.0 ); + points[4] = mir::Point2( 1.0, 0.0 ); + points[5] = mir::Point2( 2.0, 0.0 ); + } + + + int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; } //-------------------------------------------------------------------------------- From d79e4ee780065b7190e500e20ca12d640d3c0001 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 14 Jun 2019 11:39:51 -0700 Subject: [PATCH 008/290] Implemented a function for calculating the element vertex fractions for the original mesh cells using the generated mesh. --- src/axom/mir/MIRMesh.cpp | 83 +++++++++++++++++++ src/axom/mir/MIRMesh.hpp | 7 ++ src/axom/mir/examples/mir_tutorial_simple.cpp | 7 +- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index a9036ba54e..b851348720 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -380,5 +380,88 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector totalAreaOriginalElements; // the total area of the original elements + std::map newElementAreas; // the area of each of the generated child elements + std::map numChildren; // the number of child elements generated from one of the original elements + + // Compute the total area of each element of the original mesh and also the area of each new element + for (int eID = 0; eID < elems.size(); ++eID) + { + int numVertices = data.evBegins[eID + 1] - data.evBegins[eID]; + if (numVertices == 3) + { + Point2 trianglePoints[3]; + for (int i = 0; i < 3; ++i) + trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); + } + if (numVertices == 4) + { + Point2 trianglePoints[4]; + for (int i = 0; i < 4; ++i) + trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); + } + + totalAreaOriginalElements[ elementParentIDs[eID] ] += newElementAreas[eID]; + numChildren[ elementParentIDs[eID] ] += .4; + } + + // Intialize the element volume fraction vectors + std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction + elementVolumeFractions.resize( numMaterials ); + for (int i = 0; i < elementVolumeFractions.size(); ++i) + elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); + + // Compute the volume fractions for each of the original mesh elements + for (auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) + { + int parentElementID = elementParentIDs[itr->first]; + int materialID = elementDominantMaterials[itr->first]; + elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); + } + + // Print out the results // TODO: Return the values and use them. + printf("elementVolumeFractions: {\n"); + for (int matID = 0; matID < elementVolumeFractions.size(); ++matID) + { + printf("Material %d: {", matID); + for (int eID = 0; eID < elementVolumeFractions[matID].size(); ++eID) + { + printf(" %f,", elementVolumeFractions[matID][eID]); + } + printf("}\n"); + } + printf("}\n"); +} + +//-------------------------------------------------------------------------------- + +/// Computes the area of the triangle defined by the given three vertex positions. Uses Heron's formula. +axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) +{ + axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 + axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 + axom::float64 c = sqrt( ((p0.m_x - p2.m_x) * (p0.m_x - p2.m_x)) + ((p0.m_y - p2.m_y) * (p0.m_y - p2.m_y)) );// the distance from p2 to p0 + + axom::float64 s = (a + b + c) / 2; // the semi-perimeter of the triangle + + return sqrt(s * (s - a) * (s - b) * (s - c)); +} + +//-------------------------------------------------------------------------------- + +/// Computes the area of the quad defined by the given four vertex positions. +/// Note: It is assumed the points are given in consecutive order. +axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) +{ + return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); +} + +//-------------------------------------------------------------------------------- + } } \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 8a16272d9b..2145ce3172 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -69,6 +69,13 @@ namespace mir void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh + + void computeOriginalElementVolumeFractions(); + + private: + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + /**************************************************************** * VARIABLES ****************************************************************/ diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ab1b17b252..b9e0dc9b7b 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -40,6 +40,7 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) // Output results processedMesh.print(); processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + processedMesh.computeOriginalElementVolumeFractions(); return 0; } @@ -193,12 +194,8 @@ mir::MIRMesh initTestCaseTwo() mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - int numMaterials = 3; - enum { BLUE = 2, RED = 0, ORANGE = 1 }; // TODO: the order orange, blue red causes bugs... might be fixed now... was due to incorrect clipping table entries - // TODO: orange, blue, red is fine - // TODO: blue, red, orange is fine - // TODO: red, orange, blue is fine + enum { BLUE = 2, RED = 0, ORANGE = 1 }; std::vector materialVolumeFractionsData; materialVolumeFractionsData.resize(numMaterials); From bd0cd4d0144711b24d92e2471382caefdf01942a Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 18 Jun 2019 15:00:04 -0700 Subject: [PATCH 009/290] Created a mesh tester class and generated addtional test cases. Implemented the iterative mesh interface reconstruction algorithm. --- src/axom/mir/CMakeLists.txt | 6 +- src/axom/mir/CellData.cpp | 18 +- src/axom/mir/CellData.hpp | 32 +- src/axom/mir/InterfaceReconstructor.cpp | 90 +- src/axom/mir/InterfaceReconstructor.hpp | 10 +- src/axom/mir/MIRMesh.cpp | 124 ++- src/axom/mir/MIRMesh.hpp | 17 +- src/axom/mir/MeshTester.cpp | 771 ++++++++++++++++++ src/axom/mir/MeshTester.hpp | 47 ++ .../{ZooBitMaps.cpp => ZooClippingTables.cpp} | 2 +- .../{ZooBitMaps.hpp => ZooClippingTables.hpp} | 4 +- src/axom/mir/examples/CMakeLists.txt | 2 +- .../mir/examples/mirConcentricCircles.cpp | 70 ++ src/axom/mir/examples/mir_tutorial.cpp | 480 ----------- src/axom/mir/examples/mir_tutorial_simple.cpp | 315 +------ 15 files changed, 1056 insertions(+), 932 deletions(-) create mode 100644 src/axom/mir/MeshTester.cpp create mode 100644 src/axom/mir/MeshTester.hpp rename src/axom/mir/{ZooBitMaps.cpp => ZooClippingTables.cpp} (98%) rename src/axom/mir/{ZooBitMaps.hpp => ZooClippingTables.hpp} (83%) create mode 100644 src/axom/mir/examples/mirConcentricCircles.cpp delete mode 100644 src/axom/mir/examples/mir_tutorial.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index a31de3c473..e1740e6ad4 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -18,17 +18,19 @@ axom_component_requires(NAME MIR set(mir_headers MIRMesh.hpp MIRMeshTypes.hpp - ZooBitMaps.hpp + ZooClippingTables.hpp InterfaceReconstructor.hpp CellData.hpp + MeshTester.hpp ) set(mir_sources ../Axom.cpp MIRMesh.cpp InterfaceReconstructor.cpp - ZooBitMaps.cpp + ZooClippingTables.cpp CellData.cpp + MeshTester.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index df4da4d785..d1cad26aef 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -52,49 +52,49 @@ namespace mir int elementIndexOffset = numElems; // Merge the cell topology information - for (auto i = 0; i < cellToMerge.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.evInds.size(); ++i) { evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); } - for (auto i = 1; i < cellToMerge.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.evBegins.size(); ++i) { evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); } - for (auto i = 0; i < cellToMerge.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.veInds.size(); ++i) { veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); } - for (auto i = 1; i < cellToMerge.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.veBegins.size(); ++i) { veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (auto i = 0; i < cellToMerge.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.vertexPositions.size(); ++i) { vertexPositions.push_back(cellToMerge.vertexPositions[i]); } // Merge the vertex volume fractions - for (auto matID = 0; matID < vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < vertexVolumeFractions.size(); ++matID) { - for (auto vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) { vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (auto i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) { elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); } // Merge the elements' parent ids - for (auto i = 0; i < cellToMerge.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.elementParents.size(); ++i) { elementParents.push_back(cellToMerge.elementParents[i]); } diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index d91c9761ec..88edd9f453 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -19,6 +19,24 @@ namespace axom namespace mir { + // Struct for collecting data that specifies a mesh or cell's connectivity/topology. + struct CellTopologyData + { + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + }; + + // Struct for collecting the data that will populate a mesh's map data structures. + struct CellMapData + { + std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + }; + /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData @@ -47,20 +65,6 @@ namespace mir std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap }; - - class CellTopology - { - public: - CellTopology(); - ~CellTopology(); - - public: - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - }; - } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 2984a870d2..529c26ead4 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -20,14 +20,6 @@ InterfaceReconstructor::InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Constructor -InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) -{ - originalMesh = _mesh; -} - -//-------------------------------------------------------------------------------- - /// Destructor InterfaceReconstructor::~InterfaceReconstructor() { @@ -37,8 +29,11 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- /// Reconstructs the material interface and returns the resulting output mesh. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* inputMesh) { + // Store a pointer to the original mesh + originalMesh = inputMesh; + // Initialize the final mesh to be the same as the input mesh mir::MIRMesh finalMesh(originalMesh); @@ -59,12 +54,11 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() { // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; - printf("Processing materials: %d and %d for Element %d\n", currentDominantMat, matOne, eID); computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct - for (unsigned int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 1; eID < intermediateMesh.elems.size(); ++eID) temp_cellData[0].mergeCell(temp_cellData[eID]); mir::VertSet combined_verts(temp_cellData[0].numVerts); @@ -82,13 +76,6 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; finalMesh.constructMeshRelations(); - - printf("-------------Finished processing material-------------\n"); - - // Write out the intermediate meshes that are processed - std::string outFilename = "/Users/sterbentz3/Desktop/intermediateMesh" + std::to_string(matID) + ".vtk"; - finalMesh.writeMeshToFile(outFilename); - // finalMesh.print(); } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon @@ -96,6 +83,48 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() return finalMesh; } +/// Reconstructs the material interface using an iterative optimization to improve the volume fractions of the resulting mesh. +/// Based on the Meredith and Childs 2010 paper. +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent) +{ + int numElems = inputMesh->elems.size(); + int numMaterials = inputMesh->numMaterials; + + // Make a copy of the original input mesh + mir::MIRMesh meshToImprove(inputMesh); + + // Calculate the reconstruction on the unmodified, input mesh + mir::MIRMesh resultingMesh = computeReconstructedInterface(&meshToImprove); + + for (int it = 0; it < numIterations; ++it) + { + // Calculate the output element volume fractions of the resulting output mesh + std::vector > resultingElementVF = resultingMesh.computeOriginalElementVolumeFractions(); + + // Initialize the vector to store the improved element volume fractions + std::vector > improvedElementVF; + improvedElementVF.resize(numMaterials); + + // Modify the copy of the original mesh's element volume fractions by percent difference (modify meshToImprove's elementVF) + for (int matID = 0; matID < numMaterials; ++matID) + { + for (int eID = 0; eID < numElems; ++eID) + { + axom::float64 difference = inputMesh->materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; + improvedElementVF[matID].push_back( inputMesh->materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); + } + } + + // Reconstruct the element AND vertex VFs + meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); + + // Calculate the reconstruction on the modified, input mesh + resultingMesh = computeReconstructedInterface(&meshToImprove); + } + + return resultingMesh; +} + //-------------------------------------------------------------------------------- /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. @@ -120,7 +149,7 @@ void InterfaceReconstructor::computeClippingPoints(const int eID, const int matO void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf(" Processing QUAD element %d: ", eID); + // printf(" Processing QUAD element %d: ", eID); // Determine the clipping case auto elementVertices = tempMesh->bdry[eID]; @@ -130,8 +159,8 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int int lowerRightVertex = elementVertices[2]; int upperRightVertex = elementVertices[3]; - unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - printf("caseIndex: %d\n", caseIndex); + unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); + // printf("caseIndex: %d\n", caseIndex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -200,7 +229,7 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + for (unsigned long it = 0; it < itr->second.size(); ++it) { // int temp_vID = newElements[currentElementIndex][it]; int temp_vID = itr->second[it]; @@ -264,7 +293,7 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int //-------------------------------------------------------------------------------- /// Finds the bit map representing the clipping case for a quad. -unsigned int InterfaceReconstructor::determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) { // Determine the dominant color at each vertex int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; @@ -331,12 +360,6 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; - - // axom::float64 numerator = originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; - // axom::float64 denominator = -originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID] - // + originalMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] - // + originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - // - originalMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (denominator != 0.0) ret = numerator / denominator; @@ -470,8 +493,6 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapbdry[eID]; @@ -479,8 +500,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int lowerLeftVertex = elementVertices[1]; int lowerRightVertex = elementVertices[2]; - unsigned int caseIndex = determineTriangleClippingCase(eID, tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); - printf("caseIndex: %d\n", caseIndex); + unsigned int caseIndex = determineTriangleClippingCase(tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -545,7 +565,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + for (unsigned long it = 0; it < itr->second.size(); ++it) { int temp_vID = itr->second[it]; @@ -608,7 +628,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const //-------------------------------------------------------------------------------- /// Finds the bit map representing the clipping case for a triangle. -unsigned int InterfaceReconstructor::determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) { // Determine the dominant color at each vertex int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index b5e80817db..8d0f5d3aa8 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -11,7 +11,7 @@ #include "MIRMesh.hpp" #include "CellData.hpp" -#include "ZooBitMaps.hpp" +#include "ZooClippingTables.hpp" #include namespace numerics = axom::numerics; @@ -25,10 +25,10 @@ namespace mir { public: InterfaceReconstructor(); - InterfaceReconstructor(mir::MIRMesh* _mesh); ~InterfaceReconstructor(); - mir::MIRMesh computeReconstructedInterface(); + mir::MIRMesh computeReconstructedInterface(mir::MIRMesh* inputMesh); + mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent); private: mir::MIRMesh* originalMesh; @@ -50,13 +50,13 @@ namespace mir axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); // quad clipping points helper functions - unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); + unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); // triangle clipping points helper functions - unsigned int determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); + unsigned int determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); }; diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index b851348720..b116550666 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -96,51 +96,55 @@ namespace mir SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- + +/// Construct the and the element and vertex volume fraction data given the element volume fraction data +void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) +{ + // Clear the old maps + materialVolumeFractionsElement.clear(); + materialVolumeFractionsVertex.clear(); - /// Constructs the volume fraction maps on the elements and vertices given element volume fraction data. - void MIRMesh::constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData) + // Initialize the maps for all of the materials with the input volume fraction data for each material + for (int matID = 0; matID < numMaterials; ++matID) { - // Initialize the maps for all of the materials with the input volume fraction data for each material - for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + // Initialize the map for the current material + materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + + // Copy the data for the current material + for (int eID = 0; eID < elems.size(); ++eID) { - // Initialize the map for the current material - materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; + } - // Copy the data for the current material - for (int eID = 0; eID < elems.size(); ++eID) - { - materialVolumeFractionsElement[matID][eID] = materialVolumeFractionsData[matID][eID]; - } + SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + } - SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); - } + // Initialize the maps for all of the vertex volume fractions + for (int matID = 0; matID < numMaterials; ++matID) + { + // Initialize the new map for the volume fractions + materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); - // Initialize the maps for all of the vertex volume fractions - for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + // Calculate the average volume fraction value for the current vertex for the current material + for (int vID = 0; vID < verts.size(); ++vID) { - // Initialize the new map for the volume fractions - materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; - // Calculate the average volume fraction value for the current vertex for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int i = 0; i < vertexElements.size(); ++i) { - // Compute the per vertex volume fractions for the green material - axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; - - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += materialVolumeFractionsElement[matID][eID]; - } - - materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + auto eID = vertexElements[i]; + sum += materialVolumeFractionsElement[matID][eID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } +} //-------------------------------------------------------------------------------- @@ -209,30 +213,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > MIRMesh::computeOriginalElementVolumeFractions() { std::map totalAreaOriginalElements; // the total area of the original elements std::map newElementAreas; // the area of each of the generated child elements @@ -413,7 +394,7 @@ void MIRMesh::computeOriginalElementVolumeFractions() // Intialize the element volume fraction vectors std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction elementVolumeFractions.resize( numMaterials ); - for (int i = 0; i < elementVolumeFractions.size(); ++i) + for (unsigned long i = 0; i < elementVolumeFractions.size(); ++i) elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); // Compute the volume fractions for each of the original mesh elements @@ -424,18 +405,7 @@ void MIRMesh::computeOriginalElementVolumeFractions() elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); } - // Print out the results // TODO: Return the values and use them. - printf("elementVolumeFractions: {\n"); - for (int matID = 0; matID < elementVolumeFractions.size(); ++matID) - { - printf("Material %d: {", matID); - for (int eID = 0; eID < elementVolumeFractions[matID].size(); ++eID) - { - printf(" %f,", elementVolumeFractions[matID][eID]); - } - printf("}\n"); - } - printf("}\n"); + return elementVolumeFractions; } //-------------------------------------------------------------------------------- @@ -455,7 +425,7 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- /// Computes the area of the quad defined by the given four vertex positions. -/// Note: It is assumed the points are given in consecutive order. +/// Note: It is assumed the points are given in consecutive, counter-clockwise order. axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 2145ce3172..eb66116345 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -30,15 +30,6 @@ namespace mir #define NULL_MAT -1 - struct EdgeClipInfo - { - int vertexOne; - int vertexTwo; - int colorOne; - int colorTwo; - float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. - }; - //-------------------------------------------------------------------------------- class MIRMesh @@ -54,23 +45,19 @@ namespace mir void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element - void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex - void print(); // Print out a variety of useful information about the mesh void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh - - void computeOriginalElementVolumeFractions(); + std::vector > computeOriginalElementVolumeFractions(); private: axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp new file mode 100644 index 0000000000..36ed346a7a --- /dev/null +++ b/src/axom/mir/MeshTester.cpp @@ -0,0 +1,771 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "MeshTester.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +MeshTester::MeshTester() +{ + +} + +//-------------------------------------------------------------------------------- + +MeshTester::~MeshTester() +{ + +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 1 (from Meredith 2004) +MIRMesh MeshTester::initTestCaseOne() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + elementVF[GREEN] = greenVolumeFractions; + std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + elementVF[BLUE] = blueVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) +mir::MIRMesh MeshTester::initTestCaseTwo() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + int numMaterials = 3; + enum { BLUE = 0, RED = 1, ORANGE = 2 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector blueVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + elementVF[BLUE] = blueVolumeFractions; + std::vector redVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + elementVF[RED] = redVolumeFractions; + std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; + elementVF[ORANGE] = orangeVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. +mir::MIRMesh MeshTester::initTestCaseThree() +{ + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + // Create the mesh connectivity information + std::vector evInds = { + 0,1,2, // elem 0, card 3, start 0 + 1,3,4, // elem 1, card 3, start 3 + 1,4,2, // elem 2, card 3, start 6 + 2,4,5 // elem 3, card 3, start 9, end 12 + }; + + std::vector evBegins = { + 0,3,6,9,12 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1,2, // vert 1, card 3, start 1 + 0,2,3, // vert 2, card 3, start 4 + 1, // vert 3, card 1, start 7 + 1,2,3, // vert 4, card 3, start 8 + 3 // vert 5, card 1, start 11, end 12 + }; + std::vector veBegins = { + 0,1,4,7,8,11,12 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements + + int numMaterials = 2; + enum { BLUE = 0, RED = 1, }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector blueVolumeFractions = {0.0, 0.5, 0.8, 0.5}; + elementVF[BLUE] = blueVolumeFractions; + std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; + elementVF[RED] = redVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 1.0, 2.0 ); + points[1] = mir::Point2( 0.5, 1.0 ); + points[2] = mir::Point2( 1.5, 1.0 ); + points[3] = mir::Point2( 0.0, 0.0 ); + points[4] = mir::Point2( 1.0, 0.0 ); + points[5] = mir::Point2( 2.0, 0.0 ); + } + + + int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 4, a 3x3 grid with a circle of one material in the middle +mir::MIRMesh MeshTester::initTestCaseFour() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions; greenVolumeFractions.resize(numElements); + std::vector blueVolumeFractions; blueVolumeFractions.resize(numElements); + + // Generate the element volume fractions for the circle + mir::Point2 circleCenter(1.5, 1.5); + axom::float64 circleRadius = 1.25; + int gridSize = 1000; + for (int i = 0; i < numElements; ++i) + { + greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(gridSize, circleCenter, circleRadius, points[evInds[i * 4 + 0]], points[evInds[i * 4 + 1]], points[evInds[i * 4 + 2]], points[evInds[i * 4 + 3]]); + blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + } + + elementVF[GREEN] = greenVolumeFractions; + elementVF[BLUE] = blueVolumeFractions; + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Intializes a uniform grid with a circle of one material surrounded by another material. +mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) +{ + // Generate the mesh topology + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.numElems); + std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.numElems); + + // Generate the element volume fractions for the circle + int numMonteCarloSamples = 100; + for (int i = 0; i < cellData.numElems; ++i) + { + greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, + cellData.vertexPositions[cellData.evInds[i * 4 + 0]], + cellData.vertexPositions[cellData.evInds[i * 4 + 1]], + cellData.vertexPositions[cellData.evInds[i * 4 + 2]], + cellData.vertexPositions[cellData.evInds[i * 4 + 3]]); + blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + } + + elementVF[GREEN] = greenVolumeFractions; + elementVF[BLUE] = blueVolumeFractions; + + int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.numElems; ++i) + { + elementParents[i] = i; + elementDominantMaterials.push_back(NULL_MAT); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; + +} + +//-------------------------------------------------------------------------------- + +// Assumes the quad vertices are ordered the same as the clipping case. +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +{ + // Check if any of the quad's corners are within the circle + axom::float64 distP0 = distance(q_p0, circle_center); + axom::float64 distP1 = distance(q_p1, circle_center); + axom::float64 distP2 = distance(q_p2, circle_center); + axom::float64 distP3 = distance(q_p3, circle_center); + + if (distP0 < circle_radius && distP1 < circle_radius && distP2 < circle_radius && distP3 < circle_radius) + { + // The entire quad overlaps the circle + return 1.0; + } + else if (distP0 < circle_radius || distP1 < circle_radius || distP2 < circle_radius || distP3 < circle_radius) + { + // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much + axom::float64 delta_x = abs(q_p2.m_x - q_p1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(q_p0.m_y - q_p1.m_y) / (double) (gridSize - 1); + int countOverlap = 0; + for (int y = 0; y < gridSize; ++y) + { + for (int x = 0; x < gridSize; ++x) + { + mir::Point2 samplePoint(delta_x * x + q_p1.m_x, delta_y * y + q_p1.m_y); + if (distance(samplePoint, circle_center) < circle_radius) + ++countOverlap; + } + } + return countOverlap / (double) (gridSize * gridSize); + } + else + { + // None of the quad overlaps the circle + return 0; + } +} + +//-------------------------------------------------------------------------------- + +/// Generates a 2D uniform grid with n x n elements. +mir::CellData MeshTester::generateGrid(int n) +{ + // Generate the topology for a uniform quad mesh with n x n elements automatically + int numElements = n * n; + int numVertices = (n + 1) * (n + 1); + + // Generate the evInds + std::vector evInds; + for (int eID = 0; eID < numElements; ++eID) + { + int row = eID / n; // note the integer division + int vertsPerRow = n + 1; + int elemsPerRow = n; + + evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 0); + evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); + evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 1); + evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 1); + } + + // Generate the evBegins + std::vector evBegins; + evBegins.push_back(0); + for (int i = 0; i < n * n; ++i) + { + evBegins.push_back((i + 1) * 4); + } + + // Generate the veInds + std::map > veInds_data; + std::vector veInds; + for (int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) + { + int currentElementID = evInd_itr / 4; // note the integer division + veInds_data[evInds[evInd_itr]].push_back(currentElementID); + } + + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + // Sort the vector + std::sort(itr->second.begin(), itr->second.end()); + + // Add the elements associated with the current vertex to veInds + for (unsigned long i = 0; i < itr->second.size(); ++i) + veInds.push_back(itr->second[i]); + } + + // Generate the veBegins + std::vector veBegins; + veBegins.push_back(0); + int currentIndexCount = 0; + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + currentIndexCount += itr->second.size(); + veBegins.push_back(currentIndexCount); + } + + // Generate the vertex positions + std::vector points; + for (int y = n; y > -1; --y) + { + for (int x = 0; x < n + 1; ++x) + { + points.push_back(mir::Point2(x, y)); + } + } + + mir::CellData data; + data.numVerts = numVertices; + data.numElems = numElements; + data.evInds = evInds; + data.evBegins = evBegins; + data.veInds = veInds; + data.veBegins = veBegins; + data.vertexPositions = points; + + // // Print out the results + // printf("evInds: { "); + // for (int i = 0; i < evInds.size(); i++) + // { + // printf("%d ", evInds[i]); + // if ((i+1) % 4 == 0 && i != 0) + // printf("\n"); + // } + // printf("}\n"); + + // printf("evBegins: { "); + // for (int i = 0; i < evBegins.size(); i++) + // { + // printf("%d ", evBegins[i]); + // } + // printf("}\n"); + + // printf("veInds: { "); + // for (int i = 0; i < veInds.size(); i++) + // { + // printf("%d ", veInds[i]); + // } + // printf("}\n"); + + // printf("veBegins: { "); + // for (int i = 0; i < veBegins.size(); i++) + // { + // printf("%d ", veBegins[i]); + // } + // printf("}\n"); + + // printf("points: { "); + // for (int i = 0; i < numVertices; ++i) + // { + // printf("{%.2f, %.2f} ", points[i].m_x, points[i].m_y); + // } + // printf("}\n"); + + return data; +} + +//-------------------------------------------------------------------------------- + +/// Calculate the distance between the two given points. +axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) +{ + return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); +} + +//-------------------------------------------------------------------------------- + +/// Multiple materials, multiple concentric circles. +/// Note: Assumes each circle has a unique material. +mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) +{ + + // Generate the mesh topology + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + + // Generate the element volume fractions with concentric circles + int numMaterials = numCircles + 1; + int defaultMaterialID = numMaterials - 1; // default material is always the last index + + mir::Point2 circleCenter(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point + + // Initialize the radii of the circles + std::vector circleRadii; + axom::float64 maxRadius = gridSize / 2.4; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = gridSize / 8; // Note: The choice of divisor is arbitrary + + axom::float64 radiusDelta; + if (numCircles <= 1) + radiusDelta = (maxRadius - minRadius); + else + radiusDelta = (maxRadius - minRadius) / (double) (numCircles - 1); + + for (int i = 0; i < numCircles; ++i) + { + circleRadii.push_back( minRadius + (i * radiusDelta) ); + } + + // Initialize all material volume fractions to 0 + std::vector > materialVolumeFractionsData; + for (int i = 0; i < numMaterials; ++i) + { + std::vector tempVec; + tempVec.resize(cellData.numElems); + materialVolumeFractionsData.push_back(tempVec); + } + + // Use the uniform sampling method to generate volume fractions for each material + for (int eID = 0; eID < cellData.numElems; ++eID) + { + mir::Point2 v0 = cellData.vertexPositions[cellData.evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.vertexPositions[cellData.evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.vertexPositions[cellData.evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.vertexPositions[cellData.evInds[eID * 4 + 3]]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; + + for (int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + } + + axom::float64 delta_x = abs(v2.m_x - v1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(v0.m_y - v1.m_y) / (double) (gridSize - 1); + + for (int y = 0; y < gridSize; ++y) + { + for (int x = 0; x < gridSize; ++x) + { + mir::Point2 samplePoint(delta_x * x + v1.m_x, delta_y * y + v1.m_y); + bool isPointSampled = false; + for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) + { + if (distance(samplePoint, circleCenter) < circleRadii[cID]) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if (!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; + } + } + } + + // Assign the element volume fractions based on the count of the samples in each circle + for (int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + } + } + + int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.numElems; ++i) + { + elementParents[i] = i; + elementDominantMaterials.push_back(NULL_MAT); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; + +} + +//-------------------------------------------------------------------------------- + +/// Calculate the number of corners of the quad that are within the circle +int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +{ + // Check if any of the quad's corners are within the circle + axom::float64 distP0 = distance(q_p0, circle_center); + axom::float64 distP1 = distance(q_p1, circle_center); + axom::float64 distP2 = distance(q_p2, circle_center); + axom::float64 distP3 = distance(q_p3, circle_center); + + int numCorners = 0; + + if (distP0 < circle_radius) + numCorners++; + if (distP1 < circle_radius) + numCorners++; + if (distP2 < circle_radius) + numCorners++; + if (distP3 < circle_radius) + numCorners++; + + return numCorners; +} + +//-------------------------------------------------------------------------------- + +} +} diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp new file mode 100644 index 0000000000..b89cfa2b26 --- /dev/null +++ b/src/axom/mir/MeshTester.hpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MESH_TESTER_H__ +#define __MESH_TESTER_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" + +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + class MeshTester + { + public: + MeshTester(); + ~MeshTester(); + + public: + MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials + mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials + mir::MIRMesh initTestCaseThree(); // triforce, 2 materials + mir::MIRMesh initTestCaseFour(); // 3x3, 2 materials, circle of material + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles + + private: + axom::float64 distance(mir::Point2 p0, mir::Point2 p1); + mir::CellData generateGrid(int n); + axom::float64 calculatePercentOverlapMonteCarlo(int n, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + + int circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + }; +} +} + +#endif diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooClippingTables.cpp similarity index 98% rename from src/axom/mir/ZooBitMaps.cpp rename to src/axom/mir/ZooClippingTables.cpp index 3904fd525c..5cb584274c 100644 --- a/src/axom/mir/ZooBitMaps.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "ZooBitMaps.hpp" +#include "ZooClippingTables.hpp" namespace axom { diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooClippingTables.hpp similarity index 83% rename from src/axom/mir/ZooBitMaps.hpp rename to src/axom/mir/ZooClippingTables.hpp index e765ced325..fcdfc20a9e 100644 --- a/src/axom/mir/ZooBitMaps.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef __ZOO_BIT_MAPS_H__ -#define __ZOO_BIT_MAPS_H__ +#ifndef __ZOO_CLIPPING_TABLES_H__ +#define __ZOO_CLIPPING_TABLES_H__ namespace axom diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 605609bf02..b1a647e764 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -4,8 +4,8 @@ # SPDX-License-Identifier: (BSD-3-Clause) set( mir_examples - mir_tutorial.cpp mir_tutorial_simple.cpp + mirConcentricCircles.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mirConcentricCircles.cpp b/src/axom/mir/examples/mirConcentricCircles.cpp new file mode 100644 index 0000000000..b445c859e7 --- /dev/null +++ b/src/axom/mir/examples/mirConcentricCircles.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +#include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + +#include +#include + +using Clock = std::chrono::high_resolution_clock; + +// namespace aliases +namespace numerics = axom::numerics; +namespace slam = axom::slam; +namespace mir = axom::mir; + +//-------------------------------------------------------------------------------- + +/*! + * \brief Tutorial main + */ +int main( int argc, char** argv ) +{ + + if (argc != 4) + { + printf("Incorrect number of args. Args are \n"); + return 0; + } + + try + { + // Parse the command line arguments + int gridSize = std::stoi(argv[1]); + int numCircles = std::stoi(argv[2]); + std::string outputFilePath = std::string(argv[3]); + + // Intialize a mesh for testing MIR + auto start_time = Clock::now(); + mir::MeshTester tester; + mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); // grid size, numCircles + auto end_time = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + + // Begin material interface reconstruction + start_time = Clock::now(); + mir::InterfaceReconstructor reconstructor; + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); + end_time = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + + // Output results + processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); + + return 0; + } + catch (std::invalid_argument const &e) + { + printf("Bad input. Arguments are \n"); + return 0; + } + catch (std::out_of_range const &e) + { + printf("Integer overflow. Arguments are \n"); + return 0; + } +} \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp deleted file mode 100644 index 576da4d49d..0000000000 --- a/src/axom/mir/examples/mir_tutorial.cpp +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions -#include "axom/slam.hpp" - - - -// C/C++ includes -#include // for definition of M_PI, exp() -#include - -// namespace aliases -//namespace mir = axom::mir; -namespace numerics = axom::numerics; -namespace slam = axom::slam; - -#define EPSILON_ZERO = 0.00000000001f; - -//-------------------------------------------------------------------------------- - -/** - * \brief Simple 2D Point class for example - */ -struct Point2 -{ - Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} - - Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} - - Point2& operator=(const Point2& other) - { m_x = other.m_x; m_y = other.m_y; return *this; } - - Point2& operator+=(const Point2& other) - { m_x += other.m_x; m_y += other.m_y; return *this; } - - Point2& operator/=(double val) - { m_x /= val; m_y += val; return *this; } - - double& operator[] (int i) { return (i==0) ? m_x : m_y; } - const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } - - friend std::ostream& operator<<(std::ostream& os, const Point2& pt) - { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } - - double m_x, m_y; -}; - -//-------------------------------------------------------------------------------- - -struct EdgeClipInfo -{ - int vertexOne; - int vertexTwo; - int colorOne; - int colorTwo; - float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. -}; - -//-------------------------------------------------------------------------------- - -struct MIRMesh -{ - /**************************************************************** - * TYPE ALIASES - ****************************************************************/ - // SET TYPE ALIASES - using PosType = slam::DefaultPositionType; - using ElemType = slam::DefaultElementType; - - using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; - - using VertSet = slam::PositionSet< PosType, ElemType >; - using ElemSet = slam::PositionSet< PosType, ElemType >; - - // RELATION TYPE ALIASES - using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; - - // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. - // Note: It is the relation of the elements to the vertices. - using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; - using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; - - // MAP TYPE ALIASES - using BaseSet = slam::Set< PosType, ElemType >; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. - using PointMap = slam::Map< BaseSet, Point2 >; - - enum { GREEN = 0, BLUE = 1 }; - - /**************************************************************** - * MESH FUNCTIONS - ****************************************************************/ - void InitializeMesh() - { - // Create the mesh connectivity information - // data for element-vertex boundary relation - evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - - // data for vertex-element coboundary relation - veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - // Initialize the mesh sets - verts = VertSet(16); // Construct a vertex set with 11 vertices - elems = ElemSet(9); // Construct an element set with 5 elements - - // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); - - printf("Mesh has been initialized.\n"); - } - - /// Constructs the mesh boundary and coboundary relations - void constructMeshRelations() - { - - // construct boundary relation from elements to vertices using variable cardinality - { - using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( evBegins.data() ) ) - .indices ( RelationBuilder::IndicesSetBuilder() - .size( evInds.size() ) - .data( evInds.data() ) ); - } - - - { - // _quadmesh_example_construct_cobdry_relation_start - // construct coboundary relation from vertices to elements - using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( veBegins.data() ) ) - .indices( RelationBuilder::IndicesSetBuilder() - .size( veInds.size() ) - .data( veInds.data() ) ); - // _quadmesh_example_construct_cobdry_relation_end - } - - - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); - - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); - - printf("Finished constructing mesh relations.\n"); - } - - /// Constructs the volume fraction maps on the vertices - void constructMeshVolumeFractionMaps() - { - // TODO: Determine better way to store volume fraction data than 1 map per material? - // Construct the scalar map on the elements for the green material - greenVolumeFractionsElements = ScalarMap( &elems ); - - // Set the green volume fraction for each element - greenVolumeFractionsElements[0] = 1.0; - greenVolumeFractionsElements[1] = 1.0; - greenVolumeFractionsElements[2] = 1.0; - greenVolumeFractionsElements[3] = 1.0; - greenVolumeFractionsElements[4] = 0.5; - greenVolumeFractionsElements[5] = 0.2; - greenVolumeFractionsElements[6] = 0.2; - greenVolumeFractionsElements[7] = 0.0; - greenVolumeFractionsElements[8] = 0.0; - - // Construct the scalar map on the elements for the blue material - blueVolumeFractionsElements = ScalarMap( &elems ); - - blueVolumeFractionsElements[0] = 0.0; - blueVolumeFractionsElements[1] = 0.0; - blueVolumeFractionsElements[2] = 0.0; - blueVolumeFractionsElements[3] = 0.0; - blueVolumeFractionsElements[4] = 0.5; - blueVolumeFractionsElements[5] = 0.8; - blueVolumeFractionsElements[6] = 0.8; - blueVolumeFractionsElements[7] = 1.0; - blueVolumeFractionsElements[8] = 1.0; - - printf("Finished constructing volume fractions for mesh elements.\n"); - } - - /// Constucts the positions map on the vertices - void constructVertexPositionMap() - { - // construct the position map on the vertices - vertexPositions = PointMap( &verts ); - - // first vertex is at origin - vertexPositions[0] = Point2( 0.0, 3.0 ); - vertexPositions[1] = Point2( 1.0, 3.0 ); - vertexPositions[2] = Point2( 2.0, 3.0 ); - vertexPositions[3] = Point2( 3.0, 3.0 ); - - vertexPositions[4] = Point2( 0.0, 2.0 ); - vertexPositions[5] = Point2( 1.0, 2.0 ); - vertexPositions[6] = Point2( 2.0, 2.0 ); - vertexPositions[7] = Point2( 3.0, 2.0 ); - - vertexPositions[8] = Point2( 0.0, 1.0 ); - vertexPositions[9] = Point2( 1.0, 1.0 ); - vertexPositions[10] = Point2( 2.0, 1.0 ); - vertexPositions[11] = Point2( 3.0, 1.0 ); - - vertexPositions[12] = Point2( 0.0, 0.0 ); - vertexPositions[13] = Point2( 1.0, 0.0 ); - vertexPositions[14] = Point2( 2.0, 0.0 ); - vertexPositions[15] = Point2( 3.0, 0.0 ); - - SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); - - SLIC_INFO("-- Vertex positions:"); - for(int vID=0 ; vID < verts.size() ; ++vID) - { - SLIC_INFO("Position of vert " << vID << " is " << vertexPositions[vID]); - } - } - - // Computes the average volume fraction at each vertex of the mesh - void computeVolumeFractionAverages() - { - // Initialize the vertex volume fraction maps - greenVolumeFractionsVertices = ScalarMap( &verts ); - blueVolumeFractionsVertices = ScalarMap( &verts ); - - for (int vID = 0; vID < verts.size(); ++vID) - { - // Compute the per vertex volume fractions for the green material - axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += greenVolumeFractionsElements[eID]; - } - greenVolumeFractionsVertices[vID] = sum / vertexElements.size(); - - // Compute the per vertex volume fractions for the blue material - sum = 0; - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += blueVolumeFractionsElements[eID]; - } - blueVolumeFractionsVertices[vID] = sum / vertexElements.size(); - } - - printf("Finished computing volume fraction averages.\n"); - } - - /// Prints out the map values for each element - void printElementScalarMap(ScalarMap& elements, std::string prefix) - { - std::cout << prefix; - for (int i = 0; i < elems.size(); ++i) - { - printf("Element %d: %f\n", i, elements[i]); - } - } - - /// Prints out the map values for each vertex - void printVertexScalarMap(ScalarMap& vertices, std::string prefix) - { - std::cout << prefix; - for (int i = 0; i < verts.size(); ++i) - { - printf("Vertex %d: %f\n", i, vertices[i]); - } - } - - /// Use bilinear interpolation to compute the points where the given element should be clipped. - /// Returns true if clip should occur, otherwise false. - /// If a clip should occur, the clipping points will be given in p1 and p2. - void computeClippingPoints(const int eID)//, Point2* p1, Point2* p2) - { - // Find the vertices associated with each element - auto elementVertices = bdry[eID]; - - // Check if one of the two currently considered materials is not present at the vertices of the current element - if (blueVolumeFractionsElements[eID] != 0.0 && greenVolumeFractionsElements[eID] != 0.0) - { - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID); - } - } - else - { - printf("No cuts in element %d.\n", eID); - } - } - - /// Computes the points where the given quad element should be clipped - void computeQuadClippingPoints(int eID) - { - auto elementVertices = bdry[eID]; - EdgeClipInfo eci[4]; - - // Initialize the edge clipping info - for (int i = 0; i < 4; ++i) - { - eci[i].vertexOne = elementVertices[ i ]; - eci[i].vertexTwo = elementVertices[ (i + 1) % 4 ]; - - if (blueVolumeFractionsVertices[eci[i].vertexOne] > greenVolumeFractionsVertices[eci[i].vertexOne]) - eci[i].colorOne = BLUE; - else - eci[i].colorOne = GREEN; - - if (blueVolumeFractionsVertices[eci[i].vertexTwo] > greenVolumeFractionsVertices[eci[i].vertexTwo]) - eci[i].colorTwo = BLUE; - else - eci[i].colorTwo = GREEN; - - eci[i].t = -1.0; // default t value means one material dominates the other and no clip should occur on this edge - } - - // Analyze the edge clipping info and determine where to clip the cell - for (int i = 0; i < 4; ++i) - { - if (eci[i].colorOne != eci[i].colorTwo) - { - axom::float64 numerator = blueVolumeFractionsVertices[eci[i].vertexOne] - greenVolumeFractionsVertices[eci[i].vertexOne]; - axom::float64 denominator = -greenVolumeFractionsVertices[eci[i].vertexOne] - + greenVolumeFractionsVertices[eci[i].vertexTwo] - + blueVolumeFractionsVertices[eci[i].vertexOne] - - blueVolumeFractionsVertices[eci[i].vertexTwo]; - - if (denominator != 0.0) - eci[i].t = numerator / denominator; - } - } - - // Print out the cutting information - bool cut = false; - for (int i = 0; i < 4; ++i) - { - if (eci[i].t > 0.0) - { - printf("Cutting element %d between vertices %d and %d. t = %f\n", eID, eci[i].vertexOne, eci[i].vertexTwo, eci[i].t); - cut = true; - } - } - if (!cut) - printf("No cuts in element %d.\n", eID); - - } - - /// Computes the points where the given triangle element should be clipped - void computeTriangleClippingPoints(int eID) - { - printf("Triangle clipping case not yet implemented.\n"); - } - - /**************************************************************** - * VARIABLES - ****************************************************************/ - public: - // Mesh Set Definitions - VertSet verts; // the set of vertices in the mesh - ElemSet elems; // the set of elements in the mesh - - // Mesh Relation Definitions - ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements - - // Mesh Map Definitions - PointMap vertexPositions; // vertex position - ScalarMap greenVolumeFractionsElements; // the volume fractions of the green material for each element (NOT each vertex) - ScalarMap blueVolumeFractionsElements; // the volume fractions of the blue material for each element (NOT each vertex) - - ScalarMap greenVolumeFractionsVertices; - ScalarMap blueVolumeFractionsVertices; - - // support data for mesh connectivity - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; -}; - - -//-------------------------------------------------------------------------------- - - -/*! - * \file - * - * \brief Various code snippets/examples used for the Mir tutorial section. - * - * \note These examples are designed to illustrate specific Mir - * concepts and capabilities. Consult the Tutorial section of Mir's - * User Guide for more details. - * - */ - - -/*! - * \brief Tutorial main - */ -int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) -{ - printf("Beginning the MIR Tutorial program.\n"); - - MIRMesh testMesh; - testMesh.InitializeMesh(); - testMesh.constructMeshRelations(); - // testMesh.constructMeshPositionMap(); - testMesh.constructMeshVolumeFractionMaps(); - testMesh.computeVolumeFractionAverages(); - - // Print volume fraction debugging information - // testMesh.printElementScalarMap(testMesh.greenVolumeFractionsElements, "Green Volume Fractions per Element: \n"); - // testMesh.printElementScalarMap(testMesh.blueVolumeFractionsElements, "Blue Volume Fractions per Element: \n"); - - // testMesh.printVertexScalarMap(testMesh.greenVolumeFractionsVertices, "Green Volume Fractions per Vertex: \n"); - // testMesh.printVertexScalarMap(testMesh.blueVolumeFractionsVertices, "Blue Volume Fractions per Vertex: \n"); - - // Determine where to clip the cells - for (int i = 0; i < 9; ++i) - testMesh.computeClippingPoints(i); - - - return 0; -} - -//-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index b9e0dc9b7b..9feba3ec87 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -4,23 +4,17 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/mir.hpp" // for Mir classes & functions #include "axom/slam.hpp" -#include "../MIRMesh.hpp" -#include "../InterfaceReconstructor.hpp" +#include +using Clock = std::chrono::high_resolution_clock; // namespace aliases -//namespace mir = axom::mir; namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; -// function templates -mir::MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials -mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials -mir::MIRMesh initTestCaseThree(); // triforce, 2 materials - //-------------------------------------------------------------------------------- /*! @@ -28,294 +22,33 @@ mir::MIRMesh initTestCaseThree(); // triforce, 2 materials */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { + // Intialize a mesh for testing MIR - // mir::MIRMesh testMesh = initTestCaseOne(); - mir::MIRMesh testMesh = initTestCaseTwo(); - // mir::MIRMesh testMesh = initTestCaseThree(); - - // Begin material interface reconstruction - mir::InterfaceReconstructor reconstructor(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); - - // Output results - processedMesh.print(); - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); - processedMesh.computeOriginalElementVolumeFractions(); - - return 0; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 1 (from Meredith 2004) -mir::MIRMesh initTestCaseOne() -{ - int numElements = 9; - int numVertices = 16; - - // Create the mesh connectivity information - std::vector evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - std::vector evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - std::vector veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - + auto start_time = Clock::now(); + mir::MeshTester tester; + // mir::MIRMesh testMesh = tester.initTestCaseOne(); + mir::MIRMesh testMesh = tester.initTestCaseTwo(); + // mir::MIRMesh testMesh = tester.initTestCaseThree(); + // mir::MIRMesh testMesh = tester.initTestCaseFour(); + // mir::MIRMesh testMesh = tester.initTestCaseFive(20, 5); - int numMaterials = 2; - enum { GREEN = 0, BLUE = 1 }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 greenVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - materialVolumeFractionsData[GREEN] = greenVolumeFractions; - axom::float64 blueVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - - return testMesh; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) -mir::MIRMesh initTestCaseTwo() -{ - int numElements = 9; - int numVertices = 16; - - // Create the mesh connectivity information - std::vector evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - std::vector evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - std::vector veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - - int numMaterials = 3; - enum { BLUE = 2, RED = 0, ORANGE = 1 }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 blueVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - axom::float64 redVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; - materialVolumeFractionsData[RED] = redVolumeFractions; - axom::float64 orangeVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - materialVolumeFractionsData[ORANGE] = orangeVolumeFractions; - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - - return testMesh; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. -mir::MIRMesh initTestCaseThree() -{ - int numElements = 4; - int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material - - // Create the mesh connectivity information - std::vector evInds = { - 0,1,2, // elem 0, card 3, start 0 - 1,3,4, // elem 1, card 3, start 3 - 1,4,2, // elem 2, card 3, start 6 - 2,4,5 // elem 3, card 3, start 9, end 12 - }; - - std::vector evBegins = { - 0,3,6,9,12 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1,2, // vert 1, card 3, start 1 - 0,2,3, // vert 2, card 3, start 4 - 1, // vert 3, card 1, start 7 - 1,2,3, // vert 4, card 3, start 8 - 3 // vert 5, card 1, start 11, end 12 - }; - std::vector veBegins = { - 0,1,4,7,8,11,12 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements - - int numMaterials = 2; - enum { BLUE = 0, RED = 1, }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 blueVolumeFractions[] = {0.0, 0.5, 0.8, 0.5}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - axom::float64 redVolumeFractions[] = {1.0, 0.5, 0.2, 0.5}; - materialVolumeFractionsData[RED] = redVolumeFractions; - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 1.0, 2.0 ); - points[1] = mir::Point2( 0.5, 1.0 ); - points[2] = mir::Point2( 1.5, 1.0 ); - points[3] = mir::Point2( 0.0, 0.0 ); - points[4] = mir::Point2( 1.0, 0.0 ); - points[5] = mir::Point2( 2.0, 0.0 ); - } + auto end_time = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + // Begin material interface reconstruction + start_time = Clock::now(); - int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mir::InterfaceReconstructor reconstructor; + // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.9); // 5 iterations, 90 percent - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + end_time = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + // Output results + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh2.vtk"); - return testMesh; + return 0; } -//-------------------------------------------------------------------------------- - +//-------------------------------------------------------------------------------- \ No newline at end of file From 738e23f6c5afc78721d6ccb1e0013a98e08811ff Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 18 Jun 2019 17:02:23 -0700 Subject: [PATCH 010/290] Cleaned up the code. Changed CellData to be a wrapper class. --- src/axom/mir/CellData.cpp | 54 +- src/axom/mir/CellData.hpp | 15 +- src/axom/mir/InterfaceReconstructor.cpp | 100 ++-- src/axom/mir/InterfaceReconstructor.hpp | 6 +- src/axom/mir/MIRMesh.cpp | 469 +++++++++--------- src/axom/mir/MIRMesh.hpp | 30 +- src/axom/mir/MeshTester.cpp | 301 +++++------ src/axom/mir/examples/mir_tutorial_simple.cpp | 8 +- 8 files changed, 496 insertions(+), 487 deletions(-) diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index d1cad26aef..d8f35d30e4 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -19,21 +19,6 @@ namespace mir //-------------------------------------------------------------------------------- - CellData::CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, - std::vector _vertexPositions, std::vector > _vertexVolumeFractions) - { - numVerts = _numVerts; - numElems = _numElems; - evInds = _evInds; - evBegins = _evBegins; - veInds = _veInds; - veBegins = _veBegins; - vertexPositions = _vertexPositions; - vertexVolumeFractions = _vertexVolumeFractions; - } - - //-------------------------------------------------------------------------------- - CellData::~CellData() { @@ -45,64 +30,63 @@ namespace mir void CellData::mergeCell(CellData cellToMerge) { // Initialize index offsets - int evBeginsOffset = evInds.size(); - int veBeginsOffset = veInds.size(); + int evBeginsOffset = topology.evInds.size(); + int veBeginsOffset = topology.veInds.size(); int vertexIndexOffset = numVerts; int elementIndexOffset = numElems; // Merge the cell topology information - for (unsigned long i = 0; i < cellToMerge.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.topology.evInds.size(); ++i) { - evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); + topology.evInds.push_back(cellToMerge.topology.evInds[i] + vertexIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.topology.evBegins.size(); ++i) { - evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); + topology.evBegins.push_back(cellToMerge.topology.evBegins[i] + evBeginsOffset); } - for (unsigned long i = 0; i < cellToMerge.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.topology.veInds.size(); ++i) { - veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); + topology.veInds.push_back(cellToMerge.topology.veInds[i] + elementIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.topology.veBegins.size(); ++i) { - veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); + topology.veBegins.push_back(cellToMerge.topology.veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (unsigned long i = 0; i < cellToMerge.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.vertexPositions.size(); ++i) { - vertexPositions.push_back(cellToMerge.vertexPositions[i]); + mapData.vertexPositions.push_back(cellToMerge.mapData.vertexPositions[i]); } // Merge the vertex volume fractions - for (unsigned long matID = 0; matID < vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < mapData.vertexVolumeFractions.size(); ++matID) { - for (unsigned long vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.mapData.vertexVolumeFractions[matID].size(); ++vID) { - vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); + mapData.vertexVolumeFractions[matID].push_back(cellToMerge.mapData.vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (unsigned long i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.elementDominantMaterials.size(); ++i) { - elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); + mapData.elementDominantMaterials.push_back(cellToMerge.mapData.elementDominantMaterials[i]); } // Merge the elements' parent ids - for (unsigned long i = 0; i < cellToMerge.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.elementParents.size(); ++i) { - elementParents.push_back(cellToMerge.elementParents[i]); + mapData.elementParents.push_back(cellToMerge.mapData.elementParents[i]); } // Merge the total number of verts and elems in the resulting cell numVerts += cellToMerge.numVerts; numElems += cellToMerge.numElems; - } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 88edd9f453..7603582a30 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -41,11 +41,8 @@ namespace mir /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData { - public: CellData(); - CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, - std::vector _vertexPositions, std::vector > _vertexVolumeFractions); ~CellData(); void mergeCell(CellData cellToMerge); @@ -54,16 +51,8 @@ namespace mir int numVerts; int numElems; - // Cell connectivity/topology - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - - std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap - std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap - std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap - std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + CellTopologyData topology; + CellMapData mapData; }; } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 529c26ead4..6408a1b653 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -66,12 +66,8 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* // Create the final, processed mesh mir::MIRMesh processedMesh; - processedMesh.InitializeMesh(temp_cellData[0].evInds, temp_cellData[0].evBegins, temp_cellData[0].veInds, temp_cellData[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); - processedMesh.constructMeshRelations(); - processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].vertexVolumeFractions); - processedMesh.constructVertexPositionMap(temp_cellData[0].vertexPositions.data()); - processedMesh.constructElementParentMap(temp_cellData[0].elementParents.data()); - processedMesh.constructElementDominantMaterialMap(temp_cellData[0].elementDominantMaterials); + processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.numMaterials, temp_cellData[0].topology, temp_cellData[0].mapData); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].mapData.vertexVolumeFractions); // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; @@ -239,36 +235,36 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int { axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); + out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); } // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); + out_cellData.mapData.elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.mapData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; } } @@ -284,9 +280,9 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) { - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; } } @@ -387,36 +383,36 @@ void InterfaceReconstructor::generateTopologyData(std::map for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { // Push the start index of the next element - out_cellData.evBegins.push_back(currentEVBeginIndex); + out_cellData.topology.evBegins.push_back(currentEVBeginIndex); // Push the next element's vertices for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) { - out_cellData.evInds.push_back(itr->second[vIndex]); + out_cellData.topology.evInds.push_back(itr->second[vIndex]); ++currentEVBeginIndex; } } // Push the index that occurs after the last vertex - out_cellData.evBegins.push_back(currentEVBeginIndex); + out_cellData.topology.evBegins.push_back(currentEVBeginIndex); // Store the veInds and veBegins data in the output vectors int currentVEBeginIndex = 0; for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { // Push the start index of the vertex's elements - out_cellData.veBegins.push_back(currentVEBeginIndex); + out_cellData.topology.veBegins.push_back(currentVEBeginIndex); // Push the next vertex's elements for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) { - out_cellData.veInds.push_back(itr->second[eIndex]); + out_cellData.topology.veInds.push_back(itr->second[eIndex]); ++currentVEBeginIndex; } } // Push the index that occurs after the last element - out_cellData.veBegins.push_back(currentVEBeginIndex); + out_cellData.topology.veBegins.push_back(currentVEBeginIndex); } //-------------------------------------------------------------------------------- @@ -451,7 +447,7 @@ void InterfaceReconstructor::generateVertexPositionsFromQuad(std::mapsecond); + out_cellData.mapData.vertexPositions.push_back(itr->second); } } @@ -462,7 +458,7 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapnumMaterials); + out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { @@ -470,21 +466,21 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapnumMaterials; ++matID) { if (vID == 0) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); if (vID == 1) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); if (vID == 4) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 6) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); if (vID == 7) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); } } } @@ -574,36 +570,36 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const { axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); + out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); } // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); + out_cellData.mapData.elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.mapData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; } } @@ -620,8 +616,8 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (int i = 1; i < 6; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) + out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; } @@ -681,7 +677,7 @@ void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::mapsecond); + out_cellData.mapData.vertexPositions.push_back(itr->second); } } @@ -692,7 +688,7 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map< { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { @@ -700,17 +696,17 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map< for (int matID = 0; matID < tempMesh->numMaterials; ++matID) { if (vID == 0) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); if (vID == 1) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 4) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); } } } diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 8d0f5d3aa8..cbda3dcb59 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -44,14 +44,16 @@ namespace mir // quad clipping functions void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); - // quad clipping interpolation functions + // clipping interpolation functions mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + // general helper functions + void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); + // quad clipping points helper functions unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index b116550666..9243bda9ad 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -9,93 +9,118 @@ namespace axom { namespace mir { - MIRMesh::MIRMesh() - { - } +//-------------------------------------------------------------------------------- - MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor - { - data.evInds = _mesh->data.evInds; - data.evBegins = _mesh->data.evBegins; - data.veInds = _mesh->data.veInds; - data.veBegins = _mesh->data.veBegins; - verts = _mesh->verts; - elems = _mesh->elems; - bdry = _mesh->bdry; - cobdry = _mesh->cobdry; - vertexPositions = _mesh->vertexPositions; - materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; - materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; - elementParentIDs = _mesh->elementParentIDs; - elementDominantMaterials = _mesh->elementDominantMaterials; - numMaterials = _mesh->numMaterials; - } +MIRMesh::MIRMesh() +{ - MIRMesh::~MIRMesh() - { +} - } +//-------------------------------------------------------------------------------- - /// Initializes a mesh with the given topology. - void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) - { - data.evInds = _evInds; - data.evBegins = _evBegins; - data.veInds = _veInds; - data.veBegins = _veBegins; - verts = _verts; - elems = _elems; - numMaterials = _numMaterials; - - // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); - } +MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor +{ + meshTopology.evInds = _mesh->meshTopology.evInds; + meshTopology.evBegins = _mesh->meshTopology.evBegins; + meshTopology.veInds = _mesh->meshTopology.veInds; + meshTopology.veBegins = _mesh->meshTopology.veBegins; + verts = _mesh->verts; + elems = _mesh->elems; + bdry = _mesh->bdry; + cobdry = _mesh->cobdry; + vertexPositions = _mesh->vertexPositions; + materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; + materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + elementParentIDs = _mesh->elementParentIDs; + elementDominantMaterials = _mesh->elementDominantMaterials; + numMaterials = _mesh->numMaterials; +} //-------------------------------------------------------------------------------- - /// Constructs the mesh boundary and coboundary relations - void MIRMesh::constructMeshRelations() - { - // construct boundary relation from elements to vertices using variable cardinality - { - using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( data.evBegins.data() ) ) - .indices ( RelationBuilder::IndicesSetBuilder() - .size( data.evInds.size() ) - .data( data.evInds.data() ) ); - } +MIRMesh::~MIRMesh() +{ + +} +//-------------------------------------------------------------------------------- - { - // _quadmesh_example_construct_cobdry_relation_start - // construct coboundary relation from vertices to elements - using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( data.veBegins.data() ) ) - .indices( RelationBuilder::IndicesSetBuilder() - .size( data.veInds.size() ) - .data( data.veInds.data() ) ); - // _quadmesh_example_construct_cobdry_relation_end - } +/// Initialize a mesh with the given topology and data. +void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) +{ + // Initialize the vertex and element sets + verts = _verts; + elems = _elems; + + numMaterials = _numMaterials; + + // Intialize the mesh topology + meshTopology.evInds = _topology.evInds; + meshTopology.evBegins = _topology.evBegins; + meshTopology.veInds = _topology.veInds; + meshTopology.veBegins = _topology.veBegins; + + // Initialize the mesh relations + constructMeshRelations(); + + // Initialize the mesh's data maps + constructVertexPositionMap(_mapData.vertexPositions.data()); + constructElementParentMap(_mapData.elementParents.data()); + constructElementDominantMaterialMap(_mapData.elementDominantMaterials); + + // Initialize the element and vertex volume fraction maps + if (_elementVF.size() > 0) + constructMeshVolumeFractionsMaps(_elementVF); + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); +} + +//-------------------------------------------------------------------------------- + +/// Constructs the mesh boundary and coboundary relations +void MIRMesh::constructMeshRelations() +{ + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( meshTopology.evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( meshTopology.evInds.size() ) + .data( meshTopology.evInds.data() ) ); + } - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( meshTopology.veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( meshTopology.veInds.size() ) + .data( meshTopology.veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end } + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); +} + //-------------------------------------------------------------------------------- /// Construct the and the element and vertex volume fraction data given the element volume fraction data @@ -169,196 +194,196 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector dominantMaterials) - { - // Initialize the map for the elements' dominant colors - elementDominantMaterials = IntMap( &elems ); +/// Constructs the elementDominantMaterials map of each element's single most dominant material +void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) +{ + // Initialize the map for the elements' dominant colors + elementDominantMaterials = IntMap( &elems ); - // Copy the dat for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementDominantMaterials[eID] = dominantMaterials[eID]; + // Copy the dat for the elements + for (int eID = 0; eID < elems.size(); ++eID) + elementDominantMaterials[eID] = dominantMaterials[eID]; - SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); - } + SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); +} //-------------------------------------------------------------------------------- - /// Print out the properties of the mesh. - void MIRMesh::print() - { - printf("\n------------------------Printing Mesh Information:------------------------\n"); - printf("number of vertices: %d\n", verts.size()); - printf("number of elements: %d\n", elems.size()); - printf("number of materials: %d\n", numMaterials); +/// Print out the properties of the mesh. +void MIRMesh::print() +{ + printf("\n------------------------Printing Mesh Information:------------------------\n"); + printf("number of vertices: %d\n", verts.size()); + printf("number of elements: %d\n", elems.size()); + printf("number of materials: %d\n", numMaterials); - printf("evInds: { "); - for (unsigned long i = 0; i < data.evInds.size(); i++) - { - printf("%d ", data.evInds[i]); - } - printf("}\n"); + printf("evInds: { "); + for (unsigned long i = 0; i < meshTopology.evInds.size(); i++) + { + printf("%d ", meshTopology.evInds[i]); + } + printf("}\n"); - printf("evBegins: { "); - for (unsigned long i = 0; i < data.evBegins.size(); i++) - { - printf("%d ", data.evBegins[i]); - } - printf("}\n"); + printf("evBegins: { "); + for (unsigned long i = 0; i < meshTopology.evBegins.size(); i++) + { + printf("%d ", meshTopology.evBegins[i]); + } + printf("}\n"); - printf("veInds: { "); - for (unsigned long i = 0; i < data.veInds.size(); i++) - { - printf("%d ", data.veInds[i]); - } - printf("}\n"); + printf("veInds: { "); + for (unsigned long i = 0; i < meshTopology.veInds.size(); i++) + { + printf("%d ", meshTopology.veInds[i]); + } + printf("}\n"); - printf("veBegins: { "); - for (unsigned long i = 0; i < data.veBegins.size(); i++) - { - printf("%d ", data.veBegins[i]); - } - printf("}\n"); + printf("veBegins: { "); + for (unsigned long i = 0; i < meshTopology.veBegins.size(); i++) + { + printf("%d ", meshTopology.veBegins[i]); + } + printf("}\n"); - printf("vertexPositions: { "); - for (int i = 0; i < verts.size(); ++i) // TODO: This was previously vertexPositions.size() and was working... - { - printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); - } - printf("}\n"); + printf("vertexPositions: { "); + for (int i = 0; i < verts.size(); ++i) + { + printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + } + printf("}\n"); - printf("elementParentIDs: { "); - for (int i = 0; i < elems.size(); ++i) - { - printf("%d ", elementParentIDs[i]); - } - printf("}\n"); + printf("elementParentIDs: { "); + for (int i = 0; i < elems.size(); ++i) + { + printf("%d ", elementParentIDs[i]); + } + printf("}\n"); - printf("elementDominantMaterials: { "); - for (int i = 0; i < elems.size(); ++i) - { - printf("%d ", elementDominantMaterials[i]); - } - printf("}\n"); + printf("elementDominantMaterials: { "); + for (int i = 0; i < elems.size(); ++i) + { + printf("%d ", elementDominantMaterials[i]); + } + printf("}\n"); - printf("vertexVolumeFractions: { \n"); - for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + printf("vertexVolumeFractions: { \n"); + for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + { + printf(" { "); + for (int j = 0; j < verts.size(); ++j) { - printf(" { "); - for (int j = 0; j < verts.size(); ++j) - { - printf("%.3f, ", materialVolumeFractionsVertex[i][j]); - } - printf("}\n"); + printf("%.3f, ", materialVolumeFractionsVertex[i][j]); } printf("}\n"); - printf("--------------------------------------------------------------------------\n"); } + printf("}\n"); + printf("--------------------------------------------------------------------------\n"); +} //-------------------------------------------------------------------------------- - /// Reads in and constructs a mesh from the given file - /// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file - void MIRMesh::readMeshFromFile(std::string filename) - { - printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); - - // Read in header +/// Reads in and constructs a mesh from the given file +/// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file +void MIRMesh::readMeshFromFile(std::string filename) +{ + printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); + + // Read in header - // Read in POINTS + // Read in POINTS - // Read in CELLS + // Read in CELLS - // Read in CELL_TYPES + // Read in CELL_TYPES - // Read in CELL_DATA (element volume fractions) - } + // Read in CELL_DATA (element volume fractions) +} //-------------------------------------------------------------------------------- - /// Writes out the mesh to a file - void MIRMesh::writeMeshToFile(std::string filename) - { - std::ofstream meshfile; - meshfile.open(filename); - std::ostream_iterator out_it(meshfile, " "); - - // write header - meshfile << "# vtk DataFile Version 3.0\n" - << "vtk output\n" - << "ASCII\n" - << "DATASET UNSTRUCTURED_GRID\n" - << "POINTS " << verts.size() << " double\n"; - - // write positions - for (int vID = 0; vID < verts.size(); ++vID) - { - meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D - } - - meshfile << "\nCELLS " << elems.size() << " " << data.evInds.size() + elems.size(); - for (int i = 0; i < elems.size(); ++i) - { - int nVerts = data.evBegins[i+1] - data.evBegins[i]; - meshfile << "\n" << nVerts; - for (int j = 0; j < nVerts; ++j) - { - int startIndex = data.evBegins[i]; - meshfile << " " << data.evInds[startIndex + j]; - } - } +/// Writes out the mesh to a file +void MIRMesh::writeMeshToFile(std::string filename) +{ + std::ofstream meshfile; + meshfile.open(filename); + std::ostream_iterator out_it(meshfile, " "); + + // write header + meshfile << "# vtk DataFile Version 3.0\n" + << "vtk output\n" + << "ASCII\n" + << "DATASET UNSTRUCTURED_GRID\n" + << "POINTS " << verts.size() << " double\n"; + + // write positions + for (int vID = 0; vID < verts.size(); ++vID) + { + meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + } - meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; - for (int i = 0; i < elems.size(); ++i) + meshfile << "\nCELLS " << elems.size() << " " << meshTopology.evInds.size() + elems.size(); + for (int i = 0; i < elems.size(); ++i) + { + int nVerts = meshTopology.evBegins[i+1] - meshTopology.evBegins[i]; + meshfile << "\n" << nVerts; + for (int j = 0; j < nVerts; ++j) { - int nVerts = data.evBegins[i + 1] - data.evBegins[i]; - if (nVerts == 3) - meshfile << "5\n"; - else if (nVerts == 4) - meshfile << "9\n"; + int startIndex = meshTopology.evBegins[i]; + meshfile << " " << meshTopology.evInds[startIndex + j]; } + } - // write element materials - meshfile << "\n\nCELL_DATA " << elems.size() - << "\nSCALARS cellIds int 1" - << "\nLOOKUP_TABLE default \n"; - for(int i=0 ; i< elems.size() ; ++i) - { - meshfile << elementDominantMaterials[i] << " "; - } + meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; + for (int i = 0; i < elems.size(); ++i) + { + int nVerts = meshTopology.evBegins[i + 1] - meshTopology.evBegins[i]; + if (nVerts == 3) + meshfile << "5\n"; + else if (nVerts == 4) + meshfile << "9\n"; + } - meshfile <<"\n"; + // write element materials + meshfile << "\n\nCELL_DATA " << elems.size() + << "\nSCALARS cellIds int 1" + << "\nLOOKUP_TABLE default \n"; + for(int i=0 ; i< elems.size() ; ++i) + { + meshfile << elementDominantMaterials[i] << " "; } + meshfile <<"\n"; +} + //-------------------------------------------------------------------------------- /// Computes the volume fractions of the elements of the original mesh, @@ -371,19 +396,19 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr // Compute the total area of each element of the original mesh and also the area of each new element for (int eID = 0; eID < elems.size(); ++eID) { - int numVertices = data.evBegins[eID + 1] - data.evBegins[eID]; + int numVertices = meshTopology.evBegins[eID + 1] - meshTopology.evBegins[eID]; if (numVertices == 3) { Point2 trianglePoints[3]; for (int i = 0; i < 3; ++i) - trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); } if (numVertices == 4) { Point2 trianglePoints[4]; for (int i = 0; i < 4; ++i) - trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); } diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index eb66116345..c85c18e936 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -42,24 +42,24 @@ namespace mir MIRMesh(MIRMesh* _mesh); // copy constructor ~MIRMesh(); - void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); + void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); - void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps + void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations + void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map - void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices - void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent - void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - void print(); // Print out a variety of useful information about the mesh + void print(); - void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file - void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file - void attachVertexMap(); /// Adds a map of data to the mesh + void readMeshFromFile(std::string filename); + void writeMeshToFile(std::string filename); std::vector > computeOriginalElementVolumeFractions(); private: + void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent + void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); @@ -81,16 +81,12 @@ namespace mir PointMap vertexPositions; // vertex position for each vertex std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + IntMap elementParentIDs; // the ID of the parent element from the original mesh + IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) public: int numMaterials; - - public: - IntMap elementParentIDs; // the ID of the parent element from the original mesh - IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) - - public: - CellData data; // Contains mesh connectivity data, volume fraction data, vertex position data + CellTopologyData meshTopology; }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 36ed346a7a..df9d76b7d8 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -84,41 +84,43 @@ MIRMesh MeshTester::initTestCaseOne() std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; elementVF[BLUE] = blueVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -184,42 +186,43 @@ mir::MIRMesh MeshTester::initTestCaseTwo() std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; elementVF[ORANGE] = orangeVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -268,29 +271,31 @@ mir::MIRMesh MeshTester::initTestCaseThree() std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; elementVF[RED] = redVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 1.0, 2.0 ); - points[1] = mir::Point2( 0.5, 1.0 ); - points[2] = mir::Point2( 1.5, 1.0 ); - points[3] = mir::Point2( 0.0, 0.0 ); - points[4] = mir::Point2( 1.0, 0.0 ); - points[5] = mir::Point2( 2.0, 0.0 ); - } - - - int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 1.0, 2.0 ), + mir::Point2( 0.5, 1.0 ), + mir::Point2( 1.5, 1.0 ), + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ) + }; + + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -344,28 +349,28 @@ mir::MIRMesh MeshTester::initTestCaseFour() mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; @@ -388,18 +393,20 @@ mir::MIRMesh MeshTester::initTestCaseFour() elementVF[GREEN] = greenVolumeFractions; elementVF[BLUE] = blueVolumeFractions; - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -428,35 +435,40 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 for (int i = 0; i < cellData.numElems; ++i) { greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.vertexPositions[cellData.evInds[i * 4 + 0]], - cellData.vertexPositions[cellData.evInds[i * 4 + 1]], - cellData.vertexPositions[cellData.evInds[i * 4 + 2]], - cellData.vertexPositions[cellData.evInds[i * 4 + 3]]); + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 0]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 1]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 2]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 3]]); blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; } elementVF[GREEN] = greenVolumeFractions; elementVF[BLUE] = blueVolumeFractions; - int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; for (int i = 0; i < cellData.numElems; ++i) { - elementParents[i] = i; + elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } + CellTopologyData topology; + topology.evInds = cellData.topology.evInds; + topology.evBegins = cellData.topology.evBegins; + topology.veInds = cellData.topology.veInds; + topology.veBegins = cellData.topology.veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = elementDominantMaterials; + mapData.elementParents = elementParents; + mapData.vertexPositions = cellData.mapData.vertexPositions; + // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; - } //-------------------------------------------------------------------------------- @@ -572,11 +584,11 @@ mir::CellData MeshTester::generateGrid(int n) mir::CellData data; data.numVerts = numVertices; data.numElems = numElements; - data.evInds = evInds; - data.evBegins = evBegins; - data.veInds = veInds; - data.veBegins = veBegins; - data.vertexPositions = points; + data.topology.evInds = evInds; + data.topology.evBegins = evBegins; + data.topology.veInds = veInds; + data.topology.veBegins = veBegins; + data.mapData.vertexPositions = points; // // Print out the results // printf("evInds: { "); @@ -674,10 +686,10 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) // Use the uniform sampling method to generate volume fractions for each material for (int eID = 0; eID < cellData.numElems; ++eID) { - mir::Point2 v0 = cellData.vertexPositions[cellData.evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.vertexPositions[cellData.evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.vertexPositions[cellData.evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.vertexPositions[cellData.evInds[eID * 4 + 3]]; + mir::Point2 v0 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -719,25 +731,30 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } } - int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; for (int i = 0; i < cellData.numElems; ++i) { - elementParents[i] = i; + elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } + CellTopologyData topology; + topology.evInds = cellData.topology.evInds; + topology.evBegins = cellData.topology.evBegins; + topology.veInds = cellData.topology.veInds; + topology.veBegins = cellData.topology.veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = elementDominantMaterials; + mapData.elementParents = elementParents; + mapData.vertexPositions = cellData.mapData.vertexPositions; + // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, materialVolumeFractionsData); return testMesh; - } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 9feba3ec87..d35fe0bfbc 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -27,10 +27,10 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) auto start_time = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); - mir::MIRMesh testMesh = tester.initTestCaseTwo(); + // mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - // mir::MIRMesh testMesh = tester.initTestCaseFive(20, 5); + mir::MIRMesh testMesh = tester.initTestCaseFive(25, 7); auto end_time = Clock::now(); std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; @@ -40,13 +40,13 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) mir::InterfaceReconstructor reconstructor; // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.9); // 5 iterations, 90 percent + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.3); // 5 iterations, 90 percent end_time = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh2.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh5.vtk"); return 0; } From a2c2c44626855d4c4512e8a600b9472735c1cfe4 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 21 Jun 2019 08:39:09 -0700 Subject: [PATCH 011/290] Cleaned up code. Inserted doxygen comments. --- src/axom/mir/CellData.cpp | 49 ++- src/axom/mir/CellData.hpp | 70 +++- src/axom/mir/InterfaceReconstructor.cpp | 352 +++++++++--------- src/axom/mir/InterfaceReconstructor.hpp | 258 +++++++++++-- src/axom/mir/MIRMesh.cpp | 253 ++++++------- src/axom/mir/MIRMesh.hpp | 177 +++++++-- src/axom/mir/MIRMeshTypes.hpp | 6 + src/axom/mir/MeshTester.cpp | 241 ++++++------ src/axom/mir/MeshTester.hpp | 147 +++++++- src/axom/mir/ZooClippingTables.cpp | 4 +- src/axom/mir/ZooClippingTables.hpp | 17 + src/axom/mir/examples/CMakeLists.txt | 2 +- ...Circles.cpp => mir_concentric_circles.cpp} | 16 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 22 +- src/axom/mir/tests/CMakeLists.txt | 1 + .../mir/tests/mir_interface_reconstructor.cpp | 55 +++ 16 files changed, 1115 insertions(+), 555 deletions(-) rename src/axom/mir/examples/{mirConcentricCircles.cpp => mir_concentric_circles.cpp} (83%) create mode 100644 src/axom/mir/tests/mir_interface_reconstructor.cpp diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index d8f35d30e4..4e37813c86 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -26,67 +26,66 @@ namespace mir //-------------------------------------------------------------------------------- - /// Merges the cell data from the given cell into this cell - void CellData::mergeCell(CellData cellToMerge) + void CellData::mergeCell(const CellData& cellToMerge) { // Initialize index offsets - int evBeginsOffset = topology.evInds.size(); - int veBeginsOffset = topology.veInds.size(); + int evBeginsOffset = m_topology.m_evInds.size(); + int veBeginsOffset = m_topology.m_veInds.size(); - int vertexIndexOffset = numVerts; - int elementIndexOffset = numElems; + int vertexIndexOffset = m_numVerts; + int elementIndexOffset = m_numElems; // Merge the cell topology information - for (unsigned long i = 0; i < cellToMerge.topology.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_topology.m_evInds.size(); ++i) { - topology.evInds.push_back(cellToMerge.topology.evInds[i] + vertexIndexOffset); + m_topology.m_evInds.push_back(cellToMerge.m_topology.m_evInds[i] + vertexIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.topology.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.m_topology.m_evBegins.size(); ++i) { - topology.evBegins.push_back(cellToMerge.topology.evBegins[i] + evBeginsOffset); + m_topology.m_evBegins.push_back(cellToMerge.m_topology.m_evBegins[i] + evBeginsOffset); } - for (unsigned long i = 0; i < cellToMerge.topology.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_topology.m_veInds.size(); ++i) { - topology.veInds.push_back(cellToMerge.topology.veInds[i] + elementIndexOffset); + m_topology.m_veInds.push_back(cellToMerge.m_topology.m_veInds[i] + elementIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.topology.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.m_topology.m_veBegins.size(); ++i) { - topology.veBegins.push_back(cellToMerge.topology.veBegins[i] + veBeginsOffset); + m_topology.m_veBegins.push_back(cellToMerge.m_topology.m_veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (unsigned long i = 0; i < cellToMerge.mapData.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_vertexPositions.size(); ++i) { - mapData.vertexPositions.push_back(cellToMerge.mapData.vertexPositions[i]); + m_mapData.m_vertexPositions.push_back(cellToMerge.m_mapData.m_vertexPositions[i]); } // Merge the vertex volume fractions - for (unsigned long matID = 0; matID < mapData.vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < m_mapData.m_vertexVolumeFractions.size(); ++matID) { - for (unsigned long vID = 0; vID < cellToMerge.mapData.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.m_mapData.m_vertexVolumeFractions[matID].size(); ++vID) { - mapData.vertexVolumeFractions[matID].push_back(cellToMerge.mapData.vertexVolumeFractions[matID][vID]); + m_mapData.m_vertexVolumeFractions[matID].push_back(cellToMerge.m_mapData.m_vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (unsigned long i = 0; i < cellToMerge.mapData.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementDominantMaterials.size(); ++i) { - mapData.elementDominantMaterials.push_back(cellToMerge.mapData.elementDominantMaterials[i]); + m_mapData.m_elementDominantMaterials.push_back(cellToMerge.m_mapData.m_elementDominantMaterials[i]); } // Merge the elements' parent ids - for (unsigned long i = 0; i < cellToMerge.mapData.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementParents.size(); ++i) { - mapData.elementParents.push_back(cellToMerge.mapData.elementParents[i]); + m_mapData.m_elementParents.push_back(cellToMerge.m_mapData.m_elementParents[i]); } // Merge the total number of verts and elems in the resulting cell - numVerts += cellToMerge.numVerts; - numElems += cellToMerge.numElems; + m_numVerts += cellToMerge.m_numVerts; + m_numElems += cellToMerge.m_numElems; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 7603582a30..1a681e5d19 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file CellData.hpp + * + * \brief Contains the specifications for the CellData class + * and CellTopologyData and CellMapData structs. + */ + #ifndef __CELL_DATA_H__ #define __CELL_DATA_H__ @@ -19,40 +26,69 @@ namespace axom namespace mir { - // Struct for collecting data that specifies a mesh or cell's connectivity/topology. + /** + * \struct CellTopologyData + * + * \brief Struct for collecting data that specifies a mesh or cell's connectivity/topology. + */ struct CellTopologyData { - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; + std::vector m_evInds; + std::vector m_evBegins; + std::vector m_veInds; + std::vector m_veBegins; }; - // Struct for collecting the data that will populate a mesh's map data structures. + /** + * \struct CellMapData + * + * \brief Struct for collecting the data that will populate a mesh's map data structures. + */ struct CellMapData { - std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap - std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap - std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap - std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + std::vector m_vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap }; - /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). - /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. + /** + * \class CellData + * + * \brief The CellData class represents an arbitrary number of cells that are + * within the same local coordinate system (i.e. share a set of vertices + * and elements). + * + * \detail This class is intended to be used as a helper class to hold intermediate + * data while processing a mesh, and to be used as input to the MIRMesh class + * to fully initialize it. + */ class CellData { public: + /** + * \brief Default constructor. + */ CellData(); + + /** + * \brief Default destructor. + */ ~CellData(); - void mergeCell(CellData cellToMerge); + /** + * \brief Merges the cell data from the given cell into this cell. + * + * \param cellToMerge The cell whose data will be taken and merged in. + */ + void mergeCell(const CellData& cellToMerge); public: - int numVerts; - int numElems; + int m_numVerts; + int m_numElems; - CellTopologyData topology; - CellMapData mapData; + CellTopologyData m_topology; + CellMapData m_mapData; }; } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 6408a1b653..9152dbb30d 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -12,7 +12,6 @@ namespace mir //-------------------------------------------------------------------------------- -/// Default constructor InterfaceReconstructor::InterfaceReconstructor() { @@ -20,7 +19,6 @@ InterfaceReconstructor::InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Destructor InterfaceReconstructor::~InterfaceReconstructor() { @@ -28,46 +26,46 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Reconstructs the material interface and returns the resulting output mesh. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* inputMesh) +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh) { - // Store a pointer to the original mesh - originalMesh = inputMesh; + // Store a reference to the original mesh + m_originalMesh = inputMesh; // Initialize the final mesh to be the same as the input mesh - mir::MIRMesh finalMesh(originalMesh); + mir::MIRMesh finalMesh(m_originalMesh); // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) - for (int matID = 0; matID < originalMesh->numMaterials; ++matID) + for (int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) { // Copy the mesh to be split mir::MIRMesh intermediateMesh(&finalMesh); - // Update the materials upon which the split will occur - int matOne = matID; - // Create an array to store the output of each element being split. - CellData temp_cellData[intermediateMesh.elems.size()]; + CellData temp_cellData[intermediateMesh.m_elems.size()]; // Process/split each element - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 0; eID < intermediateMesh.m_elems.size(); ++eID) { - // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) - int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; - computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); + // Update the materials upon which the split will occur + int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; + int matOne = matID; + + computeClippingPoints(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct - for (int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 1; eID < intermediateMesh.m_elems.size(); ++eID) + { temp_cellData[0].mergeCell(temp_cellData[eID]); + } - mir::VertSet combined_verts(temp_cellData[0].numVerts); - mir::ElemSet combined_elems(temp_cellData[0].numElems); + mir::VertSet combined_verts(temp_cellData[0].m_numVerts); + mir::ElemSet combined_elems(temp_cellData[0].m_numElems); // Create the final, processed mesh mir::MIRMesh processedMesh; - processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.numMaterials, temp_cellData[0].topology, temp_cellData[0].mapData); - processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].mapData.vertexVolumeFractions); + processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.m_numMaterials, temp_cellData[0].m_topology, temp_cellData[0].m_mapData); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].m_mapData.m_vertexVolumeFractions); // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; @@ -79,18 +77,18 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* return finalMesh; } -/// Reconstructs the material interface using an iterative optimization to improve the volume fractions of the resulting mesh. -/// Based on the Meredith and Childs 2010 paper. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent) +//-------------------------------------------------------------------------------- + +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent) { - int numElems = inputMesh->elems.size(); - int numMaterials = inputMesh->numMaterials; + int numElems = inputMesh.m_elems.size(); + int numMaterials = inputMesh.m_numMaterials; // Make a copy of the original input mesh mir::MIRMesh meshToImprove(inputMesh); // Calculate the reconstruction on the unmodified, input mesh - mir::MIRMesh resultingMesh = computeReconstructedInterface(&meshToImprove); + mir::MIRMesh resultingMesh = computeReconstructedInterface(meshToImprove); for (int it = 0; it < numIterations; ++it) { @@ -106,8 +104,8 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: { for (int eID = 0; eID < numElems; ++eID) { - axom::float64 difference = inputMesh->materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; - improvedElementVF[matID].push_back( inputMesh->materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); + axom::float64 difference = inputMesh.m_materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; + improvedElementVF[matID].push_back( inputMesh.m_materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); } } @@ -115,7 +113,7 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); // Calculate the reconstruction on the modified, input mesh - resultingMesh = computeReconstructedInterface(&meshToImprove); + resultingMesh = computeReconstructedInterface(meshToImprove); } return resultingMesh; @@ -123,11 +121,10 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: //-------------------------------------------------------------------------------- -/// Computes the points where the element should be clipped based on the volume fractions of mat one and two. -void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { // Find the vertices associated with each element - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; // Triangle Case if (elementVertices.size() == 3) @@ -143,12 +140,10 @@ void InterfaceReconstructor::computeClippingPoints(const int eID, const int matO //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { - // printf(" Processing QUAD element %d: ", eID); - // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; int upperLeftVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; @@ -156,7 +151,6 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int int upperRightVertex = elementVertices[3]; unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - // printf("caseIndex: %d\n", caseIndex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -210,12 +204,9 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int numVertices = quadClipTable[caseIndex][i]; } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); generateTopologyData(newElements, newVertices, out_cellData); generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); @@ -224,47 +215,30 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int currentDominantMat = NULL_MAT; - for (unsigned long it = 0; it < itr->second.size(); ++it) - { - // int temp_vID = newElements[currentElementIndex][it]; - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the four original vertices of the quad element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } - } - out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); + int dominantMaterial = determineDominantMaterial(Shape::Quad, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } // Determine and store the parent of this element - out_cellData.mapData.elementParents.resize(newElements.size()); + out_cellData.m_mapData.m_elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.mapData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; + int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; } } @@ -280,16 +254,15 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) { - out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } } //-------------------------------------------------------------------------------- -/// Finds the bit map representing the clipping case for a quad. -unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) { // Determine the dominant color at each vertex int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; @@ -300,10 +273,10 @@ unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tem upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + upperLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; } // Create the index into the quad clipping lookup table using the dominant colors at each vertex @@ -318,8 +291,7 @@ unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tem //-------------------------------------------------------------------------------- -/// Performs linear interpolation between the two given vertex positions. -mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t) +mir::Point2 InterfaceReconstructor::interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t) { mir::Point2 interpolatedPoint; interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; @@ -329,24 +301,21 @@ mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertex //-------------------------------------------------------------------------------- -/// Performs linear interpolation between the two given float values -axom::float64 InterfaceReconstructor::lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t) +axom::float64 InterfaceReconstructor::lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t) { return (1 - t) * f0 + t * f1; } //-------------------------------------------------------------------------------- -/// Computes the t value as a percent from vertexOne to vertexTwo based on the two materials given. -/// The t value is the place where this edge should be clipped based on the two materials currently being considered. -axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh) +axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh) { axom::float64 ret = 0.0; - axom::float64 vfMatOneVertexOne = tempMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; // Changed these all from originalMesh->materialVolumeFractionsVertex[][] to tempMesh->... - axom::float64 vfMatTwoVertexOne = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID]; - axom::float64 vfMatOneVertexTwo = tempMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID]; - axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + axom::float64 vfMatOneVertexOne = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexOneID]; + axom::float64 vfMatTwoVertexOne = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexOneID]; + axom::float64 vfMatOneVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexTwoID]; + axom::float64 vfMatTwoVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (matOneID == NULL_MAT) vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; @@ -375,50 +344,48 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte //-------------------------------------------------------------------------------- -/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from a split. -void InterfaceReconstructor::generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData) +void InterfaceReconstructor::generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData) { // Store the evInds and evBegins data in the output vectors int currentEVBeginIndex = 0; for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { // Push the start index of the next element - out_cellData.topology.evBegins.push_back(currentEVBeginIndex); + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); // Push the next element's vertices for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) { - out_cellData.topology.evInds.push_back(itr->second[vIndex]); + out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); ++currentEVBeginIndex; } } // Push the index that occurs after the last vertex - out_cellData.topology.evBegins.push_back(currentEVBeginIndex); + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); // Store the veInds and veBegins data in the output vectors int currentVEBeginIndex = 0; for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { // Push the start index of the vertex's elements - out_cellData.topology.veBegins.push_back(currentVEBeginIndex); + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); // Push the next vertex's elements for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) { - out_cellData.topology.veInds.push_back(itr->second[eIndex]); + out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); ++currentVEBeginIndex; } } // Push the index that occurs after the last element - out_cellData.topology.veBegins.push_back(currentVEBeginIndex); + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); } //-------------------------------------------------------------------------------- -/// Generate the vertex position data for the new elements resulting from spltting a quad. -void InterfaceReconstructor::generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) { std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets @@ -427,70 +394,69 @@ void InterfaceReconstructor::generateVertexPositionsFromQuad(std::mapfirst; if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperLeftVertex]; if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; if (vID == 3) - newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperRightVertex]; if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperLeftVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperRightVertex], verticesClippingTValue[vID]); if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperRightVertex], tempMesh.m_vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); } // Store the positions of the vertices in the return vector for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) { - out_cellData.mapData.vertexPositions.push_back(itr->second); + out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); } } //-------------------------------------------------------------------------------- -/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. -void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { if (vID == 0) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex]); if (vID == 1) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex]); if (vID == 4) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 6) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); if (vID == 7) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); } } } //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; int upperVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; @@ -546,12 +512,9 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const numVertices = triangleClipTable[caseIndex][i]; } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); generateTopologyData(newElements, newVertices, out_cellData); generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); @@ -560,46 +523,30 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int currentDominantMat = NULL_MAT; - for (unsigned long it = 0; it < itr->second.size(); ++it) - { - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the three original vertices of the triangle element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } - } - out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); + int dominantMaterial = determineDominantMaterial(Shape::Triangle, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } // Determine and store the parent of this element - out_cellData.mapData.elementParents.resize(newElements.size()); + out_cellData.m_mapData.m_elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.mapData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; + int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; } } @@ -616,15 +563,14 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (int i = 1; i < 6; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) - out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; + for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } //-------------------------------------------------------------------------------- -/// Finds the bit map representing the clipping case for a triangle. -unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) { // Determine the dominant color at each vertex int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; @@ -635,9 +581,9 @@ unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* upperColor = lowerLeftColor = lowerRightColor = matOneID; if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; } // Create the index into the quad clipping lookup table using the dominant colors at each vertex @@ -651,8 +597,7 @@ unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* //-------------------------------------------------------------------------------- -/// Generate the vertex position data for the new elements resulting from spltting a quad. -void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) { std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets @@ -661,54 +606,107 @@ void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::mapfirst; if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperVertex]; if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; if (vID == 3) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperVertex], verticesClippingTValue[vID]); } // Store the positions of the vertices in the return vector for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) { - out_cellData.mapData.vertexPositions.push_back(itr->second); + out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); } } //-------------------------------------------------------------------------------- -/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. -void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { if (vID == 0) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex]); if (vID == 1) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 4) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + } + } +} + +//-------------------------------------------------------------------------------- + +int InterfaceReconstructor::determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF) +{ + int dominantMaterial = matOne; + + axom::float64 matOneVF = -1.0; + axom::float64 matTwoVF = -1.0; + + if (elementShape == Shape::Triangle) + { + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + if (vID == 0 || vID == 1 || vID == 2) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; + } + } + } + + if (elementShape == Shape::Quad) + { + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + if (vID == 0 || vID == 1 || vID == 2 || vID == 3) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; } } + } + + return dominantMaterial; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index cbda3dcb59..646e1f284e 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file InterfaceReconstructor.hpp + * + * \brief Contains the specification for the InterfaceReconstructor class. + * + */ + #ifndef __INTERFACE_RECONSTRUCTOR_H__ #define __INTERFACE_RECONSTRUCTOR_H__ @@ -12,6 +19,7 @@ #include "MIRMesh.hpp" #include "CellData.hpp" #include "ZooClippingTables.hpp" + #include namespace numerics = axom::numerics; @@ -21,46 +29,246 @@ namespace axom { namespace mir { + + /** + * \class InterfaceReconstructor + * + * \brief A class that contains the functionality for taking an input mesh + * with mixed cells and outputs a mesh with clean cells. + * + * \detail This class requires that the user create a mesh of type MIRMesh + * and pass that into one of the reconstruction methods. There are + * currently two reconstructions methods implemented: one is the + * a zoo-based algorithm described in Meredith 2004, and the other + * is the iterative version described in Meredith and Childs 2010. + */ class InterfaceReconstructor { public: + + /** + * \brief Default constructor. + */ InterfaceReconstructor(); + + + /** + * \brief Default destructor. + */ ~InterfaceReconstructor(); - mir::MIRMesh computeReconstructedInterface(mir::MIRMesh* inputMesh); - mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent); - private: - mir::MIRMesh* originalMesh; + /** + * \brief Performs material interface reconstruction using the zoo-based algorithm. + * + * \param inputMesh The mesh composed of mixed cells. + * + * \return The mesh composed of clean cells. + */ + mir::MIRMesh computeReconstructedInterface(mir::MIRMesh& inputMesh); + + + /** + * \brief Performs material interface reconstruction using an iterative version of the zoo-based algorithm. + * + * \param inputMesh The mesh made up of mixed cells. + * \param numIterations The number of iterations for which to run the algorithm. + * \param percent The percent of the difference to use when modifying the original element volume fractions. + * + * \return The mesh made up of clean cells. + */ + mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent); + + // private: + + /** + * \brief A wrapper function that calls the appropriate splitting method based on the shape of the given element. + * + * \param eID The ID of the element to be split. + * \param matOneID The ID of the first material to use for splitting the element, + * \param matTwoID The ID of the second material to use for splitting the element. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the element. + */ + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Splits the specified quad element into clean cells of the two given material types + * based on the volume fractions of the two materials. + * + * \param eID The ID of the quad element. + * \param matOneID The ID of the first material to use for splitting the quad, + * \param matTwoID The ID of the second material to use for splitting the quad. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the quad. + */ + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Splits the specified triangle element into clean cells of the two given material types + * based on the volume fractions of the two materials. + * + * \param eID The ID of the triangle element. + * \param matOneID The ID of the first material to use for splitting the triangle, + * \param matTwoID The ID of the second material to use for splitting the triangle. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the triangle. + */ + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Performs linear interpolation between the two vertex positions. + * + * \param vertexOnePos The position of the first vertex. + * \param vertexTwoPos The position of the second vertex. + * \param t The percent of the distance from vertex one to vertex two to interpolate at. + * + * \return The interpolated position. + */ + mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t); + + + /** + * \brief Performs linear interpolation between the two given float values. + * + * \param f0 The first float value. + * \param f1 The second float value. + * \param t The percent of the distance from the first float value to the second. + * + * \return The interpolated value. + */ + axom::float64 lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t); + + + /** + * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. + * + * \param vertexOneID The ID of the first vertex. + * \param vertexTwoID The ID of the second vertex. + * \param matOneID The ID of the first material to use for interpolating between. + * \param matTwoID The ID of the second material to use for interpolating between. + * \param tempMesh The intermediate mesh that is currently being processed. + * + * \return The t value, which is the percent distance from vertex one to vertex two where the two volume fractions are equal. + */ + axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh); + + + /** + * \brief Generates the topology of the new elements resulting from a split. + * + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param out_cellData Container to store the topology data of the generated elements. + */ + void generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData); + - private: + /** + * \brief Calculates the bit map representing the clipping case for a quad. + * + * \param tempMesh The intermediate mesh that is currently being processed. + * \param matOneID The ID of the first material being used for splitting. + * \param matTwoID The ID of the second material being used for splitting. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * + * \return The bitmap representing the clipping case. + */ + unsigned int determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - // general clipping function - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Generate the vertex position data for the new elements resulting from splitting a quad. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex position data of the generated elements. + */ + void generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - // triangle clipping function - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + */ + void generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh&tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - // quad clipping functions - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Calculates the bit map representing the clipping case for a triangle. + * + * \param tempMesh The intermediate mesh that is currently being processed. + * \param matOneID The ID of the first material being used for splitting. + * \param matTwoID The ID of the second material being used for splitting. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * + * \return The bitmap representing the clipping case. + */ + unsigned int determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - // clipping interpolation functions - mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); - axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); - axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + /** + * \brief Generate the vertex position data for the new elements resulting from splitting a triangle. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex position data of the generated elements. + */ + void generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - // general helper functions - void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); + /** + * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a triangle. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + */ + void generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - // quad clipping points helper functions - unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); - void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + /** + * \brief Determines the mroe dominant material of the two given for the given element. + * + * \param elementShape An enumerator denoting the element's shape. + * \param vertexIDs A list of vertex IDs into the vertexVF param. + * \param matOne The ID of the first material. + * \param matTwo The ID of the second material. + * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. + * + * \return The ID of the dominant material of the element. + * + * \note The dominant element for the 2D cases will be the same as the material present at one of the + * original vertices that existed prior to the split. So, if you can find this vertex and its dominant + * material, then you know the dominant material of this new element. + */ + int determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF); - // triangle clipping points helper functions - unsigned int determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); - void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); + private: + mir::MIRMesh m_originalMesh; }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 9243bda9ad..6e7c687dcf 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -19,22 +19,22 @@ MIRMesh::MIRMesh() //-------------------------------------------------------------------------------- -MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor +MIRMesh::MIRMesh(MIRMesh* _mesh) { - meshTopology.evInds = _mesh->meshTopology.evInds; - meshTopology.evBegins = _mesh->meshTopology.evBegins; - meshTopology.veInds = _mesh->meshTopology.veInds; - meshTopology.veBegins = _mesh->meshTopology.veBegins; - verts = _mesh->verts; - elems = _mesh->elems; - bdry = _mesh->bdry; - cobdry = _mesh->cobdry; - vertexPositions = _mesh->vertexPositions; - materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; - materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; - elementParentIDs = _mesh->elementParentIDs; - elementDominantMaterials = _mesh->elementDominantMaterials; - numMaterials = _mesh->numMaterials; + m_meshTopology.m_evInds = _mesh->m_meshTopology.m_evInds; + m_meshTopology.m_evBegins = _mesh->m_meshTopology.m_evBegins; + m_meshTopology.m_veInds = _mesh->m_meshTopology.m_veInds; + m_meshTopology.m_veBegins = _mesh->m_meshTopology.m_veBegins; + m_verts = _mesh->m_verts; + m_elems = _mesh->m_elems; + m_bdry = _mesh->m_bdry; + m_cobdry = _mesh->m_cobdry; + m_vertexPositions = _mesh->m_vertexPositions; + m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; + m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; + m_elementParentIDs = _mesh->m_elementParentIDs; + m_elementDominantMaterials = _mesh->m_elementDominantMaterials; + m_numMaterials = _mesh->m_numMaterials; } //-------------------------------------------------------------------------------- @@ -46,55 +46,53 @@ MIRMesh::~MIRMesh() //-------------------------------------------------------------------------------- -/// Initialize a mesh with the given topology and data. void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) { // Initialize the vertex and element sets - verts = _verts; - elems = _elems; + m_verts = _verts; + m_elems = _elems; - numMaterials = _numMaterials; + m_numMaterials = _numMaterials; // Intialize the mesh topology - meshTopology.evInds = _topology.evInds; - meshTopology.evBegins = _topology.evBegins; - meshTopology.veInds = _topology.veInds; - meshTopology.veBegins = _topology.veBegins; + m_meshTopology.m_evInds = _topology.m_evInds; + m_meshTopology.m_evBegins = _topology.m_evBegins; + m_meshTopology.m_veInds = _topology.m_veInds; + m_meshTopology.m_veBegins = _topology.m_veBegins; // Initialize the mesh relations constructMeshRelations(); // Initialize the mesh's data maps - constructVertexPositionMap(_mapData.vertexPositions.data()); - constructElementParentMap(_mapData.elementParents.data()); - constructElementDominantMaterialMap(_mapData.elementDominantMaterials); + constructVertexPositionMap(_mapData.m_vertexPositions.data()); + constructElementParentMap(_mapData.m_elementParents.data()); + constructElementDominantMaterialMap(_mapData.m_elementDominantMaterials); // Initialize the element and vertex volume fraction maps if (_elementVF.size() > 0) constructMeshVolumeFractionsMaps(_elementVF); // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + SLIC_ASSERT_MSG( m_verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( m_elems.isValid(), "Element set is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the mesh boundary and coboundary relations void MIRMesh::constructMeshRelations() { // construct boundary relation from elements to vertices using variable cardinality { using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) + m_bdry = RelationBuilder() + .fromSet( &m_elems ) + .toSet( &m_verts ) .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( meshTopology.evBegins.data() ) ) + .size( m_elems.size() ) + .data( m_meshTopology.m_evBegins.data() ) ) .indices ( RelationBuilder::IndicesSetBuilder() - .size( meshTopology.evInds.size() ) - .data( meshTopology.evInds.data() ) ); + .size( m_meshTopology.m_evInds.size() ) + .data( m_meshTopology.m_evInds.data() ) ); } @@ -102,206 +100,200 @@ void MIRMesh::constructMeshRelations() // _quadmesh_example_construct_cobdry_relation_start // construct coboundary relation from vertices to elements using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) + m_cobdry = RelationBuilder() + .fromSet( &m_verts ) + .toSet( &m_elems ) .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( meshTopology.veBegins.data() ) ) + .size( m_verts.size() ) + .data( m_meshTopology.m_veBegins.data() ) ) .indices( RelationBuilder::IndicesSetBuilder() - .size( meshTopology.veInds.size() ) - .data( meshTopology.veInds.data() ) ); + .size( m_meshTopology.m_veInds.size() ) + .data( m_meshTopology.m_veInds.data() ) ); // _quadmesh_example_construct_cobdry_relation_end } - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + SLIC_INFO("Elem-Vert relation has size " << m_bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << m_cobdry.totalSize()); } //-------------------------------------------------------------------------------- -/// Construct the and the element and vertex volume fraction data given the element volume fraction data void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) { // Clear the old maps - materialVolumeFractionsElement.clear(); - materialVolumeFractionsVertex.clear(); + m_materialVolumeFractionsElement.clear(); + m_materialVolumeFractionsVertex.clear(); // Initialize the maps for all of the materials with the input volume fraction data for each material - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + m_materialVolumeFractionsElement.push_back(ScalarMap( &m_elems )); // Copy the data for the current material - for (int eID = 0; eID < elems.size(); ++eID) + for (int eID = 0; eID < m_elems.size(); ++eID) { - materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; + m_materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); } // Initialize the maps for all of the vertex volume fractions - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the new map for the volume fractions - materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts ) ); // Calculate the average volume fraction value for the current vertex for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { // Compute the per vertex volume fractions for the green material axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; + auto vertexElements = m_cobdry[vID]; for (int i = 0; i < vertexElements.size(); ++i) { auto eID = vertexElements[i]; - sum += materialVolumeFractionsElement[matID][eID]; + sum += m_materialVolumeFractionsElement[matID][eID]; } - materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + m_materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } } //-------------------------------------------------------------------------------- -/// Construct the vertex volume fraction data given vertex volume fraction data void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > vertexVF) { // Initialize the maps for all of the materials with the input volume fraction data for each vertex - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - materialVolumeFractionsVertex.push_back(ScalarMap( &verts )); + m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts )); // Copy the data for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { - materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; + m_materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } } //-------------------------------------------------------------------------------- -/// Constucts the positions map on the vertices void MIRMesh::constructVertexPositionMap(Point2* data) { // construct the position map on the vertices - vertexPositions = PointMap( &verts ); + m_vertexPositions = PointMap( &m_verts ); - for (int vID = 0; vID < verts.size(); ++vID) - vertexPositions[vID] = data[vID]; + for (int vID = 0; vID < m_verts.size(); ++vID) + m_vertexPositions[vID] = data[vID]; - SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + SLIC_ASSERT_MSG( m_vertexPositions.isValid(), "Position map is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the elementParentsID map of the ID of the parent element in the original mesh for each generated element void MIRMesh::constructElementParentMap(int* elementParents) { // Initialize the map for the elements' parent IDs - elementParentIDs = IntMap( &elems ); + m_elementParentIDs = IntMap( &m_elems ); // Copy the data for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementParentIDs[eID] = elementParents[eID]; + for (int eID = 0; eID < m_elems.size(); ++eID) + m_elementParentIDs[eID] = elementParents[eID]; - SLIC_ASSERT_MSG( elementParentIDs.isValid(), "Element parent map is not valid."); + SLIC_ASSERT_MSG( m_elementParentIDs.isValid(), "Element parent map is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the elementDominantMaterials map of each element's single most dominant material void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) { // Initialize the map for the elements' dominant colors - elementDominantMaterials = IntMap( &elems ); + m_elementDominantMaterials = IntMap( &m_elems ); // Copy the dat for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementDominantMaterials[eID] = dominantMaterials[eID]; + for (int eID = 0; eID < m_elems.size(); ++eID) + m_elementDominantMaterials[eID] = dominantMaterials[eID]; - SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); + SLIC_ASSERT_MSG( m_elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); } //-------------------------------------------------------------------------------- -/// Print out the properties of the mesh. void MIRMesh::print() { printf("\n------------------------Printing Mesh Information:------------------------\n"); - printf("number of vertices: %d\n", verts.size()); - printf("number of elements: %d\n", elems.size()); - printf("number of materials: %d\n", numMaterials); + printf("number of vertices: %d\n", m_verts.size()); + printf("number of elements: %d\n", m_elems.size()); + printf("number of materials: %d\n", m_numMaterials); printf("evInds: { "); - for (unsigned long i = 0; i < meshTopology.evInds.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_evInds.size(); i++) { - printf("%d ", meshTopology.evInds[i]); + printf("%d ", m_meshTopology.m_evInds[i]); } printf("}\n"); printf("evBegins: { "); - for (unsigned long i = 0; i < meshTopology.evBegins.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_evBegins.size(); i++) { - printf("%d ", meshTopology.evBegins[i]); + printf("%d ", m_meshTopology.m_evBegins[i]); } printf("}\n"); printf("veInds: { "); - for (unsigned long i = 0; i < meshTopology.veInds.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_veInds.size(); i++) { - printf("%d ", meshTopology.veInds[i]); + printf("%d ", m_meshTopology.m_veInds[i]); } printf("}\n"); printf("veBegins: { "); - for (unsigned long i = 0; i < meshTopology.veBegins.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_veBegins.size(); i++) { - printf("%d ", meshTopology.veBegins[i]); + printf("%d ", m_meshTopology.m_veBegins[i]); } printf("}\n"); printf("vertexPositions: { "); - for (int i = 0; i < verts.size(); ++i) + for (int i = 0; i < m_verts.size(); ++i) { - printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + printf("{%.2f, %.2f} ", m_vertexPositions[i].m_x, m_vertexPositions[i].m_y); } printf("}\n"); printf("elementParentIDs: { "); - for (int i = 0; i < elems.size(); ++i) + for (int i = 0; i < m_elems.size(); ++i) { - printf("%d ", elementParentIDs[i]); + printf("%d ", m_elementParentIDs[i]); } printf("}\n"); printf("elementDominantMaterials: { "); - for (int i = 0; i < elems.size(); ++i) + for (int i = 0; i < m_elems.size(); ++i) { - printf("%d ", elementDominantMaterials[i]); + printf("%d ", m_elementDominantMaterials[i]); } printf("}\n"); printf("vertexVolumeFractions: { \n"); - for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { printf(" { "); - for (int j = 0; j < verts.size(); ++j) + for (int j = 0; j < m_verts.size(); ++j) { - printf("%.3f, ", materialVolumeFractionsVertex[i][j]); + printf("%.3f, ", m_materialVolumeFractionsVertex[i][j]); } printf("}\n"); } @@ -311,8 +303,6 @@ void MIRMesh::print() //-------------------------------------------------------------------------------- -/// Reads in and constructs a mesh from the given file -/// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file void MIRMesh::readMeshFromFile(std::string filename) { printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); @@ -330,7 +320,6 @@ void MIRMesh::readMeshFromFile(std::string filename) //-------------------------------------------------------------------------------- -/// Writes out the mesh to a file void MIRMesh::writeMeshToFile(std::string filename) { std::ofstream meshfile; @@ -342,30 +331,30 @@ void MIRMesh::writeMeshToFile(std::string filename) << "vtk output\n" << "ASCII\n" << "DATASET UNSTRUCTURED_GRID\n" - << "POINTS " << verts.size() << " double\n"; + << "POINTS " << m_verts.size() << " double\n"; // write positions - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { - meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + meshfile << m_vertexPositions[vID].m_x << " " << m_vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D } - meshfile << "\nCELLS " << elems.size() << " " << meshTopology.evInds.size() + elems.size(); - for (int i = 0; i < elems.size(); ++i) + meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); + for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = meshTopology.evBegins[i+1] - meshTopology.evBegins[i]; + int nVerts = m_meshTopology.m_evBegins[i+1] - m_meshTopology.m_evBegins[i]; meshfile << "\n" << nVerts; for (int j = 0; j < nVerts; ++j) { - int startIndex = meshTopology.evBegins[i]; - meshfile << " " << meshTopology.evInds[startIndex + j]; + int startIndex = m_meshTopology.m_evBegins[i]; + meshfile << " " << m_meshTopology.m_evInds[startIndex + j]; } } - meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; - for (int i = 0; i < elems.size(); ++i) + meshfile << "\n\nCELL_TYPES " << m_elems.size() << "\n"; + for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = meshTopology.evBegins[i + 1] - meshTopology.evBegins[i]; + int nVerts = m_meshTopology.m_evBegins[i + 1] - m_meshTopology.m_evBegins[i]; if (nVerts == 3) meshfile << "5\n"; else if (nVerts == 4) @@ -373,12 +362,12 @@ void MIRMesh::writeMeshToFile(std::string filename) } // write element materials - meshfile << "\n\nCELL_DATA " << elems.size() + meshfile << "\n\nCELL_DATA " << m_elems.size() << "\nSCALARS cellIds int 1" << "\nLOOKUP_TABLE default \n"; - for(int i=0 ; i< elems.size() ; ++i) + for(int i=0 ; i< m_elems.size() ; ++i) { - meshfile << elementDominantMaterials[i] << " "; + meshfile << m_elementDominantMaterials[i] << " "; } meshfile <<"\n"; @@ -386,7 +375,6 @@ void MIRMesh::writeMeshToFile(std::string filename) //-------------------------------------------------------------------------------- -/// Computes the volume fractions of the elements of the original mesh, std::vector > MIRMesh::computeOriginalElementVolumeFractions() { std::map totalAreaOriginalElements; // the total area of the original elements @@ -394,39 +382,39 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr std::map numChildren; // the number of child elements generated from one of the original elements // Compute the total area of each element of the original mesh and also the area of each new element - for (int eID = 0; eID < elems.size(); ++eID) + for (int eID = 0; eID < m_elems.size(); ++eID) { - int numVertices = meshTopology.evBegins[eID + 1] - meshTopology.evBegins[eID]; + int numVertices = m_meshTopology.m_evBegins[eID + 1] - m_meshTopology.m_evBegins[eID]; if (numVertices == 3) { Point2 trianglePoints[3]; for (int i = 0; i < 3; ++i) - trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; + trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); } if (numVertices == 4) { Point2 trianglePoints[4]; for (int i = 0; i < 4; ++i) - trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; + trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); } - totalAreaOriginalElements[ elementParentIDs[eID] ] += newElementAreas[eID]; - numChildren[ elementParentIDs[eID] ] += .4; + totalAreaOriginalElements[ m_elementParentIDs[eID] ] += newElementAreas[eID]; + numChildren[ m_elementParentIDs[eID] ] += .4; } // Intialize the element volume fraction vectors std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction - elementVolumeFractions.resize( numMaterials ); + elementVolumeFractions.resize( m_numMaterials ); for (unsigned long i = 0; i < elementVolumeFractions.size(); ++i) elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); // Compute the volume fractions for each of the original mesh elements for (auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) { - int parentElementID = elementParentIDs[itr->first]; - int materialID = elementDominantMaterials[itr->first]; + int parentElementID = m_elementParentIDs[itr->first]; + int materialID = m_elementDominantMaterials[itr->first]; elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); } @@ -435,7 +423,6 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr //-------------------------------------------------------------------------------- -/// Computes the area of the triangle defined by the given three vertex positions. Uses Heron's formula. axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 @@ -449,8 +436,6 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- -/// Computes the area of the quad defined by the given four vertex positions. -/// Note: It is assumed the points are given in consecutive, counter-clockwise order. axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index c85c18e936..63b23d2ccc 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MIRMesh.hpp + * + * \brief Contains the specification for the MIRMesh class. + * + */ + #ifndef __MIR_MESH_H__ #define __MIR_MESH_H__ @@ -28,65 +35,163 @@ namespace axom namespace mir { - #define NULL_MAT -1 + const int NULL_MAT = -1; //-------------------------------------------------------------------------------- + /** + * \class MIRMesh + * + * \brief The MIRMesh class represents a finite element mesh containing element volume fractions. + * + * \detail This class is meant to be used in conjunction with the InterfaceReconstructor class + * to process the mesh. + * + */ class MIRMesh { - /**************************************************************** - * MESH FUNCTIONS - ****************************************************************/ public: + /** + * \brief Default constructor. + */ MIRMesh(); - MIRMesh(MIRMesh* _mesh); // copy constructor - ~MIRMesh(); - - void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); - - void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps - void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map - void print(); + /** + * \brief Copy constrcutor. + */ + MIRMesh(MIRMesh* _mesh); - void readMeshFromFile(std::string filename); - void writeMeshToFile(std::string filename); + /** + * \brief Default destructor. + */ + ~MIRMesh(); - std::vector > computeOriginalElementVolumeFractions(); + /** + * \brief Initializes a mesh with the provided data and topology. + * + * \param _verts The set of vertices of the mesh. + * \param _elems The set of elements of the mesh. + * \param _numMaterials The number of materials present in the mesh. + * \param _topology The topology/connectivity of the mesh. + * \param _mapData The data used to initialized the maps associated with the vertex and element sets. + * \param _elementVF The volume fractions of each element. Note that this is an optional parameter. + */ + void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); + + /** + * \brief Constructs the mesh boundary and coboundary relations. + * + * \note The boundary relation is from each element to its vertices. + * \note The coboundary relation is from each vertex to its elements. + */ + void constructMeshRelations(); + + /** + * \brief Constructs the element and vertex volume fraction maps. + * + * \param elementVF The volume fractions of each element. + */ + void constructMeshVolumeFractionsMaps(std::vector > elementVF); + + /** + * \brief Constructs the vertex volume fraction map. + * + * \param vertexVF The volume fractions of each vertex. + */ + void constructMeshVolumeFractionsVertex(std::vector > vertexVF); + + /** + * \brief Prints out the data contained within this mesh in a nice format. + */ + void print(); + + /** + * \brief Reads in a mesh specified by the given file. + * + * \param filename The location where the mesh file will be read from. + */ + void readMeshFromFile(std::string filename); + + /** + * \brief Writes out the mesh to the given file. + * + * \param filename The location where the mesh file will be written. + * + * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. + */ + void writeMeshToFile(std::string filename); + + + /** + * \brief Computes the volume fractions of the elements of the original mesh. + */ + std::vector > computeOriginalElementVolumeFractions(); private: - void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices - void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent - void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); - axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + /** + * \brief Constucts the positions map on the vertices. + * + * \param data The array of position data for each vertex. + */ + void constructVertexPositionMap(Point2* data); + + /** + * \brief Constructs the map of elements to their original element parent. + * + * \param cellParents The array of parent IDs for each element of the mesh. + */ + void constructElementParentMap(int* cellParents); + + /** + * \brief Constructs the map of elements to their dominant materials. + * + * \param dominantMaterials A vector of material ids that are the dominant material of each element. + */ + void constructElementDominantMaterialMap(std::vector dominantMaterials); + + /** + * \brief Computes the area of the triangle defined by the given three vertex positions using Heron's formula. + * + * \param p0 The position of the first vertex. + * \param p1 The position of the second vertex. + * \param p2 The position of the third vertex. + */ + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + + /** + * \brief Computes the area of the quad defined by the given four vertex positions. + * + * \param p0 The position of the first vertex. + * \param p1 The position of the second vertex. + * \param p2 The position of the third vertex. + * \param p3 The position of the fourth vertex. + * + * \note It is assumed that the points are given in consecutive, counter-clockwise order. + */ + axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); /**************************************************************** * VARIABLES ****************************************************************/ public: // Mesh Set Definitions - VertSet verts; // the set of vertices in the mesh - ElemSet elems; // the set of elements in the mesh + VertSet m_verts; // the set of vertices in the mesh + ElemSet m_elems; // the set of elements in the mesh - public: // Mesh Relation Definitions - ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements + ElemToVertRelation m_bdry; // Boundary relation from elements to vertices + VertToElemRelation m_cobdry; // Coboundary relation from vertices to elements - public: // Mesh Map Definitions - PointMap vertexPositions; // vertex position for each vertex - std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element - std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex - IntMap elementParentIDs; // the ID of the parent element from the original mesh - IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) - - public: - int numMaterials; - CellTopologyData meshTopology; + PointMap m_vertexPositions; // vertex position for each vertex + std::vector m_materialVolumeFractionsElement; // the volume fractions of each material for each element + std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + IntMap m_elementParentIDs; // the ID of the parent element from the original mesh + IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + + int m_numMaterials; // the number of materials present in the mesh + CellTopologyData m_meshTopology; // the topology/connectivity of the mesh }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index d51aecd177..b6fc18ec28 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -3,6 +3,12 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MIRMeshTypes.hpp + * + * \brief Contains the specifications for types aliases used throughout the MIR component. + */ + #ifndef __MIR_MESH_TYPES_H__ #define __MIR_MESH_TYPES_H__ diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index df9d76b7d8..690c9ad35b 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -26,7 +26,6 @@ MeshTester::~MeshTester() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 1 (from Meredith 2004) MIRMesh MeshTester::initTestCaseOne() { int numElements = 9; @@ -108,15 +107,15 @@ MIRMesh MeshTester::initTestCaseOne() }; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -127,7 +126,6 @@ MIRMesh MeshTester::initTestCaseOne() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) mir::MIRMesh MeshTester::initTestCaseTwo() { int numElements = 9; @@ -210,15 +208,15 @@ mir::MIRMesh MeshTester::initTestCaseTwo() }; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -229,7 +227,6 @@ mir::MIRMesh MeshTester::initTestCaseTwo() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. mir::MIRMesh MeshTester::initTestCaseThree() { int numElements = 4; @@ -283,15 +280,15 @@ mir::MIRMesh MeshTester::initTestCaseThree() CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -302,7 +299,6 @@ mir::MIRMesh MeshTester::initTestCaseThree() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 4, a 3x3 grid with a circle of one material in the middle mir::MIRMesh MeshTester::initTestCaseFour() { int numElements = 9; @@ -394,15 +390,15 @@ mir::MIRMesh MeshTester::initTestCaseFour() elementVF[BLUE] = blueVolumeFractions; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -413,32 +409,31 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- -/// Intializes a uniform grid with a circle of one material surrounded by another material. mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; std::vector > elementVF; elementVF.resize(numMaterials); - std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.numElems); - std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.numElems); + std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.m_numElems); + std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.m_numElems); // Generate the element volume fractions for the circle int numMonteCarloSamples = 100; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 0]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 1]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 2]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 3]]); + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 0]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 1]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 2]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 3]]); blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; } @@ -447,22 +442,22 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } CellTopologyData topology; - topology.evInds = cellData.topology.evInds; - topology.evBegins = cellData.topology.evBegins; - topology.veInds = cellData.topology.veInds; - topology.veBegins = cellData.topology.veBegins; + topology.m_evInds = cellData.m_topology.m_evInds; + topology.m_evBegins = cellData.m_topology.m_evBegins; + topology.m_veInds = cellData.m_topology.m_veInds; + topology.m_veBegins = cellData.m_topology.m_veBegins; CellMapData mapData; - mapData.elementDominantMaterials = elementDominantMaterials; - mapData.elementParents = elementParents; - mapData.vertexPositions = cellData.mapData.vertexPositions; + mapData.m_elementDominantMaterials = elementDominantMaterials; + mapData.m_elementParents = elementParents; + mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; // Build the mesh mir::MIRMesh testMesh; @@ -473,32 +468,31 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 //-------------------------------------------------------------------------------- -// Assumes the quad vertices are ordered the same as the clipping case. -axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(q_p0, circle_center); - axom::float64 distP1 = distance(q_p1, circle_center); - axom::float64 distP2 = distance(q_p2, circle_center); - axom::float64 distP3 = distance(q_p3, circle_center); + axom::float64 distP0 = distance(quadP0, circleCenter); + axom::float64 distP1 = distance(quadP1, circleCenter); + axom::float64 distP2 = distance(quadP2, circleCenter); + axom::float64 distP3 = distance(quadP3, circleCenter); - if (distP0 < circle_radius && distP1 < circle_radius && distP2 < circle_radius && distP3 < circle_radius) + if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) { // The entire quad overlaps the circle return 1.0; } - else if (distP0 < circle_radius || distP1 < circle_radius || distP2 < circle_radius || distP3 < circle_radius) + else if (distP0 < circleRadius || distP1 < circleRadius || distP2 < circleRadius || distP3 < circleRadius) { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = abs(q_p2.m_x - q_p1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(q_p0.m_y - q_p1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = abs(quadP2.m_x - quadP1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(quadP0.m_y - quadP1.m_y) / (double) (gridSize - 1); int countOverlap = 0; for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + q_p1.m_x, delta_y * y + q_p1.m_y); - if (distance(samplePoint, circle_center) < circle_radius) + mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); + if (distance(samplePoint, circleCenter) < circleRadius) ++countOverlap; } } @@ -513,20 +507,19 @@ axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::P //-------------------------------------------------------------------------------- -/// Generates a 2D uniform grid with n x n elements. -mir::CellData MeshTester::generateGrid(int n) +mir::CellData MeshTester::generateGrid(int gridSize) { // Generate the topology for a uniform quad mesh with n x n elements automatically - int numElements = n * n; - int numVertices = (n + 1) * (n + 1); + int numElements = gridSize * gridSize; + int numVertices = (gridSize + 1) * (gridSize + 1); // Generate the evInds std::vector evInds; for (int eID = 0; eID < numElements; ++eID) { - int row = eID / n; // note the integer division - int vertsPerRow = n + 1; - int elemsPerRow = n; + int row = eID / gridSize; // note the integer division + int vertsPerRow = gridSize + 1; + int elemsPerRow = gridSize; evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 0); evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); @@ -537,7 +530,7 @@ mir::CellData MeshTester::generateGrid(int n) // Generate the evBegins std::vector evBegins; evBegins.push_back(0); - for (int i = 0; i < n * n; ++i) + for (int i = 0; i < numElements; ++i) { evBegins.push_back((i + 1) * 4); } @@ -573,22 +566,22 @@ mir::CellData MeshTester::generateGrid(int n) // Generate the vertex positions std::vector points; - for (int y = n; y > -1; --y) + for (int y = gridSize; y > -1; --y) { - for (int x = 0; x < n + 1; ++x) + for (int x = 0; x < gridSize + 1; ++x) { points.push_back(mir::Point2(x, y)); } } mir::CellData data; - data.numVerts = numVertices; - data.numElems = numElements; - data.topology.evInds = evInds; - data.topology.evBegins = evBegins; - data.topology.veInds = veInds; - data.topology.veBegins = veBegins; - data.mapData.vertexPositions = points; + data.m_numVerts = numVertices; + data.m_numElems = numElements; + data.m_topology.m_evInds = evInds; + data.m_topology.m_evBegins = evBegins; + data.m_topology.m_veInds = veInds; + data.m_topology.m_veBegins = veBegins; + data.m_mapData.m_vertexPositions = points; // // Print out the results // printf("evInds: { "); @@ -633,7 +626,6 @@ mir::CellData MeshTester::generateGrid(int n) //-------------------------------------------------------------------------------- -/// Calculate the distance between the two given points. axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) { return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); @@ -641,16 +633,14 @@ axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) //-------------------------------------------------------------------------------- -/// Multiple materials, multiple concentric circles. -/// Note: Assumes each circle has a unique material. mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set // Generate the element volume fractions with concentric circles int numMaterials = numCircles + 1; @@ -679,17 +669,17 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) for (int i = 0; i < numMaterials; ++i) { std::vector tempVec; - tempVec.resize(cellData.numElems); + tempVec.resize(cellData.m_numElems); materialVolumeFractionsData.push_back(tempVec); } // Use the uniform sampling method to generate volume fractions for each material - for (int eID = 0; eID < cellData.numElems; ++eID) + for (int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2 v0 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 3]]; + mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -733,22 +723,22 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } CellTopologyData topology; - topology.evInds = cellData.topology.evInds; - topology.evBegins = cellData.topology.evBegins; - topology.veInds = cellData.topology.veInds; - topology.veBegins = cellData.topology.veBegins; + topology.m_evInds = cellData.m_topology.m_evInds; + topology.m_evBegins = cellData.m_topology.m_evBegins; + topology.m_veInds = cellData.m_topology.m_veInds; + topology.m_veBegins = cellData.m_topology.m_veBegins; CellMapData mapData; - mapData.elementDominantMaterials = elementDominantMaterials; - mapData.elementParents = elementParents; - mapData.vertexPositions = cellData.mapData.vertexPositions; + mapData.m_elementDominantMaterials = elementDominantMaterials; + mapData.m_elementParents = elementParents; + mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; // Build the mesh mir::MIRMesh testMesh; @@ -759,24 +749,23 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) //-------------------------------------------------------------------------------- -/// Calculate the number of corners of the quad that are within the circle -int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(q_p0, circle_center); - axom::float64 distP1 = distance(q_p1, circle_center); - axom::float64 distP2 = distance(q_p2, circle_center); - axom::float64 distP3 = distance(q_p3, circle_center); + axom::float64 distP0 = distance(quadP0, circleCenter); + axom::float64 distP1 = distance(quadP1, circleCenter); + axom::float64 distP2 = distance(quadP2, circleCenter); + axom::float64 distP3 = distance(quadP3, circleCenter); int numCorners = 0; - if (distP0 < circle_radius) + if (distP0 < circleRadius) numCorners++; - if (distP1 < circle_radius) + if (distP1 < circleRadius) numCorners++; - if (distP2 < circle_radius) + if (distP2 < circleRadius) numCorners++; - if (distP3 < circle_radius) + if (distP3 < circleRadius) numCorners++; return numCorners; @@ -784,5 +773,41 @@ int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float //-------------------------------------------------------------------------------- +mir::MIRMesh MeshTester::initQuadClippingTestMesh() +{ + // Generate the mesh topology + int gridSize = 3; + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + + int numMaterials = 2; + + std::vector > elementVF; + elementVF.resize(numMaterials); + elementVF[0] = {1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0}; + elementVF[1] = {0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0}; + + std::vector elementParents; + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.m_numElems; ++i) + { + elementParents.push_back(i); + elementDominantMaterials.push_back(NULL_MAT); + } + + cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; + cellData.m_mapData.m_elementParents = elementParents; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, elementVF); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + } } diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index b89cfa2b26..9641fec6a1 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MeshTester.hpp + * + * \brief Contains the specification for the MeshTester class. + * + */ + #ifndef __MESH_TESTER_H__ #define __MESH_TESTER_H__ @@ -20,26 +27,144 @@ namespace axom { namespace mir { + /** + * \class MeshTester + * + * \brief A class used to generate MIRMeshs with specific properties so + * that the reconstruction output can be validated visually. + * + */ class MeshTester { public: + /** + * \brief Default constructor. + */ MeshTester(); + + /** + * \brief Default destructor. + */ ~MeshTester(); public: - MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials - mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials - mir::MIRMesh initTestCaseThree(); // triforce, 2 materials - mir::MIRMesh initTestCaseFour(); // 3x3, 2 materials, circle of material - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); - mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles - + /** + * \brief Initializes an MIRMesh based on the example from Meredith 2004 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 2 materials. + * + * \return The generated mesh. + */ + MIRMesh initTestCaseOne(); + + /** + * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 3 materials. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseTwo(); + + /** + * \brief Initializes an MIRMesh used for testing triangle clipping cases. + * + * \note The mesh is a set of four triangles with 2 materials + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseThree(); + + /** + * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and has a single circle in the center composed + * of one material and is surrounded by a second material. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseFour(); + + /** + * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform grid. + * + * \param gridSize The number of elements in the width and the height of the uniform grid. + * \param numCircles The number of concentric circles that are centered in the grid. + * + * \note Each circle is composed of a different material. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles + + /** + * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * + * \return The generated mesh. + */ + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + + /** + * \brief Intializes a mesh to be used for validating the results of quad clipping. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such + * that the mesh would be split horizontally through the middle. + * + * \return The generated mesh. + */ + mir::MIRMesh initQuadClippingTestMesh(); + private: - axom::float64 distance(mir::Point2 p0, mir::Point2 p1); - mir::CellData generateGrid(int n); - axom::float64 calculatePercentOverlapMonteCarlo(int n, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + + /** + * \brief Calculate the distance between the two given points. + * + * \param p0 The first point. + * \param p1 The second point. + * + * \return The distance between the two points. + */ + axom::float64 distance(mir::Point2 p0, mir::Point2 p1); + + /** + * \brief Generates a 2D uniform grid of n x n elements. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + */ + mir::CellData generateGrid(int gridSize); + + /** + * \brief Calculates the percent overlap between the given circle and quad. + * + * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * /return The percent value overlap of the circle and the quad between [0, 1]. + */ + axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); - int circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + /** + * \brief Calculates the number of corners of the quad that are within the circle. + * + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * \return The number of corners of the quad that are within the circle. + */ + int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); }; } } diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 5cb584274c..05e96ea2d0 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -10,7 +10,7 @@ namespace axom namespace mir { - // Quad Vertex Indices + // Quad Vertex Local Indices // // 0 7 3 // @---------@---------@ @@ -45,7 +45,7 @@ namespace mir }; - // Triangle Vertex Indices + // Triangle Vertex Local Indices // 0 // @ // / \ diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index fcdfc20a9e..924b98792a 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -6,11 +6,28 @@ #ifndef __ZOO_CLIPPING_TABLES_H__ #define __ZOO_CLIPPING_TABLES_H__ +/** + * \file ZooClippingTables.hpp + * + * \brief Contains the defintions for the clipping cases and enumerator + * for the shape types in the zoo. + */ namespace axom { namespace mir { + + enum Shape + { + Triangle, + Quad, + Tetrahedron, + Triangular_Prism, + Pyramid, + Hexahedron + }; + extern const int quadClipTable[16][19]; extern const int triangleClipTable[8][10]; } diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index b1a647e764..16e15481a7 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -5,7 +5,7 @@ set( mir_examples mir_tutorial_simple.cpp - mirConcentricCircles.cpp + mir_concentric_circles.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mirConcentricCircles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp similarity index 83% rename from src/axom/mir/examples/mirConcentricCircles.cpp rename to src/axom/mir/examples/mir_concentric_circles.cpp index b445c859e7..48d361400f 100644 --- a/src/axom/mir/examples/mirConcentricCircles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -39,18 +39,18 @@ int main( int argc, char** argv ) std::string outputFilePath = std::string(argv[3]); // Intialize a mesh for testing MIR - auto start_time = Clock::now(); + auto startTime = Clock::now(); mir::MeshTester tester; - mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); // grid size, numCircles - auto end_time = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); + auto endTime = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Begin material interface reconstruction - start_time = Clock::now(); + startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - end_time = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); + endTime = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index d35fe0bfbc..ad67317a6e 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -18,35 +18,35 @@ namespace mir = axom::mir; //-------------------------------------------------------------------------------- /*! - * \brief Tutorial main + * \brief Tutorial main showing how to initialize test cases and perform mir. */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { // Intialize a mesh for testing MIR - auto start_time = Clock::now(); + auto startTime = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); // mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - mir::MIRMesh testMesh = tester.initTestCaseFive(25, 7); + mir::MIRMesh testMesh = tester.initTestCaseFive(50, 25); - auto end_time = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + auto endTime = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Begin material interface reconstruction - start_time = Clock::now(); + startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.3); // 5 iterations, 90 percent + // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); // Process once, with original Meredith algorithm + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(testMesh, 5, 0.3); // 5 iterations, 30 percent with iterative Meredith algorithm - end_time = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + endTime = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh5.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputIterative6.vtk"); return 0; } diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 155f8ae486..15a42d39ff 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(gtest_mir_tests mir_smoke.cpp + mir_interface_reconstructor.cpp ) diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp new file mode 100644 index 0000000000..8a11c21e73 --- /dev/null +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_SMOKE_H_ +#define MIR_SMOKE_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +TEST(mir_quad_clipping, quad_clipping_case_zero) +{ + // Initialize a 3x3 mesh with 2 materials + mir::MeshTester meshGenerator; + mir::MIRMesh testMesh = meshGenerator.initQuadClippingTestMesh(); + + // Initialize its volume fractions to custom values to guarantee this clipping case + std::vector > vertexVF; + vertexVF.resize(2); + vertexVF[0] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + vertexVF[1] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + int upperLeftVertexID = 5; + int lowerLeftVertexID = 9; + int lowerRightVertexID = 10; + int upperRightVertexID = 6; + + mir::InterfaceReconstructor reconstructor; + unsigned int clippingCase = reconstructor.determineQuadClippingCase( testMesh, 0, 1, upperLeftVertexID, lowerLeftVertexID, lowerRightVertexID, upperRightVertexID ); + + EXPECT_EQ( clippingCase, 0 ); +} + + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_SMOKE_H_ From a819ddbcd3f5923b6e8060c2b7ea2592207d778c Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Wed, 26 Jun 2019 17:13:42 -0700 Subject: [PATCH 012/290] Refactored the interface reconstruction code. Ensured everything is properly commented. Added unit tests. Added a map to the mesh to store element shapes. --- src/axom/mir/CMakeLists.txt | 6 +- src/axom/mir/CellClipper.cpp | 157 +++++ src/axom/mir/CellClipper.hpp | 120 ++++ src/axom/mir/CellData.cpp | 6 + src/axom/mir/CellData.hpp | 1 + src/axom/mir/CellGenerator.cpp | 218 +++++++ src/axom/mir/CellGenerator.hpp | 150 +++++ src/axom/mir/InterfaceReconstructor.cpp | 611 +++--------------- src/axom/mir/InterfaceReconstructor.hpp | 212 +----- src/axom/mir/MIRMesh.cpp | 58 +- src/axom/mir/MIRMesh.hpp | 42 +- src/axom/mir/MIRMeshTypes.hpp | 11 + src/axom/mir/MIRUtilities.hpp | 209 ++++++ src/axom/mir/MeshTester.cpp | 39 +- src/axom/mir/MeshTester.hpp | 12 +- src/axom/mir/ZooClippingTables.cpp | 34 + src/axom/mir/ZooClippingTables.hpp | 16 +- .../mir/examples/mir_concentric_circles.cpp | 3 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 28 +- src/axom/mir/tests/CMakeLists.txt | 3 + src/axom/mir/tests/mir_cell_clipper.cpp | 451 +++++++++++++ src/axom/mir/tests/mir_cell_generator.cpp | 231 +++++++ .../mir/tests/mir_interface_reconstructor.cpp | 31 +- src/axom/mir/tests/mir_utilities.cpp | 101 +++ src/docs/dependencies.dot | 2 +- src/index.rst | 2 +- 26 files changed, 1937 insertions(+), 817 deletions(-) create mode 100644 src/axom/mir/CellClipper.cpp create mode 100644 src/axom/mir/CellClipper.hpp create mode 100644 src/axom/mir/CellGenerator.cpp create mode 100644 src/axom/mir/CellGenerator.hpp create mode 100644 src/axom/mir/MIRUtilities.hpp create mode 100644 src/axom/mir/tests/mir_cell_clipper.cpp create mode 100644 src/axom/mir/tests/mir_cell_generator.cpp create mode 100644 src/axom/mir/tests/mir_utilities.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index e1740e6ad4..db7d2fda20 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -22,15 +22,19 @@ set(mir_headers InterfaceReconstructor.hpp CellData.hpp MeshTester.hpp + CellClipper.hpp + MIRUtilities.hpp + CellGenerator.hpp ) set(mir_sources - ../Axom.cpp MIRMesh.cpp InterfaceReconstructor.cpp ZooClippingTables.cpp CellData.cpp MeshTester.cpp + CellClipper.cpp + CellGenerator.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellClipper.cpp b/src/axom/mir/CellClipper.cpp new file mode 100644 index 0000000000..c5f25d4272 --- /dev/null +++ b/src/axom/mir/CellClipper.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellClipper.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +CellClipper::CellClipper() +{ + +} + +//-------------------------------------------------------------------------------- + +CellClipper::~CellClipper() +{ + +} + +//-------------------------------------------------------------------------------- + +// Computes the t-values where each edge is clipped, as well as the topology of the new output cells after clipping the original cell +// Outputs the newElements, newVertices maps and the verticesClippingTValue array[] +void CellClipper::computeClippingPoints(const mir::Shape shapeType, + const std::vector >& vertexVF, + std::map >& newElements, + std::map >& newVertices, + axom::float64* tValues) +{ + // Determine the clipping case for the current element + unsigned int caseIndex = determineClippingCase( shapeType, vertexVF[0], vertexVF[1] ); + + std::vector > clipTable = getClipTable(shapeType); + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = clipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = clipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + + if ( vID >= mir::utilities::numVerts(shapeType) ) + { + int vertexOneID = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vertexTwoID = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + tValues[vID] = computeTValueOnEdge( vertexVF[0][vertexOneID], vertexVF[1][vertexOneID], vertexVF[0][vertexTwoID], vertexVF[1][vertexTwoID] ); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = clipTable[caseIndex][i]; + } +} + +//-------------------------------------------------------------------------------- + +unsigned int CellClipper::determineClippingCase(const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF) +{ + unsigned int caseIndex = 0; + + int numVertices = mir::utilities::numVerts(shapeType); + + for (int vID = 0; vID < numVertices; ++vID) + { + if (matOneVF[vID] > matTwoVF[vID]) + { + unsigned int bitIndex = (numVertices - 1) - vID; + + caseIndex |= (1 << bitIndex); + } + } + + return caseIndex; +} + +//-------------------------------------------------------------------------------- + +axom::float64 CellClipper::computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo) +{ + axom::float64 ret = 0.0; + + // TODO: Perhaps just handle NULL_MAT by return 0, since that is what will happen anyways? + // Handle NULL_MAT, which has a vf of -1.0, but which needs to be 0.0 for the purposes of computing the clipping point + if (vfMatOneVertexOne < 0.0) { vfMatOneVertexOne = 0.0; }; + if (vfMatTwoVertexOne < 0.0) { vfMatTwoVertexOne = 0.0; }; + if (vfMatOneVertexTwo < 0.0) { vfMatOneVertexTwo = 0.0; }; + if (vfMatTwoVertexTwo < 0.0) { vfMatTwoVertexTwo = 0.0; }; + + axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; + axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; + + if (denominator != 0.0) + { + ret = numerator / denominator; + } + + if (ret > 1.0 || ret < 0.0) + { + // This shouldn't happen... + printf(" OUT OF BOUNDS T VALUE: %f\n", ret); + + // Clamp the t value + ret = fmin(1.0, ret); + ret = fmax(0.0, ret); + } + + return ret; +} + +//-------------------------------------------------------------------------------- + +const std::vector >& CellClipper::getClipTable(const mir::Shape shapeType) +{ + switch ( shapeType ) + { + case mir::Shape::Triangle: + return triangleClipTableVec; + case mir::Shape::Quad: + return quadClipTableVec; + default: + printf("No clipping table for this shape type.\n"); + return triangleClipTableVec; + } +} + +//-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellClipper.hpp b/src/axom/mir/CellClipper.hpp new file mode 100644 index 0000000000..fcdd650db3 --- /dev/null +++ b/src/axom/mir/CellClipper.hpp @@ -0,0 +1,120 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file CellClipper.hpp + * + * \brief Contains the specification for the CellClipper class. + * + */ + +#ifndef __CELL_CLIPPER_H +#define __CELL_CLIPPER_H + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" +#include "MIRUtilities.hpp" +#include "MIRMeshTypes.hpp" +#include "CellData.hpp" +#include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /** + * \class CellClipper + * + * \brief A class that contains the functionality for taking an input cell + * and determining how it should be clipped. + * + */ + class CellClipper + { + public: + /** + * \brief Default constructor. + */ + CellClipper(); + + /** + * \brief Default destructor. + */ + ~CellClipper(); + + /** + * \brief Computes the elements, vertices, and t values resulting from splitting the element with the given vertex volume fractions. + * + * \param shapeType The shape type of the element. + * \param vertexVF The vertex volume fractions of the two materials with which to clip the cell. + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * + */ + void computeClippingPoints(const mir::Shape shapeType, + const std::vector >& vertexVF, + std::map >& newElements, + std::map >& newVertices, + axom::float64* tValues); + + /** + * \brief Determines the index into the clipping table of the given shape based on the volume fractions at its vertices. + * + * \param shapeType The shape type of the element. + * \param matOneVF The volume fractions at the vertices of the element for the first material. + * \param matTwoVF The volume fractions at the vertices of the element for the second material. + * + * \return The index into the clipping table. + */ + unsigned int determineClippingCase(const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF); + + + /** + * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. + * + * \param vfMatOneVertexOne The volume fraction of material one present at vertex one. + * \param vfMatTwoVertexOne The volume fraction of material two present at vertex one. + * \param vfMatOneVertexTwo The volume fraction of material one present at vertex two. + * \param vfMatTwoVertexTwo The volume fraction of material two present at vertex two. + * + * \return The percent of the distance from vertex one to vertex two where the edge should be clipped. + * + * \note When one material's volume fractions dominates the other material's, then the edge should not be clipped and this function will return 0.0. + * \note When one of the materials is the NULL_MAT (and has vf = -1.0), these values are set to 0 in order to interpolate properly. + */ + axom::float64 computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo); + + private: + /** + * \brief Returns a reference to the appropriate clipping table to use for the shape type. + * + * \param The shape type of the element. + * + * \return A reference to the clipping table. + */ + const std::vector >& getClipTable(const mir::Shape shapeType); + + }; + +//-------------------------------------------------------------------------------- + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index 4e37813c86..176d1d6fc7 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -83,6 +83,12 @@ namespace mir m_mapData.m_elementParents.push_back(cellToMerge.m_mapData.m_elementParents[i]); } + // Merge the elements' shape types + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_shapeTypes.size(); ++i) + { + m_mapData.m_shapeTypes.push_back(cellToMerge.m_mapData.m_shapeTypes[i]); + } + // Merge the total number of verts and elems in the resulting cell m_numVerts += cellToMerge.m_numVerts; m_numElems += cellToMerge.m_numElems; diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 1a681e5d19..a73134374c 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -50,6 +50,7 @@ namespace mir std::vector > m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + std::vector m_shapeTypes; // Data that goes into MIRMesh's shapeType IntMap }; /** diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp new file mode 100644 index 0000000000..05b61aa030 --- /dev/null +++ b/src/axom/mir/CellGenerator.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellGenerator.hpp" + + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +CellGenerator::CellGenerator() +{ + +} + +//-------------------------------------------------------------------------------- + +CellGenerator::~CellGenerator() +{ + +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateTopologyData(const std::map >& newElements, + const std::map >& newVertices, + CellData& out_cellData) +{ + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); + + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } + + // Push the index that occurs after the last vertex + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); + + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateVertexPositions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData) +{ + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + + if ( vID < mir::utilities::numVerts(shapeType) ) + { + // This vertex is one of the shape's original vertices + out_cellData.m_mapData.m_vertexPositions.push_back( vertexPositions[vID] ); + } + else + { + // This vertex is between two of the shape's original vertices + int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + out_cellData.m_mapData.m_vertexPositions.push_back( mir::utilities::interpolateVertexPosition( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); + } + } +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector >& vertexVF, + axom::float64* tValues, + CellData& out_cellData) +{ + out_cellData.m_mapData.m_vertexVolumeFractions.resize(vertexVF.size()); // vertexVF size is the number of materials + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + + for (int matID = 0; matID < vertexVF.size(); ++matID) + { + if ( vID < mir::utilities::numVerts(shapeType) ) + { + // This vertex is one of the shape's original vertices + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( vertexVF[matID][vID] ); + } + else + { + // This vertex is between two of the shape's original vertices + int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( mir::utilities::lerpFloat( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); + } + } + } +} + +//-------------------------------------------------------------------------------- + +int CellGenerator::determineCleanCellMaterial(const Shape elementShape, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector >& vertexVF) +{ + int dominantMaterial = matOne; + + axom::float64 matOneVF = -1.0; + axom::float64 matTwoVF = -1.0; + + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + + if ( vID >= 0 && vID < mir::utilities::numVerts(elementShape) ) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; + } + } + + return dominantMaterial; +} + +//-------------------------------------------------------------------------------- + +mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType, + const int numVerts) +{ + mir::Shape newShapeType; + if (parentShapeType == mir::Shape::Triangle || parentShapeType == mir::Shape::Quad) + { + // Handle the two-dimensional case + switch (numVerts) + { + case 3: + newShapeType = mir::Shape::Triangle; + break; + case 4: + newShapeType = mir::Shape::Quad; + break; + default: + newShapeType = mir::Shape::Triangle; + printf("Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + else + { + // Handle the three-dimensional case + switch (numVerts) + { + case 4: + newShapeType = mir::Shape::Tetrahedron; + break; + case 5: + newShapeType = mir::Shape::Pyramid; + break; + case 6: + newShapeType = mir::Shape::Triangular_Prism; + break; + case 8: + newShapeType = mir::Shape::Hexahedron; + break; + default: + newShapeType = mir::Shape::Tetrahedron; + printf("Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + return newShapeType; +} + + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellGenerator.hpp b/src/axom/mir/CellGenerator.hpp new file mode 100644 index 0000000000..ccf189537b --- /dev/null +++ b/src/axom/mir/CellGenerator.hpp @@ -0,0 +1,150 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file CellGenerator.hpp + * + * \brief Contains the specification for the CellGenerator class. + * + */ + +#ifndef __CELL_GENERATOR_H__ +#define __CELL_GENERATOR_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" +#include "MIRUtilities.hpp" +#include "MIRMeshTypes.hpp" +#include "CellData.hpp" +#include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" +#include "CellClipper.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /** + * \class CellGenerator + * + * \brief A class that generates that uses the clipping information to generate + * the data needed in order to produce a clean, reconstructed mesh. + */ + class CellGenerator + { + public: + + /** + * \brief Default constructor. + */ + CellGenerator(); + + /** + * \brief Default destructor. + */ + ~CellGenerator(); + + /** + * \brief Generates the topology of the new elements resulting from a split. + * + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param out_cellData Container to store the topology data of the generated elements. + */ + void generateTopologyData(const std::map >& newElements, + const std::map >& newVertices, + CellData& out_cellData); + + + /** + * \brief Generates the vertex positions values for each of the new vertices of the generated element. + * + * \param shapeType The shape type of the element. + * \param newVertices An ordered map of vertices that compose the newly generated elements. + * \param vertexPositions A vector of positions for each of the original element's vertices. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * \param out_cellData Container to store the vertex position data of the generated elements. + * + * \note New vertex positions are interpolated from the original vertex positions. + */ + void generateVertexPositions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData); + + + /** + * \brief Generates the vertex volume fractions for each of the new vertices of the generated element. + * + * \param shapeType The shape type of the element. + * \param newVertices An ordered map of vertices that compose the newly generated elements. + * \param vertexVF A vector of positions for each of the original element's vertices. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * \param out_cellData Container to store the vertex position data of the generated elements. + * + * \note New vertex positions are interpolated from the original vertex volume fractions. + */ + void generateVertexVolumeFractions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector >& vertexVF, + axom::float64* tValues, + CellData& out_cellData); + + /** + * \brief Determines the more dominant material of the two given for the given element. + * + * \param shapeType An enumerator denoting the element's shape. + * \param vertexIDs A list of vertex IDs into the vertexVF param. + * \param matOne The ID of the first material. + * \param matTwo The ID of the second material. + * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. + * + * \return The ID of the dominant material of the element. + * + * \note The dominant element for the 2D/3D cases will be the same as the material present at one of the + * original vertices that existed prior to the split. So, if you can find this vertex and its dominant + * material, then you know the dominant material of this new element. + * + * \note It is assumed that the given cell is one that results from splitting its parent cell. + */ + int determineCleanCellMaterial(const Shape shapeType, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector >& vertexVF); + + /** + * \brief Determine the shape type of an element. + * + * \param parentShapeType The shape of the element from which the new element is generated. + * \param numVerts The number of vertices of the new element. + * + * \note It is assumed that the given cell is one that results from splitting its parent cell. + */ + mir::Shape determineElementShapeType(const Shape parentShapeType, + const int numVerts); + + /** + * \brief Ensures that the element is dominated by a material that is actually present in the original parent cell. + * + * \param + */ + // void fixDominantMaterial(mir::MIRMesh& originalMesh, const std::map >& newElements, const int matOne, const int matTwo, CellData& out_cellData); + }; + +//-------------------------------------------------------------------------------- + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 9152dbb30d..e4fdab75d8 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -26,7 +26,8 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh) +void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh, + mir::MIRMesh& outputMesh) { // Store a reference to the original mesh m_originalMesh = inputMesh; @@ -50,7 +51,7 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; int matOne = matID; - computeClippingPoints(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); + generateCleanCells(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct @@ -73,13 +74,16 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon - - return finalMesh; + outputMesh = finalMesh; + // return finalMesh; } //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent) +void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh) { int numElems = inputMesh.m_elems.size(); int numMaterials = inputMesh.m_numMaterials; @@ -88,12 +92,13 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: mir::MIRMesh meshToImprove(inputMesh); // Calculate the reconstruction on the unmodified, input mesh - mir::MIRMesh resultingMesh = computeReconstructedInterface(meshToImprove); + computeReconstructedInterface(meshToImprove, outputMesh); for (int it = 0; it < numIterations; ++it) { + printf("iteration %d\n", it); // Calculate the output element volume fractions of the resulting output mesh - std::vector > resultingElementVF = resultingMesh.computeOriginalElementVolumeFractions(); + std::vector > resultingElementVF = outputMesh.computeOriginalElementVolumeFractions(); // Initialize the vector to store the improved element volume fractions std::vector > improvedElementVF; @@ -113,417 +118,100 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); // Calculate the reconstruction on the modified, input mesh - resultingMesh = computeReconstructedInterface(meshToImprove); - } - - return resultingMesh; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) -{ - // Find the vertices associated with each element - auto elementVertices = tempMesh.m_bdry[eID]; - - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + computeReconstructedInterface(meshToImprove, outputMesh); } } //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) +void InterfaceReconstructor::generateCleanCells(const int eID, + const int matOne, + const int matTwo, + mir::MIRMesh& tempMesh, + CellData& out_cellData) { - // Determine the clipping case + mir::Shape shapeType = (mir::Shape) tempMesh.m_shapeTypes[eID]; auto elementVertices = tempMesh.m_bdry[eID]; - int upperLeftVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; - int upperRightVertex = elementVertices[3]; - - unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - - // Generate new elements + // Set up data structures needed to compute how element should be clipped std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[8] = {0,0,0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[8] = {0,0,0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped - - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = quadClipTable[caseIndex][i]; + std::vector verticesPresent(mir::utilities::maxPossibleNumVerts(shapeType), 0); // Vector of flags denoting whether the vertex is present in the current case or not + axom::float64* tValues = new axom::float64[ mir::utilities::maxPossibleNumVerts(shapeType) ]{0}; // Array of t values that denote the percent value of where the edge should be clipped - // for each new element in the current clipping case - while (numVertices != -1) - { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) - { - // Find the id of the next vertex of the new element - int vID = quadClipTable[caseIndex][i + (j+1)]; - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; - - // Find t using linear interpolation for any vertex that is not one of the original 4 vertices - if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 6) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 7) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); - } - } - - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; - - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = quadClipTable[caseIndex][i]; - } - - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); - - generateTopologyData(newElements, newVertices, out_cellData); - generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - - // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + // Set up the volume fractions for the current element for the two materials currently being considered + std::vector > vertexVF(2); + for (int vID = 0; vID < elementVertices.size(); ++vID) { - int dominantMaterial = determineDominantMaterial(Shape::Quad, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); - out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); - } - - // Determine and store the parent of this element - out_cellData.m_mapData.m_elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; - } - - // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; - int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - - if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + int originalVID = elementVertices[vID]; + if (matOne == NULL_MAT) { - // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; - else - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; + vertexVF[0].push_back(-1.0); } - } - - // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present - int evIndexSubtract[8]; - for (int i = 0; i < 8; ++i) - { - if (verticesPresent[i] == 1) - evIndexSubtract[i] = 0; else - evIndexSubtract[i] = 1; - } - for (int i = 1; i < 8; ++i) - evIndexSubtract[i] += evIndexSubtract[i - 1]; - - for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) - { - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; - } -} - -//-------------------------------------------------------------------------------- - -unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) -{ - // Determine the dominant color at each vertex - int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; - - if (matOneID == NULL_MAT) - upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matTwoID; - if (matTwoID == NULL_MAT) - upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; - if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - upperRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + vertexVF[0].push_back( tempMesh.m_materialVolumeFractionsVertex[matOne][originalVID] ); } - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperLeftColor == matOneID) caseIndex |= 8; - if (lowerLeftColor == matOneID) caseIndex |= 4; - if (lowerRightColor == matOneID) caseIndex |= 2; - if (upperRightColor == matOneID) caseIndex |= 1; - - return caseIndex; -} - -//-------------------------------------------------------------------------------- - -mir::Point2 InterfaceReconstructor::interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t) -{ - mir::Point2 interpolatedPoint; - interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; - interpolatedPoint.m_y = (1 - t) * vertexOnePos.m_y + t * vertexTwoPos.m_y; - return interpolatedPoint; -} - -//-------------------------------------------------------------------------------- - -axom::float64 InterfaceReconstructor::lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t) -{ - return (1 - t) * f0 + t * f1; -} - -//-------------------------------------------------------------------------------- - -axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh) -{ - axom::float64 ret = 0.0; - - axom::float64 vfMatOneVertexOne = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexOneID]; - axom::float64 vfMatTwoVertexOne = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexOneID]; - axom::float64 vfMatOneVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexTwoID]; - axom::float64 vfMatTwoVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexTwoID]; - - if (matOneID == NULL_MAT) - vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; - - if (matTwoID == NULL_MAT) - vfMatTwoVertexOne = vfMatTwoVertexTwo = 0.0; - - axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; - axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; - - if (denominator != 0.0) - ret = numerator / denominator; - - if (ret > 1.0 || ret < 0.0) - { - // This shouldn't happen... - printf(" OUT OF BOUNDS T VALUE: %f\n", ret); - - // Clamp the t value - ret = fmin(1.0, ret); - ret = fmax(0.0, ret); - } - - return ret; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData) -{ - // Store the evInds and evBegins data in the output vectors - int currentEVBeginIndex = 0; - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + if (matTwo == NULL_MAT) { - // Push the start index of the next element - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - - // Push the next element's vertices - for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) - { - out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); - ++currentEVBeginIndex; - } + vertexVF[1].push_back(-1.0); } - - // Push the index that occurs after the last vertex - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - - // Store the veInds and veBegins data in the output vectors - int currentVEBeginIndex = 0; - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + else { - // Push the start index of the vertex's elements - out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); - - // Push the next vertex's elements - for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) - { - out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); - ++currentVEBeginIndex; - } + vertexVF[1].push_back( tempMesh.m_materialVolumeFractionsVertex[matTwo][originalVID] ); } - - // Push the index that occurs after the last element - out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); -} - -//-------------------------------------------------------------------------------- + } -void InterfaceReconstructor::generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) -{ - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + // Clip the element + CellClipper clipper; + clipper.computeClippingPoints(shapeType, vertexVF, newElements, newVertices, tValues); - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + // Determine which vertices are present for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - if (vID == 0) - newPoints[vID] = tempMesh.m_vertexPositions[upperLeftVertex]; - if (vID == 1) - newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = tempMesh.m_vertexPositions[upperRightVertex]; - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperLeftVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperRightVertex], verticesClippingTValue[vID]); - if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperRightVertex], tempMesh.m_vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - { - out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); + verticesPresent[vID] = 1; } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) -{ - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) - { - if (vID == 0) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex]); - if (vID == 1) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex]); - if (vID == 4) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 6) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); - if (vID == 7) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); - } - } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) -{ - // Determine the clipping case - auto elementVertices = tempMesh.m_bdry[eID]; - int upperVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); - unsigned int caseIndex = determineTriangleClippingCase(tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); + // Generate the topology and connectivity of the newly split element + CellGenerator cellGenerator; + cellGenerator.generateTopologyData(newElements, newVertices, out_cellData); - // Generate new elements - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[6] = {0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[6] = {0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped - - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = triangleClipTable[caseIndex][i]; + // Generate the vertex position values of the newly split element + std::vector originalElementVertexPositions; + for (int vID = 0; vID < elementVertices.size(); ++vID) + { + int originalVID = elementVertices[vID]; + originalElementVertexPositions.push_back( tempMesh.m_vertexPositions[originalVID] ); + } + cellGenerator.generateVertexPositions( shapeType, newVertices, originalElementVertexPositions, tValues, out_cellData); - // for each new element in the current clipping case - while (numVertices != -1) + // Generate the vertex volume fractions of the newly split elements + std::vector > originalElementVertexVF; + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) + std::vector materialVertexVF; + for (int vID = 0; vID < elementVertices.size(); ++vID) { - // Find the id of the next vertex of the new element - int vID = triangleClipTable[caseIndex][i + (j+1)]; - - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; + int originalVID = elementVertices[vID]; - // Find t using linear interpolation for any vertex that is not one of the original 3 vertices - if(vID == 3) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperVertex, matOneID, matTwoID, tempMesh); - } + materialVertexVF.push_back( tempMesh.m_materialVolumeFractionsVertex[matID][originalVID] ); } - - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; - - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = triangleClipTable[caseIndex][i]; + originalElementVertexVF.push_back( materialVertexVF ); } - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); - - generateTopologyData(newElements, newVertices, out_cellData); - generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); - generateVertexVolumeFractionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + cellGenerator.generateVertexVolumeFractions( shapeType, newVertices, originalElementVertexVF, tValues, out_cellData); // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int dominantMaterial = determineDominantMaterial(Shape::Triangle, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, itr->second, matOne, matTwo, out_cellData.m_mapData.m_vertexVolumeFractions); out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } @@ -534,6 +222,14 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } + // Determine the generated elements' shape types + out_cellData.m_mapData.m_shapeTypes.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.m_mapData.m_shapeTypes[itr->first] = cellGenerator.determineElementShapeType(shapeType, itr->second.size()); + } + + // TODO: Make a function that does this // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { @@ -543,170 +239,33 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; + if (currentDominantMaterial == matOne) + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwo; else - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOne; } - } - - // Modify the evIndex values to account for the fact that perhaps not all 6 possible vertices are present - int evIndexSubtract[6]; - for (int i = 0; i < 6; ++i) + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + std::vector evIndexSubtract; + evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); + for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) { if (verticesPresent[i] == 1) evIndexSubtract[i] = 0; else evIndexSubtract[i] = 1; } - - for (int i = 1; i < 6; ++i) + for (unsigned long i = 1; i < evIndexSubtract.size(); ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; - -} - -//-------------------------------------------------------------------------------- - -unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) -{ - // Determine the dominant color at each vertex - int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; - - if (matOneID == NULL_MAT) - upperColor = lowerLeftColor = lowerRightColor = matTwoID; - if (matTwoID == NULL_MAT) - upperColor = lowerLeftColor = lowerRightColor = matOneID; - if (matOneID != NULL_MAT && matTwoID != NULL_MAT) - { - upperColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - } - - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperColor == matOneID) caseIndex |= 4; - if (lowerLeftColor == matOneID) caseIndex |= 2; - if (lowerRightColor == matOneID) caseIndex |= 1; - - return caseIndex; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) -{ - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets - - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - if (vID == 0) - newPoints[vID] = tempMesh.m_vertexPositions[upperVertex]; - if (vID == 1) - newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperVertex], verticesClippingTValue[vID]); - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - { - out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); - } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) -{ - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) - { - if (vID == 0) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex]); - if (vID == 1) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 4) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); - } - } -} - -//-------------------------------------------------------------------------------- - -int InterfaceReconstructor::determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF) -{ - int dominantMaterial = matOne; - - axom::float64 matOneVF = -1.0; - axom::float64 matTwoVF = -1.0; - - if (elementShape == Shape::Triangle) + for (unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) { - for (unsigned long it = 0; it < vertexIDs.size(); ++it) - { - int vID = vertexIDs[it]; - if (vID == 0 || vID == 1 || vID == 2) - { - if (matOne != NULL_MAT) - { - matOneVF = vertexVF[matOne][vID]; - } - if (matTwo != NULL_MAT) - { - matTwoVF = vertexVF[matTwo][vID]; - } - - dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; - } - } - } - - if (elementShape == Shape::Quad) - { - for (unsigned long it = 0; it < vertexIDs.size(); ++it) - { - int vID = vertexIDs[it]; - if (vID == 0 || vID == 1 || vID == 2 || vID == 3) - { - if (matOne != NULL_MAT) - { - matOneVF = vertexVF[matOne][vID]; - } - if (matTwo != NULL_MAT) - { - matTwoVF = vertexVF[matTwo][vID]; - } - - dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; - } - } + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } - return dominantMaterial; + // Memory management + delete[] tValues; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 646e1f284e..ed674af4a4 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -19,6 +19,9 @@ #include "MIRMesh.hpp" #include "CellData.hpp" #include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" +#include "CellClipper.hpp" +#include "CellGenerator.hpp" #include @@ -62,10 +65,10 @@ namespace mir * \brief Performs material interface reconstruction using the zoo-based algorithm. * * \param inputMesh The mesh composed of mixed cells. - * - * \return The mesh composed of clean cells. + * \param outputMesh The mesh composed of clean cells. */ - mir::MIRMesh computeReconstructedInterface(mir::MIRMesh& inputMesh); + void computeReconstructedInterface(mir::MIRMesh& inputMesh, + mir::MIRMesh& outputMesh); /** @@ -74,198 +77,27 @@ namespace mir * \param inputMesh The mesh made up of mixed cells. * \param numIterations The number of iterations for which to run the algorithm. * \param percent The percent of the difference to use when modifying the original element volume fractions. - * - * \return The mesh made up of clean cells. - */ - mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent); - - // private: - - /** - * \brief A wrapper function that calls the appropriate splitting method based on the shape of the given element. - * - * \param eID The ID of the element to be split. - * \param matOneID The ID of the first material to use for splitting the element, - * \param matTwoID The ID of the second material to use for splitting the element. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the element. - */ - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Splits the specified quad element into clean cells of the two given material types - * based on the volume fractions of the two materials. - * - * \param eID The ID of the quad element. - * \param matOneID The ID of the first material to use for splitting the quad, - * \param matTwoID The ID of the second material to use for splitting the quad. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the quad. - */ - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Splits the specified triangle element into clean cells of the two given material types - * based on the volume fractions of the two materials. - * - * \param eID The ID of the triangle element. - * \param matOneID The ID of the first material to use for splitting the triangle, - * \param matTwoID The ID of the second material to use for splitting the triangle. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the triangle. - */ - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Performs linear interpolation between the two vertex positions. - * - * \param vertexOnePos The position of the first vertex. - * \param vertexTwoPos The position of the second vertex. - * \param t The percent of the distance from vertex one to vertex two to interpolate at. - * - * \return The interpolated position. - */ - mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t); - - - /** - * \brief Performs linear interpolation between the two given float values. - * - * \param f0 The first float value. - * \param f1 The second float value. - * \param t The percent of the distance from the first float value to the second. - * - * \return The interpolated value. - */ - axom::float64 lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t); - - - /** - * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. - * - * \param vertexOneID The ID of the first vertex. - * \param vertexTwoID The ID of the second vertex. - * \param matOneID The ID of the first material to use for interpolating between. - * \param matTwoID The ID of the second material to use for interpolating between. - * \param tempMesh The intermediate mesh that is currently being processed. - * - * \return The t value, which is the percent distance from vertex one to vertex two where the two volume fractions are equal. - */ - axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh); - - - /** - * \brief Generates the topology of the new elements resulting from a split. - * - * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param out_cellData Container to store the topology data of the generated elements. - */ - void generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData); - - - /** - * \brief Calculates the bit map representing the clipping case for a quad. - * - * \param tempMesh The intermediate mesh that is currently being processed. - * \param matOneID The ID of the first material being used for splitting. - * \param matTwoID The ID of the second material being used for splitting. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * - * \return The bitmap representing the clipping case. - */ - unsigned int determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - - /** - * \brief Generate the vertex position data for the new elements resulting from splitting a quad. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex position data of the generated elements. - */ - void generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - - /** - * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + * \param outputMesh The mesh composed of clean cells. */ - void generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh&tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); + void computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh); /** - * \brief Calculates the bit map representing the clipping case for a triangle. - * - * \param tempMesh The intermediate mesh that is currently being processed. - * \param matOneID The ID of the first material being used for splitting. - * \param matTwoID The ID of the second material being used for splitting. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * - * \return The bitmap representing the clipping case. - */ - unsigned int determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - - /** - * \brief Generate the vertex position data for the new elements resulting from splitting a triangle. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex position data of the generated elements. - */ - void generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - - /** - * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a triangle. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex volume fraction data of the generated elements. - */ - void generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - - /** - * \brief Determines the mroe dominant material of the two given for the given element. - * - * \param elementShape An enumerator denoting the element's shape. - * \param vertexIDs A list of vertex IDs into the vertexVF param. - * \param matOne The ID of the first material. - * \param matTwo The ID of the second material. - * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. - * - * \return The ID of the dominant material of the element. + * \brief Generates a set of clean cells by splitting the element with the two given materials. * - * \note The dominant element for the 2D cases will be the same as the material present at one of the - * original vertices that existed prior to the split. So, if you can find this vertex and its dominant - * material, then you know the dominant material of this new element. + * \param eID The ID of the element to be split. + * \param matOne The first material to split with. + * \param matTwo The second material to split with. + * \param tempMesh A reference to the mesh the element comes from. + * \param out_cellData Container to store the data of the generated elements. */ - int determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF); + void generateCleanCells(const int eID, + const int matOne, + const int matTwo, + mir::MIRMesh& tempMesh, + CellData& out_cellData); private: mir::MIRMesh m_originalMesh; diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 6e7c687dcf..c47fecf3bf 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -33,6 +33,7 @@ MIRMesh::MIRMesh(MIRMesh* _mesh) m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; m_elementParentIDs = _mesh->m_elementParentIDs; + m_shapeTypes = _mesh->m_shapeTypes; m_elementDominantMaterials = _mesh->m_elementDominantMaterials; m_numMaterials = _mesh->m_numMaterials; } @@ -46,7 +47,12 @@ MIRMesh::~MIRMesh() //-------------------------------------------------------------------------------- -void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) +void MIRMesh::initializeMesh(const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector >& _elementVF) { // Initialize the vertex and element sets m_verts = _verts; @@ -64,9 +70,10 @@ void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, constructMeshRelations(); // Initialize the mesh's data maps - constructVertexPositionMap(_mapData.m_vertexPositions.data()); - constructElementParentMap(_mapData.m_elementParents.data()); + constructVertexPositionMap(_mapData.m_vertexPositions); + constructElementParentMap(_mapData.m_elementParents); constructElementDominantMaterialMap(_mapData.m_elementDominantMaterials); + constructElementShapeTypesMap(_mapData.m_shapeTypes); // Initialize the element and vertex volume fraction maps if (_elementVF.size() > 0) @@ -121,7 +128,7 @@ void MIRMesh::constructMeshRelations() //-------------------------------------------------------------------------------- -void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) +void MIRMesh::constructMeshVolumeFractionsMaps(const std::vector >& elementVF) { // Clear the old maps m_materialVolumeFractionsElement.clear(); @@ -170,7 +177,7 @@ void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > vertexVF) +void MIRMesh::constructMeshVolumeFractionsVertex(const std::vector >& vertexVF) { // Initialize the maps for all of the materials with the input volume fraction data for each vertex for (int matID = 0; matID < m_numMaterials; ++matID) @@ -190,7 +197,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector& data) { // construct the position map on the vertices m_vertexPositions = PointMap( &m_verts ); @@ -203,7 +210,7 @@ void MIRMesh::constructVertexPositionMap(Point2* data) //-------------------------------------------------------------------------------- -void MIRMesh::constructElementParentMap(int* elementParents) +void MIRMesh::constructElementParentMap(const std::vector& elementParents) { // Initialize the map for the elements' parent IDs m_elementParentIDs = IntMap( &m_elems ); @@ -217,7 +224,7 @@ void MIRMesh::constructElementParentMap(int* elementParents) //-------------------------------------------------------------------------------- -void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) +void MIRMesh::constructElementDominantMaterialMap(const std::vector& dominantMaterials) { // Initialize the map for the elements' dominant colors m_elementDominantMaterials = IntMap( &m_elems ); @@ -231,6 +238,20 @@ void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMater //-------------------------------------------------------------------------------- +void MIRMesh::constructElementShapeTypesMap(const std::vector& shapeTypes) +{ + // Initialize the map for the elements' dominant colors + m_shapeTypes = IntMap( &m_elems ); + + // Copy the dat for the elements + for (int eID = 0; eID < m_elems.size(); ++eID) + m_shapeTypes[eID] = shapeTypes[eID]; + + SLIC_ASSERT_MSG( m_shapeTypes.isValid(), "Element dominant materials map is not valid."); +} + +//-------------------------------------------------------------------------------- + void MIRMesh::print() { printf("\n------------------------Printing Mesh Information:------------------------\n"); @@ -287,6 +308,13 @@ void MIRMesh::print() } printf("}\n"); + printf("shapeTypes: { "); + for (int i = 0; i < m_elems.size(); ++i) + { + printf("%d ", m_shapeTypes[i]); + } + printf("}\n"); + printf("vertexVolumeFractions: { \n"); for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { @@ -354,10 +382,9 @@ void MIRMesh::writeMeshToFile(std::string filename) meshfile << "\n\nCELL_TYPES " << m_elems.size() << "\n"; for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = m_meshTopology.m_evBegins[i + 1] - m_meshTopology.m_evBegins[i]; - if (nVerts == 3) + if (m_shapeTypes[i] == mir::Shape::Triangle) meshfile << "5\n"; - else if (nVerts == 4) + else if (m_shapeTypes[i] == mir::Shape::Quad) meshfile << "9\n"; } @@ -423,7 +450,9 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) +axom::float64 MIRMesh::computeTriangleArea(Point2 p0, + Point2 p1, + Point2 p2) { axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 @@ -436,7 +465,10 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) +axom::float64 MIRMesh::computeQuadArea(Point2 p0, + Point2 p1, + Point2 p2, + Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); } diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 63b23d2ccc..984ada037e 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -76,7 +76,12 @@ namespace mir * \param _mapData The data used to initialized the maps associated with the vertex and element sets. * \param _elementVF The volume fractions of each element. Note that this is an optional parameter. */ - void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); + void initializeMesh(const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector >& _elementVF = {}); /** * \brief Constructs the mesh boundary and coboundary relations. @@ -91,14 +96,14 @@ namespace mir * * \param elementVF The volume fractions of each element. */ - void constructMeshVolumeFractionsMaps(std::vector > elementVF); + void constructMeshVolumeFractionsMaps(const std::vector >& elementVF); /** * \brief Constructs the vertex volume fraction map. * * \param vertexVF The volume fractions of each vertex. */ - void constructMeshVolumeFractionsVertex(std::vector > vertexVF); + void constructMeshVolumeFractionsVertex(const std::vector >& vertexVF); /** * \brief Prints out the data contained within this mesh in a nice format. @@ -110,7 +115,7 @@ namespace mir * * \param filename The location where the mesh file will be read from. */ - void readMeshFromFile(std::string filename); + void readMeshFromFile(const std::string filename); /** * \brief Writes out the mesh to the given file. @@ -119,7 +124,7 @@ namespace mir * * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. */ - void writeMeshToFile(std::string filename); + void writeMeshToFile(const std::string filename); /** @@ -132,23 +137,30 @@ namespace mir /** * \brief Constucts the positions map on the vertices. * - * \param data The array of position data for each vertex. + * \param data The vector of position data for each vertex. */ - void constructVertexPositionMap(Point2* data); + void constructVertexPositionMap(const std::vector& data); /** * \brief Constructs the map of elements to their original element parent. * - * \param cellParents The array of parent IDs for each element of the mesh. + * \param cellParents The vector of parent IDs for each element of the mesh. */ - void constructElementParentMap(int* cellParents); + void constructElementParentMap(const std::vector& elementParents); /** * \brief Constructs the map of elements to their dominant materials. * * \param dominantMaterials A vector of material ids that are the dominant material of each element. */ - void constructElementDominantMaterialMap(std::vector dominantMaterials); + void constructElementDominantMaterialMap(const std::vector& dominantMaterials); + + /** + * \brief Constructs the map of elements to their shape types. + * + * \param shapeTypes A vector of shape enumerators that are the shape type of each element. + */ + void constructElementShapeTypesMap(const std::vector& shapeTypes); /** * \brief Computes the area of the triangle defined by the given three vertex positions using Heron's formula. @@ -157,7 +169,9 @@ namespace mir * \param p1 The position of the second vertex. * \param p2 The position of the third vertex. */ - axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + axom::float64 computeTriangleArea(Point2 p0, + Point2 p1, + Point2 p2); /** * \brief Computes the area of the quad defined by the given four vertex positions. @@ -169,7 +183,10 @@ namespace mir * * \note It is assumed that the points are given in consecutive, counter-clockwise order. */ - axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + axom::float64 computeQuadArea(Point2 p0, + Point2 p1, + Point2 p2, + Point2 p3); /**************************************************************** * VARIABLES @@ -189,6 +206,7 @@ namespace mir std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex IntMap m_elementParentIDs; // the ID of the parent element from the original mesh IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + IntMap m_shapeTypes; // the int enumerator of what type of shape each element is int m_numMaterials; // the number of materials present in the mesh CellTopologyData m_meshTopology; // the topology/connectivity of the mesh diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index b6fc18ec28..9daed29cbb 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -25,6 +25,17 @@ namespace axom { namespace mir { + + enum Shape + { + Triangle, + Quad, + Tetrahedron, + Triangular_Prism, + Pyramid, + Hexahedron + }; + /** * \brief Simple 2D Point class for example */ diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp new file mode 100644 index 0000000000..db7c537a6e --- /dev/null +++ b/src/axom/mir/MIRUtilities.hpp @@ -0,0 +1,209 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file MIRUtilities.hpp + * + * \brief Contains a set of functions that provide general utility + * within Axom's MIR component. + * + */ + +#ifndef __MIR_UTILITIES_H__ +#define __MIR_UTILITIES_H__ + +#include "ZooClippingTables.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ +namespace utilities +{ + +//-------------------------------------------------------------------------------- + + /** + * \brief Determines the number of vertices of the given shape in the finite element zoo. + * + * \param shape The shape type from the finite element zoo. + * + * \return THe number of vertices of the shape. + */ + inline int numVerts(mir::Shape shape) + { + int numVertices = -1; + switch (shape) + { + case mir::Shape::Triangle: + numVertices = 3; + break; + case mir::Shape::Quad: + numVertices = 4; + break; + case mir::Shape::Tetrahedron: + numVertices = 4; + break; + case mir::Shape::Pyramid: + numVertices = 5; + break; + case mir::Shape::Triangular_Prism: + numVertices = 6; + break; + case mir::Shape::Hexahedron: + numVertices = 8; + break; + default: + printf("Invalid shape. Cannot determine numVerts().\n"); + } + return numVertices; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Determines the maximum number of possible vertices of the given shape in the finite element zoo. + * This number includes the midpoint vertices between each of the original shape's vertices. + * + * \param shape The shape type from the finite element zoo. + * + * \return THe number of vertices of the shape. + */ + inline int maxPossibleNumVerts(mir::Shape shape) + { + int numVertices = -1; + switch (shape) + { + case mir::Shape::Triangle: + numVertices = 6; + break; + case mir::Shape::Quad: + numVertices = 8; + break; + case mir::Shape::Tetrahedron: + numVertices = 10; + break; + case mir::Shape::Pyramid: + numVertices = 13; + break; + case mir::Shape::Triangular_Prism: + numVertices = 15; + break; + case mir::Shape::Hexahedron: + numVertices = 20; + break; + default: + printf("Invalid shape. Cannot determine maxPossibleNumVerts().\n"); + } + return numVertices; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Performs linear interpolation between the two given float values. + * + * \param f0 The first float value. + * \param f1 The second float value. + * \param t The percent of the distance from the first float value to the second. + * + * \return The interpolated value. + */ + inline axom::float64 lerpFloat(const axom::float64 f0, + const axom::float64 f1, + const axom::float64 t) + { + return (1 - t) * f0 + t * f1; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Performs linear interpolation between the two vertex positions. + * + * \param vertexOnePos The position of the first vertex. + * \param vertexTwoPos The position of the second vertex. + * \param t The percent of the distance from vertex one to vertex two to interpolate at. + * + * \return The interpolated position. + */ + inline mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, + const mir::Point2& vertexTwoPos, + const float t) + { + mir::Point2 interpolatedPoint; + interpolatedPoint.m_x = lerpFloat(vertexOnePos.m_x, vertexTwoPos.m_x, t); + interpolatedPoint.m_y = lerpFloat(vertexOnePos.m_y, vertexTwoPos.m_y, t); + return interpolatedPoint; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Returns the local vertex ID of the from/to vertex that is one of + * the two endpoints that the edge the given midpoint is on. + * + * \param shapeType The shape type from the finite element zoo. + * \param midpointVertexID The ID of the vertex between the two endpoints. + * \param isFromVertex A flag denoting which of the two edge endpoints to return. + * + * \return The vertex ID of one of the endpoints. + */ + inline int getEdgeEndpoint(const mir::Shape shapeType, + const int midpointVertexID, + const bool isFromVertex) + { + switch(shapeType) + { + case mir::Shape::Triangle: + if ( midpointVertexID == 3 && isFromVertex ) { return 0; } + if ( midpointVertexID == 3 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 4 && isFromVertex ) { return 1; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 5 && isFromVertex ) { return 2; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 0; } + break; + case mir::Shape::Quad: + if ( midpointVertexID == 4 && isFromVertex ) { return 0; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 0; } + break; + default: + printf("Edge endpoint case not implemented.\n"); + return -1; + break; + } + return -1; + } + +//-------------------------------------------------------------------------------- + +/** + * \brief Calculate the distance between the two given points. + * + * \param p0 The first point. + * \param p1 The second point. + * + * \return The distance between the two points. + */ +inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) +{ + return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); +} + +//-------------------------------------------------------------------------------- + +} +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 690c9ad35b..d4a7238aeb 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -116,6 +116,7 @@ MIRMesh MeshTester::initTestCaseOne() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -217,6 +218,7 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -289,6 +291,7 @@ mir::MIRMesh MeshTester::initTestCaseThree() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle}; // Build the mesh mir::MIRMesh testMesh; @@ -399,6 +402,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -442,10 +446,12 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } CellTopologyData topology; @@ -471,10 +477,10 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(quadP0, circleCenter); - axom::float64 distP1 = distance(quadP1, circleCenter); - axom::float64 distP2 = distance(quadP2, circleCenter); - axom::float64 distP3 = distance(quadP3, circleCenter); + axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); + axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); + axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); + axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) { @@ -492,7 +498,7 @@ axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::P for (int x = 0; x < gridSize; ++x) { mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); - if (distance(samplePoint, circleCenter) < circleRadius) + if (mir::utilities::distance(samplePoint, circleCenter) < circleRadius) ++countOverlap; } } @@ -626,13 +632,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) //-------------------------------------------------------------------------------- -axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) -{ - return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); -} - -//-------------------------------------------------------------------------------- - mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) { @@ -700,7 +699,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) bool isPointSampled = false; for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) { - if (distance(samplePoint, circleCenter) < circleRadii[cID]) + if (mir::utilities::distance(samplePoint, circleCenter) < circleRadii[cID]) { materialCount[cID]++; isPointSampled = true; @@ -723,10 +722,12 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } CellTopologyData topology; @@ -739,6 +740,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) mapData.m_elementDominantMaterials = elementDominantMaterials; mapData.m_elementParents = elementParents; mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; + mapData.m_shapeTypes = elementShapeTypes; // Build the mesh mir::MIRMesh testMesh; @@ -752,10 +754,10 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(quadP0, circleCenter); - axom::float64 distP1 = distance(quadP1, circleCenter); - axom::float64 distP2 = distance(quadP2, circleCenter); - axom::float64 distP3 = distance(quadP3, circleCenter); + axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); + axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); + axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); + axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); int numCorners = 0; @@ -791,14 +793,17 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() std::vector elementParents; std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; cellData.m_mapData.m_elementParents = elementParents; + cellData.m_mapData.m_shapeTypes = elementShapeTypes; // Build the mesh mir::MIRMesh testMesh; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 9641fec6a1..37160ae894 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -17,6 +17,7 @@ #include "axom/slam.hpp" // unified header for slam classes and functions #include "MIRMesh.hpp" +#include "MIRUtilities.hpp" #include @@ -119,17 +120,6 @@ namespace mir mir::MIRMesh initQuadClippingTestMesh(); private: - - /** - * \brief Calculate the distance between the two given points. - * - * \param p0 The first point. - * \param p1 The second point. - * - * \return The distance between the two points. - */ - axom::float64 distance(mir::Point2 p0, mir::Point2 p1); - /** * \brief Generates a 2D uniform grid of n x n elements. * diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 05e96ea2d0..9f87edbe5d 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -44,6 +44,27 @@ namespace mir {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} }; + const std::vector > quadClipTableVec = + { + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} + }; + + // Triangle Vertex Local Indices // 0 @@ -66,5 +87,18 @@ namespace mir {4,0,1,4,5,3,5,4,2,-1}, {3,0,1,2,-1,-1,-1,-1,-1,-1} }; + + const std::vector > triangleClipTableVec = + { + {3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {3,0,1,2,-1,-1,-1,-1,-1,-1} + }; + } } \ No newline at end of file diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index 924b98792a..4802db44f7 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -13,23 +13,17 @@ * for the shape types in the zoo. */ +#include + namespace axom { namespace mir { - - enum Shape - { - Triangle, - Quad, - Tetrahedron, - Triangular_Prism, - Pyramid, - Hexahedron - }; - extern const int quadClipTable[16][19]; extern const int triangleClipTable[8][10]; + + extern const std::vector > triangleClipTableVec; + extern const std::vector > quadClipTableVec; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 48d361400f..6eb3493bf6 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -48,7 +48,8 @@ int main( int argc, char** argv ) // Begin material interface reconstruction startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); + mir::MIRMesh processedMesh; + reconstructor.computeReconstructedInterface(testMesh, processedMesh); endTime = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ad67317a6e..c1034b1937 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -27,10 +27,10 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) auto startTime = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); - // mir::MIRMesh testMesh = tester.initTestCaseTwo(); + mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - mir::MIRMesh testMesh = tester.initTestCaseFive(50, 25); + // mir::MIRMesh testMesh = tester.initTestCaseFive(25, 12); auto endTime = Clock::now(); std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; @@ -38,15 +38,33 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) // Begin material interface reconstruction startTime = Clock::now(); + mir::MIRMesh processedMesh; + mir::InterfaceReconstructor reconstructor; - // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); // Process once, with original Meredith algorithm - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(testMesh, 5, 0.3); // 5 iterations, 30 percent with iterative Meredith algorithm + reconstructor.computeReconstructedInterface(testMesh, processedMesh); // Process once, with original Meredith algorithm + // reconstructor.computeReconstructedInterfaceIterative(testMesh, 100, 0.3, processedMesh); // 100 iterations, 20 percent with iterative Meredith algorithm endTime = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputIterative6.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputNewMARKONEW4.vtk"); + + std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); + + // // Print out the results + // printf("elementVolumeFractions: {\n"); + // for (int matID = 0; matID < processedMesh.m_numMaterials; ++matID) + // { + // printf("Material %d: {", matID); + // for (int eID = 0; eID < materialVolumeFractionsElement[matID].size(); ++eID) + // { + // printf(" %f,", materialVolumeFractionsElement[matID][eID]); + // } + // printf("}\n"); + // } + // printf("}\n"); + // // END TESTING return 0; } diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 15a42d39ff..5e969fd329 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -14,6 +14,9 @@ set(gtest_mir_tests mir_smoke.cpp mir_interface_reconstructor.cpp + mir_cell_clipper.cpp + mir_utilities.cpp + mir_cell_generator.cpp ) diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp new file mode 100644 index 0000000000..68f538e3f4 --- /dev/null +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -0,0 +1,451 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_CELL_CLIPPER_TEST_H_ +#define MIR_CELL_CLIPPER_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_zero) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 0 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_three) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 3 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_four) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {1.0, 0.0, 0.0}; + std::vector matTwoVF = {0.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 4 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_seven) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {1.0, 1.0, 1.0}; + std::vector matTwoVF = {0.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 7 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_zero) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 0 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_one) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 1 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_two) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 2 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_three) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 3 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_five) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 5 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_ten) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {1.0, 0.0, 1.0, 0.0}; + std::vector matTwoVF = {0.0, 1.0, 0.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 10 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_fifteen) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {1.0, 1.0, 1.0, 1.0}; + std::vector matTwoVF = {0.0, 0.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 15 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_when_mat_one_dominates) +{ + axom::float64 vfMatOneVertexOne = 0.0; + axom::float64 vfMatTwoVertexOne = 1.0; + + axom::float64 vfMatOneVertexTwo = 0.0; + axom::float64 vfMatTwoVertexTwo = 1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_when_mat_two_dominates) +{ + axom::float64 vfMatOneVertexOne = 1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = 1.0; + axom::float64 vfMatTwoVertexTwo = 0.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +// //---------------------------------------------------------------------- + + +TEST(mir_clipping_place, clip_edge_in_middle) +{ + axom::float64 vfMatOneVertexOne = 1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = 0.0; + axom::float64 vfMatTwoVertexTwo = 1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.5 ); +} + +// //---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_with_one_null_material) +{ + axom::float64 vfMatOneVertexOne = -1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = -1.0; + axom::float64 vfMatTwoVertexTwo = 0.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +// //---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_with_two_null_material) +{ + axom::float64 vfMatOneVertexOne = -1.0; + axom::float64 vfMatTwoVertexOne = -1.0; + + axom::float64 vfMatOneVertexTwo = -1.0; + axom::float64 vfMatTwoVertexTwo = -1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_zero) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 1, newElements.size() ); + EXPECT_EQ( 4, newVertices.size() ); + + EXPECT_EQ( 0, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 2, newElements[0][2] ); + EXPECT_EQ( 3, newElements[0][3] ); + + EXPECT_EQ( 0, newVertices[0][0] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 0, newVertices[2][0] ); + EXPECT_EQ( 0, newVertices[3][0] ); + + for (int i = 0; i < 8; ++i) + { + EXPECT_DOUBLE_EQ( 0, tValues[i] ); + } +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_one) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 3, newElements.size() ); + EXPECT_EQ( 6, newVertices.size() ); + + // Check the first element + EXPECT_EQ( 3, newElements[0][0] ); + EXPECT_EQ( 7, newElements[0][1] ); + EXPECT_EQ( 6, newElements[0][2] ); + + // Check the second element + EXPECT_EQ( 7, newElements[1][0] ); + EXPECT_EQ( 0, newElements[1][1] ); + EXPECT_EQ( 2, newElements[1][2] ); + EXPECT_EQ( 6, newElements[1][3] ); + + // Check the third element + EXPECT_EQ( 0, newElements[2][0] ); + EXPECT_EQ( 1, newElements[2][1] ); + EXPECT_EQ( 2, newElements[2][2] ); + + // Check each vertex's associated elements + EXPECT_EQ( 1, newVertices[0][0] ); + EXPECT_EQ( 2, newVertices[0][1] ); + + EXPECT_EQ( 2, newVertices[1][0] ); + + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 2, newVertices[2][1] ); + + EXPECT_EQ( 0, newVertices[3][0] ); + + EXPECT_EQ( 0, newVertices[6][0] ); + EXPECT_EQ( 1, newVertices[6][1] ); + + EXPECT_EQ( 0, newVertices[7][0] ); + EXPECT_EQ( 1, newVertices[7][1] ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_three) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 2, newElements.size() ); + EXPECT_EQ( 6, newVertices.size() ); + + EXPECT_EQ( 0, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 5, newElements[0][2] ); + EXPECT_EQ( 7, newElements[0][3] ); + + EXPECT_EQ( 7, newElements[1][0] ); + EXPECT_EQ( 5, newElements[1][1] ); + EXPECT_EQ( 2, newElements[1][2] ); + EXPECT_EQ( 3, newElements[1][3] ); + + EXPECT_EQ( 0, newVertices[0][0] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 1, newVertices[3][0] ); + EXPECT_EQ( 0, newVertices[5][0] ); + EXPECT_EQ( 1, newVertices[5][1] ); + EXPECT_EQ( 0, newVertices[7][0] ); + EXPECT_EQ( 1, newVertices[7][1] ); + +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_five) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 4, newElements.size() ); + EXPECT_EQ( 8, newVertices.size() ); + + EXPECT_EQ( 4, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 5, newElements[0][2] ); + + EXPECT_EQ( 0, newElements[1][0] ); + EXPECT_EQ( 4, newElements[1][1] ); + EXPECT_EQ( 5, newElements[1][2] ); + EXPECT_EQ( 2, newElements[1][3] ); + + EXPECT_EQ( 0, newElements[2][0] ); + EXPECT_EQ( 2, newElements[2][1] ); + EXPECT_EQ( 6, newElements[2][2] ); + EXPECT_EQ( 7, newElements[2][3] ); + + EXPECT_EQ( 7, newElements[3][0] ); + EXPECT_EQ( 6, newElements[3][1] ); + EXPECT_EQ( 3, newElements[3][2] ); + + EXPECT_EQ( 1, newVertices[0][0] ); + EXPECT_EQ( 2, newVertices[0][1] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 2, newVertices[2][1] ); + EXPECT_EQ( 3, newVertices[3][0] ); + EXPECT_EQ( 0, newVertices[4][0] ); + EXPECT_EQ( 1, newVertices[4][1] ); + EXPECT_EQ( 0, newVertices[5][0] ); + EXPECT_EQ( 1, newVertices[5][1] ); + EXPECT_EQ( 2, newVertices[6][0] ); + EXPECT_EQ( 3, newVertices[6][1] ); + EXPECT_EQ( 2, newVertices[7][0] ); + EXPECT_EQ( 3, newVertices[7][1] ); + +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_CELL_CLIPPER_TEST_H_ diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp new file mode 100644 index 0000000000..f03b480bda --- /dev/null +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_CELL_GENERATOR_TEST_H_ +#define MIR_CELL_GENERATOR_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_quad_topology) +{ + EXPECT_EQ(true, 1); + // this function generates the evInds, evBegins, ... etc given the map of elements -> verts, and verts -> elements + std::map > elementMap; + elementMap[0] = { 4, 1, 5 }; + elementMap[1] = { 0, 4, 5, 2 }; + elementMap[2] = { 0, 2, 6, 7 }; + elementMap[3] = { 7, 6, 3 }; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + mir::CellData cellData; + mir::CellGenerator generator; + generator.generateTopologyData(elementMap, vertexMap, cellData); + + EXPECT_EQ( 4, cellData.m_topology.m_evInds[0] ); + EXPECT_EQ( 1, cellData.m_topology.m_evInds[1] ); + EXPECT_EQ( 5, cellData.m_topology.m_evInds[2] ); + EXPECT_EQ( 0, cellData.m_topology.m_evInds[3] ); + EXPECT_EQ( 4, cellData.m_topology.m_evInds[4] ); + EXPECT_EQ( 5, cellData.m_topology.m_evInds[5] ); + EXPECT_EQ( 2, cellData.m_topology.m_evInds[6] ); + EXPECT_EQ( 0, cellData.m_topology.m_evInds[7] ); + EXPECT_EQ( 2, cellData.m_topology.m_evInds[8] ); + EXPECT_EQ( 6, cellData.m_topology.m_evInds[9] ); + EXPECT_EQ( 7, cellData.m_topology.m_evInds[10] ); + EXPECT_EQ( 7, cellData.m_topology.m_evInds[11] ); + EXPECT_EQ( 6, cellData.m_topology.m_evInds[12] ); + EXPECT_EQ( 3, cellData.m_topology.m_evInds[13] ); + + EXPECT_EQ( 1, cellData.m_topology.m_veInds[0] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[1] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[2] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[3] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[4] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[5] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[6] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[7] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[8] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[9] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[10] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[11] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[12] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[13] ); + + EXPECT_EQ( 0, cellData.m_topology.m_evBegins[0] ); + EXPECT_EQ( 3, cellData.m_topology.m_evBegins[1] ); + EXPECT_EQ( 7, cellData.m_topology.m_evBegins[2] ); + EXPECT_EQ( 11, cellData.m_topology.m_evBegins[3] ); + + EXPECT_EQ( 0, cellData.m_topology.m_veBegins[0] ); + EXPECT_EQ( 2, cellData.m_topology.m_veBegins[1] ); + EXPECT_EQ( 3, cellData.m_topology.m_veBegins[2] ); + EXPECT_EQ( 5, cellData.m_topology.m_veBegins[3] ); + EXPECT_EQ( 6, cellData.m_topology.m_veBegins[4] ); + EXPECT_EQ( 8, cellData.m_topology.m_veBegins[5] ); + EXPECT_EQ( 10, cellData.m_topology.m_veBegins[6] ); + EXPECT_EQ( 12, cellData.m_topology.m_veBegins[7] ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_vertex_positions) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + std::vector originalVertexPositions; + originalVertexPositions.push_back( mir::Point2(0.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2(0.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2(1.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2(1.0, 1.0) ); + + axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + cellGenerator.generateVertexPositions(shapeType, vertexMap, originalVertexPositions, tValues, cellData); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_y, 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_y, 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_y, 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_x, 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_y, 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_x, 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_y, 1.0, 0.00001 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_vertex_volume_fractions) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + std::vector > originalVertexVF(2); + originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; + originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + + axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + cellGenerator.generateVertexVolumeFractions(shapeType, vertexMap, originalVertexVF, tValues, cellData); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][0], 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][0], 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][1], 0.33, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][1], 0.67, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][2], 0.67, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][2], 0.33, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][3], 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][3], 0.0, 0.00001 ); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][4], 0.165, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][4], 0.835, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][5], 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][5], 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][6], 0.835, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][6], 0.165, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][7], 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][7], 0.5, 0.00001 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, determine_clean_cell_material) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::vector vertexIDs = { 0, 1, 5, 7 }; + + int matOne = 0; + int matTwo = 1; + + std::vector > originalVertexVF(2); + originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; + originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + + int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, vertexIDs, matOne, matTwo, originalVertexVF); + + EXPECT_EQ( dominantMaterial, 1 ); +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_CELL_GENERATOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp index 8a11c21e73..875c7365df 100644 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef MIR_SMOKE_H_ -#define MIR_SMOKE_H_ +#ifndef MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ +#define MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ #include "gtest/gtest.h" @@ -13,31 +13,6 @@ using namespace axom; -TEST(mir_quad_clipping, quad_clipping_case_zero) -{ - // Initialize a 3x3 mesh with 2 materials - mir::MeshTester meshGenerator; - mir::MIRMesh testMesh = meshGenerator.initQuadClippingTestMesh(); - - // Initialize its volume fractions to custom values to guarantee this clipping case - std::vector > vertexVF; - vertexVF.resize(2); - vertexVF[0] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - vertexVF[1] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - int upperLeftVertexID = 5; - int lowerLeftVertexID = 9; - int lowerRightVertexID = 10; - int upperRightVertexID = 6; - - mir::InterfaceReconstructor reconstructor; - unsigned int clippingCase = reconstructor.determineQuadClippingCase( testMesh, 0, 1, upperLeftVertexID, lowerLeftVertexID, lowerRightVertexID, upperRightVertexID ); - - EXPECT_EQ( clippingCase, 0 ); -} - - //---------------------------------------------------------------------- int main(int argc, char* argv[]) @@ -52,4 +27,4 @@ int main(int argc, char* argv[]) } -#endif // MIR_SMOKE_H_ +#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp new file mode 100644 index 0000000000..82e26b89a0 --- /dev/null +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_UTILITIES_TEST_H_ +#define MIR_UTILITIES_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +TEST(mir_interpolation, float_linear_interpolation) +{ + axom::float64 f0 = 50.0; + axom::float64 f1 = 100.0; + + axom::float64 t = 0.0; + axom::float64 interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 50.0 ); + + t = 1.0; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 100.0 ); + + t = 0.5; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 75.0 ); + + t = 0.66; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 83.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_interpolation, point_linear_interpolation) +{ + mir::Point2 p0(0.25, 0.0); + mir::Point2 p1(1.25, 1.0); + + axom::float64 t = 0.0; + mir::Point2 interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 0.25); + EXPECT_DOUBLE_EQ( interpolant.m_y, 0.0); + + t = 1.0; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 1.25); + EXPECT_DOUBLE_EQ( interpolant.m_y, 1.0); + + t = 0.5; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 0.75); + EXPECT_DOUBLE_EQ( interpolant.m_y, 0.5); + + t = 0.66; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_NEAR( interpolant.m_x, 0.91, 0.00001); + EXPECT_NEAR( interpolant.m_y, 0.66, 0.00001); +} + +//---------------------------------------------------------------------- + +TEST(mir_distance_utility, compute_distance) +{ + mir::Point2 p0( 0.25, 0.0 ); + mir::Point2 p1( 1.25, 0.0 ); + axom::float64 dist = mir::utilities::distance(p0, p1); + EXPECT_DOUBLE_EQ( dist, 1.0 ); + + mir::Point2 p2( 0.25, 0.0 ); + mir::Point2 p3( 1.25, 1.0 ); + dist = mir::utilities::distance(p2, p3); + EXPECT_NEAR( dist, 1.4142, 0.001 ); + + mir::Point2 p4( 1.0, 1.0 ); + mir::Point2 p5( 1.0, 1.0 ); + dist = mir::utilities::distance(p4, p5); + EXPECT_DOUBLE_EQ( dist, 0.0 ); + +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_UTILITIES_TEST_H_ diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index 75ab74554e..cddf4cd4df 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,7 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; - mir -> {slic core}; + mir -> {slic core slam}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/index.rst b/src/index.rst index 9ac474a4b6..afcf6266ee 100644 --- a/src/index.rst +++ b/src/index.rst @@ -88,7 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic +- Mir depends on Slic and Slam - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From 3cce320634d5461894fb3d2aea7544390cae746e Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Thu, 27 Jun 2019 09:03:42 -0700 Subject: [PATCH 013/290] Implemented function for determining the two endpoints of a given midpoint for the 3D cases. --- src/axom/mir/CellGenerator.cpp | 2 +- src/axom/mir/MIRUtilities.hpp | 74 +++++++++++++++++++ src/axom/mir/examples/mir_tutorial_simple.cpp | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index 05b61aa030..c81c535f59 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -110,7 +110,7 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, { int vID = itr->first; - for (int matID = 0; matID < vertexVF.size(); ++matID) + for (unsigned long matID = 0; matID < vertexVF.size(); ++matID) { if ( vID < mir::utilities::numVerts(shapeType) ) { diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index db7c537a6e..0f3c75c084 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -177,6 +177,80 @@ namespace utilities if ( midpointVertexID == 7 && isFromVertex ) { return 3; } if ( midpointVertexID == 7 && !isFromVertex ) { return 0; } break; + case mir::Shape::Tetrahedron: + if ( midpointVertexID == 4 && isFromVertex ) { return 0; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && isFromVertex ) { return 0; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 9 && isFromVertex ) { return 3; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 1; } + case mir::Shape::Pyramid: + if ( midpointVertexID == 5 && isFromVertex ) { return 0; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 7 && isFromVertex ) { return 0; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 8 && isFromVertex ) { return 0; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 9 && isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 12 && isFromVertex ) { return 4; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 1; } + case mir::Shape::Triangular_Prism: + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 10 && isFromVertex ) { return 1; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 11 && isFromVertex ) { return 2; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 12 && isFromVertex ) { return 3; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && !isFromVertex ) { return 3; } + case mir::Shape::Hexahedron: + if ( midpointVertexID == 8 && isFromVertex ) { return 0; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 0; } + if ( midpointVertexID == 12 && isFromVertex ) { return 0; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && isFromVertex ) { return 1; } + if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && isFromVertex ) { return 2; } + if ( midpointVertexID == 14 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 15 && isFromVertex ) { return 3; } + if ( midpointVertexID == 15 && !isFromVertex ) { return 7; } + if ( midpointVertexID == 16 && isFromVertex ) { return 4; } + if ( midpointVertexID == 16 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 17 && isFromVertex ) { return 5; } + if ( midpointVertexID == 17 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 18 && isFromVertex ) { return 6; } + if ( midpointVertexID == 18 && !isFromVertex ) { return 7; } + if ( midpointVertexID == 19 && isFromVertex ) { return 7; } + if ( midpointVertexID == 19 && !isFromVertex ) { return 4; } default: printf("Edge endpoint case not implemented.\n"); return -1; diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index c1034b1937..2217f64c4b 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -48,7 +48,7 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputNewMARKONEW4.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedMesh.vtk"); std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); From b7a12c15567040a08d8a18b15740d482b45cd6fb Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Mon, 1 Jul 2019 16:51:15 -0700 Subject: [PATCH 014/290] Updates to build system for mir component --- src/axom/mainpage.md | 2 +- src/axom/mir/CMakeLists.txt | 7 ++----- src/axom/mir/examples/CMakeLists.txt | 9 ++------- src/axom/mir/tests/CMakeLists.txt | 7 +++++-- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 60708dae52..84f42826ad 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -21,7 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic +- Mir depends on Slic and Slam - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index db7d2fda20..65104b39b1 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -10,7 +10,7 @@ # Check necessary dependencies #------------------------------------------------------------------------------ axom_component_requires(NAME MIR - COMPONENTS SLIC) + COMPONENTS SLIC SLAM) #------------------------------------------------------------------------------ # Specify all headers/sources @@ -40,7 +40,7 @@ set(mir_sources #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic) +set(mir_depends_on core slic slam) blt_add_library( NAME mir @@ -61,9 +61,6 @@ axom_install_component(NAME mir #------------------------------------------------------------------------------ if (AXOM_ENABLE_TESTS) add_subdirectory(tests) - if (ENABLE_BENCHMARKS) - # add_subdirectory(benchmarks) - endif() endif() if (AXOM_ENABLE_EXAMPLES) diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 16e15481a7..24878a8dde 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -9,16 +9,11 @@ set( mir_examples ) set( mir_example_dependencies - #core + core + slic mir - #slic ) -blt_list_append( TO mir_example_dependencies ELEMENTS sidre conduit IF AXOM_MIR_USE_SIDRE ) -blt_list_append( TO mir_example_dependencies ELEMENTS raja IF RAJA_FOUND ) -blt_list_append( TO mir_example_dependencies ELEMENTS openmp IF ENABLE_OPENMP ) -blt_list_append( TO mir_example_dependencies ELEMENTS cuda IF ENABLE_CUDA ) - foreach( example ${mir_examples} ) get_filename_component( example_name ${example} NAME_WE ) diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 5e969fd329..102b1234c7 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -19,8 +19,10 @@ set(gtest_mir_tests mir_cell_generator.cpp ) - -set(mir_tests_depends_on core slic mir gtest) +set(mir_tests_depends_on + slic + mir + gtest) #------------------------------------------------------------------------------ # Add gtest based tests @@ -32,6 +34,7 @@ foreach(test ${gtest_mir_tests}) OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} DEPENDS_ON ${mir_tests_depends_on} FOLDER axom/mir/tests ) + blt_add_test( NAME ${test_name} COMMAND ${test_name}_test ) endforeach() From 238a0b15806261099846807ac9fd2212b76bb070 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:18:55 -0700 Subject: [PATCH 015/290] Adds some improvements to MIRMesh * Adds copy constructor and copy assignment operator * Adds validity check predicate -- isValid() * Adds unit tests. Initially focused on exercising constructors and assignment operators. --- src/axom/mir/InterfaceReconstructor.cpp | 4 +- src/axom/mir/MIRMesh.cpp | 254 +++++++++++++++++++++--- src/axom/mir/MIRMesh.hpp | 29 ++- src/axom/mir/tests/CMakeLists.txt | 1 + src/axom/mir/tests/mir_mesh.cpp | 229 +++++++++++++++++++++ 5 files changed, 483 insertions(+), 34 deletions(-) create mode 100644 src/axom/mir/tests/mir_mesh.cpp diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index e4fdab75d8..b18c17cfdb 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -39,7 +39,7 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe for (int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) { // Copy the mesh to be split - mir::MIRMesh intermediateMesh(&finalMesh); + mir::MIRMesh intermediateMesh(finalMesh); // Create an array to store the output of each element being split. CellData temp_cellData[intermediateMesh.m_elems.size()]; @@ -271,4 +271,4 @@ void InterfaceReconstructor::generateCleanCells(const int eID, //-------------------------------------------------------------------------------- } -} \ No newline at end of file +} diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index c47fecf3bf..96130fed54 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -5,44 +5,64 @@ #include "MIRMesh.hpp" +#include "axom/core.hpp" + namespace axom { namespace mir { -//-------------------------------------------------------------------------------- MIRMesh::MIRMesh() -{ - -} + : m_verts(0) + , m_elems(0) + , m_numMaterials(0) +{} //-------------------------------------------------------------------------------- -MIRMesh::MIRMesh(MIRMesh* _mesh) +MIRMesh::MIRMesh(const MIRMesh& other) + : m_verts(other.m_verts) + , m_elems(other.m_elems) + , m_materialVolumeFractionsElement(other.m_materialVolumeFractionsElement) + , m_materialVolumeFractionsVertex(other.m_materialVolumeFractionsVertex) + , m_numMaterials(other.m_numMaterials) + , m_meshTopology(other.m_meshTopology) { - m_meshTopology.m_evInds = _mesh->m_meshTopology.m_evInds; - m_meshTopology.m_evBegins = _mesh->m_meshTopology.m_evBegins; - m_meshTopology.m_veInds = _mesh->m_meshTopology.m_veInds; - m_meshTopology.m_veBegins = _mesh->m_meshTopology.m_veBegins; - m_verts = _mesh->m_verts; - m_elems = _mesh->m_elems; - m_bdry = _mesh->m_bdry; - m_cobdry = _mesh->m_cobdry; - m_vertexPositions = _mesh->m_vertexPositions; - m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; - m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; - m_elementParentIDs = _mesh->m_elementParentIDs; - m_shapeTypes = _mesh->m_shapeTypes; - m_elementDominantMaterials = _mesh->m_elementDominantMaterials; - m_numMaterials = _mesh->m_numMaterials; -} + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); -//-------------------------------------------------------------------------------- + m_shapeTypes= IntMap( &m_elems ); + for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } -MIRMesh::~MIRMesh() +} + +MIRMesh& MIRMesh::operator=(const MIRMesh& other) { + if(this != &other) + { + m_verts = other.m_verts; + m_elems = other.m_elems; + + m_meshTopology = other.m_meshTopology; + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); + + + m_shapeTypes = IntMap( &m_elems ); + for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } + + m_materialVolumeFractionsElement = other.m_materialVolumeFractionsElement; + m_materialVolumeFractionsVertex = other.m_materialVolumeFractionsVertex; + + m_numMaterials = other.m_numMaterials; + } + return *this; } //-------------------------------------------------------------------------------- @@ -122,8 +142,8 @@ void MIRMesh::constructMeshRelations() SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << m_bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << m_cobdry.totalSize()); + SLIC_DEBUG("Elem-Vert relation has size " << m_bdry.totalSize()); + SLIC_DEBUG("Vert-Elem relation has size " << m_cobdry.totalSize()); } //-------------------------------------------------------------------------------- @@ -243,7 +263,7 @@ void MIRMesh::constructElementShapeTypesMap(const std::vector& shape // Initialize the map for the elements' dominant colors m_shapeTypes = IntMap( &m_elems ); - // Copy the dat for the elements + // Copy the data for the elements for (int eID = 0; eID < m_elems.size(); ++eID) m_shapeTypes[eID] = shapeTypes[eID]; @@ -315,6 +335,18 @@ void MIRMesh::print() } printf("}\n"); + printf("elementVolumeFractions: { \n"); + for (unsigned long i = 0; i < m_materialVolumeFractionsElement.size(); ++i) + { + printf(" { "); + for (int j = 0; j < m_elems.size(); ++j) + { + printf("%.3f, ", m_materialVolumeFractionsElement[i][j]); + } + printf("}\n"); + } + printf("}\n"); + printf("vertexVolumeFractions: { \n"); for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { @@ -475,5 +507,173 @@ axom::float64 MIRMesh::computeQuadArea(Point2 p0, //-------------------------------------------------------------------------------- +bool MIRMesh::isValid(bool verbose) const +{ + bool bValid = true; + + // Check the topology data + bValid = bValid && isTopologyValid(verbose); + + // Check the volume fraction data + bValid = bValid && areVolumeFractionsValid(verbose); + + // Check the map data + bValid = bValid && areMapsValid(verbose); + + return bValid; +} + +bool MIRMesh::isTopologyValid(bool verbose) const +{ + bool bValid = true; + + // check the vertex and element sets + bValid = bValid && m_verts.isValid(verbose); + bValid = bValid && m_elems.isValid(verbose); + + // check the relations + if(!m_verts.empty() && !m_elems.empty() ) + { + bValid = bValid && m_bdry.isValid(verbose); + bValid = bValid && m_cobdry.isValid(verbose); + } + + if(!bValid && verbose) + { + SLIC_WARNING("MIRMesh invalid: Invalid topology"); + } + + return bValid; +} + +bool MIRMesh::areVolumeFractionsValid(bool verbose) const +{ + bool bValid = true; + + int vfElemSize = m_materialVolumeFractionsElement.size(); + if( vfElemSize != m_numMaterials) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Invalid number of materials in volume fraction array."); + } + return bValid; + } + + // Check the sizes of the volume fraction arrays + for(int i=0; i < m_numMaterials; ++i) + { + const auto& mats = m_materialVolumeFractionsElement[i]; + if( mats.size() != m_elems.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Material " << i <<" has wrong " + << " number of materials in volume fraction array." + << " Expected " << m_elems.size() + << ", got " << mats.size() << "."); + } + } + } + + if(!bValid) + { + return false; + } + + // Check that the volume fractions sum to 1.0 + for(auto el : m_elems.positions()) + { + double sum = 0; + for(int m=0; m < m_numMaterials; ++m) + { + sum += m_materialVolumeFractionsElement[m][el]; + } + + if(! axom::utilities::isNearlyEqual(sum, 1.)) + { + bValid = false; + if(verbose) + { + std::stringstream sstr; + + for(int m=0; m < m_numMaterials; ++m) + { + sstr << m_materialVolumeFractionsElement[m][el] << " "; + } + + SLIC_WARNING("MIRMesh invalid: Sum of materials in element" + << el << " was " << sum << ". Expected 1." + << " Values were: " << sstr.str() ); + } + } + } + + return bValid; +} + +bool MIRMesh::areMapsValid(bool verbose) const +{ + bool bValid = true; + + // Check the positions map + if( m_vertexPositions.size() != m_verts.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of vertex positions." + << " Expected " << m_verts.size() + << ". Got " << m_vertexPositions.size()); + } + } + + bValid = bValid && m_vertexPositions.isValid(verbose); + + // Check the element maps + bValid = bValid && m_elementParentIDs.isValid(verbose); + bValid = bValid && m_elementDominantMaterials.isValid(verbose); + bValid = bValid && m_shapeTypes.isValid(verbose); + + if( m_elementParentIDs.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem parent IDs." + << " Expected " << m_elems.size() + << ". Got " << m_elementParentIDs.size()); + } + } + + if( m_elementDominantMaterials.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem dominant mats." + << " Expected " << m_elems.size() + << ". Got " << m_elementDominantMaterials.size()); + } + } + + if( m_shapeTypes.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem shape types." + << " Expected " << m_elems.size() + << ". Got " << m_shapeTypes.size()); + } + } + + return bValid; + +} + + +} } -} \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 984ada037e..57b1e7446d 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -57,14 +57,19 @@ namespace mir MIRMesh(); /** - * \brief Copy constrcutor. + * \brief Copy constructor. */ - MIRMesh(MIRMesh* _mesh); + MIRMesh(const MIRMesh& other); /** * \brief Default destructor. */ - ~MIRMesh(); + ~MIRMesh() = default; + + /** + * \brief Copy assignment. + */ + MIRMesh& operator=(const MIRMesh& other); /** * \brief Initializes a mesh with the provided data and topology. @@ -132,10 +137,24 @@ namespace mir */ std::vector > computeOriginalElementVolumeFractions(); + /** + * \brief Checks that this instance of MIRMesh is valid + * + * \return True if this instance is valid, false otherwise + */ + bool isValid(bool verbose) const; + private: + /// Utility predicate to check if the mesh's topology is valid + bool isTopologyValid(bool verbose) const; + /// Utility predicate to check if the mesh's volume fractions are valid + bool areVolumeFractionsValid(bool verbose) const; + /// Utility predicate to check if the mesh's maps are valid + bool areMapsValid(bool verbose) const; + /** - * \brief Constucts the positions map on the vertices. + * \brief Constructs the positions map on the vertices. * * \param data The vector of position data for each vertex. */ @@ -215,4 +234,4 @@ namespace mir //-------------------------------------------------------------------------------- } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 102b1234c7..d4b750967b 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(gtest_mir_tests mir_smoke.cpp + mir_mesh.cpp mir_interface_reconstructor.cpp mir_cell_clipper.cpp mir_utilities.cpp diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp new file mode 100644 index 0000000000..0d9797e16d --- /dev/null +++ b/src/axom/mir/tests/mir_mesh.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_MESH_TEST_H_ +#define MIR_MESH_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + + +namespace mir = axom::mir; + +class MirMeshTest : public ::testing::Test +{ +public: + + template + using Vec = std::vector; + + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; + + enum { GREEN = 0, BLUE = 1 }; + +public: + + mir::MIRMesh& getMesh() { return m_mesh; } + const mir::MIRMesh& getMesh() const { return m_mesh; } + +protected: + void SetUp() override + { + m_verts= mir::VertSet(16); + m_elems= mir::ElemSet(9); + m_nMats = 2; + + // Create the mesh connectivity information + setupTopoData(); + setupMapData(); + setupVolumeFractions(); + + m_mesh.initializeMesh(this->m_verts, this->m_elems, this->m_nMats, + this->m_topoData, this->m_mapData, this->m_volFracs); + } + + // Set up the mesh's topological connectivity + void setupTopoData() + { + m_topoData.m_evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + m_topoData.m_evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + + m_topoData.m_veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + + m_topoData.m_veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + } + + // Set up the mesh's map data + void setupMapData() + { + m_mapData.m_vertexPositions = { + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + m_mapData.m_elementDominantMaterials = Vec(m_elems.size(), mir::NULL_MAT); + m_mapData.m_elementParents = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + m_mapData.m_shapeTypes = Vec(m_elems.size(), mir::Shape::Quad); + } + + // Set up the mesh's volume fraction data + void setupVolumeFractions() + { + m_volFracs.resize(m_nMats); + m_volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + m_volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + } + +protected: + mir::VertSet m_verts; + mir::ElemSet m_elems; + int m_nMats; + + mir::CellTopologyData m_topoData; + mir::CellMapData m_mapData; + mir::CellData m_cellData; + VolumeFractions m_volFracs; + + mir::MIRMesh m_mesh; +}; + + +TEST_F(MirMeshTest,default_ctor) +{ + mir::MIRMesh mesh; + EXPECT_TRUE(mesh.isValid(true)); +} + + +TEST_F(MirMeshTest,initialize) +{ + mir::MIRMesh mesh; + mesh.initializeMesh( + this->m_verts, + this->m_elems, + this->m_nMats, + this->m_topoData, + this->m_mapData, + this->m_volFracs); + + EXPECT_TRUE(mesh.isValid(true)); + + mesh.print(); +} + +TEST_F(MirMeshTest,copy_ctor) +{ + // test copy constructor + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + + mir::MIRMesh mirCopy(mesh); + EXPECT_TRUE( mirCopy.isValid(true) ); + } + + // test const copy constructor + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + // test copy constructor + const mir::MIRMesh mirCopy(cmesh); + EXPECT_TRUE( mirCopy.isValid(true) ); + } +} + +TEST_F(MirMeshTest,copy_assign) +{ + // test copy assignment from a non-const MIRMesh + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + + mir::MIRMesh mirCopy; + mirCopy = mesh; + EXPECT_TRUE( mirCopy.isValid(true) ); + } + + // test copy assignment from a const MIRMesh + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + mir::MIRMesh mirCopy; + mirCopy = cmesh; + EXPECT_TRUE( mirCopy.isValid(true) ); + } +} + + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_MESH_TEST_H_ From 271ca4a72561b5e8ccff3cde1655cb72483c58d8 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:21:47 -0700 Subject: [PATCH 016/290] Improves output of slam::Map's validity check and print functions --- src/axom/slam/Map.hpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/axom/slam/Map.hpp b/src/axom/slam/Map.hpp index 35d9c55090..60da43c560 100644 --- a/src/axom/slam/Map.hpp +++ b/src/axom/slam/Map.hpp @@ -15,7 +15,6 @@ #include #include -#include #include "axom/core.hpp" #include "axom/slic.hpp" @@ -483,23 +482,16 @@ bool Map::isValid(bool verboseOutput) const } - if(verboseOutput) + if(verboseOutput && !bValid) { std::stringstream sstr; sstr << "\n*** Detailed results of isValid on the map.\n"; - if(bValid) - { - sstr << "Map was valid." << std::endl; - } - else - { - sstr << "Map was NOT valid.\n" + sstr << "Map was NOT valid.\n" << sstr.str() << std::endl; - } - std::cout << sstr.str() << std::endl; + SLIC_DEBUG( sstr.str() ); } return bValid; @@ -510,10 +502,11 @@ template void Map::print() const { bool valid = isValid(true); - std::stringstream sstr; if (valid) { + std::stringstream sstr; + if (!m_set) { sstr << "** map is empty."; @@ -534,9 +527,14 @@ void Map::print() const } } } + + SLIC_INFO( sstr.str() ); + } + else + { + SLIC_INFO("Map was not valid."); } - std::cout << sstr.str() << std::endl; } From c41820bf02fdc143905ade82eb121596bd78ebe6 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:22:31 -0700 Subject: [PATCH 017/290] Refactors how test meshes are set up in mir's MeshTester class --- src/axom/mir/MeshTester.cpp | 292 ++++++++++++++++-------------------- src/axom/mir/MeshTester.hpp | 22 ++- 2 files changed, 141 insertions(+), 173 deletions(-) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index d4a7238aeb..6a8eb612e5 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -5,34 +5,30 @@ #include "MeshTester.hpp" +namespace numerics = axom::numerics; +namespace slam = axom::slam; + namespace axom { namespace mir { - -//-------------------------------------------------------------------------------- - -MeshTester::MeshTester() -{ - -} - -//-------------------------------------------------------------------------------- - -MeshTester::~MeshTester() -{ - -} //-------------------------------------------------------------------------------- MIRMesh MeshTester::initTestCaseOne() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -44,10 +40,10 @@ MIRMesh MeshTester::initTestCaseOne() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -65,25 +61,20 @@ MIRMesh MeshTester::initTestCaseOne() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - - int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); + + volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - std::vector greenVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - elementVF[GREEN] = greenVolumeFractions; - std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - elementVF[BLUE] = blueVolumeFractions; - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -106,21 +97,13 @@ MIRMesh MeshTester::initTestCaseOne() mir::Point2( 3.0, 0.0 ) }; - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -129,11 +112,18 @@ MIRMesh MeshTester::initTestCaseOne() mir::MIRMesh MeshTester::initTestCaseTwo() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -145,10 +135,10 @@ mir::MIRMesh MeshTester::initTestCaseTwo() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -166,26 +156,19 @@ mir::MIRMesh MeshTester::initTestCaseTwo() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - int numMaterials = 3; enum { BLUE = 0, RED = 1, ORANGE = 2 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); + volFracs[BLUE] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + volFracs[RED] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + volFracs[ORANGE] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - std::vector blueVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - elementVF[BLUE] = blueVolumeFractions; - std::vector redVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; - elementVF[RED] = redVolumeFractions; - std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - elementVF[ORANGE] = orangeVolumeFractions; - - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -208,21 +191,13 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mir::Point2( 3.0, 0.0 ) }; - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -231,21 +206,29 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mir::MIRMesh MeshTester::initTestCaseThree() { - int numElements = 4; - int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,1,2, // elem 0, card 3, start 0 1,3,4, // elem 1, card 3, start 3 1,4,2, // elem 2, card 3, start 6 2,4,5 // elem 3, card 3, start 9, end 12 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,3,6,9,12 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1,2, // vert 1, card 3, start 1 0,2,3, // vert 2, card 3, start 4 @@ -253,24 +236,19 @@ mir::MIRMesh MeshTester::initTestCaseThree() 1,2,3, // vert 4, card 3, start 8 3 // vert 5, card 1, start 11, end 12 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,4,7,8,11,12 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements int numMaterials = 2; enum { BLUE = 0, RED = 1, }; - std::vector > elementVF; elementVF.resize(numMaterials); - - std::vector blueVolumeFractions = {0.0, 0.5, 0.8, 0.5}; - elementVF[BLUE] = blueVolumeFractions; - std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; - elementVF[RED] = redVolumeFractions; + volFracs.resize(numMaterials); + volFracs[BLUE] = {0.0, 0.5, 0.8, 0.5}; + volFracs[RED] = {1.0, 0.5, 0.2, 0.5}; - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 1.0, 2.0 ), mir::Point2( 0.5, 1.0 ), @@ -280,22 +258,13 @@ mir::MIRMesh MeshTester::initTestCaseThree() mir::Point2( 2.0, 0.0 ) }; - - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Triangle); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -304,11 +273,18 @@ mir::MIRMesh MeshTester::initTestCaseThree() mir::MIRMesh MeshTester::initTestCaseFour() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -320,10 +296,10 @@ mir::MIRMesh MeshTester::initTestCaseFour() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -341,14 +317,12 @@ mir::MIRMesh MeshTester::initTestCaseFour() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -374,10 +348,15 @@ mir::MIRMesh MeshTester::initTestCaseFour() int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); - std::vector greenVolumeFractions; greenVolumeFractions.resize(numElements); - std::vector blueVolumeFractions; blueVolumeFractions.resize(numElements); + auto& greenVolumeFractions = volFracs[GREEN]; + auto& blueVolumeFractions = volFracs[BLUE]; + const auto& points = mapData.m_vertexPositions; + const auto& evInds = topoData.m_evInds; + + greenVolumeFractions.resize(numElements); + blueVolumeFractions.resize(numElements); // Generate the element volume fractions for the circle mir::Point2 circleCenter(1.5, 1.5); @@ -385,28 +364,24 @@ mir::MIRMesh MeshTester::initTestCaseFour() int gridSize = 1000; for (int i = 0; i < numElements; ++i) { - greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(gridSize, circleCenter, circleRadius, points[evInds[i * 4 + 0]], points[evInds[i * 4 + 1]], points[evInds[i * 4 + 2]], points[evInds[i * 4 + 3]]); - blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + auto vf = calculatePercentOverlapMonteCarlo(gridSize, + circleCenter, + circleRadius, + points[evInds[i * 4 + 0]], + points[evInds[i * 4 + 1]], + points[evInds[i * 4 + 2]], + points[evInds[i * 4 + 3]]); + greenVolumeFractions[i] = vf; + blueVolumeFractions[i] = 1.0 - vf; } - elementVF[GREEN] = greenVolumeFractions; - elementVF[BLUE] = blueVolumeFractions; - - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -424,50 +399,39 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); - - std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.m_numElems); - std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.m_numElems); + VolumeFractions volFracs; + volFracs.resize(numMaterials); + volFracs[GREEN].resize(cellData.m_numElems); + volFracs[BLUE].resize(cellData.m_numElems); // Generate the element volume fractions for the circle - int numMonteCarloSamples = 100; + const int numMonteCarloSamples = 100; + auto& pos = cellData.m_mapData.m_vertexPositions; + const auto& evInds = cellData.m_topology.m_evInds; for (int i = 0; i < cellData.m_numElems; ++i) { - greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 0]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 1]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 2]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 3]]); - blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + auto vf = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, + circleCenter, + circleRadius, + pos[evInds[i * 4 + 0]], + pos[evInds[i * 4 + 1]], + pos[evInds[i * 4 + 2]], + pos[evInds[i * 4 + 3]]); + volFracs[GREEN][i] = vf; + volFracs[BLUE][i] = 1.0 - vf; } - elementVF[GREEN] = greenVolumeFractions; - elementVF[BLUE] = blueVolumeFractions; - - std::vector elementParents;// For the base mesh, the parents are always themselves - std::vector elementDominantMaterials; - std::vector elementShapeTypes; - for (int i = 0; i < cellData.m_numElems; ++i) + cellData.m_mapData.m_elementDominantMaterials = Vec(cellData.m_numVerts, NULL_MAT); + cellData.m_mapData.m_shapeTypes = Vec(cellData.m_numVerts, mir::Shape::Quad); + cellData.m_mapData.m_elementParents.resize(cellData.m_numVerts); + for (auto i : elems.positions() ) { - elementParents.push_back(i); - elementDominantMaterials.push_back(NULL_MAT); - elementShapeTypes.push_back(mir::Shape::Quad); + cellData.m_mapData.m_elementParents[i] = i; } - CellTopologyData topology; - topology.m_evInds = cellData.m_topology.m_evInds; - topology.m_evBegins = cellData.m_topology.m_evBegins; - topology.m_veInds = cellData.m_topology.m_veInds; - topology.m_veBegins = cellData.m_topology.m_veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = elementDominantMaterials; - mapData.m_elementParents = elementParents; - mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; - // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, volFracs); return testMesh; } @@ -519,8 +483,13 @@ mir::CellData MeshTester::generateGrid(int gridSize) int numElements = gridSize * gridSize; int numVertices = (gridSize + 1) * (gridSize + 1); + mir::CellData data; + + data.m_numVerts = numVertices; + data.m_numElems = numElements; + // Generate the evInds - std::vector evInds; + auto& evInds = data.m_topology.m_evInds; for (int eID = 0; eID < numElements; ++eID) { int row = eID / gridSize; // note the integer division @@ -534,7 +503,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the evBegins - std::vector evBegins; + auto& evBegins = data.m_topology.m_evBegins; evBegins.push_back(0); for (int i = 0; i < numElements; ++i) { @@ -542,8 +511,9 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the veInds + auto& veInds = data.m_topology.m_veInds; + auto& veBegins = data.m_topology.m_veBegins; std::map > veInds_data; - std::vector veInds; for (int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) { int currentElementID = evInd_itr / 4; // note the integer division @@ -561,7 +531,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the veBegins - std::vector veBegins; veBegins.push_back(0); int currentIndexCount = 0; for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) @@ -571,7 +540,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the vertex positions - std::vector points; + auto& points = data.m_mapData.m_vertexPositions; for (int y = gridSize; y > -1; --y) { for (int x = 0; x < gridSize + 1; ++x) @@ -580,15 +549,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) } } - mir::CellData data; - data.m_numVerts = numVertices; - data.m_numElems = numElements; - data.m_topology.m_evInds = evInds; - data.m_topology.m_evBegins = evBegins; - data.m_topology.m_veInds = veInds; - data.m_topology.m_veBegins = veBegins; - data.m_mapData.m_vertexPositions = points; - // // Print out the results // printf("evInds: { "); // for (int i = 0; i < evInds.size(); i++) diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 37160ae894..7d4ea33c40 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -20,9 +20,7 @@ #include "MIRUtilities.hpp" #include - -namespace numerics = axom::numerics; -namespace slam = axom::slam; +#include namespace axom { @@ -37,16 +35,25 @@ namespace mir */ class MeshTester { +public: + template + using Vec = std::vector; + + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; + + public: /** * \brief Default constructor. */ - MeshTester(); + MeshTester() = default; /** * \brief Default destructor. */ - ~MeshTester(); + ~MeshTester() = default; public: /** @@ -56,7 +63,7 @@ namespace mir * * \return The generated mesh. */ - MIRMesh initTestCaseOne(); + mir::MIRMesh initTestCaseOne(); /** * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. @@ -110,7 +117,7 @@ namespace mir mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); /** - * \brief Intializes a mesh to be used for validating the results of quad clipping. + * \brief Initializes a mesh to be used for validating the results of quad clipping. * * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such * that the mesh would be split horizontally through the middle. @@ -155,6 +162,7 @@ namespace mir * \return The number of corners of the quad that are within the circle. */ int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + }; } } From 5a4a1a33e315b97486fd6bef9fc826141007c687 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:45:08 -0700 Subject: [PATCH 018/290] Adds command line arguments to mir's tutorial example --- src/axom/mir/ZooClippingTables.hpp | 4 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 252 +++++++++++++++--- 2 files changed, 217 insertions(+), 39 deletions(-) diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index 4802db44f7..de2b8be83c 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -9,7 +9,7 @@ /** * \file ZooClippingTables.hpp * - * \brief Contains the defintions for the clipping cases and enumerator + * \brief Contains the definitions for the clipping cases and enumerator * for the shape types in the zoo. */ @@ -26,4 +26,4 @@ namespace mir extern const std::vector > quadClipTableVec; } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 2217f64c4b..460a076b80 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -3,70 +3,248 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/core.hpp" // for axom macros -#include "axom/mir.hpp" // for Mir classes & functions +#include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/slam.hpp" +#include "axom/mir.hpp" -#include -using Clock = std::chrono::high_resolution_clock; +#include // namespace aliases namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; +namespace fs = axom::utilities::filesystem; //-------------------------------------------------------------------------------- +enum InputStatus +{ + SUCCESS, + INVALID, + SHOWHELP +}; + +struct Input +{ + int m_test_case; // valid values 1,2,3,4,5 + bool m_should_iterate; + int m_iter_count; + double m_iter_percent; + bool m_verbose; + std::string m_output_dir; + InputStatus m_status; + + Input() + : m_test_case(2) + , m_should_iterate(false) + , m_iter_count(100) + , m_iter_percent(.3) + , m_verbose(false) + , m_status(SUCCESS) + { + m_output_dir = fs::joinPath(AXOM_BIN_DIR, "mir_examples"); + } + + Input(int argc, char** argv) : Input() + { + for (int i = 1 ; i < argc ; /* increment i in loop */) + { + std::string arg = argv[i]; + if (arg == "--test-case") + { + m_test_case = std::stoi(argv[++i]); + } + else if (arg == "--output-dir") + { + m_output_dir = argv[++i]; + } + else if (arg == "--iter-count") + { + m_iter_count = std::stoi(argv[++i]); + m_should_iterate = true; + } + else if (arg == "--iter-percent") + { + m_iter_percent = std::stod(argv[++i]); + m_should_iterate = true; + } + else if (arg == "--verbose") + { + m_verbose = true; + } + else // help or unknown parameter + { + if(arg != "--help" && arg != "-h") + { + SLIC_WARNING("Unrecognized parameter: " << arg); + m_status = INVALID; + } + else + { + m_status = SHOWHELP; + } + return; + } + ++i; + } + + checkTestCase(); + checkOutputDir(); + checkIterationParams(); + } + + + bool shouldIterate() const { return m_should_iterate; } + int numIterations() const { return m_iter_count; } + int iterPercentage() const { return m_iter_percent; } + + + void showhelp() + { + std::cout + << "Argument usage:" + "\n --help Show this help message." + "\n --test-case N Mesh test case. Default N = 2." + "\n Valid values {1,2,3,4,5}" + "\n --output-dir dir Directory for output mesh" + "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" + "\n --iter-count N Number of iterations for iterative algorithm" + "\n Defaults to 100." + "\n Setting a value triggers iterative algorithm" + "\n --iter-percent D Volume diff percentage for iterative algorithm" + "\n Must be between 0 and 1. Defaults to 0.3" + "\n Setting a value triggers iterative algorithm" + "\n --verbose Increases verbosity of output" + << std::endl << std::endl; + }; + +private: + void checkTestCase() + { + if(m_test_case < 1 || m_test_case > 5) + { + m_status = INVALID; + SLIC_WARNING("Invalid test case " << m_test_case); + } + } + + void checkOutputDir() + { + if(! fs::pathExists(m_output_dir)) + { + fs::makeDirsForPath(m_output_dir); + } + } + + void checkIterationParams() + { + if(m_should_iterate) + { + if(m_iter_count < 1) + { + m_status = INVALID; + SLIC_WARNING("Invalid iteration count " << m_iter_count); + } + + if(m_iter_percent <= 0. || m_iter_percent > 1.) + { + m_status = INVALID; + SLIC_WARNING("Invalid iteration percentage " << m_iter_percent); + } + } + } + +}; + /*! * \brief Tutorial main showing how to initialize test cases and perform mir. */ -int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +int main(int argc, char** argv) { + axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - // Intialize a mesh for testing MIR - auto startTime = Clock::now(); + // Parse arguments + Input params(argc, argv); + + if (params.m_status != SUCCESS) + { + if (params.m_status == SHOWHELP) + { + params.showhelp(); + return 0; + } + else if (params.m_status == INVALID) + { + params.showhelp(); + return 1; + } + } + + mir::MIRMesh testMesh; mir::MeshTester tester; - // mir::MIRMesh testMesh = tester.initTestCaseOne(); - mir::MIRMesh testMesh = tester.initTestCaseTwo(); - // mir::MIRMesh testMesh = tester.initTestCaseThree(); - // mir::MIRMesh testMesh = tester.initTestCaseFour(); - // mir::MIRMesh testMesh = tester.initTestCaseFive(25, 12); - auto endTime = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + auto timer = axom::utilities::Timer(true); + + switch(params.m_test_case) + { + case 1: testMesh = tester.initTestCaseOne(); break; + case 2: testMesh = tester.initTestCaseTwo(); break; + case 3: testMesh = tester.initTestCaseThree(); break; + case 4: testMesh = tester.initTestCaseFour(); break; + case 5: testMesh = tester.initTestCaseFive(25, 12); break; + } + + timer.stop(); + SLIC_INFO( "Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + + SLIC_INFO("Test mesh is " << (testMesh.isValid(true) ? "" : " NOT") << " valid."); + + if(params.m_verbose) + { + SLIC_INFO("Initial mesh:"); + testMesh.print(); + } // Begin material interface reconstruction - startTime = Clock::now(); + timer.start(); mir::MIRMesh processedMesh; - mir::InterfaceReconstructor reconstructor; - reconstructor.computeReconstructedInterface(testMesh, processedMesh); // Process once, with original Meredith algorithm - // reconstructor.computeReconstructedInterfaceIterative(testMesh, 100, 0.3, processedMesh); // 100 iterations, 20 percent with iterative Meredith algorithm + + if(! params.shouldIterate()) // Process once, with original Meredith algorithm + { + reconstructor.computeReconstructedInterface(testMesh, processedMesh); + } + else // use iterative algorithm + { + int n = params.numIterations(); + double p = params.iterPercentage(); + + reconstructor.computeReconstructedInterfaceIterative(testMesh, n, p, processedMesh); + } - endTime = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO( "Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedMesh.vtk"); + std::string dir = params.m_output_dir; + processedMesh.writeMeshToFile( fs::joinPath(dir, "processedMesh.vtk") ); - std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); - - // // Print out the results - // printf("elementVolumeFractions: {\n"); - // for (int matID = 0; matID < processedMesh.m_numMaterials; ++matID) - // { - // printf("Material %d: {", matID); - // for (int eID = 0; eID < materialVolumeFractionsElement[matID].size(); ++eID) - // { - // printf(" %f,", materialVolumeFractionsElement[matID][eID]); - // } - // printf("}\n"); - // } - // printf("}\n"); - // // END TESTING + using VolFracs = std::vector >; + timer.start(); + VolFracs materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); + timer.stop(); + SLIC_INFO( "Computing volumes took: " << timer.elapsedTimeInMilliSec() << " ms."); + + if(params.m_verbose) + { + SLIC_INFO("Final mesh:"); + processedMesh.print(); + } return 0; } -//-------------------------------------------------------------------------------- \ No newline at end of file +//-------------------------------------------------------------------------------- From 24c2637981662940a517a18a2a88c37faa0baddd Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 16:04:21 -0700 Subject: [PATCH 019/290] Minor update to concentric circles example --- .../mir/examples/mir_concentric_circles.cpp | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 6eb3493bf6..1455d16f81 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -4,31 +4,30 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros +#include "axom/slic.hpp" #include "axom/mir.hpp" // for Mir classes & functions -#include "axom/slam.hpp" -#include #include -using Clock = std::chrono::high_resolution_clock; - // namespace aliases -namespace numerics = axom::numerics; -namespace slam = axom::slam; namespace mir = axom::mir; //-------------------------------------------------------------------------------- -/*! - * \brief Tutorial main - */ +std::string usageString() +{ + return "Args are "; +} + int main( int argc, char** argv ) { + axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); if (argc != 4) { - printf("Incorrect number of args. Args are \n"); - return 0; + SLIC_WARNING("Incorrect number of args. " << usageString() ); + return 1; } try @@ -38,20 +37,21 @@ int main( int argc, char** argv ) int numCircles = std::stoi(argv[2]); std::string outputFilePath = std::string(argv[3]); - // Intialize a mesh for testing MIR - auto startTime = Clock::now(); + // Initialize a mesh for testing MIR + auto timer = axom::utilities::Timer(true); mir::MeshTester tester; mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); - auto endTime = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); // Begin material interface reconstruction - startTime = Clock::now(); + timer.start(); mir::InterfaceReconstructor reconstructor; mir::MIRMesh processedMesh; reconstructor.computeReconstructedInterface(testMesh, processedMesh); - endTime = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO("Material interface reconstruction time: " + << timer.elapsedTimeInMilliSec() << " ms."); // Output results processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); @@ -60,12 +60,12 @@ int main( int argc, char** argv ) } catch (std::invalid_argument const &e) { - printf("Bad input. Arguments are \n"); - return 0; + SLIC_WARNING("Bad input. " << usageString() ); + return 1; } catch (std::out_of_range const &e) { - printf("Integer overflow. Arguments are \n"); - return 0; + SLIC_WARNING("Integer overflow. " << usageString() ); + return 1; } -} \ No newline at end of file +} From a8576fdebc619335030cf93e30802ceeb4e8c0b6 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:22:27 -0700 Subject: [PATCH 020/290] Adds a linear interpolation function to axom::core::utilities Implementation and tests implemented by M. Sterbentz and moved from mir component. --- src/axom/core/tests/utils_utilities.cpp | 31 +++++++++++++++++++++++++ src/axom/core/utilities/Utilities.hpp | 17 +++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/axom/core/tests/utils_utilities.cpp b/src/axom/core/tests/utils_utilities.cpp index 85c092b7e8..1b11d57a25 100644 --- a/src/axom/core/tests/utils_utilities.cpp +++ b/src/axom/core/tests/utils_utilities.cpp @@ -116,3 +116,34 @@ TEST(core_Utilities,minmax) EXPECT_EQ(a, axom::utilities::max(a,b)); } } + +TEST(core_Utilities, lerp) +{ + std::cout<<"Testing linear interpolation (lerp) function."<< std::endl; + + double f0 = 50.0; + double f1 = 100.0; + + // Test end points + { + EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f0, f1, 0.) ); + EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f1, f0, 0.) ); + + EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f0, f1, 1.) ); + EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f1, f0, 1.) ); + } + + // Test midpoint + { + double t = 0.5; + double exp = 75.; + EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + } + + // Another test + { + double t = 0.66; + double exp = 83.; + EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + } +} diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index d891db13e2..522cea8509 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -96,6 +96,22 @@ inline T log2( T& val) return std::log2(val); } + +/*! + * \brief Linearly interpolates between two values + * \param [in] val0 The first value + * \param [in] val2 The second value + * \param [in] t The interpolation parameter. + * \return The interpolated value + */ +template < typename T > +inline AXOM_HOST_DEVICE +T lerp( T v0, T v1, T t) +{ + constexpr T one = T(1); + return (one-t)*v0 + t*v1; +} + /*! * \brief Clamps an input value to a given range. * \param [in] val The value to clamp. @@ -113,7 +129,6 @@ T clampVal( T val, T lower, T upper ) : (val > upper) ? upper : val; } - /*! * \brief Clamps the upper range on an input value * From f4d39385dbfec191f1cca53c0538ee428cb6e2e6 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:25:48 -0700 Subject: [PATCH 021/290] Mir component now depends on primal --- src/axom/mainpage.md | 2 +- src/axom/mir/CMakeLists.txt | 2 +- src/docs/dependencies.dot | 2 +- src/index.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 84f42826ad..366e41642b 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -21,7 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic and Slam +- Mir depends on Slic, Slam and Primal - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 65104b39b1..ef829028f0 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -10,7 +10,7 @@ # Check necessary dependencies #------------------------------------------------------------------------------ axom_component_requires(NAME MIR - COMPONENTS SLIC SLAM) + COMPONENTS SLIC SLAM PRIMAL) #------------------------------------------------------------------------------ # Specify all headers/sources diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index cddf4cd4df..5a5b7fef92 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,7 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; - mir -> {slic core slam}; + mir -> {slic core slam primal}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/index.rst b/src/index.rst index afcf6266ee..393cba7c82 100644 --- a/src/index.rst +++ b/src/index.rst @@ -88,7 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic and Slam +- Mir depends on Slic, Slam and Primal - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From 4c326eb9606144b8b3ae680c15ab6cbee840864f Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:29:54 -0700 Subject: [PATCH 022/290] Mir implementation now uses primal for its Point class and for cell areas This replaces the custom Point2 class and lerp functionality. --- src/axom/mir/CellGenerator.cpp | 8 +- src/axom/mir/MIRMesh.cpp | 13 +- src/axom/mir/MIRMeshTypes.hpp | 33 +--- src/axom/mir/MIRUtilities.hpp | 54 +---- src/axom/mir/MeshTester.cpp | 231 ++++++++++++---------- src/axom/mir/MeshTester.hpp | 20 +- src/axom/mir/tests/mir_cell_generator.cpp | 42 ++-- src/axom/mir/tests/mir_utilities.cpp | 71 ------- 8 files changed, 178 insertions(+), 294 deletions(-) diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index c81c535f59..3e5537325f 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -91,7 +91,8 @@ void CellGenerator::generateVertexPositions(const mir::Shape shapeType, int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); - out_cellData.m_mapData.m_vertexPositions.push_back( mir::utilities::interpolateVertexPosition( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); + out_cellData.m_mapData.m_vertexPositions.push_back( + mir::Point2::lerp( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); } } } @@ -123,7 +124,8 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( mir::utilities::lerpFloat( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( + axom::utilities::lerp( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); } } } @@ -215,4 +217,4 @@ mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType } -} \ No newline at end of file +} diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 96130fed54..60ad504887 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -6,6 +6,7 @@ #include "MIRMesh.hpp" #include "axom/core.hpp" +#include "axom/primal.hpp" namespace axom { @@ -310,7 +311,7 @@ void MIRMesh::print() printf("vertexPositions: { "); for (int i = 0; i < m_verts.size(); ++i) { - printf("{%.2f, %.2f} ", m_vertexPositions[i].m_x, m_vertexPositions[i].m_y); + printf("{%.2f, %.2f} ", m_vertexPositions[i][0], m_vertexPositions[i][1]); } printf("}\n"); @@ -396,7 +397,7 @@ void MIRMesh::writeMeshToFile(std::string filename) // write positions for (int vID = 0; vID < m_verts.size(); ++vID) { - meshfile << m_vertexPositions[vID].m_x << " " << m_vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + meshfile << m_vertexPositions[vID][0] << " " << m_vertexPositions[vID][1] << " 0\n"; // must always set all 3 coords; set Z=0 for 2D } meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); @@ -486,13 +487,7 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { - axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 - axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 - axom::float64 c = sqrt( ((p0.m_x - p2.m_x) * (p0.m_x - p2.m_x)) + ((p0.m_y - p2.m_y) * (p0.m_y - p2.m_y)) );// the distance from p2 to p0 - - axom::float64 s = (a + b + c) / 2; // the semi-perimeter of the triangle - - return sqrt(s * (s - a) * (s - b) * (s - c)); + return primal::Triangle(p0,p1,p2).area(); } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 9daed29cbb..7ec2c79ba2 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -14,13 +14,10 @@ #include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions #include "axom/slam.hpp" +#include "axom/primal.hpp" -namespace numerics = axom::numerics; -namespace slam = axom::slam; - namespace axom { namespace mir @@ -36,32 +33,8 @@ namespace mir Hexahedron }; - /** - * \brief Simple 2D Point class for example - */ - struct Point2 - { - Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} - - Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} - - Point2& operator=(const Point2& other) - { m_x = other.m_x; m_y = other.m_y; return *this; } - - Point2& operator+=(const Point2& other) - { m_x += other.m_x; m_y += other.m_y; return *this; } - Point2& operator/=(double val) - { m_x /= val; m_y += val; return *this; } - - double& operator[] (int i) { return (i==0) ? m_x : m_y; } - const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } - - friend std::ostream& operator<<(std::ostream& os, const Point2& pt) - { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } - - double m_x, m_y; - }; + using Point2 = primal::Point; // SET TYPE ALIASES using PosType = slam::DefaultPositionType; @@ -88,4 +61,4 @@ namespace mir using IntMap = slam::Map< BaseSet, int >; } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index 0f3c75c084..69c5eb3c17 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -102,44 +102,6 @@ namespace utilities return numVertices; } -//-------------------------------------------------------------------------------- - - /** - * \brief Performs linear interpolation between the two given float values. - * - * \param f0 The first float value. - * \param f1 The second float value. - * \param t The percent of the distance from the first float value to the second. - * - * \return The interpolated value. - */ - inline axom::float64 lerpFloat(const axom::float64 f0, - const axom::float64 f1, - const axom::float64 t) - { - return (1 - t) * f0 + t * f1; - } - -//-------------------------------------------------------------------------------- - - /** - * \brief Performs linear interpolation between the two vertex positions. - * - * \param vertexOnePos The position of the first vertex. - * \param vertexTwoPos The position of the second vertex. - * \param t The percent of the distance from vertex one to vertex two to interpolate at. - * - * \return The interpolated position. - */ - inline mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, - const mir::Point2& vertexTwoPos, - const float t) - { - mir::Point2 interpolatedPoint; - interpolatedPoint.m_x = lerpFloat(vertexOnePos.m_x, vertexTwoPos.m_x, t); - interpolatedPoint.m_y = lerpFloat(vertexOnePos.m_y, vertexTwoPos.m_y, t); - return interpolatedPoint; - } //-------------------------------------------------------------------------------- @@ -259,20 +221,6 @@ namespace utilities return -1; } -//-------------------------------------------------------------------------------- - -/** - * \brief Calculate the distance between the two given points. - * - * \param p0 The first point. - * \param p1 The second point. - * - * \return The distance between the two points. - */ -inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) -{ - return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); -} //-------------------------------------------------------------------------------- @@ -280,4 +228,4 @@ inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 6a8eb612e5..f64e310c8c 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -76,25 +76,25 @@ MIRMesh MeshTester::initTestCaseOne() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -170,25 +170,25 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -250,12 +250,12 @@ mir::MIRMesh MeshTester::initTestCaseThree() mapData.m_vertexPositions = { - mir::Point2( 1.0, 2.0 ), - mir::Point2( 0.5, 1.0 ), - mir::Point2( 1.5, 1.0 ), - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ) + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 0.5, 1.0 ), + mir::Point2::make_point( 1.5, 1.0 ), + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -324,25 +324,25 @@ mir::MIRMesh MeshTester::initTestCaseFour() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; int numMaterials = 2; @@ -359,7 +359,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() blueVolumeFractions.resize(numElements); // Generate the element volume fractions for the circle - mir::Point2 circleCenter(1.5, 1.5); + auto circleCenter = mir::Point2::make_point(1.5, 1.5); axom::float64 circleRadius = 1.25; int gridSize = 1000; for (int i = 0; i < numElements; ++i) @@ -388,7 +388,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- -mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) +mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, const mir::Point2& circleCenter, axom::float64 circleRadius) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); @@ -438,40 +438,55 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 //-------------------------------------------------------------------------------- -axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo( + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); - axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); - axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); - axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); - - if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) + auto d0Sq = primal::squared_distance(quadP0, circleCenter); + auto d1Sq = primal::squared_distance(quadP1, circleCenter); + auto d2Sq = primal::squared_distance(quadP2, circleCenter); + auto d3Sq = primal::squared_distance(quadP3, circleCenter); + auto dRSq = circleRadius * circleRadius; + + int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) + + ((d1Sq < dRSq) ? 1 << 1 : 0) + + ((d2Sq < dRSq) ? 1 << 2 : 0) + + ((d3Sq < dRSq) ? 1 << 3 : 0); + const int allFlags = 15; + const int noFlags = 0; + + if (inFlags == allFlags ) { // The entire quad overlaps the circle - return 1.0; + return 1.; + } + else if( inFlags == noFlags) + { + return 0.; } - else if (distP0 < circleRadius || distP1 < circleRadius || distP2 < circleRadius || distP3 < circleRadius) + else { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = abs(quadP2.m_x - quadP1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(quadP0.m_y - quadP1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / static_cast(gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / static_cast(gridSize - 1); int countOverlap = 0; for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); - if (mir::utilities::distance(samplePoint, circleCenter) < circleRadius) + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + quadP1[0], + delta_y * y + quadP1[1]); + if (primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } } - return countOverlap / (double) (gridSize * gridSize); - } - else - { - // None of the quad overlaps the circle - return 0; + return countOverlap / static_cast(gridSize * gridSize); } } @@ -545,7 +560,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) { for (int x = 0; x < gridSize + 1; ++x) { - points.push_back(mir::Point2(x, y)); + points.push_back(mir::Point2::make_point(x, y)); } } @@ -583,7 +598,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) // printf("points: { "); // for (int i = 0; i < numVertices; ++i) // { - // printf("{%.2f, %.2f} ", points[i].m_x, points[i].m_y); + // printf("{%.2f, %.2f} ", points[i][0], points[i][1]); // } // printf("}\n"); @@ -605,7 +620,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) int numMaterials = numCircles + 1; int defaultMaterialID = numMaterials - 1; // default material is always the last index - mir::Point2 circleCenter(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point + mir::Point2 circleCenter = mir::Point2::make_point(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point // Initialize the radii of the circles std::vector circleRadii; @@ -633,12 +648,13 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } // Use the uniform sampling method to generate volume fractions for each material + // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation for (int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; + mir::Point2& v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; + mir::Point2& v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; + mir::Point2& v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; + //mir::Point2& v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -648,18 +664,19 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); } - axom::float64 delta_x = abs(v2.m_x - v1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(v0.m_y - v1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = axom::utilities::abs(v2[0] - v1[1]) / (double) (gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(v0[1] - v1[1]) / (double) (gridSize - 1); for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + v1.m_x, delta_y * y + v1.m_y); + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], delta_y * y + v1[1]); bool isPointSampled = false; for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) { - if (mir::utilities::distance(samplePoint, circleCenter) < circleRadii[cID]) + const auto r = circleRadii[cID]; + if (primal::squared_distance(samplePoint, circleCenter) < r*r) { materialCount[cID]++; isPointSampled = true; @@ -711,23 +728,29 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) //-------------------------------------------------------------------------------- -int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) +int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); - axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); - axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); - axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); + auto d0Sq = primal::squared_distance(quadP0, circleCenter); + auto d1Sq = primal::squared_distance(quadP1, circleCenter); + auto d2Sq = primal::squared_distance(quadP2, circleCenter); + auto d3Sq = primal::squared_distance(quadP3, circleCenter); + auto dRSq = circleRadius * circleRadius; int numCorners = 0; - if (distP0 < circleRadius) + if (d0Sq < dRSq) numCorners++; - if (distP1 < circleRadius) + if (d1Sq < dRSq) numCorners++; - if (distP2 < circleRadius) + if (d2Sq < dRSq) numCorners++; - if (distP3 < circleRadius) + if (d3Sq < dRSq) numCorners++; return numCorners; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 7d4ea33c40..dcbc380460 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -114,7 +114,9 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius); /** * \brief Initializes a mesh to be used for validating the results of quad clipping. @@ -147,7 +149,14 @@ namespace mir * * /return The percent value overlap of the circle and the quad between [0, 1]. */ - axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + axom::float64 calculatePercentOverlapMonteCarlo( + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); /** * \brief Calculates the number of corners of the quad that are within the circle. @@ -161,7 +170,12 @@ namespace mir * * \return The number of corners of the quad that are within the circle. */ - int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + int circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); }; } diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp index f03b480bda..9505eae633 100644 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -101,10 +101,10 @@ TEST(mir_cell_generator, generate_vertex_positions) vertexMap[7] = { 2, 3 }; std::vector originalVertexPositions; - originalVertexPositions.push_back( mir::Point2(0.0, 1.0) ); - originalVertexPositions.push_back( mir::Point2(0.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2(1.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2(1.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(0.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(0.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(1.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(1.0, 1.0) ); axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; @@ -112,30 +112,30 @@ TEST(mir_cell_generator, generate_vertex_positions) mir::CellGenerator cellGenerator; cellGenerator.generateVertexPositions(shapeType, vertexMap, originalVertexPositions, tValues, cellData); + const auto& positions = cellData.m_mapData.m_vertexPositions; + EXPECT_NEAR( positions[0][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[0][1], 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[1][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[1][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[2][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[2][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[3][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[3][1], 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[4][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[4][1], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_y, 0.5, 0.00001 ); + EXPECT_NEAR( positions[5][0], 0.5, 0.00001 ); + EXPECT_NEAR( positions[5][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_x, 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[6][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[6][1], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_y, 0.5, 0.00001 ); - - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_x, 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[7][0], 0.5, 0.00001 ); + EXPECT_NEAR( positions[7][1], 1.0, 0.00001 ); } //---------------------------------------------------------------------- diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 82e26b89a0..6d5a6c2dde 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -11,78 +11,7 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" -using namespace axom; -TEST(mir_interpolation, float_linear_interpolation) -{ - axom::float64 f0 = 50.0; - axom::float64 f1 = 100.0; - - axom::float64 t = 0.0; - axom::float64 interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 50.0 ); - - t = 1.0; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 100.0 ); - - t = 0.5; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 75.0 ); - - t = 0.66; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 83.0 ); -} - -//---------------------------------------------------------------------- - -TEST(mir_interpolation, point_linear_interpolation) -{ - mir::Point2 p0(0.25, 0.0); - mir::Point2 p1(1.25, 1.0); - - axom::float64 t = 0.0; - mir::Point2 interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 0.25); - EXPECT_DOUBLE_EQ( interpolant.m_y, 0.0); - - t = 1.0; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 1.25); - EXPECT_DOUBLE_EQ( interpolant.m_y, 1.0); - - t = 0.5; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 0.75); - EXPECT_DOUBLE_EQ( interpolant.m_y, 0.5); - - t = 0.66; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_NEAR( interpolant.m_x, 0.91, 0.00001); - EXPECT_NEAR( interpolant.m_y, 0.66, 0.00001); -} - -//---------------------------------------------------------------------- - -TEST(mir_distance_utility, compute_distance) -{ - mir::Point2 p0( 0.25, 0.0 ); - mir::Point2 p1( 1.25, 0.0 ); - axom::float64 dist = mir::utilities::distance(p0, p1); - EXPECT_DOUBLE_EQ( dist, 1.0 ); - - mir::Point2 p2( 0.25, 0.0 ); - mir::Point2 p3( 1.25, 1.0 ); - dist = mir::utilities::distance(p2, p3); - EXPECT_NEAR( dist, 1.4142, 0.001 ); - - mir::Point2 p4( 1.0, 1.0 ); - mir::Point2 p5( 1.0, 1.0 ); - dist = mir::utilities::distance(p4, p5); - EXPECT_DOUBLE_EQ( dist, 0.0 ); - -} //---------------------------------------------------------------------- From ed51c0e3d6c83b2a9ff1e8a15e485a3824fd6ad2 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 22 May 2019 11:16:57 -0700 Subject: [PATCH 023/290] Initial stubs for new Material Interface Reconstruction component (mir) --- src/axom/CMakeLists.txt | 1 + src/axom/config.hpp.in | 1 + src/axom/mainpage.md | 2 + src/axom/mir/CMakeLists.txt | 65 ++++++++++++++++++++++++++++++ src/axom/mir/README.md | 8 ++++ src/axom/mir/docs/sphinx/index.rst | 12 ++++++ src/axom/mir/tests/CMakeLists.txt | 34 ++++++++++++++++ src/axom/mir/tests/mir_smoke.cpp | 38 +++++++++++++++++ src/docs/dependencies.dot | 1 + src/docs/doxygen/Doxyfile.in | 1 + src/index.rst | 3 ++ 11 files changed, 166 insertions(+) create mode 100644 src/axom/mir/CMakeLists.txt create mode 100644 src/axom/mir/README.md create mode 100644 src/axom/mir/docs/sphinx/index.rst create mode 100644 src/axom/mir/tests/CMakeLists.txt create mode 100644 src/axom/mir/tests/mir_smoke.cpp diff --git a/src/axom/CMakeLists.txt b/src/axom/CMakeLists.txt index 4dc4d676d4..467e1d0f7c 100644 --- a/src/axom/CMakeLists.txt +++ b/src/axom/CMakeLists.txt @@ -30,6 +30,7 @@ axom_add_component(COMPONENT_NAME primal DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONE axom_add_component(COMPONENT_NAME spin DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME sidre DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME quest DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) +axom_add_component(COMPONENT_NAME mir DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) # Combine all component object libraries into a unified library blt_add_library(NAME axom diff --git a/src/axom/config.hpp.in b/src/axom/config.hpp.in index 9992a767c1..399d296c6b 100644 --- a/src/axom/config.hpp.in +++ b/src/axom/config.hpp.in @@ -90,6 +90,7 @@ * Compiler defines for the toolkit components */ #cmakedefine AXOM_USE_MINT +#cmakedefine AXOM_USE_MIR #cmakedefine AXOM_USE_LUMBERJACK #cmakedefine AXOM_USE_PRIMAL #cmakedefine AXOM_USE_QUEST diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 827988937a..60708dae52 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -8,6 +8,7 @@ Axom provides libraries that address common computer science needs. It grew fro * @subpage coretop provides shared utility functionality to all components. * @subpage lumberjacktop provides logging aggregation and filtering capability. * @subpage minttop provides a comprehensive mesh data model. +* @subpage mirtop provides algorithms for material interface reconstruction on multimaterial meshes. * @subpage primaltop provides an API for geometric primitives and computational geometry tests. * @subpage questtop provides an API to query point distance and position relative to meshes. * @subpage sidretop provides a data store with hierarchical structure. @@ -20,6 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre +- Mir depends on Slic - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt new file mode 100644 index 0000000000..6c7c67635e --- /dev/null +++ b/src/axom/mir/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# MIR -- Material Interface Reconstruction +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Check necessary dependencies +#------------------------------------------------------------------------------ +axom_component_requires(NAME MIR + COMPONENTS SLIC) + +#------------------------------------------------------------------------------ +# Specify all headers/sources +#------------------------------------------------------------------------------ +set(mir_headers + + ) + +set(mir_sources + ../Axom.cpp + ) + +#------------------------------------------------------------------------------ +# Build and install the library +#------------------------------------------------------------------------------ +set(mir_depends_on core slic) + +blt_add_library( + NAME mir + SOURCES ${mir_sources} + HEADERS ${mir_headers} + DEPENDS_ON ${mir_depends_on} + FOLDER axom/mir + OBJECT TRUE ) + +axom_write_unified_header(NAME mir + HEADERS ${mir_headers} ) + +axom_install_component(NAME mir + HEADERS ${mir_headers} ) + +#------------------------------------------------------------------------------ +# Add tests, benchmarks and examples +#------------------------------------------------------------------------------ +if (AXOM_ENABLE_TESTS) + add_subdirectory(tests) + if (ENABLE_BENCHMARKS) + # add_subdirectory(benchmarks) + endif() +endif() + +if (AXOM_ENABLE_EXAMPLES) + #add_subdirectory(examples) +endif() + + +#------------------------------------------------------------------------------ +# Add code checks +#------------------------------------------------------------------------------ +axom_add_code_checks( + PREFIX mir ) + diff --git a/src/axom/mir/README.md b/src/axom/mir/README.md new file mode 100644 index 0000000000..fa6b7fba6a --- /dev/null +++ b/src/axom/mir/README.md @@ -0,0 +1,8 @@ +MIR: Material Interface Reconstruction {#mirtop} +================================================ + +[MIR](@ref axom::mir) provides algorithms for reconstructing interfaces +between materials in a multimaterial mesh. + +The [Mir user documentation](../../../sphinx/axom_docs/html/axom/mir/docs/sphinx/index.html) +describes these algorithms. diff --git a/src/axom/mir/docs/sphinx/index.rst b/src/axom/mir/docs/sphinx/index.rst new file mode 100644 index 0000000000..22f5487fd6 --- /dev/null +++ b/src/axom/mir/docs/sphinx/index.rst @@ -0,0 +1,12 @@ +.. ## Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level COPYRIGHT file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +======================= +Mir User Documentation +======================= + +Axom's Material Interface Reconstruction (MIR) component provides algorithms for +reconstructing the interface surfaces between different materials in multimaterial +meshes. diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt new file mode 100644 index 0000000000..155f8ae486 --- /dev/null +++ b/src/axom/mir/tests/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) +#------------------------------------------------------------------------------ +# Mir unit tests +#------------------------------------------------------------------------------ + + +#------------------------------------------------------------------------------ +# Specify list of tests +#------------------------------------------------------------------------------ + +set(gtest_mir_tests + mir_smoke.cpp + ) + + +set(mir_tests_depends_on core slic mir gtest) + +#------------------------------------------------------------------------------ +# Add gtest based tests +#------------------------------------------------------------------------------ +foreach(test ${gtest_mir_tests}) + get_filename_component( test_name ${test} NAME_WE ) + blt_add_executable( NAME ${test_name}_test + SOURCES ${test} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_tests_depends_on} + FOLDER axom/mir/tests ) + blt_add_test( NAME ${test_name} + COMMAND ${test_name}_test ) +endforeach() + diff --git a/src/axom/mir/tests/mir_smoke.cpp b/src/axom/mir/tests/mir_smoke.cpp new file mode 100644 index 0000000000..c71092b54d --- /dev/null +++ b/src/axom/mir/tests/mir_smoke.cpp @@ -0,0 +1,38 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_SMOKE_H_ +#define MIR_SMOKE_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + + +TEST(mir,smoke) +{ + SLIC_INFO("Running smoke test for MIR component"); + + EXPECT_TRUE( true ); + EXPECT_EQ( 0, 0 ); +} + + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_SMOKE_H_ diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index 8e747c824c..75ab74554e 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,6 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; + mir -> {slic core}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index d38a7d5aad..d76fccfee4 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -786,6 +786,7 @@ INPUT = @PROJECT_SOURCE_DIR@/axom/mainpage.md \ @PROJECT_SOURCE_DIR@/axom/mint/mesh \ @PROJECT_SOURCE_DIR@/axom/mint/utils \ @PROJECT_SOURCE_DIR@/axom/mint/execution \ + @PROJECT_SOURCE_DIR@/axom/mir/README.md \ @PROJECT_SOURCE_DIR@/axom/primal/README.md \ @PROJECT_SOURCE_DIR@/axom/primal/geometry \ @PROJECT_SOURCE_DIR@/axom/primal/operators \ diff --git a/src/index.rst b/src/index.rst index c4ac47919c..9ac474a4b6 100644 --- a/src/index.rst +++ b/src/index.rst @@ -61,6 +61,7 @@ for Axom software components: Spin (Spatial indexes) Quest (Querying on surface tool) Mint (Mesh data model) + Mir (Material interface reconstruction) Primal (Computational geometry primitives) -------------------------- @@ -71,6 +72,7 @@ Source Code Documentation * `Core <../../../doxygen/axom_doxygen/html/coretop.html>`_ * `Lumberjack <../../../doxygen/axom_doxygen/html/lumberjacktop.html>`_ * `Mint <../../../doxygen/axom_doxygen/html/minttop.html>`_ + * `Mir <../../../doxygen/axom_doxygen/html/mirtop.html>`_ * `Primal <../../../doxygen/axom_doxygen/html/primaltop.html>`_ * `Quest <../../../doxygen/axom_doxygen/html/questtop.html>`_ * `Sidre <../../../doxygen/axom_doxygen/html/sidretop.html>`_ @@ -86,6 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre +- Mir depends on Slic - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From c5a79c00237b9527f1577d29db0ad897b9ef6108 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Mon, 24 Jun 2019 17:15:07 -0700 Subject: [PATCH 024/290] Add uberenv_libs to gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 501c8041a6..5ab2039dfa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ _axom_build_and_test_* tpl_dirs_summary.json *.swp *.vscode* +uberenv_libs From d13a5eb437a36c85c5717f476f10c7d3e9a05589 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Wed, 29 May 2019 08:14:31 -0700 Subject: [PATCH 025/290] Builds a simple mir program. --- src/axom/mir/CMakeLists.txt | 2 +- src/axom/mir/examples/CMakeLists.txt | 33 ++++++++++++++++++++ src/axom/mir/examples/mir_tutorial.cpp | 42 ++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/axom/mir/examples/CMakeLists.txt create mode 100644 src/axom/mir/examples/mir_tutorial.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 6c7c67635e..8005392b9f 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -53,7 +53,7 @@ if (AXOM_ENABLE_TESTS) endif() if (AXOM_ENABLE_EXAMPLES) - #add_subdirectory(examples) + add_subdirectory(examples) endif() diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt new file mode 100644 index 0000000000..b199beae56 --- /dev/null +++ b/src/axom/mir/examples/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) + +set( mir_examples + mir_tutorial.cpp + ) + +set( mir_example_dependencies + #core + mir + #slic + ) + +blt_list_append( TO mir_example_dependencies ELEMENTS sidre conduit IF AXOM_MIR_USE_SIDRE ) +blt_list_append( TO mir_example_dependencies ELEMENTS raja IF RAJA_FOUND ) +blt_list_append( TO mir_example_dependencies ELEMENTS openmp IF ENABLE_OPENMP ) +blt_list_append( TO mir_example_dependencies ELEMENTS cuda IF ENABLE_CUDA ) + +foreach( example ${mir_examples} ) + + get_filename_component( example_name ${example} NAME_WE ) + + blt_add_executable( + NAME ${example_name}_ex + SOURCES ${example} + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_example_dependencies} + FOLDER axom/mir/examples + ) + +endforeach() diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp new file mode 100644 index 0000000000..7dbcb69a0d --- /dev/null +++ b/src/axom/mir/examples/mir_tutorial.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +#include "axom/mir.hpp" // for Mir classes & functions + + + +// C/C++ includes +#include // for definition of M_PI, exp() + +// namespace aliases +//namespace mir = axom::mir; +namespace numerics = axom::numerics; + +/*! + * \file + * + * \brief Various code snippets/examples used for the Mir tutorial section. + * + * \note These examples are designed to illustrate specific Mir + * concepts and capabilities. Consult the Tutorial section of Mir's + * User Guide for more details. + * + */ + + +/*! + * \brief Tutorial main + */ +int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + + std::cout << "hello from the mir_tutorial!" << std::endl; + + return 0; +} + + + From 2dc5aafae0b9baf3532ee4683b95d7a4079178ae Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 7 Jun 2019 08:20:14 -0700 Subject: [PATCH 026/290] Basic material interface reconstruction implemented for a simple two material quad test mesh. --- src/axom/mir/CMakeLists.txt | 10 +- src/axom/mir/CellData.cpp | 99 ++++ src/axom/mir/CellData.hpp | 51 ++ src/axom/mir/InterfaceReconstructor.cpp | 489 ++++++++++++++++++ src/axom/mir/InterfaceReconstructor.hpp | 64 +++ src/axom/mir/MIRMesh.cpp | 318 ++++++++++++ src/axom/mir/MIRMesh.hpp | 100 ++++ src/axom/mir/MIRMeshTypes.hpp | 74 +++ src/axom/mir/ZooBitMaps.cpp | 47 ++ src/axom/mir/ZooBitMaps.hpp | 17 + src/axom/mir/examples/CMakeLists.txt | 1 + src/axom/mir/examples/mir_tutorial.cpp | 446 +++++++++++++++- src/axom/mir/examples/mir_tutorial_simple.cpp | 122 +++++ 13 files changed, 1833 insertions(+), 5 deletions(-) create mode 100644 src/axom/mir/CellData.cpp create mode 100644 src/axom/mir/CellData.hpp create mode 100644 src/axom/mir/InterfaceReconstructor.cpp create mode 100644 src/axom/mir/InterfaceReconstructor.hpp create mode 100644 src/axom/mir/MIRMesh.cpp create mode 100644 src/axom/mir/MIRMesh.hpp create mode 100644 src/axom/mir/MIRMeshTypes.hpp create mode 100644 src/axom/mir/ZooBitMaps.cpp create mode 100644 src/axom/mir/ZooBitMaps.hpp create mode 100644 src/axom/mir/examples/mir_tutorial_simple.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 8005392b9f..a31de3c473 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -16,11 +16,19 @@ axom_component_requires(NAME MIR # Specify all headers/sources #------------------------------------------------------------------------------ set(mir_headers - + MIRMesh.hpp + MIRMeshTypes.hpp + ZooBitMaps.hpp + InterfaceReconstructor.hpp + CellData.hpp ) set(mir_sources ../Axom.cpp + MIRMesh.cpp + InterfaceReconstructor.cpp + ZooBitMaps.cpp + CellData.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp new file mode 100644 index 0000000000..bfeee02462 --- /dev/null +++ b/src/axom/mir/CellData.cpp @@ -0,0 +1,99 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellData.hpp" + +namespace axom +{ +namespace mir +{ + + //-------------------------------------------------------------------------------- + + CellData::CellData() + { + + } + + //-------------------------------------------------------------------------------- + + CellData::CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, + std::vector _vertexPositions, std::vector > _vertexVolumeFractions) + { + numVerts = _numVerts; + numElems = _numElems; + evInds = _evInds; + evBegins = _evBegins; + veInds = _veInds; + veBegins = _veBegins; + vertexPositions = _vertexPositions; + vertexVolumeFractions = _vertexVolumeFractions; + } + + //-------------------------------------------------------------------------------- + + CellData::~CellData() + { + + } + + //-------------------------------------------------------------------------------- + + /// Merges the cell data from the given cell into this cell + void CellData::mergeCell(CellData cellToMerge) + { + // Initialize index offsets + int evBeginsOffset = evInds.size(); + int veBeginsOffset = veInds.size(); + + int vertexIndexOffset = numVerts; + int elementIndexOffset = numElems; + + // Merge the cell topology information + for (auto i = 0; i < cellToMerge.evInds.size(); ++i) + { + evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); + } + + for (auto i = 1; i < cellToMerge.evBegins.size(); ++i) + { + evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); + } + + for (auto i = 0; i < cellToMerge.veInds.size(); ++i) + { + veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); + } + + for (auto i = 1; i < cellToMerge.veBegins.size(); ++i) + { + veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); + } + + // Merge the vertex positions + for (auto i = 0; i < cellToMerge.vertexPositions.size(); ++i) + { + vertexPositions.push_back(cellToMerge.vertexPositions[i]); + } + + // Merge the vertex volume fractions + for (auto matID = 0; matID < vertexVolumeFractions.size(); ++matID) + { + for (auto vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + { + vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); + } + } + + // Merge the total number of verts and elems in the resulting cell + numVerts += cellToMerge.numVerts; + numElems += cellToMerge.numElems; + + } + + //-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp new file mode 100644 index 0000000000..d8a66764f7 --- /dev/null +++ b/src/axom/mir/CellData.hpp @@ -0,0 +1,51 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __CELL_DATA_H__ +#define __CELL_DATA_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" + +#include "MIRMesh.hpp" + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + + class CellData + { + + public: + CellData(); + CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, + std::vector _vertexPositions, std::vector > _vertexVolumeFractions); + ~CellData(); + + void mergeCell(CellData cellToMerge); + + public: + int numVerts; + int numElems; + + // Cell connectivity/topology + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + + std::vector vertexPositions; + std::vector materialsInCell; // TODO: This is not currently being used. + std::vector > vertexVolumeFractions; + }; + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp new file mode 100644 index 0000000000..1f9f17a9fb --- /dev/null +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -0,0 +1,489 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "InterfaceReconstructor.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /// Default constructor + InterfaceReconstructor::InterfaceReconstructor() + { + + } + +//-------------------------------------------------------------------------------- + + /// Constructor + InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) + { + mesh = _mesh; + } + +//-------------------------------------------------------------------------------- + + /// Destructor + InterfaceReconstructor::~InterfaceReconstructor() + { + + } + +//-------------------------------------------------------------------------------- + + /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. + /// TODO: This method needs to place the computed cell vertices into the intermediate mesh (pass this in as an argument) + void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + { + // Find the vertices associated with each element + auto elementVertices = mesh->bdry[eID]; + + // Check if one of the two currently considered materials is not present at the vertices of the current element + // if (mesh->materialVolumeFractionsElement[matOneID][eID] != 0.0 && mesh->materialVolumeFractionsElement[matTwoID][eID] != 0.0) // TODO: Figure out how to best handle this case + // { + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); // Compute how the cell should be decomposed into new cells + } + // } + // else + // { + // printf("No cuts in element %d.\n", eID); + + // TODO: Add the current cells vertices and data back into the mesh + + // } + } + +//-------------------------------------------------------------------------------- + + /// Computes the points where the triangle element should be clipped based on the volume fractions of mat one and two. + void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + { + printf("Triangle clipping case not yet implemented.\n"); + } + +//-------------------------------------------------------------------------------- + + /// Computes how the given cell should be decomposed into new cells baed on teh vertex volume fractions of matOne and matTwo. + void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, + std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts) + { + printf("Processing element %d: ", eID); + + /**************************************************************** + * DETERMINE THE CLIPPING CASE + ****************************************************************/ + // Get the vertices of the current element to clip + auto elementVertices = tempMesh->bdry[eID]; + + // Determine which vertices correspond to upper left, lower left, lower right, and upper right vertices of the quad. + int upperLeftVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + int upperRightVertex = elementVertices[3]; + + // Determine the dominant color at each vertex + int upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + int lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + int lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + int upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperLeftColor == matOneID) caseIndex |= 8; + if (lowerLeftColor == matOneID) caseIndex |= 4; + if (lowerRightColor == matOneID) caseIndex |= 2; + if (upperRightColor == matOneID) caseIndex |= 1; + + printf("caseIndex: %d\n", caseIndex); + + /**************************************************************** + * GENERATE NEW ELEMENTS + ****************************************************************/ + // Cell information indices + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped + std::map > vertexVolumeFractionsMap; // index: map[vID][matID] = vertexVolumeFractionValues[numMaterials] | Note: maps are ordered sets + + int currentElementIndex = 0; // the next available element index + + // Create the new polygons based on the clipping case + int i = 0; + int numVertices = quadClipTable[caseIndex][i]; + while (numVertices != -1) // for each new element in the current clipping case + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = quadClipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; + + // Find t using "bilinear" interpolation method for any vertex that is not one of the original 4 vertices + if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); // these might be the wrong vertex indices + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 6) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 7) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = quadClipTable[caseIndex][i]; + } + + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_numElements[0] = (int) newElements.size(); + out_numVerts[0] = (int) newVertices.size(); + + // Generate the topology of the new elements (evInds, evBegins, etc) + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_evBegins.push_back(currentEVBeginIndex); + + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } + + // Push the index that occurs after the last vertex + out_evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_veBegins.push_back(currentVEBeginIndex); + + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_veBegins.push_back(currentVEBeginIndex); + + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (verticesPresent[vID] == 1) + { + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 6) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + if (vID == 7) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + } + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_vertexPositions.push_back(itr->second); + } + + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_newVolumeFractionsAtVerts.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + // Ensure that the current vertex being considered is actually present in the current clipping case + if (verticesPresent[vID] == 1) + { + // vertexVolumeFractionsMap[vID].resize(tempMesh->numMaterials); + + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + if (vID == 1) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + if (vID == 4) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 6) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + if (vID == 7) + out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + } + } + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + int evIndexSubtract[8]; + for (int i = 0; i < 8; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + for (int i = 1; i < 8; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; + + for (unsigned int i = 0; i < out_evInds.size(); ++i) + { + out_evInds[i] -= evIndexSubtract[ out_evInds[i] ]; + } + + + + /**************************************************************** + * DEBUG PRINTING + ****************************************************************/ + { + // TESTING: Print out the values by which the evIndex values need to be substracted + // in order to ensure there are no gaps in the vertex ids in the resulting mesh. + // printf(" evIndexSubtract: { "); + // for (int i = 0; i < 8; i++) + // { + // printf("%d ", evIndexSubtract[i]); + // } + // printf("}\n"); + + // // TESTING: Print out the basic cell information + // printf(" out_numElements: %d\n", out_numElements[0]); + // printf(" out_numVerts: %d\n", out_numVerts[0]); + + // // TESTING: Print out the mesh topology information + // printf(" out_evInds: {"); + // for (int i = 0; i < out_evInds.size(); i++) + // { + // printf("%d ", out_evInds[i]); + // } + // printf("}\n"); + + // printf(" out_evBegins: {"); + // for (int i = 0; i < out_evBegins.size(); i++) + // { + // printf("%d ", out_evBegins[i]); + // } + // printf("}\n"); + + // printf(" out_veInds: {"); + // for (int i = 0; i < out_veInds.size(); i++) + // { + // printf("%d ", out_veInds[i]); + // } + // printf("}\n"); + + // printf(" out_veBegins: {"); + // for (int i = 0; i < out_veBegins.size(); i++) + // { + // printf("%d ", out_veBegins[i]); + // } + // printf("}\n"); + + // // TESTING: Print out the positions of the vertices + // for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + // { + // printf(" Vertex %d: (%f, %f)\n", itr->first, itr->second.m_x, itr->second.m_y); + // } + + // TESTING: Print out the volume fractions at the vertices from the map + // for (auto itr = vertexVolumeFractionsMap.begin(); itr != vertexVolumeFractionsMap.end(); itr++) + // { + // int vID = itr->first; + // printf(" Vertex %d Volume Fractions:\n", vID); + // for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + // { + // printf(" Mat %d: %f\n", matID, vertexVolumeFractionsMap[vID][matID]); + // } + // } + + // TESTING: Print out the volume fractions at the vertices from the vector of materials containing a vector of vertex volume fraction values + // for (int matID = 0; matID < out_newVolumeFractionsAtVerts.size(); ++matID) + // { + // printf(" Material %d:\n", matID); + // for (int vID = 0; vID < out_newVolumeFractionsAtVerts[matID].size(); ++vID) + // { + // printf(" Vertex %d: %f\n", vID, out_newVolumeFractionsAtVerts[matID][vID]); + // } + // } + } + } + +//-------------------------------------------------------------------------------- + +/// Performs linear interpolation between the two given vertex positions. +mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t) +{ + mir::Point2 interpolatedPoint; + interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; + interpolatedPoint.m_y = (1 - t) * vertexOnePos.m_y + t * vertexTwoPos.m_y; + return interpolatedPoint; +} + +//-------------------------------------------------------------------------------- + +/// Performs linear interpolation between the two given float values +axom::float64 InterfaceReconstructor::lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t) +{ + return (1 - t) * f0 + t * f1; +} + +//-------------------------------------------------------------------------------- + +/// Computes the t value as a percent from vertexOne to vertexTwo based on the two materials given. +/// The t value is the place where this edge should be clipped based on the two materials currently being considered. +axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh) +{ + axom::float64 ret = 0.0; + + axom::float64 numerator = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - mesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; + axom::float64 denominator = -mesh->materialVolumeFractionsVertex[matOneID][vertexOneID] + + mesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] + + mesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] + - mesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + + if (denominator != 0.0) + ret = numerator / denominator; + + return ret; +} + +//-------------------------------------------------------------------------------- + +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +{ + // Initialize the final mesh to be the same as the input mesh + mir::MIRMesh finalMesh(mesh); // TODO: This might just copy the reference to the original mesh, and not copy it. Could cause bugs when it gets overwritten. + + // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) + for (int matID = 0; matID < mesh->numMaterials - 1; ++matID) + { + // Copy the mesh to be split + mir::MIRMesh intermediateMesh(&finalMesh); + + // Update the materials upon which the split will occur + int matOne = matID; + int matTwo = matID + 1; + + // Process/split each element + std::vector temp_evInds[intermediateMesh.elems.size()]; // Store the vertex indices adjacent to each element for each cell + std::vector temp_evBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_evBewgins for each element-vertex for each cell + std::vector temp_veInds[intermediateMesh.elems.size()]; // Store the element indices adjacent to each vertex for each cell + std::vector temp_veBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_veInds for each vertex-element for each cell + + std::vector temp_vertexPositions[intermediateMesh.elems.size()]; // Store the positions of the vertices generated for each cell + std::vector materialInCell[intermediateMesh.elems.size()]; // Note: after splitting, the cells should all be clean and thus will have a vf of 1.0 or 0.0 for all materials + + int temp_numVerts[intermediateMesh.elems.size()]; // Store the number of vertices generated for each cell + int temp_numElements[intermediateMesh.elems.size()]; // Store the number of elements generated for each cell + + std::vector > temp_volumeFractionsVertex[intermediateMesh.elems.size()]; + + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + computeClippingPoints(eID, matOne, matTwo, &intermediateMesh, + temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], + materialInCell[eID], &(temp_numVerts[eID]), &(temp_numElements[eID]), temp_volumeFractionsVertex[eID]); + } + + // Copy the generated cell information into CellData structures + std::vector cellSplitInfo; + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + CellData nextCellSplitInfo(temp_numVerts[eID], temp_numElements[eID], temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], temp_volumeFractionsVertex[eID]); + cellSplitInfo.push_back(nextCellSplitInfo); + } + + // Merge each of the cells into the first CellData struct + for (unsigned int eID = 1; eID < cellSplitInfo.size(); ++eID) + cellSplitInfo[0].mergeCell(cellSplitInfo[eID]); + + mir::VertSet combined_verts(cellSplitInfo[0].numVerts); + mir::ElemSet combined_elems(cellSplitInfo[0].numElems); + + // TODO (later): Calculate the element volume fractions (note, they should all be either 0 or 1, since after the splits every cell should be clean) + + // Create the final, processed mesh + mir::MIRMesh processedMesh; + processedMesh.InitializeMesh(cellSplitInfo[0].evInds, cellSplitInfo[0].evBegins, cellSplitInfo[0].veInds, cellSplitInfo[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); + processedMesh.constructMeshRelations(); + processedMesh.constructMeshVolumeFractionsVertex(cellSplitInfo[0].vertexVolumeFractions); + processedMesh.constructVertexPositionMap(cellSplitInfo[0].vertexPositions.data()); + + // Store the current mesh to be passed into the next iteration + finalMesh = processedMesh; + } + + // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon + + return finalMesh; +} + +//-------------------------------------------------------------------------------- + +} +} diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp new file mode 100644 index 0000000000..ddb37b205a --- /dev/null +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -0,0 +1,64 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __INTERFACE_RECONSTRUCTOR_H__ +#define __INTERFACE_RECONSTRUCTOR_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" + +#include "MIRMesh.hpp" +#include "CellData.hpp" +#include "ZooBitMaps.hpp" +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + class InterfaceReconstructor + { + public: + InterfaceReconstructor(); + InterfaceReconstructor(mir::MIRMesh* _mesh); + ~InterfaceReconstructor(); + + mir::MIRMesh computeReconstructedInterface(); + + + // std::vector computeVolumeFractionAverages(mir::MIRMesh* tempMesh); + + private: + mir::MIRMesh* mesh; + + public: + std::vector materialVolumeFractionsVertex; // volume fractions for each material for each vertex + + private: + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); + + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, + std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); + + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, + std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, + std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts); + + mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); + axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); + axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + + void mergeCellSplitData(); + + }; +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp new file mode 100644 index 0000000000..89da4fc522 --- /dev/null +++ b/src/axom/mir/MIRMesh.cpp @@ -0,0 +1,318 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "MIRMesh.hpp" + +namespace axom +{ +namespace mir +{ + MIRMesh::MIRMesh() + { + + } + + MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor + { + evInds = _mesh->evInds; + evBegins = _mesh->evBegins; + veInds = _mesh->veInds; + veBegins = _mesh->veBegins; + verts = _mesh->verts; + elems = _mesh->elems; + bdry = _mesh->bdry; + cobdry = _mesh->cobdry; + vertexPositions = _mesh->vertexPositions; + materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; + materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + numMaterials = _mesh->numMaterials; + } + + MIRMesh::~MIRMesh() + { + + } + + /// Initializes a mesh with the given topology. + void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) + { + evInds = _evInds; + evBegins = _evBegins; + veInds = _veInds; + veBegins = _veBegins; + verts = _verts; + elems = _elems; + numMaterials = _numMaterials; + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + } + +//-------------------------------------------------------------------------------- + + /// Constructs the mesh boundary and coboundary relations + void MIRMesh::constructMeshRelations() + { + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( evInds.size() ) + .data( evInds.data() ) ); + } + + + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( veInds.size() ) + .data( veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end + } + + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + } + +//-------------------------------------------------------------------------------- + + /// Constructs the volume fraction maps on the elements and vertices given element volume fraction data. + void MIRMesh::constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData) + { + // Initialize the maps for all of the materials with the input volume fraction data for each material + for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + { + // Initialize the map for the current material + materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + + // Copy the data for the current material + for (int eID = 0; eID < elems.size(); ++eID) + { + materialVolumeFractionsElement[matID][eID] = materialVolumeFractionsData[matID][eID]; + } + + SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + } + + // Initialize the maps for all of the vertex volume fractions + for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + { + // Initialize the new map for the volume fractions + materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + + // Calculate the average volume fraction value for the current vertex for the current material + for (int vID = 0; vID < verts.size(); ++vID) + { + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; + + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += materialVolumeFractionsElement[matID][eID]; + } + + materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + } + } + +//-------------------------------------------------------------------------------- + +/// Construct the vertex volume fraction data given vertex volume fraction data +void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > vertexVF) +{ + // Initialize the maps for all of the materials with the input volume fraction data for each vertex + for (int matID = 0; matID < numMaterials; ++matID) + { + // Initialize the map for the current material + materialVolumeFractionsVertex.push_back(ScalarMap( &verts )); + + // Copy the data for the current material + for (int vID = 0; vID < verts.size(); ++vID) + { + materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; + } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + } +} + +//-------------------------------------------------------------------------------- + + /// Constucts the positions map on the vertices + void MIRMesh::constructVertexPositionMap(Point2* data) + { + // construct the position map on the vertices + vertexPositions = PointMap( &verts ); + + for (int vID = 0; vID < verts.size(); ++vID) + vertexPositions[vID] = data[vID]; + + SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + } + +//-------------------------------------------------------------------------------- + + /// Prints out the map values for each element + void MIRMesh::printElementScalarMap(ScalarMap& elements, std::string prefix) + { + std::cout << prefix; + for (int eID = 0; eID < elems.size(); ++eID) + { + printf("Element %d: %f\n", eID, elements[eID]); + } + } + +//-------------------------------------------------------------------------------- + + /// Prints out the map values for each vertex + void MIRMesh::printVertexScalarMap(ScalarMap& vertices, std::string prefix) + { + std::cout << prefix; + for (int vID = 0; vID < verts.size(); ++vID) + { + printf("Vertex %d: %f\n", vID, vertices[vID]); + } + } + +//-------------------------------------------------------------------------------- + + void MIRMesh::print() + { + printf("------------------------Printing Mesh Information:------------------------\n"); + printf("number of vertices: %d\n", verts.size()); + printf("number of elements: %d\n", elems.size()); + printf("number of materials: %d\n", numMaterials); + + printf("evInds: { "); + for (int i = 0; i < evInds.size(); i++) + { + printf("%d ", evInds[i]); + } + printf("}\n"); + + printf("evBegins: { "); + for (int i = 0; i < evBegins.size(); i++) + { + printf("%d ", evBegins[i]); + } + printf("}\n"); + + printf("veInds: { "); + for (int i = 0; i < veInds.size(); i++) + { + printf("%d ", veInds[i]); + } + printf("}\n"); + + printf("veBegins: { "); + for (int i = 0; i < veBegins.size(); i++) + { + printf("%d ", veBegins[i]); + } + printf("}\n"); + + printf("vertexPositions: { "); + for (int i = 0; i < vertexPositions.size(); ++i) + { + printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + } + + printf("}\n"); + printf("--------------------------------------------------------------------------\n"); + } + +//-------------------------------------------------------------------------------- + + /// Reads in a constructs a mesh from the given file + void MIRMesh::readMeshFromFile() + { + printf("Mesh writing functionality not implemented yet."); + + } + +//-------------------------------------------------------------------------------- + + /// Writes out the mesh to a file + void MIRMesh::writeMeshToFile(std::string filename) + { + std::ofstream meshfile; + meshfile.open(filename); + std::ostream_iterator out_it(meshfile, " "); + + // write header + meshfile << "# vtk DataFile Version 3.0\n" + << "vtk output\n" + << "ASCII\n" + << "DATASET UNSTRUCTURED_GRID\n\n" + << "POINTS " << verts.size() << " double\n"; + + // write positions + for (int vID = 0; vID < verts.size(); ++vID) + { + meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + } + + // // write elem-to-vert boundary relation + // meshfile << "\nCELLS " << elems.size() << " " << 5 * elems.size(); // TODO: This will not always be 5 + // for(auto e: elems) + // { + // meshfile<<"\n4 "; // TODO: This will not always be 4 + // std::copy ( bdry.begin(e), bdry.end(e), out_it ); + // } + + // // write element types ( 9 == VTK_QUAD, 5 == VTK_TRIANGLE ) + // meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; + // for(int i=0 ; i< elems.size() ; ++i) + // { + // meshfile << "9 "; // TODO: This will not always be 9, but will be 5 for any triangle elements + // } + + // // write element ids + // meshfile << "\n\nCELL_DATA " << elems.size() + // << "\nSCALARS cellIds int 1" + // << "\nLOOKUP_TABLE default \n"; + // for(int i=0 ; i< elems.size() ; ++i) + // { + // meshfile << elems[i] <<" "; + // } + + // // write vertex ids + // meshfile << "\n\nPOINT_DATA " << verts.size() + // << "\nSCALARS vertIds int 1" + // << "\nLOOKUP_TABLE default \n"; + // for(int i=0 ; i< verts.size() ; ++i) + // { + // meshfile << verts[i] <<" "; + // } + meshfile <<"\n"; + } + +//-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp new file mode 100644 index 0000000000..696967840d --- /dev/null +++ b/src/axom/mir/MIRMesh.hpp @@ -0,0 +1,100 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MIR_MESH_H__ +#define __MIR_MESH_H__ + + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMeshTypes.hpp" + +// C/C++ includes +#include // for definition of M_PI, exp() +#include +#include +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + + +//-------------------------------------------------------------------------------- +namespace axom +{ +namespace mir +{ + struct EdgeClipInfo + { + int vertexOne; + int vertexTwo; + int colorOne; + int colorTwo; + float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. + }; + + //-------------------------------------------------------------------------------- + + class MIRMesh + { + /**************************************************************** + * MESH FUNCTIONS + ****************************************************************/ + public: + MIRMesh(); + MIRMesh(MIRMesh* _mesh); // copy constructor + ~MIRMesh(); + + void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); + + void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations + void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map + void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + + void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element + void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex + + void print(); // Print out a variety of useful information about the mesh + + void readMeshFromFile(); /// Reads in and constructs a mesh from a file + void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file + void attachVertexMap(); /// Adds a map of data to the mesh + + /**************************************************************** + * VARIABLES + ****************************************************************/ + public: + // support data for mesh connectivity + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + + public: + // Mesh Set Definitions + VertSet verts; // the set of vertices in the mesh + ElemSet elems; // the set of elements in the mesh + + public: + // Mesh Relation Definitions + ElemToVertRelation bdry; // Boundary relation from elements to vertices + VertToElemRelation cobdry; // Coboundary relation from vertices to elements + + public: + // Mesh Map Definitions + PointMap vertexPositions; // vertex position + std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element + std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + + public: + int numMaterials; + }; + + //-------------------------------------------------------------------------------- +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp new file mode 100644 index 0000000000..d90ecbeb35 --- /dev/null +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -0,0 +1,74 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MIR_MESH_TYPES_H__ +#define __MIR_MESH_TYPES_H__ + + +#include "axom/core.hpp" // for axom macros +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + /** + * \brief Simple 2D Point class for example + */ + struct Point2 + { + Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} + + Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} + + Point2& operator=(const Point2& other) + { m_x = other.m_x; m_y = other.m_y; return *this; } + + Point2& operator+=(const Point2& other) + { m_x += other.m_x; m_y += other.m_y; return *this; } + + Point2& operator/=(double val) + { m_x /= val; m_y += val; return *this; } + + double& operator[] (int i) { return (i==0) ? m_x : m_y; } + const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } + + friend std::ostream& operator<<(std::ostream& os, const Point2& pt) + { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } + + double m_x, m_y; + }; + + // SET TYPE ALIASES + using PosType = slam::DefaultPositionType; + using ElemType = slam::DefaultElementType; + + using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + + using VertSet = slam::PositionSet< PosType, ElemType >; + using ElemSet = slam::PositionSet< PosType, ElemType >; + + // RELATION TYPE ALIASES + using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; + + // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. + // Note: It is the relation of the elements to the vertices. + + using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; + using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; + + // MAP TYPE ALIASES + using BaseSet = slam::Set< PosType, ElemType >; + using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using PointMap = slam::Map< BaseSet, Point2 >; + +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooBitMaps.cpp new file mode 100644 index 0000000000..d2adf217a3 --- /dev/null +++ b/src/axom/mir/ZooBitMaps.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "ZooBitMaps.hpp" + +namespace axom +{ +namespace mir +{ + + // Quad Vertex Indices + // + // 0 7 3 + // @---------@---------@ + // | | + // | | + // | | + // 4 @ @ 6 + // | | + // | | + // | | + // @---------@---------@ + // 1 5 2 + // + const int quadClipTable[16][19] = + { + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} + }; +} +} \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooBitMaps.hpp new file mode 100644 index 0000000000..09f76ede1e --- /dev/null +++ b/src/axom/mir/ZooBitMaps.hpp @@ -0,0 +1,17 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __ZOO_BIT_MAPS_H__ +#define __ZOO_BIT_MAPS_H__ + + +namespace axom +{ +namespace mir +{ + extern const int quadClipTable[16][19]; +} +} +#endif \ No newline at end of file diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index b199beae56..605609bf02 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -5,6 +5,7 @@ set( mir_examples mir_tutorial.cpp + mir_tutorial_simple.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp index 7dbcb69a0d..576da4d49d 100644 --- a/src/axom/mir/examples/mir_tutorial.cpp +++ b/src/axom/mir/examples/mir_tutorial.cpp @@ -4,16 +4,437 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros -#include "axom/mir.hpp" // for Mir classes & functions +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" // C/C++ includes #include // for definition of M_PI, exp() +#include // namespace aliases //namespace mir = axom::mir; namespace numerics = axom::numerics; +namespace slam = axom::slam; + +#define EPSILON_ZERO = 0.00000000001f; + +//-------------------------------------------------------------------------------- + +/** + * \brief Simple 2D Point class for example + */ +struct Point2 +{ + Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} + + Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} + + Point2& operator=(const Point2& other) + { m_x = other.m_x; m_y = other.m_y; return *this; } + + Point2& operator+=(const Point2& other) + { m_x += other.m_x; m_y += other.m_y; return *this; } + + Point2& operator/=(double val) + { m_x /= val; m_y += val; return *this; } + + double& operator[] (int i) { return (i==0) ? m_x : m_y; } + const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } + + friend std::ostream& operator<<(std::ostream& os, const Point2& pt) + { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } + + double m_x, m_y; +}; + +//-------------------------------------------------------------------------------- + +struct EdgeClipInfo +{ + int vertexOne; + int vertexTwo; + int colorOne; + int colorTwo; + float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. +}; + +//-------------------------------------------------------------------------------- + +struct MIRMesh +{ + /**************************************************************** + * TYPE ALIASES + ****************************************************************/ + // SET TYPE ALIASES + using PosType = slam::DefaultPositionType; + using ElemType = slam::DefaultElementType; + + using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + + using VertSet = slam::PositionSet< PosType, ElemType >; + using ElemSet = slam::PositionSet< PosType, ElemType >; + + // RELATION TYPE ALIASES + using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; + + // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. + // Note: It is the relation of the elements to the vertices. + using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; + using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; + + // MAP TYPE ALIASES + using BaseSet = slam::Set< PosType, ElemType >; + using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using PointMap = slam::Map< BaseSet, Point2 >; + + enum { GREEN = 0, BLUE = 1 }; + + /**************************************************************** + * MESH FUNCTIONS + ****************************************************************/ + void InitializeMesh() + { + // Create the mesh connectivity information + // data for element-vertex boundary relation + evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + + // data for vertex-element coboundary relation + veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + // Initialize the mesh sets + verts = VertSet(16); // Construct a vertex set with 11 vertices + elems = ElemSet(9); // Construct an element set with 5 elements + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + + printf("Mesh has been initialized.\n"); + } + + /// Constructs the mesh boundary and coboundary relations + void constructMeshRelations() + { + + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( evInds.size() ) + .data( evInds.data() ) ); + } + + + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( veInds.size() ) + .data( veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end + } + + + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + + printf("Finished constructing mesh relations.\n"); + } + + /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionMaps() + { + // TODO: Determine better way to store volume fraction data than 1 map per material? + // Construct the scalar map on the elements for the green material + greenVolumeFractionsElements = ScalarMap( &elems ); + + // Set the green volume fraction for each element + greenVolumeFractionsElements[0] = 1.0; + greenVolumeFractionsElements[1] = 1.0; + greenVolumeFractionsElements[2] = 1.0; + greenVolumeFractionsElements[3] = 1.0; + greenVolumeFractionsElements[4] = 0.5; + greenVolumeFractionsElements[5] = 0.2; + greenVolumeFractionsElements[6] = 0.2; + greenVolumeFractionsElements[7] = 0.0; + greenVolumeFractionsElements[8] = 0.0; + + // Construct the scalar map on the elements for the blue material + blueVolumeFractionsElements = ScalarMap( &elems ); + + blueVolumeFractionsElements[0] = 0.0; + blueVolumeFractionsElements[1] = 0.0; + blueVolumeFractionsElements[2] = 0.0; + blueVolumeFractionsElements[3] = 0.0; + blueVolumeFractionsElements[4] = 0.5; + blueVolumeFractionsElements[5] = 0.8; + blueVolumeFractionsElements[6] = 0.8; + blueVolumeFractionsElements[7] = 1.0; + blueVolumeFractionsElements[8] = 1.0; + + printf("Finished constructing volume fractions for mesh elements.\n"); + } + + /// Constucts the positions map on the vertices + void constructVertexPositionMap() + { + // construct the position map on the vertices + vertexPositions = PointMap( &verts ); + + // first vertex is at origin + vertexPositions[0] = Point2( 0.0, 3.0 ); + vertexPositions[1] = Point2( 1.0, 3.0 ); + vertexPositions[2] = Point2( 2.0, 3.0 ); + vertexPositions[3] = Point2( 3.0, 3.0 ); + + vertexPositions[4] = Point2( 0.0, 2.0 ); + vertexPositions[5] = Point2( 1.0, 2.0 ); + vertexPositions[6] = Point2( 2.0, 2.0 ); + vertexPositions[7] = Point2( 3.0, 2.0 ); + + vertexPositions[8] = Point2( 0.0, 1.0 ); + vertexPositions[9] = Point2( 1.0, 1.0 ); + vertexPositions[10] = Point2( 2.0, 1.0 ); + vertexPositions[11] = Point2( 3.0, 1.0 ); + + vertexPositions[12] = Point2( 0.0, 0.0 ); + vertexPositions[13] = Point2( 1.0, 0.0 ); + vertexPositions[14] = Point2( 2.0, 0.0 ); + vertexPositions[15] = Point2( 3.0, 0.0 ); + + SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + + SLIC_INFO("-- Vertex positions:"); + for(int vID=0 ; vID < verts.size() ; ++vID) + { + SLIC_INFO("Position of vert " << vID << " is " << vertexPositions[vID]); + } + } + + // Computes the average volume fraction at each vertex of the mesh + void computeVolumeFractionAverages() + { + // Initialize the vertex volume fraction maps + greenVolumeFractionsVertices = ScalarMap( &verts ); + blueVolumeFractionsVertices = ScalarMap( &verts ); + + for (int vID = 0; vID < verts.size(); ++vID) + { + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += greenVolumeFractionsElements[eID]; + } + greenVolumeFractionsVertices[vID] = sum / vertexElements.size(); + + // Compute the per vertex volume fractions for the blue material + sum = 0; + for (int i = 0; i < vertexElements.size(); ++i) + { + auto eID = vertexElements[i]; + sum += blueVolumeFractionsElements[eID]; + } + blueVolumeFractionsVertices[vID] = sum / vertexElements.size(); + } + + printf("Finished computing volume fraction averages.\n"); + } + + /// Prints out the map values for each element + void printElementScalarMap(ScalarMap& elements, std::string prefix) + { + std::cout << prefix; + for (int i = 0; i < elems.size(); ++i) + { + printf("Element %d: %f\n", i, elements[i]); + } + } + + /// Prints out the map values for each vertex + void printVertexScalarMap(ScalarMap& vertices, std::string prefix) + { + std::cout << prefix; + for (int i = 0; i < verts.size(); ++i) + { + printf("Vertex %d: %f\n", i, vertices[i]); + } + } + + /// Use bilinear interpolation to compute the points where the given element should be clipped. + /// Returns true if clip should occur, otherwise false. + /// If a clip should occur, the clipping points will be given in p1 and p2. + void computeClippingPoints(const int eID)//, Point2* p1, Point2* p2) + { + // Find the vertices associated with each element + auto elementVertices = bdry[eID]; + + // Check if one of the two currently considered materials is not present at the vertices of the current element + if (blueVolumeFractionsElements[eID] != 0.0 && greenVolumeFractionsElements[eID] != 0.0) + { + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID); + } + } + else + { + printf("No cuts in element %d.\n", eID); + } + } + + /// Computes the points where the given quad element should be clipped + void computeQuadClippingPoints(int eID) + { + auto elementVertices = bdry[eID]; + EdgeClipInfo eci[4]; + + // Initialize the edge clipping info + for (int i = 0; i < 4; ++i) + { + eci[i].vertexOne = elementVertices[ i ]; + eci[i].vertexTwo = elementVertices[ (i + 1) % 4 ]; + + if (blueVolumeFractionsVertices[eci[i].vertexOne] > greenVolumeFractionsVertices[eci[i].vertexOne]) + eci[i].colorOne = BLUE; + else + eci[i].colorOne = GREEN; + + if (blueVolumeFractionsVertices[eci[i].vertexTwo] > greenVolumeFractionsVertices[eci[i].vertexTwo]) + eci[i].colorTwo = BLUE; + else + eci[i].colorTwo = GREEN; + + eci[i].t = -1.0; // default t value means one material dominates the other and no clip should occur on this edge + } + + // Analyze the edge clipping info and determine where to clip the cell + for (int i = 0; i < 4; ++i) + { + if (eci[i].colorOne != eci[i].colorTwo) + { + axom::float64 numerator = blueVolumeFractionsVertices[eci[i].vertexOne] - greenVolumeFractionsVertices[eci[i].vertexOne]; + axom::float64 denominator = -greenVolumeFractionsVertices[eci[i].vertexOne] + + greenVolumeFractionsVertices[eci[i].vertexTwo] + + blueVolumeFractionsVertices[eci[i].vertexOne] + - blueVolumeFractionsVertices[eci[i].vertexTwo]; + + if (denominator != 0.0) + eci[i].t = numerator / denominator; + } + } + + // Print out the cutting information + bool cut = false; + for (int i = 0; i < 4; ++i) + { + if (eci[i].t > 0.0) + { + printf("Cutting element %d between vertices %d and %d. t = %f\n", eID, eci[i].vertexOne, eci[i].vertexTwo, eci[i].t); + cut = true; + } + } + if (!cut) + printf("No cuts in element %d.\n", eID); + + } + + /// Computes the points where the given triangle element should be clipped + void computeTriangleClippingPoints(int eID) + { + printf("Triangle clipping case not yet implemented.\n"); + } + + /**************************************************************** + * VARIABLES + ****************************************************************/ + public: + // Mesh Set Definitions + VertSet verts; // the set of vertices in the mesh + ElemSet elems; // the set of elements in the mesh + + // Mesh Relation Definitions + ElemToVertRelation bdry; // Boundary relation from elements to vertices + VertToElemRelation cobdry; // Coboundary relation from vertices to elements + + // Mesh Map Definitions + PointMap vertexPositions; // vertex position + ScalarMap greenVolumeFractionsElements; // the volume fractions of the green material for each element (NOT each vertex) + ScalarMap blueVolumeFractionsElements; // the volume fractions of the blue material for each element (NOT each vertex) + + ScalarMap greenVolumeFractionsVertices; + ScalarMap blueVolumeFractionsVertices; + + // support data for mesh connectivity + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; +}; + + +//-------------------------------------------------------------------------------- + /*! * \file @@ -32,11 +453,28 @@ namespace numerics = axom::numerics; */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { + printf("Beginning the MIR Tutorial program.\n"); - std::cout << "hello from the mir_tutorial!" << std::endl; + MIRMesh testMesh; + testMesh.InitializeMesh(); + testMesh.constructMeshRelations(); + // testMesh.constructMeshPositionMap(); + testMesh.constructMeshVolumeFractionMaps(); + testMesh.computeVolumeFractionAverages(); - return 0; -} + // Print volume fraction debugging information + // testMesh.printElementScalarMap(testMesh.greenVolumeFractionsElements, "Green Volume Fractions per Element: \n"); + // testMesh.printElementScalarMap(testMesh.blueVolumeFractionsElements, "Blue Volume Fractions per Element: \n"); + // testMesh.printVertexScalarMap(testMesh.greenVolumeFractionsVertices, "Green Volume Fractions per Vertex: \n"); + // testMesh.printVertexScalarMap(testMesh.blueVolumeFractionsVertices, "Blue Volume Fractions per Vertex: \n"); + // Determine where to clip the cells + for (int i = 0; i < 9; ++i) + testMesh.computeClippingPoints(i); + + + return 0; +} +//-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp new file mode 100644 index 0000000000..23bfc09769 --- /dev/null +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -0,0 +1,122 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + +#include "../MIRMesh.hpp" +#include "../InterfaceReconstructor.hpp" + +// namespace aliases +//namespace mir = axom::mir; +namespace numerics = axom::numerics; +namespace slam = axom::slam; +namespace mir = axom::mir; + +//-------------------------------------------------------------------------------- + +/*! + * \brief Tutorial main + */ +int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 greenVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + materialVolumeFractionsData[GREEN] = greenVolumeFractions; + axom::float64 blueVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + + // Begin material interface reconstruction + mir::InterfaceReconstructor reconstructor(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); + processedMesh.print(); + // processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/markoTestMesh.vtk"); + + return 0; +} + +//-------------------------------------------------------------------------------- + From 39cb6a83724142f65e8260ffc883e691b55dbbbc Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Mon, 10 Jun 2019 15:24:26 -0700 Subject: [PATCH 027/290] Debugged element combination code. Implemented dominant color tracking. Added support for null material. Implemented parent element tracking. Added post-processing to ensure generated elements will always contain an acceptable dominant material. Implemented basic vtk file writing. --- src/axom/mir/CellData.cpp | 12 + src/axom/mir/CellData.hpp | 24 +- src/axom/mir/InterfaceReconstructor.cpp | 571 +++++++++--------- src/axom/mir/InterfaceReconstructor.hpp | 34 +- src/axom/mir/MIRMesh.cpp | 186 ++++-- src/axom/mir/MIRMesh.hpp | 27 +- src/axom/mir/MIRMeshTypes.hpp | 2 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 8 +- 8 files changed, 478 insertions(+), 386 deletions(-) diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index bfeee02462..df4da4d785 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -86,6 +86,18 @@ namespace mir vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); } } + + // Merge the elements' dominant materials + for (auto i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + { + elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); + } + + // Merge the elements' parent ids + for (auto i = 0; i < cellToMerge.elementParents.size(); ++i) + { + elementParents.push_back(cellToMerge.elementParents[i]); + } // Merge the total number of verts and elems in the resulting cell numVerts += cellToMerge.numVerts; diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index d8a66764f7..d91c9761ec 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -9,7 +9,7 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" -#include "MIRMesh.hpp" +#include "MIRMeshTypes.hpp" namespace numerics = axom::numerics; namespace slam = axom::slam; @@ -19,6 +19,8 @@ namespace axom namespace mir { + /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). + /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData { @@ -40,9 +42,23 @@ namespace mir std::vector veInds; std::vector veBegins; - std::vector vertexPositions; - std::vector materialsInCell; // TODO: This is not currently being used. - std::vector > vertexVolumeFractions; + std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + }; + + class CellTopology + { + public: + CellTopology(); + ~CellTopology(); + + public: + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; }; } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 1f9f17a9fb..0160709fde 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -23,7 +23,7 @@ namespace mir /// Constructor InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) { - mesh = _mesh; + originalMesh = _mesh; } //-------------------------------------------------------------------------------- @@ -36,101 +36,111 @@ namespace mir //-------------------------------------------------------------------------------- - /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. - /// TODO: This method needs to place the computed cell vertices into the intermediate mesh (pass this in as an argument) - void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) +/// Reconstructs the material interface and returns the resulting output mesh. +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +{ + // Initialize the final mesh to be the same as the input mesh + mir::MIRMesh finalMesh(originalMesh); + + // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) + for (int matID = 0; matID < originalMesh->numMaterials; ++matID) { - // Find the vertices associated with each element - auto elementVertices = mesh->bdry[eID]; + // Copy the mesh to be split + mir::MIRMesh intermediateMesh(&finalMesh); - // Check if one of the two currently considered materials is not present at the vertices of the current element - // if (mesh->materialVolumeFractionsElement[matOneID][eID] != 0.0 && mesh->materialVolumeFractionsElement[matTwoID][eID] != 0.0) // TODO: Figure out how to best handle this case - // { - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, _evInds, _evBegins, _veInds, _veBegins, _vertexPositions, _materialInCell, _numVerts, _numElements, _newVolumeFractionsAtVerts); // Compute how the cell should be decomposed into new cells - } - // } - // else - // { - // printf("No cuts in element %d.\n", eID); + // Update the materials upon which the split will occur + int matOne = matID; + + // Create an array to store the output of each element being split. + CellData temp_cellData[intermediateMesh.elems.size()]; + + // Process/split each element + for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + { + // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) + int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; + printf("Processing materials: %d and %d for Element %d\n", currentDominantMat, matOne, eID); + computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); + } - // TODO: Add the current cells vertices and data back into the mesh + // Merge each of the cells into the first CellData struct + for (unsigned int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + temp_cellData[0].mergeCell(temp_cellData[eID]); + + mir::VertSet combined_verts(temp_cellData[0].numVerts); + mir::ElemSet combined_elems(temp_cellData[0].numElems); - // } + // Create the final, processed mesh + mir::MIRMesh processedMesh; + processedMesh.InitializeMesh(temp_cellData[0].evInds, temp_cellData[0].evBegins, temp_cellData[0].veInds, temp_cellData[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); + processedMesh.constructMeshRelations(); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].vertexVolumeFractions); + processedMesh.constructVertexPositionMap(temp_cellData[0].vertexPositions.data()); + processedMesh.constructElementParentMap(temp_cellData[0].elementParents.data()); + processedMesh.constructElementDominantMaterialMap(temp_cellData[0].elementDominantMaterials); + + // Store the current mesh to be passed into the next iteration + finalMesh = processedMesh; + finalMesh.constructMeshRelations(); // TODO: Figure out why this is necessary here... + + printf("-------------Finished processing material-------------\n"); } + // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon + + return finalMesh; +} + //-------------------------------------------------------------------------------- - /// Computes the points where the triangle element should be clipped based on the volume fractions of mat one and two. - void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts) + /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. + void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf("Triangle clipping case not yet implemented.\n"); + // Find the vertices associated with each element + auto elementVertices = tempMesh->bdry[eID]; + + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } } //-------------------------------------------------------------------------------- - /// Computes how the given cell should be decomposed into new cells baed on teh vertex volume fractions of matOne and matTwo. - void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, - std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts) + void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf("Processing element %d: ", eID); + printf(" Processing element %d: ", eID); - /**************************************************************** - * DETERMINE THE CLIPPING CASE - ****************************************************************/ - // Get the vertices of the current element to clip + // Determine the clipping case auto elementVertices = tempMesh->bdry[eID]; - // Determine which vertices correspond to upper left, lower left, lower right, and upper right vertices of the quad. int upperLeftVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; int lowerRightVertex = elementVertices[2]; int upperRightVertex = elementVertices[3]; - // Determine the dominant color at each vertex - int upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - int lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - int lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - int upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; - - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperLeftColor == matOneID) caseIndex |= 8; - if (lowerLeftColor == matOneID) caseIndex |= 4; - if (lowerRightColor == matOneID) caseIndex |= 2; - if (upperRightColor == matOneID) caseIndex |= 1; - + unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); printf("caseIndex: %d\n", caseIndex); - /**************************************************************** - * GENERATE NEW ELEMENTS - ****************************************************************/ - // Cell information indices - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped - std::map > vertexVolumeFractionsMap; // index: map[vID][matID] = vertexVolumeFractionValues[numMaterials] | Note: maps are ordered sets - - int currentElementIndex = 0; // the next available element index // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index int i = 0; int numVertices = quadClipTable[caseIndex][i]; - while (numVertices != -1) // for each new element in the current clipping case + + // for each new element in the current clipping case + while (numVertices != -1) { // for each vertex of the new element for (int j = 0; j < numVertices; ++j) @@ -143,10 +153,10 @@ namespace mir newVertices[vID].push_back(currentElementIndex); verticesPresent[vID] = 1; - // Find t using "bilinear" interpolation method for any vertex that is not one of the original 4 vertices + // Find t using linear interpolation for any vertex that is not one of the original 4 vertices if(vID == 4) { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); // these might be the wrong vertex indices + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); } else if(vID == 5) { @@ -174,111 +184,60 @@ namespace mir * CALCULATE THE NEW CELLS' DATA ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_numElements[0] = (int) newElements.size(); - out_numVerts[0] = (int) newVertices.size(); + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); - // Generate the topology of the new elements (evInds, evBegins, etc) - // Store the evInds and evBegins data in the output vectors - int currentEVBeginIndex = 0; + generateTopologyDataFromQuad(newElements, newVertices, out_cellData); + generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + + // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - // Push the start index of the next element - out_evBegins.push_back(currentEVBeginIndex); - - // Push the next element's vertices - for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) { - out_evInds.push_back(itr->second[vIndex]); - ++currentEVBeginIndex; - } - } - - // Push the index that occurs after the last vertex - out_evBegins.push_back(currentEVBeginIndex); + // int temp_vID = newElements[currentElementIndex][it]; + int temp_vID = itr->second[it]; - // Store the veInds and veBegins data in the output vectors - int currentVEBeginIndex = 0; - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - // Push the start index of the vertex's elements - out_veBegins.push_back(currentVEBeginIndex); + // Find the vertex that is one of the four original vertices of the quad element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) + { + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; - // Push the next vertex's elements - for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) - { - out_veInds.push_back(itr->second[eIndex]); - ++currentVEBeginIndex; + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; + } } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); } - - // Push the index that occurs after the last element - out_veBegins.push_back(currentVEBeginIndex); - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - if (verticesPresent[vID] == 1) - { - if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; - if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); - if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); - } - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_vertexPositions.push_back(itr->second); + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_newVolumeFractionsAtVerts.resize(tempMesh->numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int vID = itr->first; - // Ensure that the current vertex being considered is actually present in the current clipping case - if (verticesPresent[vID] == 1) - { - // vertexVolumeFractionsMap[vID].resize(tempMesh->numMaterials); + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) - { - if (vID == 0) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); - if (vID == 1) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_newVolumeFractionsAtVerts[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); - if (vID == 4) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 6) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); - if (vID == 7) - out_newVolumeFractionsAtVerts[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); - } + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + { + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; + else + out_cellData.elementDominantMaterials[itr->first] = matOneID; } } - + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present int evIndexSubtract[8]; for (int i = 0; i < 8; ++i) @@ -291,87 +250,41 @@ namespace mir for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) { - out_evInds[i] -= evIndexSubtract[ out_evInds[i] ]; + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; } + } +//-------------------------------------------------------------------------------- +/// Finds the bit map representing the clipping case for a quad. +unsigned int InterfaceReconstructor::determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +{ + // Determine the dominant color at each vertex + int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; - /**************************************************************** - * DEBUG PRINTING - ****************************************************************/ + if (matOneID == NULL_MAT) + upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matTwoID; + if (matTwoID == NULL_MAT) + upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; + if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - // TESTING: Print out the values by which the evIndex values need to be substracted - // in order to ensure there are no gaps in the vertex ids in the resulting mesh. - // printf(" evIndexSubtract: { "); - // for (int i = 0; i < 8; i++) - // { - // printf("%d ", evIndexSubtract[i]); - // } - // printf("}\n"); - - // // TESTING: Print out the basic cell information - // printf(" out_numElements: %d\n", out_numElements[0]); - // printf(" out_numVerts: %d\n", out_numVerts[0]); - - // // TESTING: Print out the mesh topology information - // printf(" out_evInds: {"); - // for (int i = 0; i < out_evInds.size(); i++) - // { - // printf("%d ", out_evInds[i]); - // } - // printf("}\n"); - - // printf(" out_evBegins: {"); - // for (int i = 0; i < out_evBegins.size(); i++) - // { - // printf("%d ", out_evBegins[i]); - // } - // printf("}\n"); - - // printf(" out_veInds: {"); - // for (int i = 0; i < out_veInds.size(); i++) - // { - // printf("%d ", out_veInds[i]); - // } - // printf("}\n"); - - // printf(" out_veBegins: {"); - // for (int i = 0; i < out_veBegins.size(); i++) - // { - // printf("%d ", out_veBegins[i]); - // } - // printf("}\n"); - - // // TESTING: Print out the positions of the vertices - // for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - // { - // printf(" Vertex %d: (%f, %f)\n", itr->first, itr->second.m_x, itr->second.m_y); - // } - - // TESTING: Print out the volume fractions at the vertices from the map - // for (auto itr = vertexVolumeFractionsMap.begin(); itr != vertexVolumeFractionsMap.end(); itr++) - // { - // int vID = itr->first; - // printf(" Vertex %d Volume Fractions:\n", vID); - // for (int matID = 0; matID < tempMesh->numMaterials; ++matID) - // { - // printf(" Mat %d: %f\n", matID, vertexVolumeFractionsMap[vID][matID]); - // } - // } - - // TESTING: Print out the volume fractions at the vertices from the vector of materials containing a vector of vertex volume fraction values - // for (int matID = 0; matID < out_newVolumeFractionsAtVerts.size(); ++matID) - // { - // printf(" Material %d:\n", matID); - // for (int vID = 0; vID < out_newVolumeFractionsAtVerts[matID].size(); ++vID) - // { - // printf(" Vertex %d: %f\n", vID, out_newVolumeFractionsAtVerts[matID][vID]); - // } - // } + upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; } - } + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperLeftColor == matOneID) caseIndex |= 8; + if (lowerLeftColor == matOneID) caseIndex |= 4; + if (lowerRightColor == matOneID) caseIndex |= 2; + if (upperRightColor == matOneID) caseIndex |= 1; + + return caseIndex; +} //-------------------------------------------------------------------------------- @@ -400,87 +313,165 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte { axom::float64 ret = 0.0; - axom::float64 numerator = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - mesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; - axom::float64 denominator = -mesh->materialVolumeFractionsVertex[matOneID][vertexOneID] - + mesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] - + mesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - - mesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + axom::float64 vfMatOneVertexOne = tempMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; // Changed these all from originalMesh->materialVolumeFractionsVertex[][] to tempMesh->... + axom::float64 vfMatTwoVertexOne = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID]; + axom::float64 vfMatOneVertexTwo = tempMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID]; + axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + + if (matOneID == NULL_MAT) + { + vfMatOneVertexOne = 0.0; + vfMatOneVertexTwo = 0.0; + } + + if (matTwoID == NULL_MAT) + { + vfMatTwoVertexOne = 0.0; + vfMatTwoVertexTwo = 0.0; + } + + axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; + axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; + + // axom::float64 numerator = originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; + // axom::float64 denominator = -originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID] + // + originalMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] + // + originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] + // - originalMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (denominator != 0.0) ret = numerator / denominator; + if (ret > 1.0 || ret < 0.0) + { + // This shouldn't happen... + printf("OUT OF BOUNDS T VALUE: %f\n", ret); + + // Clamp the t value + ret = fmin(1.0, ret); + ret = fmax(0.0, ret); + } + return ret; } //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from splitting a quad. +void InterfaceReconstructor::generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData) { - // Initialize the final mesh to be the same as the input mesh - mir::MIRMesh finalMesh(mesh); // TODO: This might just copy the reference to the original mesh, and not copy it. Could cause bugs when it gets overwritten. + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_cellData.evBegins.push_back(currentEVBeginIndex); - // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) - for (int matID = 0; matID < mesh->numMaterials - 1; ++matID) - { - // Copy the mesh to be split - mir::MIRMesh intermediateMesh(&finalMesh); + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_cellData.evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } - // Update the materials upon which the split will occur - int matOne = matID; - int matTwo = matID + 1; + // Push the index that occurs after the last vertex + out_cellData.evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_cellData.veBegins.push_back(currentVEBeginIndex); - // Process/split each element - std::vector temp_evInds[intermediateMesh.elems.size()]; // Store the vertex indices adjacent to each element for each cell - std::vector temp_evBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_evBewgins for each element-vertex for each cell - std::vector temp_veInds[intermediateMesh.elems.size()]; // Store the element indices adjacent to each vertex for each cell - std::vector temp_veBegins[intermediateMesh.elems.size()]; // Store the starting indices into temp_veInds for each vertex-element for each cell + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_cellData.veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_cellData.veBegins.push_back(currentVEBeginIndex); +} - std::vector temp_vertexPositions[intermediateMesh.elems.size()]; // Store the positions of the vertices generated for each cell - std::vector materialInCell[intermediateMesh.elems.size()]; // Note: after splitting, the cells should all be clean and thus will have a vf of 1.0 or 0.0 for all materials +//-------------------------------------------------------------------------------- - int temp_numVerts[intermediateMesh.elems.size()]; // Store the number of vertices generated for each cell - int temp_numElements[intermediateMesh.elems.size()]; // Store the number of elements generated for each cell +/// Generate the vertex position data for the new elements resulting from spltting a quad. +void InterfaceReconstructor::generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +{ + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets - std::vector > temp_volumeFractionsVertex[intermediateMesh.elems.size()]; + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 6) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + if (vID == 7) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_cellData.vertexPositions.push_back(itr->second); + } +} - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) - { - computeClippingPoints(eID, matOne, matTwo, &intermediateMesh, - temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], - materialInCell[eID], &(temp_numVerts[eID]), &(temp_numElements[eID]), temp_volumeFractionsVertex[eID]); - } +//-------------------------------------------------------------------------------- - // Copy the generated cell information into CellData structures - std::vector cellSplitInfo; - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) +/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. +void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +{ + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { - CellData nextCellSplitInfo(temp_numVerts[eID], temp_numElements[eID], temp_evInds[eID], temp_evBegins[eID], temp_veInds[eID], temp_veBegins[eID], temp_vertexPositions[eID], temp_volumeFractionsVertex[eID]); - cellSplitInfo.push_back(nextCellSplitInfo); + int vID = itr->first; + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + if (vID == 1) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + if (vID == 4) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 6) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + if (vID == 7) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + } } +} - // Merge each of the cells into the first CellData struct - for (unsigned int eID = 1; eID < cellSplitInfo.size(); ++eID) - cellSplitInfo[0].mergeCell(cellSplitInfo[eID]); - - mir::VertSet combined_verts(cellSplitInfo[0].numVerts); - mir::ElemSet combined_elems(cellSplitInfo[0].numElems); - - // TODO (later): Calculate the element volume fractions (note, they should all be either 0 or 1, since after the splits every cell should be clean) - - // Create the final, processed mesh - mir::MIRMesh processedMesh; - processedMesh.InitializeMesh(cellSplitInfo[0].evInds, cellSplitInfo[0].evBegins, cellSplitInfo[0].veInds, cellSplitInfo[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); - processedMesh.constructMeshRelations(); - processedMesh.constructMeshVolumeFractionsVertex(cellSplitInfo[0].vertexVolumeFractions); - processedMesh.constructVertexPositionMap(cellSplitInfo[0].vertexPositions.data()); - - // Store the current mesh to be passed into the next iteration - finalMesh = processedMesh; - } - - // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon +//-------------------------------------------------------------------------------- - return finalMesh; +void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + printf("Triangle clipping case not yet implemented.\n"); } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index ddb37b205a..7f620f4405 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -30,34 +30,30 @@ namespace mir mir::MIRMesh computeReconstructedInterface(); - - // std::vector computeVolumeFractionAverages(mir::MIRMesh* tempMesh); - private: - mir::MIRMesh* mesh; - - public: - std::vector materialVolumeFractionsVertex; // volume fractions for each material for each vertex + mir::MIRMesh* originalMesh; private: - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); - - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& _evInds, std::vector& _evBegins, std::vector& _veInds, std::vector& _veBegins, - std::vector& _vertexPositions, std::vector& _materialInCell, int* _numVerts, int* _numElements, std::vector >& _newVolumeFractionsAtVerts); - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, - std::vector& out_evInds, std::vector& out_evBegins, std::vector& out_veInds, std::vector& out_veBegins, - std::vector& out_vertexPositions, std::vector& out_materialInCell, int* out_numVerts, int* out_numElements, std::vector >& out_newVolumeFractionsAtVerts); + // general clipping function + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + + // triangle clipping function + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + // quad clipping functions + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + + // quad clipping interpolation functions mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); - void mergeCellSplitData(); - + // quad clipping points helper functions + unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); + void generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData); + void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 89da4fc522..11dcc49e84 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -16,10 +16,10 @@ namespace mir MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor { - evInds = _mesh->evInds; - evBegins = _mesh->evBegins; - veInds = _mesh->veInds; - veBegins = _mesh->veBegins; + data.evInds = _mesh->data.evInds; + data.evBegins = _mesh->data.evBegins; + data.veInds = _mesh->data.veInds; + data.veBegins = _mesh->data.veBegins; verts = _mesh->verts; elems = _mesh->elems; bdry = _mesh->bdry; @@ -27,6 +27,8 @@ namespace mir vertexPositions = _mesh->vertexPositions; materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + elementParentIDs = _mesh->elementParentIDs; + elementDominantMaterials = _mesh->elementDominantMaterials; numMaterials = _mesh->numMaterials; } @@ -38,10 +40,10 @@ namespace mir /// Initializes a mesh with the given topology. void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) { - evInds = _evInds; - evBegins = _evBegins; - veInds = _veInds; - veBegins = _veBegins; + data.evInds = _evInds; + data.evBegins = _evBegins; + data.veInds = _veInds; + data.veBegins = _veBegins; verts = _verts; elems = _elems; numMaterials = _numMaterials; @@ -64,10 +66,10 @@ namespace mir .toSet( &verts ) .begins( RelationBuilder::BeginsSetBuilder() .size( elems.size() ) - .data( evBegins.data() ) ) + .data( data.evBegins.data() ) ) .indices ( RelationBuilder::IndicesSetBuilder() - .size( evInds.size() ) - .data( evInds.data() ) ); + .size( data.evInds.size() ) + .data( data.evInds.data() ) ); } @@ -80,10 +82,10 @@ namespace mir .toSet( &elems ) .begins( RelationBuilder::BeginsSetBuilder() .size( verts.size() ) - .data( veBegins.data() ) ) + .data( data.veBegins.data() ) ) .indices( RelationBuilder::IndicesSetBuilder() - .size( veInds.size() ) - .data( veInds.data() ) ); + .size( data.veInds.size() ) + .data( data.veInds.data() ) ); // _quadmesh_example_construct_cobdry_relation_end } @@ -175,6 +177,36 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector dominantMaterials) + { + // Initialize the map for the elements' dominant colors + elementDominantMaterials = IntMap( &elems ); + + // Copy the dat for the elements + for (int eID = 0; eID < elems.size(); ++eID) + elementDominantMaterials[eID] = dominantMaterials[eID]; + + SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); + } + //-------------------------------------------------------------------------------- /// Prints out the map values for each element @@ -203,56 +235,91 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector // for definition of M_PI, exp() @@ -21,12 +22,14 @@ namespace numerics = axom::numerics; namespace slam = axom::slam; - //-------------------------------------------------------------------------------- namespace axom { namespace mir { + + #define NULL_MAT -1 + struct EdgeClipInfo { int vertexOne; @@ -54,26 +57,21 @@ namespace mir void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent + void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex void print(); // Print out a variety of useful information about the mesh - void readMeshFromFile(); /// Reads in and constructs a mesh from a file + void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh /**************************************************************** * VARIABLES ****************************************************************/ - public: - // support data for mesh connectivity - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - public: // Mesh Set Definitions VertSet verts; // the set of vertices in the mesh @@ -82,16 +80,23 @@ namespace mir public: // Mesh Relation Definitions ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements + VertToElemRelation cobdry; // Coboundary relation from vertices to elements public: // Mesh Map Definitions - PointMap vertexPositions; // vertex position + PointMap vertexPositions; // vertex position for each vertex std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex public: int numMaterials; + + public: + IntMap elementParentIDs; // the ID of the parent element from the original mesh + IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + + public: + CellData data; // Contains mesh connectivity data, volume fraction data, vertex position data }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index d90ecbeb35..e861385932 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -68,7 +68,7 @@ namespace mir using BaseSet = slam::Set< PosType, ElemType >; using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. using PointMap = slam::Map< BaseSet, Point2 >; - + using IntMap = slam::Map< BaseSet, int >; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 23bfc09769..ae04cbf93f 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -102,18 +102,24 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) points[15] = mir::Point2( 3.0, 0.0 ); } + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + // Build the mesh mir::MIRMesh testMesh; testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); testMesh.constructMeshRelations(); testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); // Begin material interface reconstruction mir::InterfaceReconstructor reconstructor(&testMesh); mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); processedMesh.print(); - // processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/markoTestMesh.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); return 0; } From 9314f5432c36044ef9171a966a9aa106657248bf Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 14 Jun 2019 08:48:20 -0700 Subject: [PATCH 028/290] Implemented triangle clipping functionality. --- src/axom/mir/InterfaceReconstructor.cpp | 545 ++++++++++++------ src/axom/mir/InterfaceReconstructor.hpp | 7 +- src/axom/mir/MIRMesh.cpp | 2 +- src/axom/mir/MIRMeshTypes.hpp | 2 +- src/axom/mir/ZooBitMaps.cpp | 27 +- src/axom/mir/ZooBitMaps.hpp | 1 + src/axom/mir/examples/mir_tutorial_simple.cpp | 208 ++++++- 7 files changed, 619 insertions(+), 173 deletions(-) diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 0160709fde..2984a870d2 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -12,27 +12,27 @@ namespace mir //-------------------------------------------------------------------------------- - /// Default constructor - InterfaceReconstructor::InterfaceReconstructor() - { - - } +/// Default constructor +InterfaceReconstructor::InterfaceReconstructor() +{ + +} //-------------------------------------------------------------------------------- - /// Constructor - InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) - { - originalMesh = _mesh; - } +/// Constructor +InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) +{ + originalMesh = _mesh; +} //-------------------------------------------------------------------------------- - /// Destructor - InterfaceReconstructor::~InterfaceReconstructor() - { +/// Destructor +InterfaceReconstructor::~InterfaceReconstructor() +{ - } +} //-------------------------------------------------------------------------------- @@ -81,9 +81,14 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; - finalMesh.constructMeshRelations(); // TODO: Figure out why this is necessary here... + finalMesh.constructMeshRelations(); printf("-------------Finished processing material-------------\n"); + + // Write out the intermediate meshes that are processed + std::string outFilename = "/Users/sterbentz3/Desktop/intermediateMesh" + std::to_string(matID) + ".vtk"; + finalMesh.writeMeshToFile(outFilename); + // finalMesh.print(); } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon @@ -93,168 +98,168 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() //-------------------------------------------------------------------------------- - /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. - void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) - { - // Find the vertices associated with each element - auto elementVertices = tempMesh->bdry[eID]; +/// Computes the points where the element should be clipped based on the volume fractions of mat one and two. +void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + // Find the vertices associated with each element + auto elementVertices = tempMesh->bdry[eID]; - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } + // Triangle Case + if (elementVertices.size() == 3) + { + computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + } + // Quad Case + if (elementVertices.size() == 4) + { + computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); } +} //-------------------------------------------------------------------------------- - void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +{ + printf(" Processing QUAD element %d: ", eID); + + // Determine the clipping case + auto elementVertices = tempMesh->bdry[eID]; + + int upperLeftVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + int upperRightVertex = elementVertices[3]; + + unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); + printf("caseIndex: %d\n", caseIndex); + + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + int verticesPresent[8] = {0,0,0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[8] = {0,0,0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = quadClipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) { - printf(" Processing element %d: ", eID); - - // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; - - int upperLeftVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; - int upperRightVertex = elementVertices[3]; - - unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - printf("caseIndex: %d\n", caseIndex); + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = quadClipTable[caseIndex][i + (j+1)]; - // Generate new elements - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[8]; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[8]; // Array of t values that denote the percent value of where the edge should be clipped + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = quadClipTable[caseIndex][i]; - - // for each new element in the current clipping case - while (numVertices != -1) - { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) + // Find t using linear interpolation for any vertex that is not one of the original 4 vertices + if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 6) { - // Find the id of the next vertex of the new element - int vID = quadClipTable[caseIndex][i + (j+1)]; - - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; - - // Find t using linear interpolation for any vertex that is not one of the original 4 vertices - if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 6) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 7) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); - } + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); } + else if(vID == 7) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); + } + } - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = quadClipTable[caseIndex][i]; - } + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = quadClipTable[caseIndex][i]; + } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); - generateTopologyDataFromQuad(newElements, newVertices, out_cellData); - generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateTopologyData(newElements, newVertices, out_cellData); + generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); + generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + // Determine and store the dominant material of this element + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) { - int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + // int temp_vID = newElements[currentElementIndex][it]; + int temp_vID = itr->second[it]; + + // Find the vertex that is one of the four original vertices of the quad element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) { - // int temp_vID = newElements[currentElementIndex][it]; - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the four original vertices of the quad element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); + } - // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; - } + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + } - // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) - { - // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; - else - out_cellData.elementDominantMaterials[itr->first] = matOneID; - } - } - - // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present - int evIndexSubtract[8]; - for (int i = 0; i < 8; ++i) + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { - if (verticesPresent[i] == 1) - evIndexSubtract[i] = 0; + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; else - evIndexSubtract[i] = 1; + out_cellData.elementDominantMaterials[itr->first] = matOneID; } - for (int i = 1; i < 8; ++i) - evIndexSubtract[i] += evIndexSubtract[i - 1]; + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + int evIndexSubtract[8]; + for (int i = 0; i < 8; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + for (int i = 1; i < 8; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) - { - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; - } + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + { + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; } +} //-------------------------------------------------------------------------------- @@ -319,16 +324,10 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (matOneID == NULL_MAT) - { - vfMatOneVertexOne = 0.0; - vfMatOneVertexTwo = 0.0; - } + vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; if (matTwoID == NULL_MAT) - { - vfMatTwoVertexOne = 0.0; - vfMatTwoVertexTwo = 0.0; - } + vfMatTwoVertexOne = vfMatTwoVertexTwo = 0.0; axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; @@ -345,7 +344,7 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte if (ret > 1.0 || ret < 0.0) { // This shouldn't happen... - printf("OUT OF BOUNDS T VALUE: %f\n", ret); + printf(" OUT OF BOUNDS T VALUE: %f\n", ret); // Clamp the t value ret = fmin(1.0, ret); @@ -357,8 +356,8 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte //-------------------------------------------------------------------------------- -/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from splitting a quad. -void InterfaceReconstructor::generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData) +/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from a split. +void InterfaceReconstructor::generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData) { // Store the evInds and evBegins data in the output vectors int currentEVBeginIndex = 0; @@ -471,10 +470,232 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapbdry[eID]; + + int upperVertex = elementVertices[0]; + int lowerLeftVertex = elementVertices[1]; + int lowerRightVertex = elementVertices[2]; + + unsigned int caseIndex = determineTriangleClippingCase(eID, tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); + printf("caseIndex: %d\n", caseIndex); + + // Generate new elements + std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + int verticesPresent[6] = {0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not + axom::float64 verticesClippingTValue[6] = {0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = triangleClipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = triangleClipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + verticesPresent[vID] = 1; + + // Find t using linear interpolation for any vertex that is not one of the original 3 vertices + if(vID == 3) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(upperVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 4) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); + } + else if(vID == 5) + { + verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperVertex, matOneID, matTwoID, tempMesh); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = triangleClipTable[caseIndex][i]; + } + + /**************************************************************** + * CALCULATE THE NEW CELLS' DATA + ****************************************************************/ + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.numElems = (int) newElements.size(); + out_cellData.numVerts = (int) newVertices.size(); + + generateTopologyData(newElements, newVertices, out_cellData); + generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + generateVertexVolumeFractionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + + // Determine and store the dominant material of this element + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int currentDominantMat = NULL_MAT; + for (int it = 0; it < itr->second.size(); ++it) + { + int temp_vID = itr->second[it]; + + // Find the vertex that is one of the three original vertices of the triangle element + if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2) + { + axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; + if (matOneID != NULL_MAT) + matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + if (matTwoID != NULL_MAT) + matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + + currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; + } + } + out_cellData.elementDominantMaterials.push_back(currentDominantMat); + } + + // Determine and store the parent of this element + out_cellData.elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + } + + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int parentElementID = out_cellData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + + if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + { + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOneID) + out_cellData.elementDominantMaterials[itr->first] = matTwoID; + else + out_cellData.elementDominantMaterials[itr->first] = matOneID; + } + } + + // Modify the evIndex values to account for the fact that perhaps not all 6 possible vertices are present + int evIndexSubtract[6]; + for (int i = 0; i < 6; ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; + else + evIndexSubtract[i] = 1; + } + + for (int i = 1; i < 6; ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; + + for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + } //-------------------------------------------------------------------------------- +/// Finds the bit map representing the clipping case for a triangle. +unsigned int InterfaceReconstructor::determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +{ + // Determine the dominant color at each vertex + int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; + + if (matOneID == NULL_MAT) + upperColor = lowerLeftColor = lowerRightColor = matTwoID; + if (matTwoID == NULL_MAT) + upperColor = lowerLeftColor = lowerRightColor = matOneID; + if (matOneID != NULL_MAT && matTwoID != NULL_MAT) + { + upperColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + } + + // Create the index into the quad clipping lookup table using the dominant colors at each vertex + unsigned int caseIndex = 0; + if (upperColor == matOneID) caseIndex |= 4; + if (lowerLeftColor == matOneID) caseIndex |= 2; + if (lowerRightColor == matOneID) caseIndex |= 1; + + return caseIndex; } + +//-------------------------------------------------------------------------------- + +/// Generate the vertex position data for the new elements resulting from spltting a quad. +void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +{ + std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + + // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + if (vID == 0) + newPoints[vID] = tempMesh->vertexPositions[upperVertex]; + if (vID == 1) + newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + if (vID == 2) + newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + if (vID == 3) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + if (vID == 4) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + if (vID == 5) + newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperVertex], verticesClippingTValue[vID]); + } + + // Store the positions of the vertices in the return vector + for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) + { + out_cellData.vertexPositions.push_back(itr->second); + } +} + +//-------------------------------------------------------------------------------- + +/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. +void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +{ + // Calculate the vertex fractions at each vertex (use t value!) + // Make sure the output volume fractions containers are the proper size + out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + { + if (vID == 0) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + if (vID == 1) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + if (vID == 2) + out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + if (vID == 3) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + if (vID == 4) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + if (vID == 5) + out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + } + } +} + +//-------------------------------------------------------------------------------- + } +} \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 7f620f4405..b5e80817db 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -51,9 +51,14 @@ namespace mir // quad clipping points helper functions unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateTopologyDataFromQuad(std::map > newElements, std::map > newVertices, CellData& out_cellData); + void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + + // triangle clipping points helper functions + unsigned int determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); + void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); + void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 11dcc49e84..a9036ba54e 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -276,7 +276,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. + using ScalarMap = slam::Map< BaseSet, axom::float64 >; using PointMap = slam::Map< BaseSet, Point2 >; using IntMap = slam::Map< BaseSet, int >; } diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooBitMaps.cpp index d2adf217a3..3904fd525c 100644 --- a/src/axom/mir/ZooBitMaps.cpp +++ b/src/axom/mir/ZooBitMaps.cpp @@ -30,18 +30,41 @@ namespace mir {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {3,4,1,5,4,0,4,5,2,3,7,6,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} }; + + + // Triangle Vertex Indices + // 0 + // @ + // / \ + // / \ + // 3 @ @ 5 + // / \ + // / \ + // 1 @-----@-----@ 2 + // 4 + const int triangleClipTable[8][10] = + { + {3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {3,0,1,2,-1,-1,-1,-1,-1,-1} + }; } } \ No newline at end of file diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooBitMaps.hpp index 09f76ede1e..e765ced325 100644 --- a/src/axom/mir/ZooBitMaps.hpp +++ b/src/axom/mir/ZooBitMaps.hpp @@ -12,6 +12,7 @@ namespace axom namespace mir { extern const int quadClipTable[16][19]; + extern const int triangleClipTable[8][10]; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ae04cbf93f..ab1b17b252 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -16,12 +16,38 @@ namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; +// function templates +mir::MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials +mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials +mir::MIRMesh initTestCaseThree(); // triforce, 2 materials + //-------------------------------------------------------------------------------- /*! * \brief Tutorial main */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +{ + // Intialize a mesh for testing MIR + // mir::MIRMesh testMesh = initTestCaseOne(); + mir::MIRMesh testMesh = initTestCaseTwo(); + // mir::MIRMesh testMesh = initTestCaseThree(); + + // Begin material interface reconstruction + mir::InterfaceReconstructor reconstructor(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); + + // Output results + processedMesh.print(); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + + return 0; +} + +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 1 (from Meredith 2004) +mir::MIRMesh initTestCaseOne() { int numElements = 9; int numVertices = 16; @@ -115,13 +141,183 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) testMesh.constructElementParentMap(elementParents); testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - // Begin material interface reconstruction - mir::InterfaceReconstructor reconstructor(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); - processedMesh.print(); - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + return testMesh; +} - return 0; +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) +mir::MIRMesh initTestCaseTwo() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 3; + enum { BLUE = 2, RED = 0, ORANGE = 1 }; // TODO: the order orange, blue red causes bugs... might be fixed now... was due to incorrect clipping table entries + // TODO: orange, blue, red is fine + // TODO: blue, red, orange is fine + // TODO: red, orange, blue is fine + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 blueVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + axom::float64 redVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + materialVolumeFractionsData[RED] = redVolumeFractions; + axom::float64 orangeVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; + materialVolumeFractionsData[ORANGE] = orangeVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. +mir::MIRMesh initTestCaseThree() +{ + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + // Create the mesh connectivity information + std::vector evInds = { + 0,1,2, // elem 0, card 3, start 0 + 1,3,4, // elem 1, card 3, start 3 + 1,4,2, // elem 2, card 3, start 6 + 2,4,5 // elem 3, card 3, start 9, end 12 + }; + + std::vector evBegins = { + 0,3,6,9,12 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1,2, // vert 1, card 3, start 1 + 0,2,3, // vert 2, card 3, start 4 + 1, // vert 3, card 1, start 7 + 1,2,3, // vert 4, card 3, start 8 + 3 // vert 5, card 1, start 11, end 12 + }; + std::vector veBegins = { + 0,1,4,7,8,11,12 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements + + int numMaterials = 2; + enum { BLUE = 0, RED = 1, }; + + std::vector materialVolumeFractionsData; + materialVolumeFractionsData.resize(numMaterials); + axom::float64 blueVolumeFractions[] = {0.0, 0.5, 0.8, 0.5}; + materialVolumeFractionsData[BLUE] = blueVolumeFractions; + axom::float64 redVolumeFractions[] = {1.0, 0.5, 0.2, 0.5}; + materialVolumeFractionsData[RED] = redVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 1.0, 2.0 ); + points[1] = mir::Point2( 0.5, 1.0 ); + points[2] = mir::Point2( 1.5, 1.0 ); + points[3] = mir::Point2( 0.0, 0.0 ); + points[4] = mir::Point2( 1.0, 0.0 ); + points[5] = mir::Point2( 2.0, 0.0 ); + } + + + int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; } //-------------------------------------------------------------------------------- From 91ee83bd8aeb47399d877a3903963d12508ff9eb Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 14 Jun 2019 11:39:51 -0700 Subject: [PATCH 029/290] Implemented a function for calculating the element vertex fractions for the original mesh cells using the generated mesh. --- src/axom/mir/MIRMesh.cpp | 83 +++++++++++++++++++ src/axom/mir/MIRMesh.hpp | 7 ++ src/axom/mir/examples/mir_tutorial_simple.cpp | 7 +- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index a9036ba54e..b851348720 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -380,5 +380,88 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector totalAreaOriginalElements; // the total area of the original elements + std::map newElementAreas; // the area of each of the generated child elements + std::map numChildren; // the number of child elements generated from one of the original elements + + // Compute the total area of each element of the original mesh and also the area of each new element + for (int eID = 0; eID < elems.size(); ++eID) + { + int numVertices = data.evBegins[eID + 1] - data.evBegins[eID]; + if (numVertices == 3) + { + Point2 trianglePoints[3]; + for (int i = 0; i < 3; ++i) + trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); + } + if (numVertices == 4) + { + Point2 trianglePoints[4]; + for (int i = 0; i < 4; ++i) + trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); + } + + totalAreaOriginalElements[ elementParentIDs[eID] ] += newElementAreas[eID]; + numChildren[ elementParentIDs[eID] ] += .4; + } + + // Intialize the element volume fraction vectors + std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction + elementVolumeFractions.resize( numMaterials ); + for (int i = 0; i < elementVolumeFractions.size(); ++i) + elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); + + // Compute the volume fractions for each of the original mesh elements + for (auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) + { + int parentElementID = elementParentIDs[itr->first]; + int materialID = elementDominantMaterials[itr->first]; + elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); + } + + // Print out the results // TODO: Return the values and use them. + printf("elementVolumeFractions: {\n"); + for (int matID = 0; matID < elementVolumeFractions.size(); ++matID) + { + printf("Material %d: {", matID); + for (int eID = 0; eID < elementVolumeFractions[matID].size(); ++eID) + { + printf(" %f,", elementVolumeFractions[matID][eID]); + } + printf("}\n"); + } + printf("}\n"); +} + +//-------------------------------------------------------------------------------- + +/// Computes the area of the triangle defined by the given three vertex positions. Uses Heron's formula. +axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) +{ + axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 + axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 + axom::float64 c = sqrt( ((p0.m_x - p2.m_x) * (p0.m_x - p2.m_x)) + ((p0.m_y - p2.m_y) * (p0.m_y - p2.m_y)) );// the distance from p2 to p0 + + axom::float64 s = (a + b + c) / 2; // the semi-perimeter of the triangle + + return sqrt(s * (s - a) * (s - b) * (s - c)); +} + +//-------------------------------------------------------------------------------- + +/// Computes the area of the quad defined by the given four vertex positions. +/// Note: It is assumed the points are given in consecutive order. +axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) +{ + return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); +} + +//-------------------------------------------------------------------------------- + } } \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 8a16272d9b..2145ce3172 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -69,6 +69,13 @@ namespace mir void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh + + void computeOriginalElementVolumeFractions(); + + private: + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + /**************************************************************** * VARIABLES ****************************************************************/ diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ab1b17b252..b9e0dc9b7b 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -40,6 +40,7 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) // Output results processedMesh.print(); processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); + processedMesh.computeOriginalElementVolumeFractions(); return 0; } @@ -193,12 +194,8 @@ mir::MIRMesh initTestCaseTwo() mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - int numMaterials = 3; - enum { BLUE = 2, RED = 0, ORANGE = 1 }; // TODO: the order orange, blue red causes bugs... might be fixed now... was due to incorrect clipping table entries - // TODO: orange, blue, red is fine - // TODO: blue, red, orange is fine - // TODO: red, orange, blue is fine + enum { BLUE = 2, RED = 0, ORANGE = 1 }; std::vector materialVolumeFractionsData; materialVolumeFractionsData.resize(numMaterials); From 5f8ee35271cfc6b908a1cf32b642637dc4ea70cd Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 18 Jun 2019 15:00:04 -0700 Subject: [PATCH 030/290] Created a mesh tester class and generated addtional test cases. Implemented the iterative mesh interface reconstruction algorithm. --- src/axom/mir/CMakeLists.txt | 6 +- src/axom/mir/CellData.cpp | 18 +- src/axom/mir/CellData.hpp | 32 +- src/axom/mir/InterfaceReconstructor.cpp | 90 +- src/axom/mir/InterfaceReconstructor.hpp | 10 +- src/axom/mir/MIRMesh.cpp | 124 ++- src/axom/mir/MIRMesh.hpp | 17 +- src/axom/mir/MeshTester.cpp | 771 ++++++++++++++++++ src/axom/mir/MeshTester.hpp | 47 ++ .../{ZooBitMaps.cpp => ZooClippingTables.cpp} | 2 +- .../{ZooBitMaps.hpp => ZooClippingTables.hpp} | 4 +- src/axom/mir/examples/CMakeLists.txt | 2 +- .../mir/examples/mirConcentricCircles.cpp | 70 ++ src/axom/mir/examples/mir_tutorial.cpp | 480 ----------- src/axom/mir/examples/mir_tutorial_simple.cpp | 315 +------ 15 files changed, 1056 insertions(+), 932 deletions(-) create mode 100644 src/axom/mir/MeshTester.cpp create mode 100644 src/axom/mir/MeshTester.hpp rename src/axom/mir/{ZooBitMaps.cpp => ZooClippingTables.cpp} (98%) rename src/axom/mir/{ZooBitMaps.hpp => ZooClippingTables.hpp} (83%) create mode 100644 src/axom/mir/examples/mirConcentricCircles.cpp delete mode 100644 src/axom/mir/examples/mir_tutorial.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index a31de3c473..e1740e6ad4 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -18,17 +18,19 @@ axom_component_requires(NAME MIR set(mir_headers MIRMesh.hpp MIRMeshTypes.hpp - ZooBitMaps.hpp + ZooClippingTables.hpp InterfaceReconstructor.hpp CellData.hpp + MeshTester.hpp ) set(mir_sources ../Axom.cpp MIRMesh.cpp InterfaceReconstructor.cpp - ZooBitMaps.cpp + ZooClippingTables.cpp CellData.cpp + MeshTester.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index df4da4d785..d1cad26aef 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -52,49 +52,49 @@ namespace mir int elementIndexOffset = numElems; // Merge the cell topology information - for (auto i = 0; i < cellToMerge.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.evInds.size(); ++i) { evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); } - for (auto i = 1; i < cellToMerge.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.evBegins.size(); ++i) { evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); } - for (auto i = 0; i < cellToMerge.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.veInds.size(); ++i) { veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); } - for (auto i = 1; i < cellToMerge.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.veBegins.size(); ++i) { veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (auto i = 0; i < cellToMerge.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.vertexPositions.size(); ++i) { vertexPositions.push_back(cellToMerge.vertexPositions[i]); } // Merge the vertex volume fractions - for (auto matID = 0; matID < vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < vertexVolumeFractions.size(); ++matID) { - for (auto vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) { vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (auto i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) { elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); } // Merge the elements' parent ids - for (auto i = 0; i < cellToMerge.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.elementParents.size(); ++i) { elementParents.push_back(cellToMerge.elementParents[i]); } diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index d91c9761ec..88edd9f453 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -19,6 +19,24 @@ namespace axom namespace mir { + // Struct for collecting data that specifies a mesh or cell's connectivity/topology. + struct CellTopologyData + { + std::vector evInds; + std::vector evBegins; + std::vector veInds; + std::vector veBegins; + }; + + // Struct for collecting the data that will populate a mesh's map data structures. + struct CellMapData + { + std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + }; + /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData @@ -47,20 +65,6 @@ namespace mir std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap }; - - class CellTopology - { - public: - CellTopology(); - ~CellTopology(); - - public: - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - }; - } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 2984a870d2..529c26ead4 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -20,14 +20,6 @@ InterfaceReconstructor::InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Constructor -InterfaceReconstructor::InterfaceReconstructor(mir::MIRMesh* _mesh) -{ - originalMesh = _mesh; -} - -//-------------------------------------------------------------------------------- - /// Destructor InterfaceReconstructor::~InterfaceReconstructor() { @@ -37,8 +29,11 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- /// Reconstructs the material interface and returns the resulting output mesh. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* inputMesh) { + // Store a pointer to the original mesh + originalMesh = inputMesh; + // Initialize the final mesh to be the same as the input mesh mir::MIRMesh finalMesh(originalMesh); @@ -59,12 +54,11 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() { // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; - printf("Processing materials: %d and %d for Element %d\n", currentDominantMat, matOne, eID); computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct - for (unsigned int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 1; eID < intermediateMesh.elems.size(); ++eID) temp_cellData[0].mergeCell(temp_cellData[eID]); mir::VertSet combined_verts(temp_cellData[0].numVerts); @@ -82,13 +76,6 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; finalMesh.constructMeshRelations(); - - printf("-------------Finished processing material-------------\n"); - - // Write out the intermediate meshes that are processed - std::string outFilename = "/Users/sterbentz3/Desktop/intermediateMesh" + std::to_string(matID) + ".vtk"; - finalMesh.writeMeshToFile(outFilename); - // finalMesh.print(); } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon @@ -96,6 +83,48 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface() return finalMesh; } +/// Reconstructs the material interface using an iterative optimization to improve the volume fractions of the resulting mesh. +/// Based on the Meredith and Childs 2010 paper. +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent) +{ + int numElems = inputMesh->elems.size(); + int numMaterials = inputMesh->numMaterials; + + // Make a copy of the original input mesh + mir::MIRMesh meshToImprove(inputMesh); + + // Calculate the reconstruction on the unmodified, input mesh + mir::MIRMesh resultingMesh = computeReconstructedInterface(&meshToImprove); + + for (int it = 0; it < numIterations; ++it) + { + // Calculate the output element volume fractions of the resulting output mesh + std::vector > resultingElementVF = resultingMesh.computeOriginalElementVolumeFractions(); + + // Initialize the vector to store the improved element volume fractions + std::vector > improvedElementVF; + improvedElementVF.resize(numMaterials); + + // Modify the copy of the original mesh's element volume fractions by percent difference (modify meshToImprove's elementVF) + for (int matID = 0; matID < numMaterials; ++matID) + { + for (int eID = 0; eID < numElems; ++eID) + { + axom::float64 difference = inputMesh->materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; + improvedElementVF[matID].push_back( inputMesh->materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); + } + } + + // Reconstruct the element AND vertex VFs + meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); + + // Calculate the reconstruction on the modified, input mesh + resultingMesh = computeReconstructedInterface(&meshToImprove); + } + + return resultingMesh; +} + //-------------------------------------------------------------------------------- /// Computes the points where the element should be clipped based on the volume fractions of mat one and two. @@ -120,7 +149,7 @@ void InterfaceReconstructor::computeClippingPoints(const int eID, const int matO void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) { - printf(" Processing QUAD element %d: ", eID); + // printf(" Processing QUAD element %d: ", eID); // Determine the clipping case auto elementVertices = tempMesh->bdry[eID]; @@ -130,8 +159,8 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int int lowerRightVertex = elementVertices[2]; int upperRightVertex = elementVertices[3]; - unsigned int caseIndex = determineQuadClippingCase(eID, tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - printf("caseIndex: %d\n", caseIndex); + unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); + // printf("caseIndex: %d\n", caseIndex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -200,7 +229,7 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + for (unsigned long it = 0; it < itr->second.size(); ++it) { // int temp_vID = newElements[currentElementIndex][it]; int temp_vID = itr->second[it]; @@ -264,7 +293,7 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int //-------------------------------------------------------------------------------- /// Finds the bit map representing the clipping case for a quad. -unsigned int InterfaceReconstructor::determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) { // Determine the dominant color at each vertex int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; @@ -331,12 +360,6 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; - - // axom::float64 numerator = originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; - // axom::float64 denominator = -originalMesh->materialVolumeFractionsVertex[matOneID][vertexOneID] - // + originalMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID] - // + originalMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID] - // - originalMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (denominator != 0.0) ret = numerator / denominator; @@ -470,8 +493,6 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapbdry[eID]; @@ -479,8 +500,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int lowerLeftVertex = elementVertices[1]; int lowerRightVertex = elementVertices[2]; - unsigned int caseIndex = determineTriangleClippingCase(eID, tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); - printf("caseIndex: %d\n", caseIndex); + unsigned int caseIndex = determineTriangleClippingCase(tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -545,7 +565,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { int currentDominantMat = NULL_MAT; - for (int it = 0; it < itr->second.size(); ++it) + for (unsigned long it = 0; it < itr->second.size(); ++it) { int temp_vID = itr->second[it]; @@ -608,7 +628,7 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const //-------------------------------------------------------------------------------- /// Finds the bit map representing the clipping case for a triangle. -unsigned int InterfaceReconstructor::determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) { // Determine the dominant color at each vertex int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index b5e80817db..8d0f5d3aa8 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -11,7 +11,7 @@ #include "MIRMesh.hpp" #include "CellData.hpp" -#include "ZooBitMaps.hpp" +#include "ZooClippingTables.hpp" #include namespace numerics = axom::numerics; @@ -25,10 +25,10 @@ namespace mir { public: InterfaceReconstructor(); - InterfaceReconstructor(mir::MIRMesh* _mesh); ~InterfaceReconstructor(); - mir::MIRMesh computeReconstructedInterface(); + mir::MIRMesh computeReconstructedInterface(mir::MIRMesh* inputMesh); + mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent); private: mir::MIRMesh* originalMesh; @@ -50,13 +50,13 @@ namespace mir axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); // quad clipping points helper functions - unsigned int determineQuadClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); + unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); // triangle clipping points helper functions - unsigned int determineTriangleClippingCase(const int eID, mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); + unsigned int determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); }; diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index b851348720..b116550666 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -96,51 +96,55 @@ namespace mir SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- + +/// Construct the and the element and vertex volume fraction data given the element volume fraction data +void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) +{ + // Clear the old maps + materialVolumeFractionsElement.clear(); + materialVolumeFractionsVertex.clear(); - /// Constructs the volume fraction maps on the elements and vertices given element volume fraction data. - void MIRMesh::constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData) + // Initialize the maps for all of the materials with the input volume fraction data for each material + for (int matID = 0; matID < numMaterials; ++matID) { - // Initialize the maps for all of the materials with the input volume fraction data for each material - for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + // Initialize the map for the current material + materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + + // Copy the data for the current material + for (int eID = 0; eID < elems.size(); ++eID) { - // Initialize the map for the current material - materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; + } - // Copy the data for the current material - for (int eID = 0; eID < elems.size(); ++eID) - { - materialVolumeFractionsElement[matID][eID] = materialVolumeFractionsData[matID][eID]; - } + SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + } - SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); - } + // Initialize the maps for all of the vertex volume fractions + for (int matID = 0; matID < numMaterials; ++matID) + { + // Initialize the new map for the volume fractions + materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); - // Initialize the maps for all of the vertex volume fractions - for (int matID = 0; matID < materialVolumeFractionsData.size(); ++matID) + // Calculate the average volume fraction value for the current vertex for the current material + for (int vID = 0; vID < verts.size(); ++vID) { - // Initialize the new map for the volume fractions - materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + // Compute the per vertex volume fractions for the green material + axom::float64 sum = 0; + auto vertexElements = cobdry[vID]; - // Calculate the average volume fraction value for the current vertex for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int i = 0; i < vertexElements.size(); ++i) { - // Compute the per vertex volume fractions for the green material - axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; - - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += materialVolumeFractionsElement[matID][eID]; - } - - materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + auto eID = vertexElements[i]; + sum += materialVolumeFractionsElement[matID][eID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); } + + SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } +} //-------------------------------------------------------------------------------- @@ -209,30 +213,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > MIRMesh::computeOriginalElementVolumeFractions() { std::map totalAreaOriginalElements; // the total area of the original elements std::map newElementAreas; // the area of each of the generated child elements @@ -413,7 +394,7 @@ void MIRMesh::computeOriginalElementVolumeFractions() // Intialize the element volume fraction vectors std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction elementVolumeFractions.resize( numMaterials ); - for (int i = 0; i < elementVolumeFractions.size(); ++i) + for (unsigned long i = 0; i < elementVolumeFractions.size(); ++i) elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); // Compute the volume fractions for each of the original mesh elements @@ -424,18 +405,7 @@ void MIRMesh::computeOriginalElementVolumeFractions() elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); } - // Print out the results // TODO: Return the values and use them. - printf("elementVolumeFractions: {\n"); - for (int matID = 0; matID < elementVolumeFractions.size(); ++matID) - { - printf("Material %d: {", matID); - for (int eID = 0; eID < elementVolumeFractions[matID].size(); ++eID) - { - printf(" %f,", elementVolumeFractions[matID][eID]); - } - printf("}\n"); - } - printf("}\n"); + return elementVolumeFractions; } //-------------------------------------------------------------------------------- @@ -455,7 +425,7 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- /// Computes the area of the quad defined by the given four vertex positions. -/// Note: It is assumed the points are given in consecutive order. +/// Note: It is assumed the points are given in consecutive, counter-clockwise order. axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 2145ce3172..eb66116345 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -30,15 +30,6 @@ namespace mir #define NULL_MAT -1 - struct EdgeClipInfo - { - int vertexOne; - int vertexTwo; - int colorOne; - int colorTwo; - float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. - }; - //-------------------------------------------------------------------------------- class MIRMesh @@ -54,23 +45,19 @@ namespace mir void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionMaps(std::vector materialVolumeFractionsData); /// Constructs the volume fraction maps on the vertices + void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - void printElementScalarMap(ScalarMap& elements, std::string prefix); /// Prints out the map values for each element - void printVertexScalarMap(ScalarMap& vertices, std::string prefix); /// Prints out the map values for each vertex - void print(); // Print out a variety of useful information about the mesh void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file void attachVertexMap(); /// Adds a map of data to the mesh - - void computeOriginalElementVolumeFractions(); + std::vector > computeOriginalElementVolumeFractions(); private: axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp new file mode 100644 index 0000000000..36ed346a7a --- /dev/null +++ b/src/axom/mir/MeshTester.cpp @@ -0,0 +1,771 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "MeshTester.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +MeshTester::MeshTester() +{ + +} + +//-------------------------------------------------------------------------------- + +MeshTester::~MeshTester() +{ + +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 1 (from Meredith 2004) +MIRMesh MeshTester::initTestCaseOne() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + elementVF[GREEN] = greenVolumeFractions; + std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + elementVF[BLUE] = blueVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) +mir::MIRMesh MeshTester::initTestCaseTwo() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + int numMaterials = 3; + enum { BLUE = 0, RED = 1, ORANGE = 2 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector blueVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + elementVF[BLUE] = blueVolumeFractions; + std::vector redVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + elementVF[RED] = redVolumeFractions; + std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; + elementVF[ORANGE] = orangeVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. +mir::MIRMesh MeshTester::initTestCaseThree() +{ + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + // Create the mesh connectivity information + std::vector evInds = { + 0,1,2, // elem 0, card 3, start 0 + 1,3,4, // elem 1, card 3, start 3 + 1,4,2, // elem 2, card 3, start 6 + 2,4,5 // elem 3, card 3, start 9, end 12 + }; + + std::vector evBegins = { + 0,3,6,9,12 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1,2, // vert 1, card 3, start 1 + 0,2,3, // vert 2, card 3, start 4 + 1, // vert 3, card 1, start 7 + 1,2,3, // vert 4, card 3, start 8 + 3 // vert 5, card 1, start 11, end 12 + }; + std::vector veBegins = { + 0,1,4,7,8,11,12 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements + + int numMaterials = 2; + enum { BLUE = 0, RED = 1, }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector blueVolumeFractions = {0.0, 0.5, 0.8, 0.5}; + elementVF[BLUE] = blueVolumeFractions; + std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; + elementVF[RED] = redVolumeFractions; + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 1.0, 2.0 ); + points[1] = mir::Point2( 0.5, 1.0 ); + points[2] = mir::Point2( 1.5, 1.0 ); + points[3] = mir::Point2( 0.0, 0.0 ); + points[4] = mir::Point2( 1.0, 0.0 ); + points[5] = mir::Point2( 2.0, 0.0 ); + } + + + int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Initialize mesh for Test Case 4, a 3x3 grid with a circle of one material in the middle +mir::MIRMesh MeshTester::initTestCaseFour() +{ + int numElements = 9; + int numVertices = 16; + + // Create the mesh connectivity information + std::vector evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + std::vector evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + std::vector veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + std::vector veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + + mir::Point2 points[numVertices]; + { + points[0] = mir::Point2( 0.0, 3.0 ); + points[1] = mir::Point2( 1.0, 3.0 ); + points[2] = mir::Point2( 2.0, 3.0 ); + points[3] = mir::Point2( 3.0, 3.0 ); + + points[4] = mir::Point2( 0.0, 2.0 ); + points[5] = mir::Point2( 1.0, 2.0 ); + points[6] = mir::Point2( 2.0, 2.0 ); + points[7] = mir::Point2( 3.0, 2.0 ); + + points[8] = mir::Point2( 0.0, 1.0 ); + points[9] = mir::Point2( 1.0, 1.0 ); + points[10] = mir::Point2( 2.0, 1.0 ); + points[11] = mir::Point2( 3.0, 1.0 ); + + points[12] = mir::Point2( 0.0, 0.0 ); + points[13] = mir::Point2( 1.0, 0.0 ); + points[14] = mir::Point2( 2.0, 0.0 ); + points[15] = mir::Point2( 3.0, 0.0 ); + } + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions; greenVolumeFractions.resize(numElements); + std::vector blueVolumeFractions; blueVolumeFractions.resize(numElements); + + // Generate the element volume fractions for the circle + mir::Point2 circleCenter(1.5, 1.5); + axom::float64 circleRadius = 1.25; + int gridSize = 1000; + for (int i = 0; i < numElements; ++i) + { + greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(gridSize, circleCenter, circleRadius, points[evInds[i * 4 + 0]], points[evInds[i * 4 + 1]], points[evInds[i * 4 + 2]], points[evInds[i * 4 + 3]]); + blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + } + + elementVF[GREEN] = greenVolumeFractions; + elementVF[BLUE] = blueVolumeFractions; + + int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + + std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(points); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +/// Intializes a uniform grid with a circle of one material surrounded by another material. +mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) +{ + // Generate the mesh topology + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + + int numMaterials = 2; + enum { GREEN = 0, BLUE = 1 }; + + std::vector > elementVF; elementVF.resize(numMaterials); + + std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.numElems); + std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.numElems); + + // Generate the element volume fractions for the circle + int numMonteCarloSamples = 100; + for (int i = 0; i < cellData.numElems; ++i) + { + greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, + cellData.vertexPositions[cellData.evInds[i * 4 + 0]], + cellData.vertexPositions[cellData.evInds[i * 4 + 1]], + cellData.vertexPositions[cellData.evInds[i * 4 + 2]], + cellData.vertexPositions[cellData.evInds[i * 4 + 3]]); + blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + } + + elementVF[GREEN] = greenVolumeFractions; + elementVF[BLUE] = blueVolumeFractions; + + int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.numElems; ++i) + { + elementParents[i] = i; + elementDominantMaterials.push_back(NULL_MAT); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(elementVF); + testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; + +} + +//-------------------------------------------------------------------------------- + +// Assumes the quad vertices are ordered the same as the clipping case. +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +{ + // Check if any of the quad's corners are within the circle + axom::float64 distP0 = distance(q_p0, circle_center); + axom::float64 distP1 = distance(q_p1, circle_center); + axom::float64 distP2 = distance(q_p2, circle_center); + axom::float64 distP3 = distance(q_p3, circle_center); + + if (distP0 < circle_radius && distP1 < circle_radius && distP2 < circle_radius && distP3 < circle_radius) + { + // The entire quad overlaps the circle + return 1.0; + } + else if (distP0 < circle_radius || distP1 < circle_radius || distP2 < circle_radius || distP3 < circle_radius) + { + // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much + axom::float64 delta_x = abs(q_p2.m_x - q_p1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(q_p0.m_y - q_p1.m_y) / (double) (gridSize - 1); + int countOverlap = 0; + for (int y = 0; y < gridSize; ++y) + { + for (int x = 0; x < gridSize; ++x) + { + mir::Point2 samplePoint(delta_x * x + q_p1.m_x, delta_y * y + q_p1.m_y); + if (distance(samplePoint, circle_center) < circle_radius) + ++countOverlap; + } + } + return countOverlap / (double) (gridSize * gridSize); + } + else + { + // None of the quad overlaps the circle + return 0; + } +} + +//-------------------------------------------------------------------------------- + +/// Generates a 2D uniform grid with n x n elements. +mir::CellData MeshTester::generateGrid(int n) +{ + // Generate the topology for a uniform quad mesh with n x n elements automatically + int numElements = n * n; + int numVertices = (n + 1) * (n + 1); + + // Generate the evInds + std::vector evInds; + for (int eID = 0; eID < numElements; ++eID) + { + int row = eID / n; // note the integer division + int vertsPerRow = n + 1; + int elemsPerRow = n; + + evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 0); + evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); + evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 1); + evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 1); + } + + // Generate the evBegins + std::vector evBegins; + evBegins.push_back(0); + for (int i = 0; i < n * n; ++i) + { + evBegins.push_back((i + 1) * 4); + } + + // Generate the veInds + std::map > veInds_data; + std::vector veInds; + for (int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) + { + int currentElementID = evInd_itr / 4; // note the integer division + veInds_data[evInds[evInd_itr]].push_back(currentElementID); + } + + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + // Sort the vector + std::sort(itr->second.begin(), itr->second.end()); + + // Add the elements associated with the current vertex to veInds + for (unsigned long i = 0; i < itr->second.size(); ++i) + veInds.push_back(itr->second[i]); + } + + // Generate the veBegins + std::vector veBegins; + veBegins.push_back(0); + int currentIndexCount = 0; + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + currentIndexCount += itr->second.size(); + veBegins.push_back(currentIndexCount); + } + + // Generate the vertex positions + std::vector points; + for (int y = n; y > -1; --y) + { + for (int x = 0; x < n + 1; ++x) + { + points.push_back(mir::Point2(x, y)); + } + } + + mir::CellData data; + data.numVerts = numVertices; + data.numElems = numElements; + data.evInds = evInds; + data.evBegins = evBegins; + data.veInds = veInds; + data.veBegins = veBegins; + data.vertexPositions = points; + + // // Print out the results + // printf("evInds: { "); + // for (int i = 0; i < evInds.size(); i++) + // { + // printf("%d ", evInds[i]); + // if ((i+1) % 4 == 0 && i != 0) + // printf("\n"); + // } + // printf("}\n"); + + // printf("evBegins: { "); + // for (int i = 0; i < evBegins.size(); i++) + // { + // printf("%d ", evBegins[i]); + // } + // printf("}\n"); + + // printf("veInds: { "); + // for (int i = 0; i < veInds.size(); i++) + // { + // printf("%d ", veInds[i]); + // } + // printf("}\n"); + + // printf("veBegins: { "); + // for (int i = 0; i < veBegins.size(); i++) + // { + // printf("%d ", veBegins[i]); + // } + // printf("}\n"); + + // printf("points: { "); + // for (int i = 0; i < numVertices; ++i) + // { + // printf("{%.2f, %.2f} ", points[i].m_x, points[i].m_y); + // } + // printf("}\n"); + + return data; +} + +//-------------------------------------------------------------------------------- + +/// Calculate the distance between the two given points. +axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) +{ + return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); +} + +//-------------------------------------------------------------------------------- + +/// Multiple materials, multiple concentric circles. +/// Note: Assumes each circle has a unique material. +mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) +{ + + // Generate the mesh topology + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + + // Generate the element volume fractions with concentric circles + int numMaterials = numCircles + 1; + int defaultMaterialID = numMaterials - 1; // default material is always the last index + + mir::Point2 circleCenter(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point + + // Initialize the radii of the circles + std::vector circleRadii; + axom::float64 maxRadius = gridSize / 2.4; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = gridSize / 8; // Note: The choice of divisor is arbitrary + + axom::float64 radiusDelta; + if (numCircles <= 1) + radiusDelta = (maxRadius - minRadius); + else + radiusDelta = (maxRadius - minRadius) / (double) (numCircles - 1); + + for (int i = 0; i < numCircles; ++i) + { + circleRadii.push_back( minRadius + (i * radiusDelta) ); + } + + // Initialize all material volume fractions to 0 + std::vector > materialVolumeFractionsData; + for (int i = 0; i < numMaterials; ++i) + { + std::vector tempVec; + tempVec.resize(cellData.numElems); + materialVolumeFractionsData.push_back(tempVec); + } + + // Use the uniform sampling method to generate volume fractions for each material + for (int eID = 0; eID < cellData.numElems; ++eID) + { + mir::Point2 v0 = cellData.vertexPositions[cellData.evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.vertexPositions[cellData.evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.vertexPositions[cellData.evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.vertexPositions[cellData.evInds[eID * 4 + 3]]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; + + for (int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + } + + axom::float64 delta_x = abs(v2.m_x - v1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(v0.m_y - v1.m_y) / (double) (gridSize - 1); + + for (int y = 0; y < gridSize; ++y) + { + for (int x = 0; x < gridSize; ++x) + { + mir::Point2 samplePoint(delta_x * x + v1.m_x, delta_y * y + v1.m_y); + bool isPointSampled = false; + for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) + { + if (distance(samplePoint, circleCenter) < circleRadii[cID]) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if (!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; + } + } + } + + // Assign the element volume fractions based on the count of the samples in each circle + for (int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + } + } + + int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.numElems; ++i) + { + elementParents[i] = i; + elementDominantMaterials.push_back(NULL_MAT); + } + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); + testMesh.constructMeshRelations(); + testMesh.constructMeshVolumeFractionsMaps(materialVolumeFractionsData); + testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); + testMesh.constructElementParentMap(elementParents); + testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + + return testMesh; + +} + +//-------------------------------------------------------------------------------- + +/// Calculate the number of corners of the quad that are within the circle +int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +{ + // Check if any of the quad's corners are within the circle + axom::float64 distP0 = distance(q_p0, circle_center); + axom::float64 distP1 = distance(q_p1, circle_center); + axom::float64 distP2 = distance(q_p2, circle_center); + axom::float64 distP3 = distance(q_p3, circle_center); + + int numCorners = 0; + + if (distP0 < circle_radius) + numCorners++; + if (distP1 < circle_radius) + numCorners++; + if (distP2 < circle_radius) + numCorners++; + if (distP3 < circle_radius) + numCorners++; + + return numCorners; +} + +//-------------------------------------------------------------------------------- + +} +} diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp new file mode 100644 index 0000000000..b89cfa2b26 --- /dev/null +++ b/src/axom/mir/MeshTester.hpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef __MESH_TESTER_H__ +#define __MESH_TESTER_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" + +#include + +namespace numerics = axom::numerics; +namespace slam = axom::slam; + +namespace axom +{ +namespace mir +{ + class MeshTester + { + public: + MeshTester(); + ~MeshTester(); + + public: + MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials + mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials + mir::MIRMesh initTestCaseThree(); // triforce, 2 materials + mir::MIRMesh initTestCaseFour(); // 3x3, 2 materials, circle of material + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles + + private: + axom::float64 distance(mir::Point2 p0, mir::Point2 p1); + mir::CellData generateGrid(int n); + axom::float64 calculatePercentOverlapMonteCarlo(int n, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + + int circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + }; +} +} + +#endif diff --git a/src/axom/mir/ZooBitMaps.cpp b/src/axom/mir/ZooClippingTables.cpp similarity index 98% rename from src/axom/mir/ZooBitMaps.cpp rename to src/axom/mir/ZooClippingTables.cpp index 3904fd525c..5cb584274c 100644 --- a/src/axom/mir/ZooBitMaps.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "ZooBitMaps.hpp" +#include "ZooClippingTables.hpp" namespace axom { diff --git a/src/axom/mir/ZooBitMaps.hpp b/src/axom/mir/ZooClippingTables.hpp similarity index 83% rename from src/axom/mir/ZooBitMaps.hpp rename to src/axom/mir/ZooClippingTables.hpp index e765ced325..fcdfc20a9e 100644 --- a/src/axom/mir/ZooBitMaps.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef __ZOO_BIT_MAPS_H__ -#define __ZOO_BIT_MAPS_H__ +#ifndef __ZOO_CLIPPING_TABLES_H__ +#define __ZOO_CLIPPING_TABLES_H__ namespace axom diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 605609bf02..b1a647e764 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -4,8 +4,8 @@ # SPDX-License-Identifier: (BSD-3-Clause) set( mir_examples - mir_tutorial.cpp mir_tutorial_simple.cpp + mirConcentricCircles.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mirConcentricCircles.cpp b/src/axom/mir/examples/mirConcentricCircles.cpp new file mode 100644 index 0000000000..b445c859e7 --- /dev/null +++ b/src/axom/mir/examples/mirConcentricCircles.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" // for axom macros +#include "axom/mir.hpp" // for Mir classes & functions +#include "axom/slam.hpp" + +#include +#include + +using Clock = std::chrono::high_resolution_clock; + +// namespace aliases +namespace numerics = axom::numerics; +namespace slam = axom::slam; +namespace mir = axom::mir; + +//-------------------------------------------------------------------------------- + +/*! + * \brief Tutorial main + */ +int main( int argc, char** argv ) +{ + + if (argc != 4) + { + printf("Incorrect number of args. Args are \n"); + return 0; + } + + try + { + // Parse the command line arguments + int gridSize = std::stoi(argv[1]); + int numCircles = std::stoi(argv[2]); + std::string outputFilePath = std::string(argv[3]); + + // Intialize a mesh for testing MIR + auto start_time = Clock::now(); + mir::MeshTester tester; + mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); // grid size, numCircles + auto end_time = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + + // Begin material interface reconstruction + start_time = Clock::now(); + mir::InterfaceReconstructor reconstructor; + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); + end_time = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + + // Output results + processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); + + return 0; + } + catch (std::invalid_argument const &e) + { + printf("Bad input. Arguments are \n"); + return 0; + } + catch (std::out_of_range const &e) + { + printf("Integer overflow. Arguments are \n"); + return 0; + } +} \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial.cpp b/src/axom/mir/examples/mir_tutorial.cpp deleted file mode 100644 index 576da4d49d..0000000000 --- a/src/axom/mir/examples/mir_tutorial.cpp +++ /dev/null @@ -1,480 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions -#include "axom/slam.hpp" - - - -// C/C++ includes -#include // for definition of M_PI, exp() -#include - -// namespace aliases -//namespace mir = axom::mir; -namespace numerics = axom::numerics; -namespace slam = axom::slam; - -#define EPSILON_ZERO = 0.00000000001f; - -//-------------------------------------------------------------------------------- - -/** - * \brief Simple 2D Point class for example - */ -struct Point2 -{ - Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} - - Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} - - Point2& operator=(const Point2& other) - { m_x = other.m_x; m_y = other.m_y; return *this; } - - Point2& operator+=(const Point2& other) - { m_x += other.m_x; m_y += other.m_y; return *this; } - - Point2& operator/=(double val) - { m_x /= val; m_y += val; return *this; } - - double& operator[] (int i) { return (i==0) ? m_x : m_y; } - const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } - - friend std::ostream& operator<<(std::ostream& os, const Point2& pt) - { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } - - double m_x, m_y; -}; - -//-------------------------------------------------------------------------------- - -struct EdgeClipInfo -{ - int vertexOne; - int vertexTwo; - int colorOne; - int colorTwo; - float t; // The percent distance from vertexOne to VertexTwo where the clip occurs. -}; - -//-------------------------------------------------------------------------------- - -struct MIRMesh -{ - /**************************************************************** - * TYPE ALIASES - ****************************************************************/ - // SET TYPE ALIASES - using PosType = slam::DefaultPositionType; - using ElemType = slam::DefaultElementType; - - using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; - - using VertSet = slam::PositionSet< PosType, ElemType >; - using ElemSet = slam::PositionSet< PosType, ElemType >; - - // RELATION TYPE ALIASES - using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; - - // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. - // Note: It is the relation of the elements to the vertices. - using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; - using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; - - // MAP TYPE ALIASES - using BaseSet = slam::Set< PosType, ElemType >; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; // Note: Documentation has a typo in it. - using PointMap = slam::Map< BaseSet, Point2 >; - - enum { GREEN = 0, BLUE = 1 }; - - /**************************************************************** - * MESH FUNCTIONS - ****************************************************************/ - void InitializeMesh() - { - // Create the mesh connectivity information - // data for element-vertex boundary relation - evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - - // data for vertex-element coboundary relation - veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - // Initialize the mesh sets - verts = VertSet(16); // Construct a vertex set with 11 vertices - elems = ElemSet(9); // Construct an element set with 5 elements - - // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); - - printf("Mesh has been initialized.\n"); - } - - /// Constructs the mesh boundary and coboundary relations - void constructMeshRelations() - { - - // construct boundary relation from elements to vertices using variable cardinality - { - using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( evBegins.data() ) ) - .indices ( RelationBuilder::IndicesSetBuilder() - .size( evInds.size() ) - .data( evInds.data() ) ); - } - - - { - // _quadmesh_example_construct_cobdry_relation_start - // construct coboundary relation from vertices to elements - using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( veBegins.data() ) ) - .indices( RelationBuilder::IndicesSetBuilder() - .size( veInds.size() ) - .data( veInds.data() ) ); - // _quadmesh_example_construct_cobdry_relation_end - } - - - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); - - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); - - printf("Finished constructing mesh relations.\n"); - } - - /// Constructs the volume fraction maps on the vertices - void constructMeshVolumeFractionMaps() - { - // TODO: Determine better way to store volume fraction data than 1 map per material? - // Construct the scalar map on the elements for the green material - greenVolumeFractionsElements = ScalarMap( &elems ); - - // Set the green volume fraction for each element - greenVolumeFractionsElements[0] = 1.0; - greenVolumeFractionsElements[1] = 1.0; - greenVolumeFractionsElements[2] = 1.0; - greenVolumeFractionsElements[3] = 1.0; - greenVolumeFractionsElements[4] = 0.5; - greenVolumeFractionsElements[5] = 0.2; - greenVolumeFractionsElements[6] = 0.2; - greenVolumeFractionsElements[7] = 0.0; - greenVolumeFractionsElements[8] = 0.0; - - // Construct the scalar map on the elements for the blue material - blueVolumeFractionsElements = ScalarMap( &elems ); - - blueVolumeFractionsElements[0] = 0.0; - blueVolumeFractionsElements[1] = 0.0; - blueVolumeFractionsElements[2] = 0.0; - blueVolumeFractionsElements[3] = 0.0; - blueVolumeFractionsElements[4] = 0.5; - blueVolumeFractionsElements[5] = 0.8; - blueVolumeFractionsElements[6] = 0.8; - blueVolumeFractionsElements[7] = 1.0; - blueVolumeFractionsElements[8] = 1.0; - - printf("Finished constructing volume fractions for mesh elements.\n"); - } - - /// Constucts the positions map on the vertices - void constructVertexPositionMap() - { - // construct the position map on the vertices - vertexPositions = PointMap( &verts ); - - // first vertex is at origin - vertexPositions[0] = Point2( 0.0, 3.0 ); - vertexPositions[1] = Point2( 1.0, 3.0 ); - vertexPositions[2] = Point2( 2.0, 3.0 ); - vertexPositions[3] = Point2( 3.0, 3.0 ); - - vertexPositions[4] = Point2( 0.0, 2.0 ); - vertexPositions[5] = Point2( 1.0, 2.0 ); - vertexPositions[6] = Point2( 2.0, 2.0 ); - vertexPositions[7] = Point2( 3.0, 2.0 ); - - vertexPositions[8] = Point2( 0.0, 1.0 ); - vertexPositions[9] = Point2( 1.0, 1.0 ); - vertexPositions[10] = Point2( 2.0, 1.0 ); - vertexPositions[11] = Point2( 3.0, 1.0 ); - - vertexPositions[12] = Point2( 0.0, 0.0 ); - vertexPositions[13] = Point2( 1.0, 0.0 ); - vertexPositions[14] = Point2( 2.0, 0.0 ); - vertexPositions[15] = Point2( 3.0, 0.0 ); - - SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); - - SLIC_INFO("-- Vertex positions:"); - for(int vID=0 ; vID < verts.size() ; ++vID) - { - SLIC_INFO("Position of vert " << vID << " is " << vertexPositions[vID]); - } - } - - // Computes the average volume fraction at each vertex of the mesh - void computeVolumeFractionAverages() - { - // Initialize the vertex volume fraction maps - greenVolumeFractionsVertices = ScalarMap( &verts ); - blueVolumeFractionsVertices = ScalarMap( &verts ); - - for (int vID = 0; vID < verts.size(); ++vID) - { - // Compute the per vertex volume fractions for the green material - axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += greenVolumeFractionsElements[eID]; - } - greenVolumeFractionsVertices[vID] = sum / vertexElements.size(); - - // Compute the per vertex volume fractions for the blue material - sum = 0; - for (int i = 0; i < vertexElements.size(); ++i) - { - auto eID = vertexElements[i]; - sum += blueVolumeFractionsElements[eID]; - } - blueVolumeFractionsVertices[vID] = sum / vertexElements.size(); - } - - printf("Finished computing volume fraction averages.\n"); - } - - /// Prints out the map values for each element - void printElementScalarMap(ScalarMap& elements, std::string prefix) - { - std::cout << prefix; - for (int i = 0; i < elems.size(); ++i) - { - printf("Element %d: %f\n", i, elements[i]); - } - } - - /// Prints out the map values for each vertex - void printVertexScalarMap(ScalarMap& vertices, std::string prefix) - { - std::cout << prefix; - for (int i = 0; i < verts.size(); ++i) - { - printf("Vertex %d: %f\n", i, vertices[i]); - } - } - - /// Use bilinear interpolation to compute the points where the given element should be clipped. - /// Returns true if clip should occur, otherwise false. - /// If a clip should occur, the clipping points will be given in p1 and p2. - void computeClippingPoints(const int eID)//, Point2* p1, Point2* p2) - { - // Find the vertices associated with each element - auto elementVertices = bdry[eID]; - - // Check if one of the two currently considered materials is not present at the vertices of the current element - if (blueVolumeFractionsElements[eID] != 0.0 && greenVolumeFractionsElements[eID] != 0.0) - { - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID); - } - } - else - { - printf("No cuts in element %d.\n", eID); - } - } - - /// Computes the points where the given quad element should be clipped - void computeQuadClippingPoints(int eID) - { - auto elementVertices = bdry[eID]; - EdgeClipInfo eci[4]; - - // Initialize the edge clipping info - for (int i = 0; i < 4; ++i) - { - eci[i].vertexOne = elementVertices[ i ]; - eci[i].vertexTwo = elementVertices[ (i + 1) % 4 ]; - - if (blueVolumeFractionsVertices[eci[i].vertexOne] > greenVolumeFractionsVertices[eci[i].vertexOne]) - eci[i].colorOne = BLUE; - else - eci[i].colorOne = GREEN; - - if (blueVolumeFractionsVertices[eci[i].vertexTwo] > greenVolumeFractionsVertices[eci[i].vertexTwo]) - eci[i].colorTwo = BLUE; - else - eci[i].colorTwo = GREEN; - - eci[i].t = -1.0; // default t value means one material dominates the other and no clip should occur on this edge - } - - // Analyze the edge clipping info and determine where to clip the cell - for (int i = 0; i < 4; ++i) - { - if (eci[i].colorOne != eci[i].colorTwo) - { - axom::float64 numerator = blueVolumeFractionsVertices[eci[i].vertexOne] - greenVolumeFractionsVertices[eci[i].vertexOne]; - axom::float64 denominator = -greenVolumeFractionsVertices[eci[i].vertexOne] - + greenVolumeFractionsVertices[eci[i].vertexTwo] - + blueVolumeFractionsVertices[eci[i].vertexOne] - - blueVolumeFractionsVertices[eci[i].vertexTwo]; - - if (denominator != 0.0) - eci[i].t = numerator / denominator; - } - } - - // Print out the cutting information - bool cut = false; - for (int i = 0; i < 4; ++i) - { - if (eci[i].t > 0.0) - { - printf("Cutting element %d between vertices %d and %d. t = %f\n", eID, eci[i].vertexOne, eci[i].vertexTwo, eci[i].t); - cut = true; - } - } - if (!cut) - printf("No cuts in element %d.\n", eID); - - } - - /// Computes the points where the given triangle element should be clipped - void computeTriangleClippingPoints(int eID) - { - printf("Triangle clipping case not yet implemented.\n"); - } - - /**************************************************************** - * VARIABLES - ****************************************************************/ - public: - // Mesh Set Definitions - VertSet verts; // the set of vertices in the mesh - ElemSet elems; // the set of elements in the mesh - - // Mesh Relation Definitions - ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements - - // Mesh Map Definitions - PointMap vertexPositions; // vertex position - ScalarMap greenVolumeFractionsElements; // the volume fractions of the green material for each element (NOT each vertex) - ScalarMap blueVolumeFractionsElements; // the volume fractions of the blue material for each element (NOT each vertex) - - ScalarMap greenVolumeFractionsVertices; - ScalarMap blueVolumeFractionsVertices; - - // support data for mesh connectivity - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; -}; - - -//-------------------------------------------------------------------------------- - - -/*! - * \file - * - * \brief Various code snippets/examples used for the Mir tutorial section. - * - * \note These examples are designed to illustrate specific Mir - * concepts and capabilities. Consult the Tutorial section of Mir's - * User Guide for more details. - * - */ - - -/*! - * \brief Tutorial main - */ -int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) -{ - printf("Beginning the MIR Tutorial program.\n"); - - MIRMesh testMesh; - testMesh.InitializeMesh(); - testMesh.constructMeshRelations(); - // testMesh.constructMeshPositionMap(); - testMesh.constructMeshVolumeFractionMaps(); - testMesh.computeVolumeFractionAverages(); - - // Print volume fraction debugging information - // testMesh.printElementScalarMap(testMesh.greenVolumeFractionsElements, "Green Volume Fractions per Element: \n"); - // testMesh.printElementScalarMap(testMesh.blueVolumeFractionsElements, "Blue Volume Fractions per Element: \n"); - - // testMesh.printVertexScalarMap(testMesh.greenVolumeFractionsVertices, "Green Volume Fractions per Vertex: \n"); - // testMesh.printVertexScalarMap(testMesh.blueVolumeFractionsVertices, "Blue Volume Fractions per Vertex: \n"); - - // Determine where to clip the cells - for (int i = 0; i < 9; ++i) - testMesh.computeClippingPoints(i); - - - return 0; -} - -//-------------------------------------------------------------------------------- \ No newline at end of file diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index b9e0dc9b7b..9feba3ec87 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -4,23 +4,17 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions +#include "axom/mir.hpp" // for Mir classes & functions #include "axom/slam.hpp" -#include "../MIRMesh.hpp" -#include "../InterfaceReconstructor.hpp" +#include +using Clock = std::chrono::high_resolution_clock; // namespace aliases -//namespace mir = axom::mir; namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; -// function templates -mir::MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials -mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials -mir::MIRMesh initTestCaseThree(); // triforce, 2 materials - //-------------------------------------------------------------------------------- /*! @@ -28,294 +22,33 @@ mir::MIRMesh initTestCaseThree(); // triforce, 2 materials */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { + // Intialize a mesh for testing MIR - // mir::MIRMesh testMesh = initTestCaseOne(); - mir::MIRMesh testMesh = initTestCaseTwo(); - // mir::MIRMesh testMesh = initTestCaseThree(); - - // Begin material interface reconstruction - mir::InterfaceReconstructor reconstructor(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(); - - // Output results - processedMesh.print(); - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedTestMesh.vtk"); - processedMesh.computeOriginalElementVolumeFractions(); - - return 0; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 1 (from Meredith 2004) -mir::MIRMesh initTestCaseOne() -{ - int numElements = 9; - int numVertices = 16; - - // Create the mesh connectivity information - std::vector evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - std::vector evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - std::vector veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - + auto start_time = Clock::now(); + mir::MeshTester tester; + // mir::MIRMesh testMesh = tester.initTestCaseOne(); + mir::MIRMesh testMesh = tester.initTestCaseTwo(); + // mir::MIRMesh testMesh = tester.initTestCaseThree(); + // mir::MIRMesh testMesh = tester.initTestCaseFour(); + // mir::MIRMesh testMesh = tester.initTestCaseFive(20, 5); - int numMaterials = 2; - enum { GREEN = 0, BLUE = 1 }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 greenVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - materialVolumeFractionsData[GREEN] = greenVolumeFractions; - axom::float64 blueVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - - return testMesh; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) -mir::MIRMesh initTestCaseTwo() -{ - int numElements = 9; - int numVertices = 16; - - // Create the mesh connectivity information - std::vector evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - std::vector evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - std::vector veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - - int numMaterials = 3; - enum { BLUE = 2, RED = 0, ORANGE = 1 }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 blueVolumeFractions[] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - axom::float64 redVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; - materialVolumeFractionsData[RED] = redVolumeFractions; - axom::float64 orangeVolumeFractions[] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - materialVolumeFractionsData[ORANGE] = orangeVolumeFractions; - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); - - return testMesh; -} - -//-------------------------------------------------------------------------------- - -// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. -mir::MIRMesh initTestCaseThree() -{ - int numElements = 4; - int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material - - // Create the mesh connectivity information - std::vector evInds = { - 0,1,2, // elem 0, card 3, start 0 - 1,3,4, // elem 1, card 3, start 3 - 1,4,2, // elem 2, card 3, start 6 - 2,4,5 // elem 3, card 3, start 9, end 12 - }; - - std::vector evBegins = { - 0,3,6,9,12 - }; - std::vector veInds = { - 0, // vert 0, card 1, start 0 - 0,1,2, // vert 1, card 3, start 1 - 0,2,3, // vert 2, card 3, start 4 - 1, // vert 3, card 1, start 7 - 1,2,3, // vert 4, card 3, start 8 - 3 // vert 5, card 1, start 11, end 12 - }; - std::vector veBegins = { - 0,1,4,7,8,11,12 - }; - - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements - - int numMaterials = 2; - enum { BLUE = 0, RED = 1, }; - - std::vector materialVolumeFractionsData; - materialVolumeFractionsData.resize(numMaterials); - axom::float64 blueVolumeFractions[] = {0.0, 0.5, 0.8, 0.5}; - materialVolumeFractionsData[BLUE] = blueVolumeFractions; - axom::float64 redVolumeFractions[] = {1.0, 0.5, 0.2, 0.5}; - materialVolumeFractionsData[RED] = redVolumeFractions; - - mir::Point2 points[numVertices]; - { - points[0] = mir::Point2( 1.0, 2.0 ); - points[1] = mir::Point2( 0.5, 1.0 ); - points[2] = mir::Point2( 1.5, 1.0 ); - points[3] = mir::Point2( 0.0, 0.0 ); - points[4] = mir::Point2( 1.0, 0.0 ); - points[5] = mir::Point2( 2.0, 0.0 ); - } + auto end_time = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + // Begin material interface reconstruction + start_time = Clock::now(); - int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mir::InterfaceReconstructor reconstructor; + // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.9); // 5 iterations, 90 percent - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + end_time = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; - // Build the mesh - mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + // Output results + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh2.vtk"); - return testMesh; + return 0; } -//-------------------------------------------------------------------------------- - +//-------------------------------------------------------------------------------- \ No newline at end of file From 1275d1b3b6a81529cee37776d1000ec02581c629 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 18 Jun 2019 17:02:23 -0700 Subject: [PATCH 031/290] Cleaned up the code. Changed CellData to be a wrapper class. --- src/axom/mir/CellData.cpp | 54 +- src/axom/mir/CellData.hpp | 15 +- src/axom/mir/InterfaceReconstructor.cpp | 100 ++-- src/axom/mir/InterfaceReconstructor.hpp | 6 +- src/axom/mir/MIRMesh.cpp | 469 +++++++++--------- src/axom/mir/MIRMesh.hpp | 30 +- src/axom/mir/MeshTester.cpp | 301 +++++------ src/axom/mir/examples/mir_tutorial_simple.cpp | 8 +- 8 files changed, 496 insertions(+), 487 deletions(-) diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index d1cad26aef..d8f35d30e4 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -19,21 +19,6 @@ namespace mir //-------------------------------------------------------------------------------- - CellData::CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, - std::vector _vertexPositions, std::vector > _vertexVolumeFractions) - { - numVerts = _numVerts; - numElems = _numElems; - evInds = _evInds; - evBegins = _evBegins; - veInds = _veInds; - veBegins = _veBegins; - vertexPositions = _vertexPositions; - vertexVolumeFractions = _vertexVolumeFractions; - } - - //-------------------------------------------------------------------------------- - CellData::~CellData() { @@ -45,64 +30,63 @@ namespace mir void CellData::mergeCell(CellData cellToMerge) { // Initialize index offsets - int evBeginsOffset = evInds.size(); - int veBeginsOffset = veInds.size(); + int evBeginsOffset = topology.evInds.size(); + int veBeginsOffset = topology.veInds.size(); int vertexIndexOffset = numVerts; int elementIndexOffset = numElems; // Merge the cell topology information - for (unsigned long i = 0; i < cellToMerge.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.topology.evInds.size(); ++i) { - evInds.push_back(cellToMerge.evInds[i] + vertexIndexOffset); + topology.evInds.push_back(cellToMerge.topology.evInds[i] + vertexIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.topology.evBegins.size(); ++i) { - evBegins.push_back(cellToMerge.evBegins[i] + evBeginsOffset); + topology.evBegins.push_back(cellToMerge.topology.evBegins[i] + evBeginsOffset); } - for (unsigned long i = 0; i < cellToMerge.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.topology.veInds.size(); ++i) { - veInds.push_back(cellToMerge.veInds[i] + elementIndexOffset); + topology.veInds.push_back(cellToMerge.topology.veInds[i] + elementIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.topology.veBegins.size(); ++i) { - veBegins.push_back(cellToMerge.veBegins[i] + veBeginsOffset); + topology.veBegins.push_back(cellToMerge.topology.veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (unsigned long i = 0; i < cellToMerge.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.vertexPositions.size(); ++i) { - vertexPositions.push_back(cellToMerge.vertexPositions[i]); + mapData.vertexPositions.push_back(cellToMerge.mapData.vertexPositions[i]); } // Merge the vertex volume fractions - for (unsigned long matID = 0; matID < vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < mapData.vertexVolumeFractions.size(); ++matID) { - for (unsigned long vID = 0; vID < cellToMerge.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.mapData.vertexVolumeFractions[matID].size(); ++vID) { - vertexVolumeFractions[matID].push_back(cellToMerge.vertexVolumeFractions[matID][vID]); + mapData.vertexVolumeFractions[matID].push_back(cellToMerge.mapData.vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (unsigned long i = 0; i < cellToMerge.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.elementDominantMaterials.size(); ++i) { - elementDominantMaterials.push_back(cellToMerge.elementDominantMaterials[i]); + mapData.elementDominantMaterials.push_back(cellToMerge.mapData.elementDominantMaterials[i]); } // Merge the elements' parent ids - for (unsigned long i = 0; i < cellToMerge.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.mapData.elementParents.size(); ++i) { - elementParents.push_back(cellToMerge.elementParents[i]); + mapData.elementParents.push_back(cellToMerge.mapData.elementParents[i]); } // Merge the total number of verts and elems in the resulting cell numVerts += cellToMerge.numVerts; numElems += cellToMerge.numElems; - } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 88edd9f453..7603582a30 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -41,11 +41,8 @@ namespace mir /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. class CellData { - public: CellData(); - CellData(int _numVerts, int _numElems, std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, - std::vector _vertexPositions, std::vector > _vertexVolumeFractions); ~CellData(); void mergeCell(CellData cellToMerge); @@ -54,16 +51,8 @@ namespace mir int numVerts; int numElems; - // Cell connectivity/topology - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; - - std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap - std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap - std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap - std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + CellTopologyData topology; + CellMapData mapData; }; } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 529c26ead4..6408a1b653 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -66,12 +66,8 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* // Create the final, processed mesh mir::MIRMesh processedMesh; - processedMesh.InitializeMesh(temp_cellData[0].evInds, temp_cellData[0].evBegins, temp_cellData[0].veInds, temp_cellData[0].veBegins, combined_verts, combined_elems, intermediateMesh.numMaterials); - processedMesh.constructMeshRelations(); - processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].vertexVolumeFractions); - processedMesh.constructVertexPositionMap(temp_cellData[0].vertexPositions.data()); - processedMesh.constructElementParentMap(temp_cellData[0].elementParents.data()); - processedMesh.constructElementDominantMaterialMap(temp_cellData[0].elementDominantMaterials); + processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.numMaterials, temp_cellData[0].topology, temp_cellData[0].mapData); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].mapData.vertexVolumeFractions); // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; @@ -239,36 +235,36 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int { axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); + out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); } // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); + out_cellData.mapData.elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.mapData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; } } @@ -284,9 +280,9 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) { - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; } } @@ -387,36 +383,36 @@ void InterfaceReconstructor::generateTopologyData(std::map for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { // Push the start index of the next element - out_cellData.evBegins.push_back(currentEVBeginIndex); + out_cellData.topology.evBegins.push_back(currentEVBeginIndex); // Push the next element's vertices for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) { - out_cellData.evInds.push_back(itr->second[vIndex]); + out_cellData.topology.evInds.push_back(itr->second[vIndex]); ++currentEVBeginIndex; } } // Push the index that occurs after the last vertex - out_cellData.evBegins.push_back(currentEVBeginIndex); + out_cellData.topology.evBegins.push_back(currentEVBeginIndex); // Store the veInds and veBegins data in the output vectors int currentVEBeginIndex = 0; for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { // Push the start index of the vertex's elements - out_cellData.veBegins.push_back(currentVEBeginIndex); + out_cellData.topology.veBegins.push_back(currentVEBeginIndex); // Push the next vertex's elements for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) { - out_cellData.veInds.push_back(itr->second[eIndex]); + out_cellData.topology.veInds.push_back(itr->second[eIndex]); ++currentVEBeginIndex; } } // Push the index that occurs after the last element - out_cellData.veBegins.push_back(currentVEBeginIndex); + out_cellData.topology.veBegins.push_back(currentVEBeginIndex); } //-------------------------------------------------------------------------------- @@ -451,7 +447,7 @@ void InterfaceReconstructor::generateVertexPositionsFromQuad(std::mapsecond); + out_cellData.mapData.vertexPositions.push_back(itr->second); } } @@ -462,7 +458,7 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapnumMaterials); + out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { @@ -470,21 +466,21 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::mapnumMaterials; ++matID) { if (vID == 0) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); if (vID == 1) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); if (vID == 4) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 6) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); if (vID == 7) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); } } } @@ -574,36 +570,36 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const { axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.vertexVolumeFractions[matOneID][temp_vID]; + matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.vertexVolumeFractions[matTwoID][temp_vID]; + matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; } } - out_cellData.elementDominantMaterials.push_back(currentDominantMat); + out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); } // Determine and store the parent of this element - out_cellData.elementParents.resize(newElements.size()); + out_cellData.mapData.elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.mapData.elementParents[itr->first]; + int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; } } @@ -620,8 +616,8 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (int i = 1; i < 6; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.evInds.size(); ++i) - out_cellData.evInds[i] -= evIndexSubtract[ out_cellData.evInds[i] ]; + for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) + out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; } @@ -681,7 +677,7 @@ void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::mapsecond); + out_cellData.mapData.vertexPositions.push_back(itr->second); } } @@ -692,7 +688,7 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map< { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { @@ -700,17 +696,17 @@ void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map< for (int matID = 0; matID < tempMesh->numMaterials; ++matID) { if (vID == 0) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); if (vID == 1) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 4) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); } } } diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 8d0f5d3aa8..cbda3dcb59 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -44,14 +44,16 @@ namespace mir // quad clipping functions void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); - // quad clipping interpolation functions + // clipping interpolation functions mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + // general helper functions + void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); + // quad clipping points helper functions unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index b116550666..9243bda9ad 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -9,93 +9,118 @@ namespace axom { namespace mir { - MIRMesh::MIRMesh() - { - } +//-------------------------------------------------------------------------------- - MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor - { - data.evInds = _mesh->data.evInds; - data.evBegins = _mesh->data.evBegins; - data.veInds = _mesh->data.veInds; - data.veBegins = _mesh->data.veBegins; - verts = _mesh->verts; - elems = _mesh->elems; - bdry = _mesh->bdry; - cobdry = _mesh->cobdry; - vertexPositions = _mesh->vertexPositions; - materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; - materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; - elementParentIDs = _mesh->elementParentIDs; - elementDominantMaterials = _mesh->elementDominantMaterials; - numMaterials = _mesh->numMaterials; - } +MIRMesh::MIRMesh() +{ - MIRMesh::~MIRMesh() - { +} - } +//-------------------------------------------------------------------------------- - /// Initializes a mesh with the given topology. - void MIRMesh::InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials) - { - data.evInds = _evInds; - data.evBegins = _evBegins; - data.veInds = _veInds; - data.veBegins = _veBegins; - verts = _verts; - elems = _elems; - numMaterials = _numMaterials; - - // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); - } +MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor +{ + meshTopology.evInds = _mesh->meshTopology.evInds; + meshTopology.evBegins = _mesh->meshTopology.evBegins; + meshTopology.veInds = _mesh->meshTopology.veInds; + meshTopology.veBegins = _mesh->meshTopology.veBegins; + verts = _mesh->verts; + elems = _mesh->elems; + bdry = _mesh->bdry; + cobdry = _mesh->cobdry; + vertexPositions = _mesh->vertexPositions; + materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; + materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; + elementParentIDs = _mesh->elementParentIDs; + elementDominantMaterials = _mesh->elementDominantMaterials; + numMaterials = _mesh->numMaterials; +} //-------------------------------------------------------------------------------- - /// Constructs the mesh boundary and coboundary relations - void MIRMesh::constructMeshRelations() - { - // construct boundary relation from elements to vertices using variable cardinality - { - using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( data.evBegins.data() ) ) - .indices ( RelationBuilder::IndicesSetBuilder() - .size( data.evInds.size() ) - .data( data.evInds.data() ) ); - } +MIRMesh::~MIRMesh() +{ + +} +//-------------------------------------------------------------------------------- - { - // _quadmesh_example_construct_cobdry_relation_start - // construct coboundary relation from vertices to elements - using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( data.veBegins.data() ) ) - .indices( RelationBuilder::IndicesSetBuilder() - .size( data.veInds.size() ) - .data( data.veInds.data() ) ); - // _quadmesh_example_construct_cobdry_relation_end - } +/// Initialize a mesh with the given topology and data. +void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) +{ + // Initialize the vertex and element sets + verts = _verts; + elems = _elems; + + numMaterials = _numMaterials; + + // Intialize the mesh topology + meshTopology.evInds = _topology.evInds; + meshTopology.evBegins = _topology.evBegins; + meshTopology.veInds = _topology.veInds; + meshTopology.veBegins = _topology.veBegins; + + // Initialize the mesh relations + constructMeshRelations(); + + // Initialize the mesh's data maps + constructVertexPositionMap(_mapData.vertexPositions.data()); + constructElementParentMap(_mapData.elementParents.data()); + constructElementDominantMaterialMap(_mapData.elementDominantMaterials); + + // Initialize the element and vertex volume fraction maps + if (_elementVF.size() > 0) + constructMeshVolumeFractionsMaps(_elementVF); + + // Check validity of the sets + SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); +} + +//-------------------------------------------------------------------------------- + +/// Constructs the mesh boundary and coboundary relations +void MIRMesh::constructMeshRelations() +{ + // construct boundary relation from elements to vertices using variable cardinality + { + using RelationBuilder = ElemToVertRelation::RelationBuilder; + bdry = RelationBuilder() + .fromSet( &elems ) + .toSet( &verts ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( elems.size() ) + .data( meshTopology.evBegins.data() ) ) + .indices ( RelationBuilder::IndicesSetBuilder() + .size( meshTopology.evInds.size() ) + .data( meshTopology.evInds.data() ) ); + } - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + { + // _quadmesh_example_construct_cobdry_relation_start + // construct coboundary relation from vertices to elements + using RelationBuilder = VertToElemRelation::RelationBuilder; + cobdry = RelationBuilder() + .fromSet( &verts ) + .toSet( &elems ) + .begins( RelationBuilder::BeginsSetBuilder() + .size( verts.size() ) + .data( meshTopology.veBegins.data() ) ) + .indices( RelationBuilder::IndicesSetBuilder() + .size( meshTopology.veInds.size() ) + .data( meshTopology.veInds.data() ) ); + // _quadmesh_example_construct_cobdry_relation_end } + SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + + SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); +} + //-------------------------------------------------------------------------------- /// Construct the and the element and vertex volume fraction data given the element volume fraction data @@ -169,196 +194,196 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector dominantMaterials) - { - // Initialize the map for the elements' dominant colors - elementDominantMaterials = IntMap( &elems ); +/// Constructs the elementDominantMaterials map of each element's single most dominant material +void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) +{ + // Initialize the map for the elements' dominant colors + elementDominantMaterials = IntMap( &elems ); - // Copy the dat for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementDominantMaterials[eID] = dominantMaterials[eID]; + // Copy the dat for the elements + for (int eID = 0; eID < elems.size(); ++eID) + elementDominantMaterials[eID] = dominantMaterials[eID]; - SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); - } + SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); +} //-------------------------------------------------------------------------------- - /// Print out the properties of the mesh. - void MIRMesh::print() - { - printf("\n------------------------Printing Mesh Information:------------------------\n"); - printf("number of vertices: %d\n", verts.size()); - printf("number of elements: %d\n", elems.size()); - printf("number of materials: %d\n", numMaterials); +/// Print out the properties of the mesh. +void MIRMesh::print() +{ + printf("\n------------------------Printing Mesh Information:------------------------\n"); + printf("number of vertices: %d\n", verts.size()); + printf("number of elements: %d\n", elems.size()); + printf("number of materials: %d\n", numMaterials); - printf("evInds: { "); - for (unsigned long i = 0; i < data.evInds.size(); i++) - { - printf("%d ", data.evInds[i]); - } - printf("}\n"); + printf("evInds: { "); + for (unsigned long i = 0; i < meshTopology.evInds.size(); i++) + { + printf("%d ", meshTopology.evInds[i]); + } + printf("}\n"); - printf("evBegins: { "); - for (unsigned long i = 0; i < data.evBegins.size(); i++) - { - printf("%d ", data.evBegins[i]); - } - printf("}\n"); + printf("evBegins: { "); + for (unsigned long i = 0; i < meshTopology.evBegins.size(); i++) + { + printf("%d ", meshTopology.evBegins[i]); + } + printf("}\n"); - printf("veInds: { "); - for (unsigned long i = 0; i < data.veInds.size(); i++) - { - printf("%d ", data.veInds[i]); - } - printf("}\n"); + printf("veInds: { "); + for (unsigned long i = 0; i < meshTopology.veInds.size(); i++) + { + printf("%d ", meshTopology.veInds[i]); + } + printf("}\n"); - printf("veBegins: { "); - for (unsigned long i = 0; i < data.veBegins.size(); i++) - { - printf("%d ", data.veBegins[i]); - } - printf("}\n"); + printf("veBegins: { "); + for (unsigned long i = 0; i < meshTopology.veBegins.size(); i++) + { + printf("%d ", meshTopology.veBegins[i]); + } + printf("}\n"); - printf("vertexPositions: { "); - for (int i = 0; i < verts.size(); ++i) // TODO: This was previously vertexPositions.size() and was working... - { - printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); - } - printf("}\n"); + printf("vertexPositions: { "); + for (int i = 0; i < verts.size(); ++i) + { + printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + } + printf("}\n"); - printf("elementParentIDs: { "); - for (int i = 0; i < elems.size(); ++i) - { - printf("%d ", elementParentIDs[i]); - } - printf("}\n"); + printf("elementParentIDs: { "); + for (int i = 0; i < elems.size(); ++i) + { + printf("%d ", elementParentIDs[i]); + } + printf("}\n"); - printf("elementDominantMaterials: { "); - for (int i = 0; i < elems.size(); ++i) - { - printf("%d ", elementDominantMaterials[i]); - } - printf("}\n"); + printf("elementDominantMaterials: { "); + for (int i = 0; i < elems.size(); ++i) + { + printf("%d ", elementDominantMaterials[i]); + } + printf("}\n"); - printf("vertexVolumeFractions: { \n"); - for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + printf("vertexVolumeFractions: { \n"); + for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + { + printf(" { "); + for (int j = 0; j < verts.size(); ++j) { - printf(" { "); - for (int j = 0; j < verts.size(); ++j) - { - printf("%.3f, ", materialVolumeFractionsVertex[i][j]); - } - printf("}\n"); + printf("%.3f, ", materialVolumeFractionsVertex[i][j]); } printf("}\n"); - printf("--------------------------------------------------------------------------\n"); } + printf("}\n"); + printf("--------------------------------------------------------------------------\n"); +} //-------------------------------------------------------------------------------- - /// Reads in and constructs a mesh from the given file - /// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file - void MIRMesh::readMeshFromFile(std::string filename) - { - printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); - - // Read in header +/// Reads in and constructs a mesh from the given file +/// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file +void MIRMesh::readMeshFromFile(std::string filename) +{ + printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); + + // Read in header - // Read in POINTS + // Read in POINTS - // Read in CELLS + // Read in CELLS - // Read in CELL_TYPES + // Read in CELL_TYPES - // Read in CELL_DATA (element volume fractions) - } + // Read in CELL_DATA (element volume fractions) +} //-------------------------------------------------------------------------------- - /// Writes out the mesh to a file - void MIRMesh::writeMeshToFile(std::string filename) - { - std::ofstream meshfile; - meshfile.open(filename); - std::ostream_iterator out_it(meshfile, " "); - - // write header - meshfile << "# vtk DataFile Version 3.0\n" - << "vtk output\n" - << "ASCII\n" - << "DATASET UNSTRUCTURED_GRID\n" - << "POINTS " << verts.size() << " double\n"; - - // write positions - for (int vID = 0; vID < verts.size(); ++vID) - { - meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D - } - - meshfile << "\nCELLS " << elems.size() << " " << data.evInds.size() + elems.size(); - for (int i = 0; i < elems.size(); ++i) - { - int nVerts = data.evBegins[i+1] - data.evBegins[i]; - meshfile << "\n" << nVerts; - for (int j = 0; j < nVerts; ++j) - { - int startIndex = data.evBegins[i]; - meshfile << " " << data.evInds[startIndex + j]; - } - } +/// Writes out the mesh to a file +void MIRMesh::writeMeshToFile(std::string filename) +{ + std::ofstream meshfile; + meshfile.open(filename); + std::ostream_iterator out_it(meshfile, " "); + + // write header + meshfile << "# vtk DataFile Version 3.0\n" + << "vtk output\n" + << "ASCII\n" + << "DATASET UNSTRUCTURED_GRID\n" + << "POINTS " << verts.size() << " double\n"; + + // write positions + for (int vID = 0; vID < verts.size(); ++vID) + { + meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + } - meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; - for (int i = 0; i < elems.size(); ++i) + meshfile << "\nCELLS " << elems.size() << " " << meshTopology.evInds.size() + elems.size(); + for (int i = 0; i < elems.size(); ++i) + { + int nVerts = meshTopology.evBegins[i+1] - meshTopology.evBegins[i]; + meshfile << "\n" << nVerts; + for (int j = 0; j < nVerts; ++j) { - int nVerts = data.evBegins[i + 1] - data.evBegins[i]; - if (nVerts == 3) - meshfile << "5\n"; - else if (nVerts == 4) - meshfile << "9\n"; + int startIndex = meshTopology.evBegins[i]; + meshfile << " " << meshTopology.evInds[startIndex + j]; } + } - // write element materials - meshfile << "\n\nCELL_DATA " << elems.size() - << "\nSCALARS cellIds int 1" - << "\nLOOKUP_TABLE default \n"; - for(int i=0 ; i< elems.size() ; ++i) - { - meshfile << elementDominantMaterials[i] << " "; - } + meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; + for (int i = 0; i < elems.size(); ++i) + { + int nVerts = meshTopology.evBegins[i + 1] - meshTopology.evBegins[i]; + if (nVerts == 3) + meshfile << "5\n"; + else if (nVerts == 4) + meshfile << "9\n"; + } - meshfile <<"\n"; + // write element materials + meshfile << "\n\nCELL_DATA " << elems.size() + << "\nSCALARS cellIds int 1" + << "\nLOOKUP_TABLE default \n"; + for(int i=0 ; i< elems.size() ; ++i) + { + meshfile << elementDominantMaterials[i] << " "; } + meshfile <<"\n"; +} + //-------------------------------------------------------------------------------- /// Computes the volume fractions of the elements of the original mesh, @@ -371,19 +396,19 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr // Compute the total area of each element of the original mesh and also the area of each new element for (int eID = 0; eID < elems.size(); ++eID) { - int numVertices = data.evBegins[eID + 1] - data.evBegins[eID]; + int numVertices = meshTopology.evBegins[eID + 1] - meshTopology.evBegins[eID]; if (numVertices == 3) { Point2 trianglePoints[3]; for (int i = 0; i < 3; ++i) - trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); } if (numVertices == 4) { Point2 trianglePoints[4]; for (int i = 0; i < 4; ++i) - trianglePoints[i] = vertexPositions[data.evInds[ data.evBegins[eID] + i] ]; + trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); } diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index eb66116345..c85c18e936 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -42,24 +42,24 @@ namespace mir MIRMesh(MIRMesh* _mesh); // copy constructor ~MIRMesh(); - void InitializeMesh(std::vector _evInds, std::vector _evBegins, std::vector _veInds, std::vector _veBegins, VertSet _verts, ElemSet _elems, int _numMaterials); + void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); - void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps + void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations + void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map - void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices - void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent - void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - void print(); // Print out a variety of useful information about the mesh + void print(); - void readMeshFromFile(std::string filename); /// Reads in and constructs a mesh from a file - void writeMeshToFile(std::string filename); /// Writes out the current mesh to a file - void attachVertexMap(); /// Adds a map of data to the mesh + void readMeshFromFile(std::string filename); + void writeMeshToFile(std::string filename); std::vector > computeOriginalElementVolumeFractions(); private: + void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices + void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent + void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); @@ -81,16 +81,12 @@ namespace mir PointMap vertexPositions; // vertex position for each vertex std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + IntMap elementParentIDs; // the ID of the parent element from the original mesh + IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) public: int numMaterials; - - public: - IntMap elementParentIDs; // the ID of the parent element from the original mesh - IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) - - public: - CellData data; // Contains mesh connectivity data, volume fraction data, vertex position data + CellTopologyData meshTopology; }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 36ed346a7a..df9d76b7d8 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -84,41 +84,43 @@ MIRMesh MeshTester::initTestCaseOne() std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; elementVF[BLUE] = blueVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -184,42 +186,43 @@ mir::MIRMesh MeshTester::initTestCaseTwo() std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; elementVF[ORANGE] = orangeVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } - - - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -268,29 +271,31 @@ mir::MIRMesh MeshTester::initTestCaseThree() std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; elementVF[RED] = redVolumeFractions; - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 1.0, 2.0 ); - points[1] = mir::Point2( 0.5, 1.0 ); - points[2] = mir::Point2( 1.5, 1.0 ); - points[3] = mir::Point2( 0.0, 0.0 ); - points[4] = mir::Point2( 1.0, 0.0 ); - points[5] = mir::Point2( 2.0, 0.0 ); - } - - - int elementParents[4] = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mir::Point2( 1.0, 2.0 ), + mir::Point2( 0.5, 1.0 ), + mir::Point2( 1.5, 1.0 ), + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ) + }; + + + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -344,28 +349,28 @@ mir::MIRMesh MeshTester::initTestCaseFour() mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - mir::Point2 points[numVertices]; + std::vector points = { - points[0] = mir::Point2( 0.0, 3.0 ); - points[1] = mir::Point2( 1.0, 3.0 ); - points[2] = mir::Point2( 2.0, 3.0 ); - points[3] = mir::Point2( 3.0, 3.0 ); - - points[4] = mir::Point2( 0.0, 2.0 ); - points[5] = mir::Point2( 1.0, 2.0 ); - points[6] = mir::Point2( 2.0, 2.0 ); - points[7] = mir::Point2( 3.0, 2.0 ); - - points[8] = mir::Point2( 0.0, 1.0 ); - points[9] = mir::Point2( 1.0, 1.0 ); - points[10] = mir::Point2( 2.0, 1.0 ); - points[11] = mir::Point2( 3.0, 1.0 ); - - points[12] = mir::Point2( 0.0, 0.0 ); - points[13] = mir::Point2( 1.0, 0.0 ); - points[14] = mir::Point2( 2.0, 0.0 ); - points[15] = mir::Point2( 3.0, 0.0 ); - } + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; @@ -388,18 +393,20 @@ mir::MIRMesh MeshTester::initTestCaseFour() elementVF[GREEN] = greenVolumeFractions; elementVF[BLUE] = blueVolumeFractions; - int elementParents[9] = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + CellTopologyData topology; + topology.evInds = evInds; + topology.evBegins = evBegins; + topology.veInds = veInds; + topology.veBegins = veBegins; - std::vector elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + CellMapData mapData; + mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(evInds, evBegins, veInds, veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(points); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; } @@ -428,35 +435,40 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 for (int i = 0; i < cellData.numElems; ++i) { greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.vertexPositions[cellData.evInds[i * 4 + 0]], - cellData.vertexPositions[cellData.evInds[i * 4 + 1]], - cellData.vertexPositions[cellData.evInds[i * 4 + 2]], - cellData.vertexPositions[cellData.evInds[i * 4 + 3]]); + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 0]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 1]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 2]], + cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 3]]); blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; } elementVF[GREEN] = greenVolumeFractions; elementVF[BLUE] = blueVolumeFractions; - int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; for (int i = 0; i < cellData.numElems; ++i) { - elementParents[i] = i; + elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } + CellTopologyData topology; + topology.evInds = cellData.topology.evInds; + topology.evBegins = cellData.topology.evBegins; + topology.veInds = cellData.topology.veInds; + topology.veBegins = cellData.topology.veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = elementDominantMaterials; + mapData.elementParents = elementParents; + mapData.vertexPositions = cellData.mapData.vertexPositions; + // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(elementVF); - testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); return testMesh; - } //-------------------------------------------------------------------------------- @@ -572,11 +584,11 @@ mir::CellData MeshTester::generateGrid(int n) mir::CellData data; data.numVerts = numVertices; data.numElems = numElements; - data.evInds = evInds; - data.evBegins = evBegins; - data.veInds = veInds; - data.veBegins = veBegins; - data.vertexPositions = points; + data.topology.evInds = evInds; + data.topology.evBegins = evBegins; + data.topology.veInds = veInds; + data.topology.veBegins = veBegins; + data.mapData.vertexPositions = points; // // Print out the results // printf("evInds: { "); @@ -674,10 +686,10 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) // Use the uniform sampling method to generate volume fractions for each material for (int eID = 0; eID < cellData.numElems; ++eID) { - mir::Point2 v0 = cellData.vertexPositions[cellData.evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.vertexPositions[cellData.evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.vertexPositions[cellData.evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.vertexPositions[cellData.evInds[eID * 4 + 3]]; + mir::Point2 v0 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -719,25 +731,30 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } } - int elementParents[cellData.numElems]; // For the base mesh, the parents are always themselves + std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; for (int i = 0; i < cellData.numElems; ++i) { - elementParents[i] = i; + elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } + CellTopologyData topology; + topology.evInds = cellData.topology.evInds; + topology.evBegins = cellData.topology.evBegins; + topology.veInds = cellData.topology.veInds; + topology.veBegins = cellData.topology.veBegins; + + CellMapData mapData; + mapData.elementDominantMaterials = elementDominantMaterials; + mapData.elementParents = elementParents; + mapData.vertexPositions = cellData.mapData.vertexPositions; + // Build the mesh mir::MIRMesh testMesh; - testMesh.InitializeMesh(cellData.evInds, cellData.evBegins, cellData.veInds, cellData.veBegins, verts, elems, numMaterials); - testMesh.constructMeshRelations(); - testMesh.constructMeshVolumeFractionsMaps(materialVolumeFractionsData); - testMesh.constructVertexPositionMap(cellData.vertexPositions.data()); - testMesh.constructElementParentMap(elementParents); - testMesh.constructElementDominantMaterialMap(elementDominantMaterials); + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, materialVolumeFractionsData); return testMesh; - } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 9feba3ec87..d35fe0bfbc 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -27,10 +27,10 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) auto start_time = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); - mir::MIRMesh testMesh = tester.initTestCaseTwo(); + // mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - // mir::MIRMesh testMesh = tester.initTestCaseFive(20, 5); + mir::MIRMesh testMesh = tester.initTestCaseFive(25, 7); auto end_time = Clock::now(); std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; @@ -40,13 +40,13 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) mir::InterfaceReconstructor reconstructor; // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.9); // 5 iterations, 90 percent + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.3); // 5 iterations, 90 percent end_time = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh2.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh5.vtk"); return 0; } From a6892f1d27eff425579d1f6f627d4e193f7ceaf0 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 21 Jun 2019 08:39:09 -0700 Subject: [PATCH 032/290] Cleaned up code. Inserted doxygen comments. --- src/axom/mir/CellData.cpp | 49 ++- src/axom/mir/CellData.hpp | 70 +++- src/axom/mir/InterfaceReconstructor.cpp | 352 +++++++++--------- src/axom/mir/InterfaceReconstructor.hpp | 258 +++++++++++-- src/axom/mir/MIRMesh.cpp | 253 ++++++------- src/axom/mir/MIRMesh.hpp | 177 +++++++-- src/axom/mir/MIRMeshTypes.hpp | 6 + src/axom/mir/MeshTester.cpp | 241 ++++++------ src/axom/mir/MeshTester.hpp | 147 +++++++- src/axom/mir/ZooClippingTables.cpp | 4 +- src/axom/mir/ZooClippingTables.hpp | 17 + src/axom/mir/examples/CMakeLists.txt | 2 +- ...Circles.cpp => mir_concentric_circles.cpp} | 16 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 22 +- src/axom/mir/tests/CMakeLists.txt | 1 + .../mir/tests/mir_interface_reconstructor.cpp | 55 +++ 16 files changed, 1115 insertions(+), 555 deletions(-) rename src/axom/mir/examples/{mirConcentricCircles.cpp => mir_concentric_circles.cpp} (83%) create mode 100644 src/axom/mir/tests/mir_interface_reconstructor.cpp diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index d8f35d30e4..4e37813c86 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -26,67 +26,66 @@ namespace mir //-------------------------------------------------------------------------------- - /// Merges the cell data from the given cell into this cell - void CellData::mergeCell(CellData cellToMerge) + void CellData::mergeCell(const CellData& cellToMerge) { // Initialize index offsets - int evBeginsOffset = topology.evInds.size(); - int veBeginsOffset = topology.veInds.size(); + int evBeginsOffset = m_topology.m_evInds.size(); + int veBeginsOffset = m_topology.m_veInds.size(); - int vertexIndexOffset = numVerts; - int elementIndexOffset = numElems; + int vertexIndexOffset = m_numVerts; + int elementIndexOffset = m_numElems; // Merge the cell topology information - for (unsigned long i = 0; i < cellToMerge.topology.evInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_topology.m_evInds.size(); ++i) { - topology.evInds.push_back(cellToMerge.topology.evInds[i] + vertexIndexOffset); + m_topology.m_evInds.push_back(cellToMerge.m_topology.m_evInds[i] + vertexIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.topology.evBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.m_topology.m_evBegins.size(); ++i) { - topology.evBegins.push_back(cellToMerge.topology.evBegins[i] + evBeginsOffset); + m_topology.m_evBegins.push_back(cellToMerge.m_topology.m_evBegins[i] + evBeginsOffset); } - for (unsigned long i = 0; i < cellToMerge.topology.veInds.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_topology.m_veInds.size(); ++i) { - topology.veInds.push_back(cellToMerge.topology.veInds[i] + elementIndexOffset); + m_topology.m_veInds.push_back(cellToMerge.m_topology.m_veInds[i] + elementIndexOffset); } - for (unsigned long i = 1; i < cellToMerge.topology.veBegins.size(); ++i) + for (unsigned long i = 1; i < cellToMerge.m_topology.m_veBegins.size(); ++i) { - topology.veBegins.push_back(cellToMerge.topology.veBegins[i] + veBeginsOffset); + m_topology.m_veBegins.push_back(cellToMerge.m_topology.m_veBegins[i] + veBeginsOffset); } // Merge the vertex positions - for (unsigned long i = 0; i < cellToMerge.mapData.vertexPositions.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_vertexPositions.size(); ++i) { - mapData.vertexPositions.push_back(cellToMerge.mapData.vertexPositions[i]); + m_mapData.m_vertexPositions.push_back(cellToMerge.m_mapData.m_vertexPositions[i]); } // Merge the vertex volume fractions - for (unsigned long matID = 0; matID < mapData.vertexVolumeFractions.size(); ++matID) + for (unsigned long matID = 0; matID < m_mapData.m_vertexVolumeFractions.size(); ++matID) { - for (unsigned long vID = 0; vID < cellToMerge.mapData.vertexVolumeFractions[matID].size(); ++vID) + for (unsigned long vID = 0; vID < cellToMerge.m_mapData.m_vertexVolumeFractions[matID].size(); ++vID) { - mapData.vertexVolumeFractions[matID].push_back(cellToMerge.mapData.vertexVolumeFractions[matID][vID]); + m_mapData.m_vertexVolumeFractions[matID].push_back(cellToMerge.m_mapData.m_vertexVolumeFractions[matID][vID]); } } // Merge the elements' dominant materials - for (unsigned long i = 0; i < cellToMerge.mapData.elementDominantMaterials.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementDominantMaterials.size(); ++i) { - mapData.elementDominantMaterials.push_back(cellToMerge.mapData.elementDominantMaterials[i]); + m_mapData.m_elementDominantMaterials.push_back(cellToMerge.m_mapData.m_elementDominantMaterials[i]); } // Merge the elements' parent ids - for (unsigned long i = 0; i < cellToMerge.mapData.elementParents.size(); ++i) + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementParents.size(); ++i) { - mapData.elementParents.push_back(cellToMerge.mapData.elementParents[i]); + m_mapData.m_elementParents.push_back(cellToMerge.m_mapData.m_elementParents[i]); } // Merge the total number of verts and elems in the resulting cell - numVerts += cellToMerge.numVerts; - numElems += cellToMerge.numElems; + m_numVerts += cellToMerge.m_numVerts; + m_numElems += cellToMerge.m_numElems; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 7603582a30..1a681e5d19 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file CellData.hpp + * + * \brief Contains the specifications for the CellData class + * and CellTopologyData and CellMapData structs. + */ + #ifndef __CELL_DATA_H__ #define __CELL_DATA_H__ @@ -19,40 +26,69 @@ namespace axom namespace mir { - // Struct for collecting data that specifies a mesh or cell's connectivity/topology. + /** + * \struct CellTopologyData + * + * \brief Struct for collecting data that specifies a mesh or cell's connectivity/topology. + */ struct CellTopologyData { - std::vector evInds; - std::vector evBegins; - std::vector veInds; - std::vector veBegins; + std::vector m_evInds; + std::vector m_evBegins; + std::vector m_veInds; + std::vector m_veBegins; }; - // Struct for collecting the data that will populate a mesh's map data structures. + /** + * \struct CellMapData + * + * \brief Struct for collecting the data that will populate a mesh's map data structures. + */ struct CellMapData { - std::vector vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap - std::vector > vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap - std::vector elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap - std::vector elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + std::vector m_vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector > m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap }; - /// Represents an arbitrary number of cells that are within the same local coordinate system (i.e. share a set of vertices and elements). - /// Intended to be used as a helper class to hold intermediate data while processing a mesh, and to be used as input to the MIRMesh class to fully initialize it. + /** + * \class CellData + * + * \brief The CellData class represents an arbitrary number of cells that are + * within the same local coordinate system (i.e. share a set of vertices + * and elements). + * + * \detail This class is intended to be used as a helper class to hold intermediate + * data while processing a mesh, and to be used as input to the MIRMesh class + * to fully initialize it. + */ class CellData { public: + /** + * \brief Default constructor. + */ CellData(); + + /** + * \brief Default destructor. + */ ~CellData(); - void mergeCell(CellData cellToMerge); + /** + * \brief Merges the cell data from the given cell into this cell. + * + * \param cellToMerge The cell whose data will be taken and merged in. + */ + void mergeCell(const CellData& cellToMerge); public: - int numVerts; - int numElems; + int m_numVerts; + int m_numElems; - CellTopologyData topology; - CellMapData mapData; + CellTopologyData m_topology; + CellMapData m_mapData; }; } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 6408a1b653..9152dbb30d 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -12,7 +12,6 @@ namespace mir //-------------------------------------------------------------------------------- -/// Default constructor InterfaceReconstructor::InterfaceReconstructor() { @@ -20,7 +19,6 @@ InterfaceReconstructor::InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Destructor InterfaceReconstructor::~InterfaceReconstructor() { @@ -28,46 +26,46 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- -/// Reconstructs the material interface and returns the resulting output mesh. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* inputMesh) +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh) { - // Store a pointer to the original mesh - originalMesh = inputMesh; + // Store a reference to the original mesh + m_originalMesh = inputMesh; // Initialize the final mesh to be the same as the input mesh - mir::MIRMesh finalMesh(originalMesh); + mir::MIRMesh finalMesh(m_originalMesh); // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) - for (int matID = 0; matID < originalMesh->numMaterials; ++matID) + for (int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) { // Copy the mesh to be split mir::MIRMesh intermediateMesh(&finalMesh); - // Update the materials upon which the split will occur - int matOne = matID; - // Create an array to store the output of each element being split. - CellData temp_cellData[intermediateMesh.elems.size()]; + CellData temp_cellData[intermediateMesh.m_elems.size()]; // Process/split each element - for (int eID = 0; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 0; eID < intermediateMesh.m_elems.size(); ++eID) { - // Update the materials upon which the split will occur (should be the currently dominant material and the next material in the list that hasn't yet been split on) - int currentDominantMat = intermediateMesh.elementDominantMaterials[eID]; - computeClippingPoints(eID, currentDominantMat, matOne, &intermediateMesh, temp_cellData[eID]); + // Update the materials upon which the split will occur + int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; + int matOne = matID; + + computeClippingPoints(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct - for (int eID = 1; eID < intermediateMesh.elems.size(); ++eID) + for (int eID = 1; eID < intermediateMesh.m_elems.size(); ++eID) + { temp_cellData[0].mergeCell(temp_cellData[eID]); + } - mir::VertSet combined_verts(temp_cellData[0].numVerts); - mir::ElemSet combined_elems(temp_cellData[0].numElems); + mir::VertSet combined_verts(temp_cellData[0].m_numVerts); + mir::ElemSet combined_elems(temp_cellData[0].m_numElems); // Create the final, processed mesh mir::MIRMesh processedMesh; - processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.numMaterials, temp_cellData[0].topology, temp_cellData[0].mapData); - processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].mapData.vertexVolumeFractions); + processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.m_numMaterials, temp_cellData[0].m_topology, temp_cellData[0].m_mapData); + processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].m_mapData.m_vertexVolumeFractions); // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; @@ -79,18 +77,18 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh* return finalMesh; } -/// Reconstructs the material interface using an iterative optimization to improve the volume fractions of the resulting mesh. -/// Based on the Meredith and Childs 2010 paper. -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent) +//-------------------------------------------------------------------------------- + +mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent) { - int numElems = inputMesh->elems.size(); - int numMaterials = inputMesh->numMaterials; + int numElems = inputMesh.m_elems.size(); + int numMaterials = inputMesh.m_numMaterials; // Make a copy of the original input mesh mir::MIRMesh meshToImprove(inputMesh); // Calculate the reconstruction on the unmodified, input mesh - mir::MIRMesh resultingMesh = computeReconstructedInterface(&meshToImprove); + mir::MIRMesh resultingMesh = computeReconstructedInterface(meshToImprove); for (int it = 0; it < numIterations; ++it) { @@ -106,8 +104,8 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: { for (int eID = 0; eID < numElems; ++eID) { - axom::float64 difference = inputMesh->materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; - improvedElementVF[matID].push_back( inputMesh->materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); + axom::float64 difference = inputMesh.m_materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; + improvedElementVF[matID].push_back( inputMesh.m_materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); } } @@ -115,7 +113,7 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); // Calculate the reconstruction on the modified, input mesh - resultingMesh = computeReconstructedInterface(&meshToImprove); + resultingMesh = computeReconstructedInterface(meshToImprove); } return resultingMesh; @@ -123,11 +121,10 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: //-------------------------------------------------------------------------------- -/// Computes the points where the element should be clipped based on the volume fractions of mat one and two. -void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { // Find the vertices associated with each element - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; // Triangle Case if (elementVertices.size() == 3) @@ -143,12 +140,10 @@ void InterfaceReconstructor::computeClippingPoints(const int eID, const int matO //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { - // printf(" Processing QUAD element %d: ", eID); - // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; int upperLeftVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; @@ -156,7 +151,6 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int int upperRightVertex = elementVertices[3]; unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - // printf("caseIndex: %d\n", caseIndex); // Generate new elements std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets @@ -210,12 +204,9 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int numVertices = quadClipTable[caseIndex][i]; } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); generateTopologyData(newElements, newVertices, out_cellData); generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); @@ -224,47 +215,30 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int currentDominantMat = NULL_MAT; - for (unsigned long it = 0; it < itr->second.size(); ++it) - { - // int temp_vID = newElements[currentElementIndex][it]; - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the four original vertices of the quad element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2 || temp_vID == 3) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } - } - out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); + int dominantMaterial = determineDominantMaterial(Shape::Quad, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } // Determine and store the parent of this element - out_cellData.mapData.elementParents.resize(newElements.size()); + out_cellData.m_mapData.m_elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.mapData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; + int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; } } @@ -280,16 +254,15 @@ void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int for (int i = 1; i < 8; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) + for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) { - out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } } //-------------------------------------------------------------------------------- -/// Finds the bit map representing the clipping case for a quad. -unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) +unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) { // Determine the dominant color at each vertex int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; @@ -300,10 +273,10 @@ unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tem upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - upperRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + upperLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; } // Create the index into the quad clipping lookup table using the dominant colors at each vertex @@ -318,8 +291,7 @@ unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh* tem //-------------------------------------------------------------------------------- -/// Performs linear interpolation between the two given vertex positions. -mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t) +mir::Point2 InterfaceReconstructor::interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t) { mir::Point2 interpolatedPoint; interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; @@ -329,24 +301,21 @@ mir::Point2 InterfaceReconstructor::interpolateVertexPosition(mir::Point2 vertex //-------------------------------------------------------------------------------- -/// Performs linear interpolation between the two given float values -axom::float64 InterfaceReconstructor::lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t) +axom::float64 InterfaceReconstructor::lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t) { return (1 - t) * f0 + t * f1; } //-------------------------------------------------------------------------------- -/// Computes the t value as a percent from vertexOne to vertexTwo based on the two materials given. -/// The t value is the place where this edge should be clipped based on the two materials currently being considered. -axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh) +axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh) { axom::float64 ret = 0.0; - axom::float64 vfMatOneVertexOne = tempMesh->materialVolumeFractionsVertex[matOneID][vertexOneID]; // Changed these all from originalMesh->materialVolumeFractionsVertex[][] to tempMesh->... - axom::float64 vfMatTwoVertexOne = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexOneID]; - axom::float64 vfMatOneVertexTwo = tempMesh->materialVolumeFractionsVertex[matOneID][vertexTwoID]; - axom::float64 vfMatTwoVertexTwo = tempMesh->materialVolumeFractionsVertex[matTwoID][vertexTwoID]; + axom::float64 vfMatOneVertexOne = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexOneID]; + axom::float64 vfMatTwoVertexOne = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexOneID]; + axom::float64 vfMatOneVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexTwoID]; + axom::float64 vfMatTwoVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexTwoID]; if (matOneID == NULL_MAT) vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; @@ -375,50 +344,48 @@ axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int verte //-------------------------------------------------------------------------------- -/// Generate the topology of the new elements (evInds, evBegins, veInds, veBegins) resulting from a split. -void InterfaceReconstructor::generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData) +void InterfaceReconstructor::generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData) { // Store the evInds and evBegins data in the output vectors int currentEVBeginIndex = 0; for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { // Push the start index of the next element - out_cellData.topology.evBegins.push_back(currentEVBeginIndex); + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); // Push the next element's vertices for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) { - out_cellData.topology.evInds.push_back(itr->second[vIndex]); + out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); ++currentEVBeginIndex; } } // Push the index that occurs after the last vertex - out_cellData.topology.evBegins.push_back(currentEVBeginIndex); + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); // Store the veInds and veBegins data in the output vectors int currentVEBeginIndex = 0; for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { // Push the start index of the vertex's elements - out_cellData.topology.veBegins.push_back(currentVEBeginIndex); + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); // Push the next vertex's elements for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) { - out_cellData.topology.veInds.push_back(itr->second[eIndex]); + out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); ++currentVEBeginIndex; } } // Push the index that occurs after the last element - out_cellData.topology.veBegins.push_back(currentVEBeginIndex); + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); } //-------------------------------------------------------------------------------- -/// Generate the vertex position data for the new elements resulting from spltting a quad. -void InterfaceReconstructor::generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) { std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets @@ -427,70 +394,69 @@ void InterfaceReconstructor::generateVertexPositionsFromQuad(std::mapfirst; if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperLeftVertex]; if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; if (vID == 3) - newPoints[vID] = tempMesh->vertexPositions[upperRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperRightVertex]; if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperLeftVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperLeftVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperRightVertex], verticesClippingTValue[vID]); if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperRightVertex], tempMesh->vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperRightVertex], tempMesh.m_vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); } // Store the positions of the vertices in the return vector for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) { - out_cellData.mapData.vertexPositions.push_back(itr->second); + out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); } } //-------------------------------------------------------------------------------- -/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. -void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { if (vID == 0) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex]); if (vID == 1) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex]); if (vID == 4) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 6) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); if (vID == 7) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); } } } //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData) +void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) { // Determine the clipping case - auto elementVertices = tempMesh->bdry[eID]; + auto elementVertices = tempMesh.m_bdry[eID]; int upperVertex = elementVertices[0]; int lowerLeftVertex = elementVertices[1]; @@ -546,12 +512,9 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const numVertices = triangleClipTable[caseIndex][i]; } - /**************************************************************** - * CALCULATE THE NEW CELLS' DATA - ****************************************************************/ // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.numElems = (int) newElements.size(); - out_cellData.numVerts = (int) newVertices.size(); + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); generateTopologyData(newElements, newVertices, out_cellData); generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); @@ -560,46 +523,30 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int currentDominantMat = NULL_MAT; - for (unsigned long it = 0; it < itr->second.size(); ++it) - { - int temp_vID = itr->second[it]; - - // Find the vertex that is one of the three original vertices of the triangle element - if (temp_vID == 0 || temp_vID == 1 || temp_vID == 2) - { - axom::float64 matOneVolumeFraction = -1.0, matTwoVolumeFraction = -1.0; - if (matOneID != NULL_MAT) - matOneVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matOneID][temp_vID]; - if (matTwoID != NULL_MAT) - matTwoVolumeFraction = out_cellData.mapData.vertexVolumeFractions[matTwoID][temp_vID]; - - currentDominantMat = (matOneVolumeFraction > matTwoVolumeFraction) ? matOneID : matTwoID; - } - } - out_cellData.mapData.elementDominantMaterials.push_back(currentDominantMat); + int dominantMaterial = determineDominantMaterial(Shape::Triangle, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } // Determine and store the parent of this element - out_cellData.mapData.elementParents.resize(newElements.size()); + out_cellData.m_mapData.m_elementParents.resize(newElements.size()); for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.mapData.elementParents[itr->first] = tempMesh->elementParentIDs[eID]; + out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int parentElementID = out_cellData.mapData.elementParents[itr->first]; - int currentDominantMaterial = out_cellData.mapData.elementDominantMaterials[itr->first]; + int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; + int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - if (originalMesh->materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from if (currentDominantMaterial == matOneID) - out_cellData.mapData.elementDominantMaterials[itr->first] = matTwoID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; else - out_cellData.mapData.elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; } } @@ -616,15 +563,14 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const for (int i = 1; i < 6; ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.topology.evInds.size(); ++i) - out_cellData.topology.evInds[i] -= evIndexSubtract[ out_cellData.topology.evInds[i] ]; + for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } //-------------------------------------------------------------------------------- -/// Finds the bit map representing the clipping case for a triangle. -unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) +unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) { // Determine the dominant color at each vertex int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; @@ -635,9 +581,9 @@ unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* upperColor = lowerLeftColor = lowerRightColor = matOneID; if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperColor = tempMesh->materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh->materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh->materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; + upperColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; + lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; + lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; } // Create the index into the quad clipping lookup table using the dominant colors at each vertex @@ -651,8 +597,7 @@ unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh* //-------------------------------------------------------------------------------- -/// Generate the vertex position data for the new elements resulting from spltting a quad. -void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) { std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets @@ -661,54 +606,107 @@ void InterfaceReconstructor::generateVertexPositionsFromTriangle(std::mapfirst; if (vID == 0) - newPoints[vID] = tempMesh->vertexPositions[upperVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[upperVertex]; if (vID == 1) - newPoints[vID] = tempMesh->vertexPositions[lowerLeftVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; if (vID == 2) - newPoints[vID] = tempMesh->vertexPositions[lowerRightVertex]; + newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; if (vID == 3) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[upperVertex], tempMesh->vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerLeftVertex], tempMesh->vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh->vertexPositions[lowerRightVertex], tempMesh->vertexPositions[upperVertex], verticesClippingTValue[vID]); + newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperVertex], verticesClippingTValue[vID]); } // Store the positions of the vertices in the return vector for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) { - out_cellData.mapData.vertexPositions.push_back(itr->second); + out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); } } //-------------------------------------------------------------------------------- -/// Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. -void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData) +void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) { // Calculate the vertex fractions at each vertex (use t value!) // Make sure the output volume fractions containers are the proper size - out_cellData.mapData.vertexVolumeFractions.resize(tempMesh->numMaterials); + out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - for (int matID = 0; matID < tempMesh->numMaterials; ++matID) + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { if (vID == 0) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][upperVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex]); if (vID == 1) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); if (vID == 2) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex]); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); if (vID == 3) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][upperVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); if (vID == 4) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); if (vID == 5) - out_cellData.mapData.vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh->materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh->materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); + } + } +} + +//-------------------------------------------------------------------------------- + +int InterfaceReconstructor::determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF) +{ + int dominantMaterial = matOne; + + axom::float64 matOneVF = -1.0; + axom::float64 matTwoVF = -1.0; + + if (elementShape == Shape::Triangle) + { + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + if (vID == 0 || vID == 1 || vID == 2) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; + } + } + } + + if (elementShape == Shape::Quad) + { + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + if (vID == 0 || vID == 1 || vID == 2 || vID == 3) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; } } + } + + return dominantMaterial; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index cbda3dcb59..646e1f284e 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file InterfaceReconstructor.hpp + * + * \brief Contains the specification for the InterfaceReconstructor class. + * + */ + #ifndef __INTERFACE_RECONSTRUCTOR_H__ #define __INTERFACE_RECONSTRUCTOR_H__ @@ -12,6 +19,7 @@ #include "MIRMesh.hpp" #include "CellData.hpp" #include "ZooClippingTables.hpp" + #include namespace numerics = axom::numerics; @@ -21,46 +29,246 @@ namespace axom { namespace mir { + + /** + * \class InterfaceReconstructor + * + * \brief A class that contains the functionality for taking an input mesh + * with mixed cells and outputs a mesh with clean cells. + * + * \detail This class requires that the user create a mesh of type MIRMesh + * and pass that into one of the reconstruction methods. There are + * currently two reconstructions methods implemented: one is the + * a zoo-based algorithm described in Meredith 2004, and the other + * is the iterative version described in Meredith and Childs 2010. + */ class InterfaceReconstructor { public: + + /** + * \brief Default constructor. + */ InterfaceReconstructor(); + + + /** + * \brief Default destructor. + */ ~InterfaceReconstructor(); - mir::MIRMesh computeReconstructedInterface(mir::MIRMesh* inputMesh); - mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh* inputMesh, int numIterations, axom::float64 percent); - private: - mir::MIRMesh* originalMesh; + /** + * \brief Performs material interface reconstruction using the zoo-based algorithm. + * + * \param inputMesh The mesh composed of mixed cells. + * + * \return The mesh composed of clean cells. + */ + mir::MIRMesh computeReconstructedInterface(mir::MIRMesh& inputMesh); + + + /** + * \brief Performs material interface reconstruction using an iterative version of the zoo-based algorithm. + * + * \param inputMesh The mesh made up of mixed cells. + * \param numIterations The number of iterations for which to run the algorithm. + * \param percent The percent of the difference to use when modifying the original element volume fractions. + * + * \return The mesh made up of clean cells. + */ + mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent); + + // private: + + /** + * \brief A wrapper function that calls the appropriate splitting method based on the shape of the given element. + * + * \param eID The ID of the element to be split. + * \param matOneID The ID of the first material to use for splitting the element, + * \param matTwoID The ID of the second material to use for splitting the element. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the element. + */ + void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Splits the specified quad element into clean cells of the two given material types + * based on the volume fractions of the two materials. + * + * \param eID The ID of the quad element. + * \param matOneID The ID of the first material to use for splitting the quad, + * \param matTwoID The ID of the second material to use for splitting the quad. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the quad. + */ + void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Splits the specified triangle element into clean cells of the two given material types + * based on the volume fractions of the two materials. + * + * \param eID The ID of the triangle element. + * \param matOneID The ID of the first material to use for splitting the triangle, + * \param matTwoID The ID of the second material to use for splitting the triangle. + * \param tempMesh A pointer to the intermediate mesh that is currently being processed. + * \param out_cellData Container to store the output of splitting the triangle. + */ + void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); + + + /** + * \brief Performs linear interpolation between the two vertex positions. + * + * \param vertexOnePos The position of the first vertex. + * \param vertexTwoPos The position of the second vertex. + * \param t The percent of the distance from vertex one to vertex two to interpolate at. + * + * \return The interpolated position. + */ + mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t); + + + /** + * \brief Performs linear interpolation between the two given float values. + * + * \param f0 The first float value. + * \param f1 The second float value. + * \param t The percent of the distance from the first float value to the second. + * + * \return The interpolated value. + */ + axom::float64 lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t); + + + /** + * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. + * + * \param vertexOneID The ID of the first vertex. + * \param vertexTwoID The ID of the second vertex. + * \param matOneID The ID of the first material to use for interpolating between. + * \param matTwoID The ID of the second material to use for interpolating between. + * \param tempMesh The intermediate mesh that is currently being processed. + * + * \return The t value, which is the percent distance from vertex one to vertex two where the two volume fractions are equal. + */ + axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh); + + + /** + * \brief Generates the topology of the new elements resulting from a split. + * + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param out_cellData Container to store the topology data of the generated elements. + */ + void generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData); + - private: + /** + * \brief Calculates the bit map representing the clipping case for a quad. + * + * \param tempMesh The intermediate mesh that is currently being processed. + * \param matOneID The ID of the first material being used for splitting. + * \param matTwoID The ID of the second material being used for splitting. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * + * \return The bitmap representing the clipping case. + */ + unsigned int determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - // general clipping function - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Generate the vertex position data for the new elements resulting from splitting a quad. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex position data of the generated elements. + */ + void generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - // triangle clipping function - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + */ + void generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh&tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - // quad clipping functions - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh, CellData& out_cellData); + /** + * \brief Calculates the bit map representing the clipping case for a triangle. + * + * \param tempMesh The intermediate mesh that is currently being processed. + * \param matOneID The ID of the first material being used for splitting. + * \param matTwoID The ID of the second material being used for splitting. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * + * \return The bitmap representing the clipping case. + */ + unsigned int determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - // clipping interpolation functions - mir::Point2 interpolateVertexPosition(mir::Point2 vertexOnePos, mir::Point2 vertexTwoPos, float t); - axom::float64 lerpFloat(axom::float64 f0, axom::float64 f1, axom::float64 t); - axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh* tempMesh); + /** + * \brief Generate the vertex position data for the new elements resulting from splitting a triangle. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex position data of the generated elements. + */ + void generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - // general helper functions - void generateTopologyData(std::map > newElements, std::map > newVertices, CellData& out_cellData); + /** + * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a triangle. + * + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tempMesh The intermediate mesh that is currently being processed. + * \param verticesClippingTValue The set of t values where the quad should be split on each edge. + * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. + * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. + * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. + * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + */ + void generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - // quad clipping points helper functions - unsigned int determineQuadClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - void generateVertexPositionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); - void generateVertexVolumeFractionsFromQuad(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperLeftVertex, int lowerLeftVertex, int lowerRightVertex, int upperRightVertex, CellData& out_cellData); + /** + * \brief Determines the mroe dominant material of the two given for the given element. + * + * \param elementShape An enumerator denoting the element's shape. + * \param vertexIDs A list of vertex IDs into the vertexVF param. + * \param matOne The ID of the first material. + * \param matTwo The ID of the second material. + * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. + * + * \return The ID of the dominant material of the element. + * + * \note The dominant element for the 2D cases will be the same as the material present at one of the + * original vertices that existed prior to the split. So, if you can find this vertex and its dominant + * material, then you know the dominant material of this new element. + */ + int determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF); - // triangle clipping points helper functions - unsigned int determineTriangleClippingCase(mir::MIRMesh* tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - void generateVertexPositionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); - void generateVertexVolumeFractionsFromTriangle(std::map > newVertices, mir::MIRMesh* tempMesh, axom::float64* verticesClippingTValue, int upperVertex, int lowerLeftVertex, int lowerRightVertex, CellData& out_cellData); + private: + mir::MIRMesh m_originalMesh; }; } } diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 9243bda9ad..6e7c687dcf 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -19,22 +19,22 @@ MIRMesh::MIRMesh() //-------------------------------------------------------------------------------- -MIRMesh::MIRMesh(MIRMesh* _mesh) // copy constructor +MIRMesh::MIRMesh(MIRMesh* _mesh) { - meshTopology.evInds = _mesh->meshTopology.evInds; - meshTopology.evBegins = _mesh->meshTopology.evBegins; - meshTopology.veInds = _mesh->meshTopology.veInds; - meshTopology.veBegins = _mesh->meshTopology.veBegins; - verts = _mesh->verts; - elems = _mesh->elems; - bdry = _mesh->bdry; - cobdry = _mesh->cobdry; - vertexPositions = _mesh->vertexPositions; - materialVolumeFractionsElement = _mesh->materialVolumeFractionsElement; - materialVolumeFractionsVertex = _mesh->materialVolumeFractionsVertex; - elementParentIDs = _mesh->elementParentIDs; - elementDominantMaterials = _mesh->elementDominantMaterials; - numMaterials = _mesh->numMaterials; + m_meshTopology.m_evInds = _mesh->m_meshTopology.m_evInds; + m_meshTopology.m_evBegins = _mesh->m_meshTopology.m_evBegins; + m_meshTopology.m_veInds = _mesh->m_meshTopology.m_veInds; + m_meshTopology.m_veBegins = _mesh->m_meshTopology.m_veBegins; + m_verts = _mesh->m_verts; + m_elems = _mesh->m_elems; + m_bdry = _mesh->m_bdry; + m_cobdry = _mesh->m_cobdry; + m_vertexPositions = _mesh->m_vertexPositions; + m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; + m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; + m_elementParentIDs = _mesh->m_elementParentIDs; + m_elementDominantMaterials = _mesh->m_elementDominantMaterials; + m_numMaterials = _mesh->m_numMaterials; } //-------------------------------------------------------------------------------- @@ -46,55 +46,53 @@ MIRMesh::~MIRMesh() //-------------------------------------------------------------------------------- -/// Initialize a mesh with the given topology and data. void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) { // Initialize the vertex and element sets - verts = _verts; - elems = _elems; + m_verts = _verts; + m_elems = _elems; - numMaterials = _numMaterials; + m_numMaterials = _numMaterials; // Intialize the mesh topology - meshTopology.evInds = _topology.evInds; - meshTopology.evBegins = _topology.evBegins; - meshTopology.veInds = _topology.veInds; - meshTopology.veBegins = _topology.veBegins; + m_meshTopology.m_evInds = _topology.m_evInds; + m_meshTopology.m_evBegins = _topology.m_evBegins; + m_meshTopology.m_veInds = _topology.m_veInds; + m_meshTopology.m_veBegins = _topology.m_veBegins; // Initialize the mesh relations constructMeshRelations(); // Initialize the mesh's data maps - constructVertexPositionMap(_mapData.vertexPositions.data()); - constructElementParentMap(_mapData.elementParents.data()); - constructElementDominantMaterialMap(_mapData.elementDominantMaterials); + constructVertexPositionMap(_mapData.m_vertexPositions.data()); + constructElementParentMap(_mapData.m_elementParents.data()); + constructElementDominantMaterialMap(_mapData.m_elementDominantMaterials); // Initialize the element and vertex volume fraction maps if (_elementVF.size() > 0) constructMeshVolumeFractionsMaps(_elementVF); // Check validity of the sets - SLIC_ASSERT_MSG( verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( elems.isValid(), "Element set is not valid."); + SLIC_ASSERT_MSG( m_verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG( m_elems.isValid(), "Element set is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the mesh boundary and coboundary relations void MIRMesh::constructMeshRelations() { // construct boundary relation from elements to vertices using variable cardinality { using RelationBuilder = ElemToVertRelation::RelationBuilder; - bdry = RelationBuilder() - .fromSet( &elems ) - .toSet( &verts ) + m_bdry = RelationBuilder() + .fromSet( &m_elems ) + .toSet( &m_verts ) .begins( RelationBuilder::BeginsSetBuilder() - .size( elems.size() ) - .data( meshTopology.evBegins.data() ) ) + .size( m_elems.size() ) + .data( m_meshTopology.m_evBegins.data() ) ) .indices ( RelationBuilder::IndicesSetBuilder() - .size( meshTopology.evInds.size() ) - .data( meshTopology.evInds.data() ) ); + .size( m_meshTopology.m_evInds.size() ) + .data( m_meshTopology.m_evInds.data() ) ); } @@ -102,206 +100,200 @@ void MIRMesh::constructMeshRelations() // _quadmesh_example_construct_cobdry_relation_start // construct coboundary relation from vertices to elements using RelationBuilder = VertToElemRelation::RelationBuilder; - cobdry = RelationBuilder() - .fromSet( &verts ) - .toSet( &elems ) + m_cobdry = RelationBuilder() + .fromSet( &m_verts ) + .toSet( &m_elems ) .begins( RelationBuilder::BeginsSetBuilder() - .size( verts.size() ) - .data( meshTopology.veBegins.data() ) ) + .size( m_verts.size() ) + .data( m_meshTopology.m_veBegins.data() ) ) .indices( RelationBuilder::IndicesSetBuilder() - .size( meshTopology.veInds.size() ) - .data( meshTopology.veInds.data() ) ); + .size( m_meshTopology.m_veInds.size() ) + .data( m_meshTopology.m_veInds.data() ) ); // _quadmesh_example_construct_cobdry_relation_end } - SLIC_ASSERT_MSG( bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( cobdry.isValid(), "Coboundary relation is not valid."); + SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << cobdry.totalSize()); + SLIC_INFO("Elem-Vert relation has size " << m_bdry.totalSize()); + SLIC_INFO("Vert-Elem relation has size " << m_cobdry.totalSize()); } //-------------------------------------------------------------------------------- -/// Construct the and the element and vertex volume fraction data given the element volume fraction data void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) { // Clear the old maps - materialVolumeFractionsElement.clear(); - materialVolumeFractionsVertex.clear(); + m_materialVolumeFractionsElement.clear(); + m_materialVolumeFractionsVertex.clear(); // Initialize the maps for all of the materials with the input volume fraction data for each material - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - materialVolumeFractionsElement.push_back(ScalarMap( &elems )); + m_materialVolumeFractionsElement.push_back(ScalarMap( &m_elems )); // Copy the data for the current material - for (int eID = 0; eID < elems.size(); ++eID) + for (int eID = 0; eID < m_elems.size(); ++eID) { - materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; + m_materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); } // Initialize the maps for all of the vertex volume fractions - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the new map for the volume fractions - materialVolumeFractionsVertex.push_back(ScalarMap( &verts ) ); + m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts ) ); // Calculate the average volume fraction value for the current vertex for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { // Compute the per vertex volume fractions for the green material axom::float64 sum = 0; - auto vertexElements = cobdry[vID]; + auto vertexElements = m_cobdry[vID]; for (int i = 0; i < vertexElements.size(); ++i) { auto eID = vertexElements[i]; - sum += materialVolumeFractionsElement[matID][eID]; + sum += m_materialVolumeFractionsElement[matID][eID]; } - materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); + m_materialVolumeFractionsVertex[matID][vID] = sum / vertexElements.size(); } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } } //-------------------------------------------------------------------------------- -/// Construct the vertex volume fraction data given vertex volume fraction data void MIRMesh::constructMeshVolumeFractionsVertex(std::vector > vertexVF) { // Initialize the maps for all of the materials with the input volume fraction data for each vertex - for (int matID = 0; matID < numMaterials; ++matID) + for (int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - materialVolumeFractionsVertex.push_back(ScalarMap( &verts )); + m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts )); // Copy the data for the current material - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { - materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; + m_materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; } - SLIC_ASSERT_MSG( materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); + SLIC_ASSERT_MSG( m_materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); } } //-------------------------------------------------------------------------------- -/// Constucts the positions map on the vertices void MIRMesh::constructVertexPositionMap(Point2* data) { // construct the position map on the vertices - vertexPositions = PointMap( &verts ); + m_vertexPositions = PointMap( &m_verts ); - for (int vID = 0; vID < verts.size(); ++vID) - vertexPositions[vID] = data[vID]; + for (int vID = 0; vID < m_verts.size(); ++vID) + m_vertexPositions[vID] = data[vID]; - SLIC_ASSERT_MSG( vertexPositions.isValid(), "Position map is not valid."); + SLIC_ASSERT_MSG( m_vertexPositions.isValid(), "Position map is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the elementParentsID map of the ID of the parent element in the original mesh for each generated element void MIRMesh::constructElementParentMap(int* elementParents) { // Initialize the map for the elements' parent IDs - elementParentIDs = IntMap( &elems ); + m_elementParentIDs = IntMap( &m_elems ); // Copy the data for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementParentIDs[eID] = elementParents[eID]; + for (int eID = 0; eID < m_elems.size(); ++eID) + m_elementParentIDs[eID] = elementParents[eID]; - SLIC_ASSERT_MSG( elementParentIDs.isValid(), "Element parent map is not valid."); + SLIC_ASSERT_MSG( m_elementParentIDs.isValid(), "Element parent map is not valid."); } //-------------------------------------------------------------------------------- -/// Constructs the elementDominantMaterials map of each element's single most dominant material void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) { // Initialize the map for the elements' dominant colors - elementDominantMaterials = IntMap( &elems ); + m_elementDominantMaterials = IntMap( &m_elems ); // Copy the dat for the elements - for (int eID = 0; eID < elems.size(); ++eID) - elementDominantMaterials[eID] = dominantMaterials[eID]; + for (int eID = 0; eID < m_elems.size(); ++eID) + m_elementDominantMaterials[eID] = dominantMaterials[eID]; - SLIC_ASSERT_MSG( elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); + SLIC_ASSERT_MSG( m_elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); } //-------------------------------------------------------------------------------- -/// Print out the properties of the mesh. void MIRMesh::print() { printf("\n------------------------Printing Mesh Information:------------------------\n"); - printf("number of vertices: %d\n", verts.size()); - printf("number of elements: %d\n", elems.size()); - printf("number of materials: %d\n", numMaterials); + printf("number of vertices: %d\n", m_verts.size()); + printf("number of elements: %d\n", m_elems.size()); + printf("number of materials: %d\n", m_numMaterials); printf("evInds: { "); - for (unsigned long i = 0; i < meshTopology.evInds.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_evInds.size(); i++) { - printf("%d ", meshTopology.evInds[i]); + printf("%d ", m_meshTopology.m_evInds[i]); } printf("}\n"); printf("evBegins: { "); - for (unsigned long i = 0; i < meshTopology.evBegins.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_evBegins.size(); i++) { - printf("%d ", meshTopology.evBegins[i]); + printf("%d ", m_meshTopology.m_evBegins[i]); } printf("}\n"); printf("veInds: { "); - for (unsigned long i = 0; i < meshTopology.veInds.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_veInds.size(); i++) { - printf("%d ", meshTopology.veInds[i]); + printf("%d ", m_meshTopology.m_veInds[i]); } printf("}\n"); printf("veBegins: { "); - for (unsigned long i = 0; i < meshTopology.veBegins.size(); i++) + for (unsigned long i = 0; i < m_meshTopology.m_veBegins.size(); i++) { - printf("%d ", meshTopology.veBegins[i]); + printf("%d ", m_meshTopology.m_veBegins[i]); } printf("}\n"); printf("vertexPositions: { "); - for (int i = 0; i < verts.size(); ++i) + for (int i = 0; i < m_verts.size(); ++i) { - printf("{%.2f, %.2f} ", vertexPositions[i].m_x, vertexPositions[i].m_y); + printf("{%.2f, %.2f} ", m_vertexPositions[i].m_x, m_vertexPositions[i].m_y); } printf("}\n"); printf("elementParentIDs: { "); - for (int i = 0; i < elems.size(); ++i) + for (int i = 0; i < m_elems.size(); ++i) { - printf("%d ", elementParentIDs[i]); + printf("%d ", m_elementParentIDs[i]); } printf("}\n"); printf("elementDominantMaterials: { "); - for (int i = 0; i < elems.size(); ++i) + for (int i = 0; i < m_elems.size(); ++i) { - printf("%d ", elementDominantMaterials[i]); + printf("%d ", m_elementDominantMaterials[i]); } printf("}\n"); printf("vertexVolumeFractions: { \n"); - for (unsigned long i = 0; i < materialVolumeFractionsVertex.size(); ++i) + for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { printf(" { "); - for (int j = 0; j < verts.size(); ++j) + for (int j = 0; j < m_verts.size(); ++j) { - printf("%.3f, ", materialVolumeFractionsVertex[i][j]); + printf("%.3f, ", m_materialVolumeFractionsVertex[i][j]); } printf("}\n"); } @@ -311,8 +303,6 @@ void MIRMesh::print() //-------------------------------------------------------------------------------- -/// Reads in and constructs a mesh from the given file -/// Note: Must currently be an ASCII, UNSTRUCTURED_GRID .vtk file void MIRMesh::readMeshFromFile(std::string filename) { printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); @@ -330,7 +320,6 @@ void MIRMesh::readMeshFromFile(std::string filename) //-------------------------------------------------------------------------------- -/// Writes out the mesh to a file void MIRMesh::writeMeshToFile(std::string filename) { std::ofstream meshfile; @@ -342,30 +331,30 @@ void MIRMesh::writeMeshToFile(std::string filename) << "vtk output\n" << "ASCII\n" << "DATASET UNSTRUCTURED_GRID\n" - << "POINTS " << verts.size() << " double\n"; + << "POINTS " << m_verts.size() << " double\n"; // write positions - for (int vID = 0; vID < verts.size(); ++vID) + for (int vID = 0; vID < m_verts.size(); ++vID) { - meshfile << vertexPositions[vID].m_x << " " << vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + meshfile << m_vertexPositions[vID].m_x << " " << m_vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D } - meshfile << "\nCELLS " << elems.size() << " " << meshTopology.evInds.size() + elems.size(); - for (int i = 0; i < elems.size(); ++i) + meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); + for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = meshTopology.evBegins[i+1] - meshTopology.evBegins[i]; + int nVerts = m_meshTopology.m_evBegins[i+1] - m_meshTopology.m_evBegins[i]; meshfile << "\n" << nVerts; for (int j = 0; j < nVerts; ++j) { - int startIndex = meshTopology.evBegins[i]; - meshfile << " " << meshTopology.evInds[startIndex + j]; + int startIndex = m_meshTopology.m_evBegins[i]; + meshfile << " " << m_meshTopology.m_evInds[startIndex + j]; } } - meshfile << "\n\nCELL_TYPES " << elems.size() << "\n"; - for (int i = 0; i < elems.size(); ++i) + meshfile << "\n\nCELL_TYPES " << m_elems.size() << "\n"; + for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = meshTopology.evBegins[i + 1] - meshTopology.evBegins[i]; + int nVerts = m_meshTopology.m_evBegins[i + 1] - m_meshTopology.m_evBegins[i]; if (nVerts == 3) meshfile << "5\n"; else if (nVerts == 4) @@ -373,12 +362,12 @@ void MIRMesh::writeMeshToFile(std::string filename) } // write element materials - meshfile << "\n\nCELL_DATA " << elems.size() + meshfile << "\n\nCELL_DATA " << m_elems.size() << "\nSCALARS cellIds int 1" << "\nLOOKUP_TABLE default \n"; - for(int i=0 ; i< elems.size() ; ++i) + for(int i=0 ; i< m_elems.size() ; ++i) { - meshfile << elementDominantMaterials[i] << " "; + meshfile << m_elementDominantMaterials[i] << " "; } meshfile <<"\n"; @@ -386,7 +375,6 @@ void MIRMesh::writeMeshToFile(std::string filename) //-------------------------------------------------------------------------------- -/// Computes the volume fractions of the elements of the original mesh, std::vector > MIRMesh::computeOriginalElementVolumeFractions() { std::map totalAreaOriginalElements; // the total area of the original elements @@ -394,39 +382,39 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr std::map numChildren; // the number of child elements generated from one of the original elements // Compute the total area of each element of the original mesh and also the area of each new element - for (int eID = 0; eID < elems.size(); ++eID) + for (int eID = 0; eID < m_elems.size(); ++eID) { - int numVertices = meshTopology.evBegins[eID + 1] - meshTopology.evBegins[eID]; + int numVertices = m_meshTopology.m_evBegins[eID + 1] - m_meshTopology.m_evBegins[eID]; if (numVertices == 3) { Point2 trianglePoints[3]; for (int i = 0; i < 3; ++i) - trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; + trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); } if (numVertices == 4) { Point2 trianglePoints[4]; for (int i = 0; i < 4; ++i) - trianglePoints[i] = vertexPositions[meshTopology.evInds[ meshTopology.evBegins[eID] + i] ]; + trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); } - totalAreaOriginalElements[ elementParentIDs[eID] ] += newElementAreas[eID]; - numChildren[ elementParentIDs[eID] ] += .4; + totalAreaOriginalElements[ m_elementParentIDs[eID] ] += newElementAreas[eID]; + numChildren[ m_elementParentIDs[eID] ] += .4; } // Intialize the element volume fraction vectors std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction - elementVolumeFractions.resize( numMaterials ); + elementVolumeFractions.resize( m_numMaterials ); for (unsigned long i = 0; i < elementVolumeFractions.size(); ++i) elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); // Compute the volume fractions for each of the original mesh elements for (auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) { - int parentElementID = elementParentIDs[itr->first]; - int materialID = elementDominantMaterials[itr->first]; + int parentElementID = m_elementParentIDs[itr->first]; + int materialID = m_elementDominantMaterials[itr->first]; elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); } @@ -435,7 +423,6 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr //-------------------------------------------------------------------------------- -/// Computes the area of the triangle defined by the given three vertex positions. Uses Heron's formula. axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 @@ -449,8 +436,6 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- -/// Computes the area of the quad defined by the given four vertex positions. -/// Note: It is assumed the points are given in consecutive, counter-clockwise order. axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index c85c18e936..63b23d2ccc 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MIRMesh.hpp + * + * \brief Contains the specification for the MIRMesh class. + * + */ + #ifndef __MIR_MESH_H__ #define __MIR_MESH_H__ @@ -28,65 +35,163 @@ namespace axom namespace mir { - #define NULL_MAT -1 + const int NULL_MAT = -1; //-------------------------------------------------------------------------------- + /** + * \class MIRMesh + * + * \brief The MIRMesh class represents a finite element mesh containing element volume fractions. + * + * \detail This class is meant to be used in conjunction with the InterfaceReconstructor class + * to process the mesh. + * + */ class MIRMesh { - /**************************************************************** - * MESH FUNCTIONS - ****************************************************************/ public: + /** + * \brief Default constructor. + */ MIRMesh(); - MIRMesh(MIRMesh* _mesh); // copy constructor - ~MIRMesh(); - - void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); - - void constructMeshRelations(); /// Constructs the mesh boundary and coboundary relations - void constructMeshVolumeFractionsMaps(std::vector > elementVF); /// Constructs the element and vertex volume fraction maps - void constructMeshVolumeFractionsVertex(std::vector > vertexVF); /// Constructs the vertex volume fraction map - void print(); + /** + * \brief Copy constrcutor. + */ + MIRMesh(MIRMesh* _mesh); - void readMeshFromFile(std::string filename); - void writeMeshToFile(std::string filename); + /** + * \brief Default destructor. + */ + ~MIRMesh(); - std::vector > computeOriginalElementVolumeFractions(); + /** + * \brief Initializes a mesh with the provided data and topology. + * + * \param _verts The set of vertices of the mesh. + * \param _elems The set of elements of the mesh. + * \param _numMaterials The number of materials present in the mesh. + * \param _topology The topology/connectivity of the mesh. + * \param _mapData The data used to initialized the maps associated with the vertex and element sets. + * \param _elementVF The volume fractions of each element. Note that this is an optional parameter. + */ + void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); + + /** + * \brief Constructs the mesh boundary and coboundary relations. + * + * \note The boundary relation is from each element to its vertices. + * \note The coboundary relation is from each vertex to its elements. + */ + void constructMeshRelations(); + + /** + * \brief Constructs the element and vertex volume fraction maps. + * + * \param elementVF The volume fractions of each element. + */ + void constructMeshVolumeFractionsMaps(std::vector > elementVF); + + /** + * \brief Constructs the vertex volume fraction map. + * + * \param vertexVF The volume fractions of each vertex. + */ + void constructMeshVolumeFractionsVertex(std::vector > vertexVF); + + /** + * \brief Prints out the data contained within this mesh in a nice format. + */ + void print(); + + /** + * \brief Reads in a mesh specified by the given file. + * + * \param filename The location where the mesh file will be read from. + */ + void readMeshFromFile(std::string filename); + + /** + * \brief Writes out the mesh to the given file. + * + * \param filename The location where the mesh file will be written. + * + * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. + */ + void writeMeshToFile(std::string filename); + + + /** + * \brief Computes the volume fractions of the elements of the original mesh. + */ + std::vector > computeOriginalElementVolumeFractions(); private: - void constructVertexPositionMap(Point2* data); /// Constucts the positions map on the vertices - void constructElementParentMap(int* cellParents); /// Constructs the map of cells to their original cell parent - void constructElementDominantMaterialMap(std::vector dominantMaterials); /// Constructs the map of elements to their dominant materials - axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); - axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + /** + * \brief Constucts the positions map on the vertices. + * + * \param data The array of position data for each vertex. + */ + void constructVertexPositionMap(Point2* data); + + /** + * \brief Constructs the map of elements to their original element parent. + * + * \param cellParents The array of parent IDs for each element of the mesh. + */ + void constructElementParentMap(int* cellParents); + + /** + * \brief Constructs the map of elements to their dominant materials. + * + * \param dominantMaterials A vector of material ids that are the dominant material of each element. + */ + void constructElementDominantMaterialMap(std::vector dominantMaterials); + + /** + * \brief Computes the area of the triangle defined by the given three vertex positions using Heron's formula. + * + * \param p0 The position of the first vertex. + * \param p1 The position of the second vertex. + * \param p2 The position of the third vertex. + */ + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + + /** + * \brief Computes the area of the quad defined by the given four vertex positions. + * + * \param p0 The position of the first vertex. + * \param p1 The position of the second vertex. + * \param p2 The position of the third vertex. + * \param p3 The position of the fourth vertex. + * + * \note It is assumed that the points are given in consecutive, counter-clockwise order. + */ + axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); /**************************************************************** * VARIABLES ****************************************************************/ public: // Mesh Set Definitions - VertSet verts; // the set of vertices in the mesh - ElemSet elems; // the set of elements in the mesh + VertSet m_verts; // the set of vertices in the mesh + ElemSet m_elems; // the set of elements in the mesh - public: // Mesh Relation Definitions - ElemToVertRelation bdry; // Boundary relation from elements to vertices - VertToElemRelation cobdry; // Coboundary relation from vertices to elements + ElemToVertRelation m_bdry; // Boundary relation from elements to vertices + VertToElemRelation m_cobdry; // Coboundary relation from vertices to elements - public: // Mesh Map Definitions - PointMap vertexPositions; // vertex position for each vertex - std::vector materialVolumeFractionsElement; // the volume fractions of each material for each element - std::vector materialVolumeFractionsVertex; // the volume fractions of each material for each vertex - IntMap elementParentIDs; // the ID of the parent element from the original mesh - IntMap elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) - - public: - int numMaterials; - CellTopologyData meshTopology; + PointMap m_vertexPositions; // vertex position for each vertex + std::vector m_materialVolumeFractionsElement; // the volume fractions of each material for each element + std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + IntMap m_elementParentIDs; // the ID of the parent element from the original mesh + IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + + int m_numMaterials; // the number of materials present in the mesh + CellTopologyData m_meshTopology; // the topology/connectivity of the mesh }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index d51aecd177..b6fc18ec28 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -3,6 +3,12 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MIRMeshTypes.hpp + * + * \brief Contains the specifications for types aliases used throughout the MIR component. + */ + #ifndef __MIR_MESH_TYPES_H__ #define __MIR_MESH_TYPES_H__ diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index df9d76b7d8..690c9ad35b 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -26,7 +26,6 @@ MeshTester::~MeshTester() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 1 (from Meredith 2004) MIRMesh MeshTester::initTestCaseOne() { int numElements = 9; @@ -108,15 +107,15 @@ MIRMesh MeshTester::initTestCaseOne() }; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -127,7 +126,6 @@ MIRMesh MeshTester::initTestCaseOne() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 2 (from Meredith and Childs 2010) mir::MIRMesh MeshTester::initTestCaseTwo() { int numElements = 9; @@ -210,15 +208,15 @@ mir::MIRMesh MeshTester::initTestCaseTwo() }; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -229,7 +227,6 @@ mir::MIRMesh MeshTester::initTestCaseTwo() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 3, which tests all the triangle clipping cases. mir::MIRMesh MeshTester::initTestCaseThree() { int numElements = 4; @@ -283,15 +280,15 @@ mir::MIRMesh MeshTester::initTestCaseThree() CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -302,7 +299,6 @@ mir::MIRMesh MeshTester::initTestCaseThree() //-------------------------------------------------------------------------------- -/// Initialize mesh for Test Case 4, a 3x3 grid with a circle of one material in the middle mir::MIRMesh MeshTester::initTestCaseFour() { int numElements = 9; @@ -394,15 +390,15 @@ mir::MIRMesh MeshTester::initTestCaseFour() elementVF[BLUE] = blueVolumeFractions; CellTopologyData topology; - topology.evInds = evInds; - topology.evBegins = evBegins; - topology.veInds = veInds; - topology.veBegins = veBegins; + topology.m_evInds = evInds; + topology.m_evBegins = evBegins; + topology.m_veInds = veInds; + topology.m_veBegins = veBegins; CellMapData mapData; - mapData.elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; - mapData.elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.vertexPositions = points; + mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_vertexPositions = points; // Build the mesh mir::MIRMesh testMesh; @@ -413,32 +409,31 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- -/// Intializes a uniform grid with a circle of one material surrounded by another material. mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; std::vector > elementVF; elementVF.resize(numMaterials); - std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.numElems); - std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.numElems); + std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.m_numElems); + std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.m_numElems); // Generate the element volume fractions for the circle int numMonteCarloSamples = 100; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 0]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 1]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 2]], - cellData.mapData.vertexPositions[cellData.topology.evInds[i * 4 + 3]]); + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 0]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 1]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 2]], + cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 3]]); blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; } @@ -447,22 +442,22 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } CellTopologyData topology; - topology.evInds = cellData.topology.evInds; - topology.evBegins = cellData.topology.evBegins; - topology.veInds = cellData.topology.veInds; - topology.veBegins = cellData.topology.veBegins; + topology.m_evInds = cellData.m_topology.m_evInds; + topology.m_evBegins = cellData.m_topology.m_evBegins; + topology.m_veInds = cellData.m_topology.m_veInds; + topology.m_veBegins = cellData.m_topology.m_veBegins; CellMapData mapData; - mapData.elementDominantMaterials = elementDominantMaterials; - mapData.elementParents = elementParents; - mapData.vertexPositions = cellData.mapData.vertexPositions; + mapData.m_elementDominantMaterials = elementDominantMaterials; + mapData.m_elementParents = elementParents; + mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; // Build the mesh mir::MIRMesh testMesh; @@ -473,32 +468,31 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 //-------------------------------------------------------------------------------- -// Assumes the quad vertices are ordered the same as the clipping case. -axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(q_p0, circle_center); - axom::float64 distP1 = distance(q_p1, circle_center); - axom::float64 distP2 = distance(q_p2, circle_center); - axom::float64 distP3 = distance(q_p3, circle_center); + axom::float64 distP0 = distance(quadP0, circleCenter); + axom::float64 distP1 = distance(quadP1, circleCenter); + axom::float64 distP2 = distance(quadP2, circleCenter); + axom::float64 distP3 = distance(quadP3, circleCenter); - if (distP0 < circle_radius && distP1 < circle_radius && distP2 < circle_radius && distP3 < circle_radius) + if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) { // The entire quad overlaps the circle return 1.0; } - else if (distP0 < circle_radius || distP1 < circle_radius || distP2 < circle_radius || distP3 < circle_radius) + else if (distP0 < circleRadius || distP1 < circleRadius || distP2 < circleRadius || distP3 < circleRadius) { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = abs(q_p2.m_x - q_p1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(q_p0.m_y - q_p1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = abs(quadP2.m_x - quadP1.m_x) / (double) (gridSize - 1); + axom::float64 delta_y = abs(quadP0.m_y - quadP1.m_y) / (double) (gridSize - 1); int countOverlap = 0; for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + q_p1.m_x, delta_y * y + q_p1.m_y); - if (distance(samplePoint, circle_center) < circle_radius) + mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); + if (distance(samplePoint, circleCenter) < circleRadius) ++countOverlap; } } @@ -513,20 +507,19 @@ axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::P //-------------------------------------------------------------------------------- -/// Generates a 2D uniform grid with n x n elements. -mir::CellData MeshTester::generateGrid(int n) +mir::CellData MeshTester::generateGrid(int gridSize) { // Generate the topology for a uniform quad mesh with n x n elements automatically - int numElements = n * n; - int numVertices = (n + 1) * (n + 1); + int numElements = gridSize * gridSize; + int numVertices = (gridSize + 1) * (gridSize + 1); // Generate the evInds std::vector evInds; for (int eID = 0; eID < numElements; ++eID) { - int row = eID / n; // note the integer division - int vertsPerRow = n + 1; - int elemsPerRow = n; + int row = eID / gridSize; // note the integer division + int vertsPerRow = gridSize + 1; + int elemsPerRow = gridSize; evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 0); evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); @@ -537,7 +530,7 @@ mir::CellData MeshTester::generateGrid(int n) // Generate the evBegins std::vector evBegins; evBegins.push_back(0); - for (int i = 0; i < n * n; ++i) + for (int i = 0; i < numElements; ++i) { evBegins.push_back((i + 1) * 4); } @@ -573,22 +566,22 @@ mir::CellData MeshTester::generateGrid(int n) // Generate the vertex positions std::vector points; - for (int y = n; y > -1; --y) + for (int y = gridSize; y > -1; --y) { - for (int x = 0; x < n + 1; ++x) + for (int x = 0; x < gridSize + 1; ++x) { points.push_back(mir::Point2(x, y)); } } mir::CellData data; - data.numVerts = numVertices; - data.numElems = numElements; - data.topology.evInds = evInds; - data.topology.evBegins = evBegins; - data.topology.veInds = veInds; - data.topology.veBegins = veBegins; - data.mapData.vertexPositions = points; + data.m_numVerts = numVertices; + data.m_numElems = numElements; + data.m_topology.m_evInds = evInds; + data.m_topology.m_evBegins = evBegins; + data.m_topology.m_veInds = veInds; + data.m_topology.m_veBegins = veBegins; + data.m_mapData.m_vertexPositions = points; // // Print out the results // printf("evInds: { "); @@ -633,7 +626,6 @@ mir::CellData MeshTester::generateGrid(int n) //-------------------------------------------------------------------------------- -/// Calculate the distance between the two given points. axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) { return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); @@ -641,16 +633,14 @@ axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) //-------------------------------------------------------------------------------- -/// Multiple materials, multiple concentric circles. -/// Note: Assumes each circle has a unique material. mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.numElems); // Construct the element set + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set // Generate the element volume fractions with concentric circles int numMaterials = numCircles + 1; @@ -679,17 +669,17 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) for (int i = 0; i < numMaterials; ++i) { std::vector tempVec; - tempVec.resize(cellData.numElems); + tempVec.resize(cellData.m_numElems); materialVolumeFractionsData.push_back(tempVec); } // Use the uniform sampling method to generate volume fractions for each material - for (int eID = 0; eID < cellData.numElems; ++eID) + for (int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2 v0 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.mapData.vertexPositions[cellData.topology.evInds[eID * 4 + 3]]; + mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; + mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; + mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; + mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -733,22 +723,22 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; - for (int i = 0; i < cellData.numElems; ++i) + for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); } CellTopologyData topology; - topology.evInds = cellData.topology.evInds; - topology.evBegins = cellData.topology.evBegins; - topology.veInds = cellData.topology.veInds; - topology.veBegins = cellData.topology.veBegins; + topology.m_evInds = cellData.m_topology.m_evInds; + topology.m_evBegins = cellData.m_topology.m_evBegins; + topology.m_veInds = cellData.m_topology.m_veInds; + topology.m_veBegins = cellData.m_topology.m_veBegins; CellMapData mapData; - mapData.elementDominantMaterials = elementDominantMaterials; - mapData.elementParents = elementParents; - mapData.vertexPositions = cellData.mapData.vertexPositions; + mapData.m_elementDominantMaterials = elementDominantMaterials; + mapData.m_elementParents = elementParents; + mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; // Build the mesh mir::MIRMesh testMesh; @@ -759,24 +749,23 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) //-------------------------------------------------------------------------------- -/// Calculate the number of corners of the quad that are within the circle -int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3) +int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(q_p0, circle_center); - axom::float64 distP1 = distance(q_p1, circle_center); - axom::float64 distP2 = distance(q_p2, circle_center); - axom::float64 distP3 = distance(q_p3, circle_center); + axom::float64 distP0 = distance(quadP0, circleCenter); + axom::float64 distP1 = distance(quadP1, circleCenter); + axom::float64 distP2 = distance(quadP2, circleCenter); + axom::float64 distP3 = distance(quadP3, circleCenter); int numCorners = 0; - if (distP0 < circle_radius) + if (distP0 < circleRadius) numCorners++; - if (distP1 < circle_radius) + if (distP1 < circleRadius) numCorners++; - if (distP2 < circle_radius) + if (distP2 < circleRadius) numCorners++; - if (distP3 < circle_radius) + if (distP3 < circleRadius) numCorners++; return numCorners; @@ -784,5 +773,41 @@ int MeshTester::circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float //-------------------------------------------------------------------------------- +mir::MIRMesh MeshTester::initQuadClippingTestMesh() +{ + // Generate the mesh topology + int gridSize = 3; + mir::CellData cellData = generateGrid(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + + int numMaterials = 2; + + std::vector > elementVF; + elementVF.resize(numMaterials); + elementVF[0] = {1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0}; + elementVF[1] = {0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0}; + + std::vector elementParents; + std::vector elementDominantMaterials; + for (int i = 0; i < cellData.m_numElems; ++i) + { + elementParents.push_back(i); + elementDominantMaterials.push_back(NULL_MAT); + } + + cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; + cellData.m_mapData.m_elementParents = elementParents; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, elementVF); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + } } diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index b89cfa2b26..9641fec6a1 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -3,6 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +/** + * \file MeshTester.hpp + * + * \brief Contains the specification for the MeshTester class. + * + */ + #ifndef __MESH_TESTER_H__ #define __MESH_TESTER_H__ @@ -20,26 +27,144 @@ namespace axom { namespace mir { + /** + * \class MeshTester + * + * \brief A class used to generate MIRMeshs with specific properties so + * that the reconstruction output can be validated visually. + * + */ class MeshTester { public: + /** + * \brief Default constructor. + */ MeshTester(); + + /** + * \brief Default destructor. + */ ~MeshTester(); public: - MIRMesh initTestCaseOne(); // 3x3 grid, 2 materials - mir::MIRMesh initTestCaseTwo(); // 3x3 grid, 3 materials - mir::MIRMesh initTestCaseThree(); // triforce, 2 materials - mir::MIRMesh initTestCaseFour(); // 3x3, 2 materials, circle of material - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); - mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles - + /** + * \brief Initializes an MIRMesh based on the example from Meredith 2004 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 2 materials. + * + * \return The generated mesh. + */ + MIRMesh initTestCaseOne(); + + /** + * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 3 materials. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseTwo(); + + /** + * \brief Initializes an MIRMesh used for testing triangle clipping cases. + * + * \note The mesh is a set of four triangles with 2 materials + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseThree(); + + /** + * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and has a single circle in the center composed + * of one material and is surrounded by a second material. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseFour(); + + /** + * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform grid. + * + * \param gridSize The number of elements in the width and the height of the uniform grid. + * \param numCircles The number of concentric circles that are centered in the grid. + * + * \note Each circle is composed of a different material. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles + + /** + * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * + * \return The generated mesh. + */ + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + + /** + * \brief Intializes a mesh to be used for validating the results of quad clipping. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such + * that the mesh would be split horizontally through the middle. + * + * \return The generated mesh. + */ + mir::MIRMesh initQuadClippingTestMesh(); + private: - axom::float64 distance(mir::Point2 p0, mir::Point2 p1); - mir::CellData generateGrid(int n); - axom::float64 calculatePercentOverlapMonteCarlo(int n, mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + + /** + * \brief Calculate the distance between the two given points. + * + * \param p0 The first point. + * \param p1 The second point. + * + * \return The distance between the two points. + */ + axom::float64 distance(mir::Point2 p0, mir::Point2 p1); + + /** + * \brief Generates a 2D uniform grid of n x n elements. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + */ + mir::CellData generateGrid(int gridSize); + + /** + * \brief Calculates the percent overlap between the given circle and quad. + * + * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * /return The percent value overlap of the circle and the quad between [0, 1]. + */ + axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); - int circleQuadCornersOverlaps(mir::Point2 circle_center, axom::float64 circle_radius, mir::Point2 q_p0, mir::Point2 q_p1, mir::Point2 q_p2, mir::Point2 q_p3); + /** + * \brief Calculates the number of corners of the quad that are within the circle. + * + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * \return The number of corners of the quad that are within the circle. + */ + int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); }; } } diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 5cb584274c..05e96ea2d0 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -10,7 +10,7 @@ namespace axom namespace mir { - // Quad Vertex Indices + // Quad Vertex Local Indices // // 0 7 3 // @---------@---------@ @@ -45,7 +45,7 @@ namespace mir }; - // Triangle Vertex Indices + // Triangle Vertex Local Indices // 0 // @ // / \ diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index fcdfc20a9e..924b98792a 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -6,11 +6,28 @@ #ifndef __ZOO_CLIPPING_TABLES_H__ #define __ZOO_CLIPPING_TABLES_H__ +/** + * \file ZooClippingTables.hpp + * + * \brief Contains the defintions for the clipping cases and enumerator + * for the shape types in the zoo. + */ namespace axom { namespace mir { + + enum Shape + { + Triangle, + Quad, + Tetrahedron, + Triangular_Prism, + Pyramid, + Hexahedron + }; + extern const int quadClipTable[16][19]; extern const int triangleClipTable[8][10]; } diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index b1a647e764..16e15481a7 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -5,7 +5,7 @@ set( mir_examples mir_tutorial_simple.cpp - mirConcentricCircles.cpp + mir_concentric_circles.cpp ) set( mir_example_dependencies diff --git a/src/axom/mir/examples/mirConcentricCircles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp similarity index 83% rename from src/axom/mir/examples/mirConcentricCircles.cpp rename to src/axom/mir/examples/mir_concentric_circles.cpp index b445c859e7..48d361400f 100644 --- a/src/axom/mir/examples/mirConcentricCircles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -39,18 +39,18 @@ int main( int argc, char** argv ) std::string outputFilePath = std::string(argv[3]); // Intialize a mesh for testing MIR - auto start_time = Clock::now(); + auto startTime = Clock::now(); mir::MeshTester tester; - mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); // grid size, numCircles - auto end_time = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); + auto endTime = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Begin material interface reconstruction - start_time = Clock::now(); + startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - end_time = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); + endTime = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index d35fe0bfbc..ad67317a6e 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -18,35 +18,35 @@ namespace mir = axom::mir; //-------------------------------------------------------------------------------- /*! - * \brief Tutorial main + * \brief Tutorial main showing how to initialize test cases and perform mir. */ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) { // Intialize a mesh for testing MIR - auto start_time = Clock::now(); + auto startTime = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); // mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - mir::MIRMesh testMesh = tester.initTestCaseFive(25, 7); + mir::MIRMesh testMesh = tester.initTestCaseFive(50, 25); - auto end_time = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + auto endTime = Clock::now(); + std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Begin material interface reconstruction - start_time = Clock::now(); + startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(&testMesh); - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(&testMesh, 5, 0.3); // 5 iterations, 90 percent + // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); // Process once, with original Meredith algorithm + mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(testMesh, 5, 0.3); // 5 iterations, 30 percent with iterative Meredith algorithm - end_time = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(end_time - start_time).count() << " ms" << std::endl; + endTime = Clock::now(); + std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/iterativeOutputTestMesh5.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputIterative6.vtk"); return 0; } diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 155f8ae486..15a42d39ff 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(gtest_mir_tests mir_smoke.cpp + mir_interface_reconstructor.cpp ) diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp new file mode 100644 index 0000000000..8a11c21e73 --- /dev/null +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_SMOKE_H_ +#define MIR_SMOKE_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +TEST(mir_quad_clipping, quad_clipping_case_zero) +{ + // Initialize a 3x3 mesh with 2 materials + mir::MeshTester meshGenerator; + mir::MIRMesh testMesh = meshGenerator.initQuadClippingTestMesh(); + + // Initialize its volume fractions to custom values to guarantee this clipping case + std::vector > vertexVF; + vertexVF.resize(2); + vertexVF[0] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + vertexVF[1] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + int upperLeftVertexID = 5; + int lowerLeftVertexID = 9; + int lowerRightVertexID = 10; + int upperRightVertexID = 6; + + mir::InterfaceReconstructor reconstructor; + unsigned int clippingCase = reconstructor.determineQuadClippingCase( testMesh, 0, 1, upperLeftVertexID, lowerLeftVertexID, lowerRightVertexID, upperRightVertexID ); + + EXPECT_EQ( clippingCase, 0 ); +} + + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_SMOKE_H_ From fcc143692b0929f9af94aadfbc61e15627a55fda Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Wed, 26 Jun 2019 17:13:42 -0700 Subject: [PATCH 033/290] Refactored the interface reconstruction code. Ensured everything is properly commented. Added unit tests. Added a map to the mesh to store element shapes. --- src/axom/mir/CMakeLists.txt | 6 +- src/axom/mir/CellClipper.cpp | 157 +++++ src/axom/mir/CellClipper.hpp | 120 ++++ src/axom/mir/CellData.cpp | 6 + src/axom/mir/CellData.hpp | 1 + src/axom/mir/CellGenerator.cpp | 218 +++++++ src/axom/mir/CellGenerator.hpp | 150 +++++ src/axom/mir/InterfaceReconstructor.cpp | 611 +++--------------- src/axom/mir/InterfaceReconstructor.hpp | 212 +----- src/axom/mir/MIRMesh.cpp | 58 +- src/axom/mir/MIRMesh.hpp | 42 +- src/axom/mir/MIRMeshTypes.hpp | 11 + src/axom/mir/MIRUtilities.hpp | 209 ++++++ src/axom/mir/MeshTester.cpp | 39 +- src/axom/mir/MeshTester.hpp | 12 +- src/axom/mir/ZooClippingTables.cpp | 34 + src/axom/mir/ZooClippingTables.hpp | 16 +- .../mir/examples/mir_concentric_circles.cpp | 3 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 28 +- src/axom/mir/tests/CMakeLists.txt | 3 + src/axom/mir/tests/mir_cell_clipper.cpp | 451 +++++++++++++ src/axom/mir/tests/mir_cell_generator.cpp | 231 +++++++ .../mir/tests/mir_interface_reconstructor.cpp | 31 +- src/axom/mir/tests/mir_utilities.cpp | 101 +++ src/docs/dependencies.dot | 2 +- src/index.rst | 2 +- 26 files changed, 1937 insertions(+), 817 deletions(-) create mode 100644 src/axom/mir/CellClipper.cpp create mode 100644 src/axom/mir/CellClipper.hpp create mode 100644 src/axom/mir/CellGenerator.cpp create mode 100644 src/axom/mir/CellGenerator.hpp create mode 100644 src/axom/mir/MIRUtilities.hpp create mode 100644 src/axom/mir/tests/mir_cell_clipper.cpp create mode 100644 src/axom/mir/tests/mir_cell_generator.cpp create mode 100644 src/axom/mir/tests/mir_utilities.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index e1740e6ad4..db7d2fda20 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -22,15 +22,19 @@ set(mir_headers InterfaceReconstructor.hpp CellData.hpp MeshTester.hpp + CellClipper.hpp + MIRUtilities.hpp + CellGenerator.hpp ) set(mir_sources - ../Axom.cpp MIRMesh.cpp InterfaceReconstructor.cpp ZooClippingTables.cpp CellData.cpp MeshTester.cpp + CellClipper.cpp + CellGenerator.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellClipper.cpp b/src/axom/mir/CellClipper.cpp new file mode 100644 index 0000000000..c5f25d4272 --- /dev/null +++ b/src/axom/mir/CellClipper.cpp @@ -0,0 +1,157 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellClipper.hpp" + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +CellClipper::CellClipper() +{ + +} + +//-------------------------------------------------------------------------------- + +CellClipper::~CellClipper() +{ + +} + +//-------------------------------------------------------------------------------- + +// Computes the t-values where each edge is clipped, as well as the topology of the new output cells after clipping the original cell +// Outputs the newElements, newVertices maps and the verticesClippingTValue array[] +void CellClipper::computeClippingPoints(const mir::Shape shapeType, + const std::vector >& vertexVF, + std::map >& newElements, + std::map >& newVertices, + axom::float64* tValues) +{ + // Determine the clipping case for the current element + unsigned int caseIndex = determineClippingCase( shapeType, vertexVF[0], vertexVF[1] ); + + std::vector > clipTable = getClipTable(shapeType); + + // Create the new polygons based on the clipping case + int currentElementIndex = 0; // the next available element index + int i = 0; + int numVertices = clipTable[caseIndex][i]; + + // for each new element in the current clipping case + while (numVertices != -1) + { + // for each vertex of the new element + for (int j = 0; j < numVertices; ++j) + { + // Find the id of the next vertex of the new element + int vID = clipTable[caseIndex][i + (j+1)]; + + // Associate the vertex and element together + newElements[currentElementIndex].push_back(vID); + newVertices[vID].push_back(currentElementIndex); + + if ( vID >= mir::utilities::numVerts(shapeType) ) + { + int vertexOneID = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vertexTwoID = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + tValues[vID] = computeTValueOnEdge( vertexVF[0][vertexOneID], vertexVF[1][vertexOneID], vertexVF[0][vertexTwoID], vertexVF[1][vertexTwoID] ); + } + } + + // Increment the element index counter, marking the current element as being finished processed + currentElementIndex++; + + // Increase index into lookup table to the next element + i += (numVertices + 1); + numVertices = clipTable[caseIndex][i]; + } +} + +//-------------------------------------------------------------------------------- + +unsigned int CellClipper::determineClippingCase(const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF) +{ + unsigned int caseIndex = 0; + + int numVertices = mir::utilities::numVerts(shapeType); + + for (int vID = 0; vID < numVertices; ++vID) + { + if (matOneVF[vID] > matTwoVF[vID]) + { + unsigned int bitIndex = (numVertices - 1) - vID; + + caseIndex |= (1 << bitIndex); + } + } + + return caseIndex; +} + +//-------------------------------------------------------------------------------- + +axom::float64 CellClipper::computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo) +{ + axom::float64 ret = 0.0; + + // TODO: Perhaps just handle NULL_MAT by return 0, since that is what will happen anyways? + // Handle NULL_MAT, which has a vf of -1.0, but which needs to be 0.0 for the purposes of computing the clipping point + if (vfMatOneVertexOne < 0.0) { vfMatOneVertexOne = 0.0; }; + if (vfMatTwoVertexOne < 0.0) { vfMatTwoVertexOne = 0.0; }; + if (vfMatOneVertexTwo < 0.0) { vfMatOneVertexTwo = 0.0; }; + if (vfMatTwoVertexTwo < 0.0) { vfMatTwoVertexTwo = 0.0; }; + + axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; + axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; + + if (denominator != 0.0) + { + ret = numerator / denominator; + } + + if (ret > 1.0 || ret < 0.0) + { + // This shouldn't happen... + printf(" OUT OF BOUNDS T VALUE: %f\n", ret); + + // Clamp the t value + ret = fmin(1.0, ret); + ret = fmax(0.0, ret); + } + + return ret; +} + +//-------------------------------------------------------------------------------- + +const std::vector >& CellClipper::getClipTable(const mir::Shape shapeType) +{ + switch ( shapeType ) + { + case mir::Shape::Triangle: + return triangleClipTableVec; + case mir::Shape::Quad: + return quadClipTableVec; + default: + printf("No clipping table for this shape type.\n"); + return triangleClipTableVec; + } +} + +//-------------------------------------------------------------------------------- + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellClipper.hpp b/src/axom/mir/CellClipper.hpp new file mode 100644 index 0000000000..fcdd650db3 --- /dev/null +++ b/src/axom/mir/CellClipper.hpp @@ -0,0 +1,120 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file CellClipper.hpp + * + * \brief Contains the specification for the CellClipper class. + * + */ + +#ifndef __CELL_CLIPPER_H +#define __CELL_CLIPPER_H + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" +#include "MIRUtilities.hpp" +#include "MIRMeshTypes.hpp" +#include "CellData.hpp" +#include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /** + * \class CellClipper + * + * \brief A class that contains the functionality for taking an input cell + * and determining how it should be clipped. + * + */ + class CellClipper + { + public: + /** + * \brief Default constructor. + */ + CellClipper(); + + /** + * \brief Default destructor. + */ + ~CellClipper(); + + /** + * \brief Computes the elements, vertices, and t values resulting from splitting the element with the given vertex volume fractions. + * + * \param shapeType The shape type of the element. + * \param vertexVF The vertex volume fractions of the two materials with which to clip the cell. + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * + */ + void computeClippingPoints(const mir::Shape shapeType, + const std::vector >& vertexVF, + std::map >& newElements, + std::map >& newVertices, + axom::float64* tValues); + + /** + * \brief Determines the index into the clipping table of the given shape based on the volume fractions at its vertices. + * + * \param shapeType The shape type of the element. + * \param matOneVF The volume fractions at the vertices of the element for the first material. + * \param matTwoVF The volume fractions at the vertices of the element for the second material. + * + * \return The index into the clipping table. + */ + unsigned int determineClippingCase(const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF); + + + /** + * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. + * + * \param vfMatOneVertexOne The volume fraction of material one present at vertex one. + * \param vfMatTwoVertexOne The volume fraction of material two present at vertex one. + * \param vfMatOneVertexTwo The volume fraction of material one present at vertex two. + * \param vfMatTwoVertexTwo The volume fraction of material two present at vertex two. + * + * \return The percent of the distance from vertex one to vertex two where the edge should be clipped. + * + * \note When one material's volume fractions dominates the other material's, then the edge should not be clipped and this function will return 0.0. + * \note When one of the materials is the NULL_MAT (and has vf = -1.0), these values are set to 0 in order to interpolate properly. + */ + axom::float64 computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo); + + private: + /** + * \brief Returns a reference to the appropriate clipping table to use for the shape type. + * + * \param The shape type of the element. + * + * \return A reference to the clipping table. + */ + const std::vector >& getClipTable(const mir::Shape shapeType); + + }; + +//-------------------------------------------------------------------------------- + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index 4e37813c86..176d1d6fc7 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -83,6 +83,12 @@ namespace mir m_mapData.m_elementParents.push_back(cellToMerge.m_mapData.m_elementParents[i]); } + // Merge the elements' shape types + for (unsigned long i = 0; i < cellToMerge.m_mapData.m_shapeTypes.size(); ++i) + { + m_mapData.m_shapeTypes.push_back(cellToMerge.m_mapData.m_shapeTypes[i]); + } + // Merge the total number of verts and elems in the resulting cell m_numVerts += cellToMerge.m_numVerts; m_numElems += cellToMerge.m_numElems; diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 1a681e5d19..a73134374c 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -50,6 +50,7 @@ namespace mir std::vector > m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + std::vector m_shapeTypes; // Data that goes into MIRMesh's shapeType IntMap }; /** diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp new file mode 100644 index 0000000000..05b61aa030 --- /dev/null +++ b/src/axom/mir/CellGenerator.cpp @@ -0,0 +1,218 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "CellGenerator.hpp" + + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + +CellGenerator::CellGenerator() +{ + +} + +//-------------------------------------------------------------------------------- + +CellGenerator::~CellGenerator() +{ + +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateTopologyData(const std::map >& newElements, + const std::map >& newVertices, + CellData& out_cellData) +{ + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); + + // Push the next element's vertices + for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; + } + } + + // Push the index that occurs after the last vertex + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); + + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); + + // Push the next vertex's elements + for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateVertexPositions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData) +{ + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + + if ( vID < mir::utilities::numVerts(shapeType) ) + { + // This vertex is one of the shape's original vertices + out_cellData.m_mapData.m_vertexPositions.push_back( vertexPositions[vID] ); + } + else + { + // This vertex is between two of the shape's original vertices + int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + out_cellData.m_mapData.m_vertexPositions.push_back( mir::utilities::interpolateVertexPosition( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); + } + } +} + +//-------------------------------------------------------------------------------- + +void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector >& vertexVF, + axom::float64* tValues, + CellData& out_cellData) +{ + out_cellData.m_mapData.m_vertexVolumeFractions.resize(vertexVF.size()); // vertexVF size is the number of materials + + for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + int vID = itr->first; + + for (int matID = 0; matID < vertexVF.size(); ++matID) + { + if ( vID < mir::utilities::numVerts(shapeType) ) + { + // This vertex is one of the shape's original vertices + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( vertexVF[matID][vID] ); + } + else + { + // This vertex is between two of the shape's original vertices + int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); + int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); + + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( mir::utilities::lerpFloat( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); + } + } + } +} + +//-------------------------------------------------------------------------------- + +int CellGenerator::determineCleanCellMaterial(const Shape elementShape, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector >& vertexVF) +{ + int dominantMaterial = matOne; + + axom::float64 matOneVF = -1.0; + axom::float64 matTwoVF = -1.0; + + for (unsigned long it = 0; it < vertexIDs.size(); ++it) + { + int vID = vertexIDs[it]; + + if ( vID >= 0 && vID < mir::utilities::numVerts(elementShape) ) + { + if (matOne != NULL_MAT) + { + matOneVF = vertexVF[matOne][vID]; + } + if (matTwo != NULL_MAT) + { + matTwoVF = vertexVF[matTwo][vID]; + } + + dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; + } + } + + return dominantMaterial; +} + +//-------------------------------------------------------------------------------- + +mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType, + const int numVerts) +{ + mir::Shape newShapeType; + if (parentShapeType == mir::Shape::Triangle || parentShapeType == mir::Shape::Quad) + { + // Handle the two-dimensional case + switch (numVerts) + { + case 3: + newShapeType = mir::Shape::Triangle; + break; + case 4: + newShapeType = mir::Shape::Quad; + break; + default: + newShapeType = mir::Shape::Triangle; + printf("Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + else + { + // Handle the three-dimensional case + switch (numVerts) + { + case 4: + newShapeType = mir::Shape::Tetrahedron; + break; + case 5: + newShapeType = mir::Shape::Pyramid; + break; + case 6: + newShapeType = mir::Shape::Triangular_Prism; + break; + case 8: + newShapeType = mir::Shape::Hexahedron; + break; + default: + newShapeType = mir::Shape::Tetrahedron; + printf("Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + return newShapeType; +} + + +} +} \ No newline at end of file diff --git a/src/axom/mir/CellGenerator.hpp b/src/axom/mir/CellGenerator.hpp new file mode 100644 index 0000000000..ccf189537b --- /dev/null +++ b/src/axom/mir/CellGenerator.hpp @@ -0,0 +1,150 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file CellGenerator.hpp + * + * \brief Contains the specification for the CellGenerator class. + * + */ + +#ifndef __CELL_GENERATOR_H__ +#define __CELL_GENERATOR_H__ + +#include "axom/core.hpp" // for axom macros +#include "axom/slam.hpp" // unified header for slam classes and functions + +#include "MIRMesh.hpp" +#include "MIRUtilities.hpp" +#include "MIRMeshTypes.hpp" +#include "CellData.hpp" +#include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" +#include "CellClipper.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ + +//-------------------------------------------------------------------------------- + + /** + * \class CellGenerator + * + * \brief A class that generates that uses the clipping information to generate + * the data needed in order to produce a clean, reconstructed mesh. + */ + class CellGenerator + { + public: + + /** + * \brief Default constructor. + */ + CellGenerator(); + + /** + * \brief Default destructor. + */ + ~CellGenerator(); + + /** + * \brief Generates the topology of the new elements resulting from a split. + * + * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. + * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. + * \param out_cellData Container to store the topology data of the generated elements. + */ + void generateTopologyData(const std::map >& newElements, + const std::map >& newVertices, + CellData& out_cellData); + + + /** + * \brief Generates the vertex positions values for each of the new vertices of the generated element. + * + * \param shapeType The shape type of the element. + * \param newVertices An ordered map of vertices that compose the newly generated elements. + * \param vertexPositions A vector of positions for each of the original element's vertices. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * \param out_cellData Container to store the vertex position data of the generated elements. + * + * \note New vertex positions are interpolated from the original vertex positions. + */ + void generateVertexPositions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData); + + + /** + * \brief Generates the vertex volume fractions for each of the new vertices of the generated element. + * + * \param shapeType The shape type of the element. + * \param newVertices An ordered map of vertices that compose the newly generated elements. + * \param vertexVF A vector of positions for each of the original element's vertices. + * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. + * \param out_cellData Container to store the vertex position data of the generated elements. + * + * \note New vertex positions are interpolated from the original vertex volume fractions. + */ + void generateVertexVolumeFractions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector >& vertexVF, + axom::float64* tValues, + CellData& out_cellData); + + /** + * \brief Determines the more dominant material of the two given for the given element. + * + * \param shapeType An enumerator denoting the element's shape. + * \param vertexIDs A list of vertex IDs into the vertexVF param. + * \param matOne The ID of the first material. + * \param matTwo The ID of the second material. + * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. + * + * \return The ID of the dominant material of the element. + * + * \note The dominant element for the 2D/3D cases will be the same as the material present at one of the + * original vertices that existed prior to the split. So, if you can find this vertex and its dominant + * material, then you know the dominant material of this new element. + * + * \note It is assumed that the given cell is one that results from splitting its parent cell. + */ + int determineCleanCellMaterial(const Shape shapeType, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector >& vertexVF); + + /** + * \brief Determine the shape type of an element. + * + * \param parentShapeType The shape of the element from which the new element is generated. + * \param numVerts The number of vertices of the new element. + * + * \note It is assumed that the given cell is one that results from splitting its parent cell. + */ + mir::Shape determineElementShapeType(const Shape parentShapeType, + const int numVerts); + + /** + * \brief Ensures that the element is dominated by a material that is actually present in the original parent cell. + * + * \param + */ + // void fixDominantMaterial(mir::MIRMesh& originalMesh, const std::map >& newElements, const int matOne, const int matTwo, CellData& out_cellData); + }; + +//-------------------------------------------------------------------------------- + +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 9152dbb30d..e4fdab75d8 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -26,7 +26,8 @@ InterfaceReconstructor::~InterfaceReconstructor() //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh) +void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh, + mir::MIRMesh& outputMesh) { // Store a reference to the original mesh m_originalMesh = inputMesh; @@ -50,7 +51,7 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; int matOne = matID; - computeClippingPoints(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); + generateCleanCells(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); } // Merge each of the cells into the first CellData struct @@ -73,13 +74,16 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& } // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon - - return finalMesh; + outputMesh = finalMesh; + // return finalMesh; } //-------------------------------------------------------------------------------- -mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent) +void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh) { int numElems = inputMesh.m_elems.size(); int numMaterials = inputMesh.m_numMaterials; @@ -88,12 +92,13 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: mir::MIRMesh meshToImprove(inputMesh); // Calculate the reconstruction on the unmodified, input mesh - mir::MIRMesh resultingMesh = computeReconstructedInterface(meshToImprove); + computeReconstructedInterface(meshToImprove, outputMesh); for (int it = 0; it < numIterations; ++it) { + printf("iteration %d\n", it); // Calculate the output element volume fractions of the resulting output mesh - std::vector > resultingElementVF = resultingMesh.computeOriginalElementVolumeFractions(); + std::vector > resultingElementVF = outputMesh.computeOriginalElementVolumeFractions(); // Initialize the vector to store the improved element volume fractions std::vector > improvedElementVF; @@ -113,417 +118,100 @@ mir::MIRMesh InterfaceReconstructor::computeReconstructedInterfaceIterative(mir: meshToImprove.constructMeshVolumeFractionsMaps(improvedElementVF); // Calculate the reconstruction on the modified, input mesh - resultingMesh = computeReconstructedInterface(meshToImprove); - } - - return resultingMesh; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) -{ - // Find the vertices associated with each element - auto elementVertices = tempMesh.m_bdry[eID]; - - // Triangle Case - if (elementVertices.size() == 3) - { - computeTriangleClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); - } - // Quad Case - if (elementVertices.size() == 4) - { - computeQuadClippingPoints(eID, matOneID, matTwoID, tempMesh, out_cellData); + computeReconstructedInterface(meshToImprove, outputMesh); } } //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) +void InterfaceReconstructor::generateCleanCells(const int eID, + const int matOne, + const int matTwo, + mir::MIRMesh& tempMesh, + CellData& out_cellData) { - // Determine the clipping case + mir::Shape shapeType = (mir::Shape) tempMesh.m_shapeTypes[eID]; auto elementVertices = tempMesh.m_bdry[eID]; - int upperLeftVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; - int upperRightVertex = elementVertices[3]; - - unsigned int caseIndex = determineQuadClippingCase(tempMesh, matOneID, matTwoID, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex); - - // Generate new elements + // Set up data structures needed to compute how element should be clipped std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[8] = {0,0,0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[8] = {0,0,0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped - - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = quadClipTable[caseIndex][i]; + std::vector verticesPresent(mir::utilities::maxPossibleNumVerts(shapeType), 0); // Vector of flags denoting whether the vertex is present in the current case or not + axom::float64* tValues = new axom::float64[ mir::utilities::maxPossibleNumVerts(shapeType) ]{0}; // Array of t values that denote the percent value of where the edge should be clipped - // for each new element in the current clipping case - while (numVertices != -1) - { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) - { - // Find the id of the next vertex of the new element - int vID = quadClipTable[caseIndex][i + (j+1)]; - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; - - // Find t using linear interpolation for any vertex that is not one of the original 4 vertices - if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperLeftVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 6) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 7) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperRightVertex, upperLeftVertex, matOneID, matTwoID, tempMesh); - } - } - - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; - - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = quadClipTable[caseIndex][i]; - } - - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); - - generateTopologyData(newElements, newVertices, out_cellData); - generateVertexPositionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - generateVertexVolumeFractionsFromQuad(newVertices, tempMesh, verticesClippingTValue, upperLeftVertex, lowerLeftVertex, lowerRightVertex, upperRightVertex, out_cellData); - - // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + // Set up the volume fractions for the current element for the two materials currently being considered + std::vector > vertexVF(2); + for (int vID = 0; vID < elementVertices.size(); ++vID) { - int dominantMaterial = determineDominantMaterial(Shape::Quad, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); - out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); - } - - // Determine and store the parent of this element - out_cellData.m_mapData.m_elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; - } - - // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; - int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - - if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + int originalVID = elementVertices[vID]; + if (matOne == NULL_MAT) { - // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; - else - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; + vertexVF[0].push_back(-1.0); } - } - - // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present - int evIndexSubtract[8]; - for (int i = 0; i < 8; ++i) - { - if (verticesPresent[i] == 1) - evIndexSubtract[i] = 0; else - evIndexSubtract[i] = 1; - } - for (int i = 1; i < 8; ++i) - evIndexSubtract[i] += evIndexSubtract[i - 1]; - - for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) - { - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; - } -} - -//-------------------------------------------------------------------------------- - -unsigned int InterfaceReconstructor::determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex) -{ - // Determine the dominant color at each vertex - int upperLeftColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID, upperRightColor = matOneID; - - if (matOneID == NULL_MAT) - upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matTwoID; - if (matTwoID == NULL_MAT) - upperLeftColor = lowerLeftColor = lowerRightColor = upperRightColor = matOneID; - if (matOneID != NULL_MAT && matTwoID != NULL_MAT) { - upperLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperLeftVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - upperRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperRightVertex] ? matOneID : matTwoID; + vertexVF[0].push_back( tempMesh.m_materialVolumeFractionsVertex[matOne][originalVID] ); } - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperLeftColor == matOneID) caseIndex |= 8; - if (lowerLeftColor == matOneID) caseIndex |= 4; - if (lowerRightColor == matOneID) caseIndex |= 2; - if (upperRightColor == matOneID) caseIndex |= 1; - - return caseIndex; -} - -//-------------------------------------------------------------------------------- - -mir::Point2 InterfaceReconstructor::interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t) -{ - mir::Point2 interpolatedPoint; - interpolatedPoint.m_x = (1 - t) * vertexOnePos.m_x + t * vertexTwoPos.m_x; - interpolatedPoint.m_y = (1 - t) * vertexOnePos.m_y + t * vertexTwoPos.m_y; - return interpolatedPoint; -} - -//-------------------------------------------------------------------------------- - -axom::float64 InterfaceReconstructor::lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t) -{ - return (1 - t) * f0 + t * f1; -} - -//-------------------------------------------------------------------------------- - -axom::float64 InterfaceReconstructor::computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh) -{ - axom::float64 ret = 0.0; - - axom::float64 vfMatOneVertexOne = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexOneID]; - axom::float64 vfMatTwoVertexOne = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexOneID]; - axom::float64 vfMatOneVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matOneID][vertexTwoID]; - axom::float64 vfMatTwoVertexTwo = tempMesh.m_materialVolumeFractionsVertex[matTwoID][vertexTwoID]; - - if (matOneID == NULL_MAT) - vfMatOneVertexOne = vfMatOneVertexTwo = 0.0; - - if (matTwoID == NULL_MAT) - vfMatTwoVertexOne = vfMatTwoVertexTwo = 0.0; - - axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; - axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; - - if (denominator != 0.0) - ret = numerator / denominator; - - if (ret > 1.0 || ret < 0.0) - { - // This shouldn't happen... - printf(" OUT OF BOUNDS T VALUE: %f\n", ret); - - // Clamp the t value - ret = fmin(1.0, ret); - ret = fmax(0.0, ret); - } - - return ret; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData) -{ - // Store the evInds and evBegins data in the output vectors - int currentEVBeginIndex = 0; - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + if (matTwo == NULL_MAT) { - // Push the start index of the next element - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - - // Push the next element's vertices - for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) - { - out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); - ++currentEVBeginIndex; - } + vertexVF[1].push_back(-1.0); } - - // Push the index that occurs after the last vertex - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - - // Store the veInds and veBegins data in the output vectors - int currentVEBeginIndex = 0; - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + else { - // Push the start index of the vertex's elements - out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); - - // Push the next vertex's elements - for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) - { - out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); - ++currentVEBeginIndex; - } + vertexVF[1].push_back( tempMesh.m_materialVolumeFractionsVertex[matTwo][originalVID] ); } - - // Push the index that occurs after the last element - out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); -} - -//-------------------------------------------------------------------------------- + } -void InterfaceReconstructor::generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) -{ - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets + // Clip the element + CellClipper clipper; + clipper.computeClippingPoints(shapeType, vertexVF, newElements, newVertices, tValues); - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector + // Determine which vertices are present for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - if (vID == 0) - newPoints[vID] = tempMesh.m_vertexPositions[upperLeftVertex]; - if (vID == 1) - newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = tempMesh.m_vertexPositions[upperRightVertex]; - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperLeftVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 6) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperRightVertex], verticesClippingTValue[vID]); - if (vID == 7) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperRightVertex], tempMesh.m_vertexPositions[upperLeftVertex], verticesClippingTValue[vID]); - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - { - out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); + verticesPresent[vID] = 1; } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData) -{ - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) - { - if (vID == 0) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex]); - if (vID == 1) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex]); - if (vID == 4) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 6) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], verticesClippingTValue[vID])); - if (vID == 7) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperLeftVertex], verticesClippingTValue[vID])); - } - } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData) -{ - // Determine the clipping case - auto elementVertices = tempMesh.m_bdry[eID]; - int upperVertex = elementVertices[0]; - int lowerLeftVertex = elementVertices[1]; - int lowerRightVertex = elementVertices[2]; + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); - unsigned int caseIndex = determineTriangleClippingCase(tempMesh, matOneID, matTwoID, upperVertex, lowerLeftVertex, lowerRightVertex); + // Generate the topology and connectivity of the newly split element + CellGenerator cellGenerator; + cellGenerator.generateTopologyData(newElements, newVertices, out_cellData); - // Generate new elements - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - int verticesPresent[6] = {0,0,0,0,0,0}; // Array of flags denoting whether the vertex is present in the current case or not - axom::float64 verticesClippingTValue[6] = {0,0,0,0,0,0}; // Array of t values that denote the percent value of where the edge should be clipped - - // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index - int i = 0; - int numVertices = triangleClipTable[caseIndex][i]; + // Generate the vertex position values of the newly split element + std::vector originalElementVertexPositions; + for (int vID = 0; vID < elementVertices.size(); ++vID) + { + int originalVID = elementVertices[vID]; + originalElementVertexPositions.push_back( tempMesh.m_vertexPositions[originalVID] ); + } + cellGenerator.generateVertexPositions( shapeType, newVertices, originalElementVertexPositions, tValues, out_cellData); - // for each new element in the current clipping case - while (numVertices != -1) + // Generate the vertex volume fractions of the newly split elements + std::vector > originalElementVertexVF; + for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) { - // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) + std::vector materialVertexVF; + for (int vID = 0; vID < elementVertices.size(); ++vID) { - // Find the id of the next vertex of the new element - int vID = triangleClipTable[caseIndex][i + (j+1)]; - - // Associate the vertex and element together - newElements[currentElementIndex].push_back(vID); - newVertices[vID].push_back(currentElementIndex); - verticesPresent[vID] = 1; + int originalVID = elementVertices[vID]; - // Find t using linear interpolation for any vertex that is not one of the original 3 vertices - if(vID == 3) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(upperVertex, lowerLeftVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 4) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerLeftVertex, lowerRightVertex, matOneID, matTwoID, tempMesh); - } - else if(vID == 5) - { - verticesClippingTValue[vID] = computeClippingPointOnEdge(lowerRightVertex, upperVertex, matOneID, matTwoID, tempMesh); - } + materialVertexVF.push_back( tempMesh.m_materialVolumeFractionsVertex[matID][originalVID] ); } - - // Increment the element index counter, marking the current element as being finished processed - currentElementIndex++; - - // Increase index into lookup table to the next element - i += (numVertices + 1); - numVertices = triangleClipTable[caseIndex][i]; + originalElementVertexVF.push_back( materialVertexVF ); } - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); - - generateTopologyData(newElements, newVertices, out_cellData); - generateVertexPositionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); - generateVertexVolumeFractionsFromTriangle(newVertices, tempMesh, verticesClippingTValue, upperVertex, lowerLeftVertex, lowerRightVertex, out_cellData); + cellGenerator.generateVertexVolumeFractions( shapeType, newVertices, originalElementVertexVF, tValues, out_cellData); // Determine and store the dominant material of this element for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int dominantMaterial = determineDominantMaterial(Shape::Triangle, itr->second, matOneID, matTwoID, out_cellData.m_mapData.m_vertexVolumeFractions); + int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, itr->second, matOne, matTwo, out_cellData.m_mapData.m_vertexVolumeFractions); out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); } @@ -534,6 +222,14 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; } + // Determine the generated elements' shape types + out_cellData.m_mapData.m_shapeTypes.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.m_mapData.m_shapeTypes[itr->first] = cellGenerator.determineElementShapeType(shapeType, itr->second.size()); + } + + // TODO: Make a function that does this // Check that each element is dominated by a material that is actually present in the original parent cell for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { @@ -543,170 +239,33 @@ void InterfaceReconstructor::computeTriangleClippingPoints(const int eID, const if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) { // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOneID) - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwoID; + if (currentDominantMaterial == matOne) + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwo; else - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOneID; + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOne; } - } - - // Modify the evIndex values to account for the fact that perhaps not all 6 possible vertices are present - int evIndexSubtract[6]; - for (int i = 0; i < 6; ++i) + } + + // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + std::vector evIndexSubtract; + evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); + for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) { if (verticesPresent[i] == 1) evIndexSubtract[i] = 0; else evIndexSubtract[i] = 1; } - - for (int i = 1; i < 6; ++i) + for (unsigned long i = 1; i < evIndexSubtract.size(); ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned int i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; - -} - -//-------------------------------------------------------------------------------- - -unsigned int InterfaceReconstructor::determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex) -{ - // Determine the dominant color at each vertex - int upperColor = matOneID, lowerLeftColor = matOneID, lowerRightColor = matOneID; - - if (matOneID == NULL_MAT) - upperColor = lowerLeftColor = lowerRightColor = matTwoID; - if (matTwoID == NULL_MAT) - upperColor = lowerLeftColor = lowerRightColor = matOneID; - if (matOneID != NULL_MAT && matTwoID != NULL_MAT) - { - upperColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][upperVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][upperVertex] ? matOneID : matTwoID; - lowerLeftColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerLeftVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerLeftVertex] ? matOneID : matTwoID; - lowerRightColor = tempMesh.m_materialVolumeFractionsVertex[matOneID][lowerRightVertex] > tempMesh.m_materialVolumeFractionsVertex[matTwoID][lowerRightVertex] ? matOneID : matTwoID; - } - - // Create the index into the quad clipping lookup table using the dominant colors at each vertex - unsigned int caseIndex = 0; - if (upperColor == matOneID) caseIndex |= 4; - if (lowerLeftColor == matOneID) caseIndex |= 2; - if (lowerRightColor == matOneID) caseIndex |= 1; - - return caseIndex; -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) -{ - std::map newPoints; // hashmap of the new vertices' positions | Note: maps are ordered sets - - // Find the position coordinates of the vertices and then store the positions of the vertices in the return vector - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - if (vID == 0) - newPoints[vID] = tempMesh.m_vertexPositions[upperVertex]; - if (vID == 1) - newPoints[vID] = tempMesh.m_vertexPositions[lowerLeftVertex]; - if (vID == 2) - newPoints[vID] = tempMesh.m_vertexPositions[lowerRightVertex]; - if (vID == 3) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[upperVertex], tempMesh.m_vertexPositions[lowerLeftVertex], verticesClippingTValue[vID]); - if (vID == 4) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerLeftVertex], tempMesh.m_vertexPositions[lowerRightVertex], verticesClippingTValue[vID]); - if (vID == 5) - newPoints[vID] = interpolateVertexPosition(tempMesh.m_vertexPositions[lowerRightVertex], tempMesh.m_vertexPositions[upperVertex], verticesClippingTValue[vID]); - } - - // Store the positions of the vertices in the return vector - for (auto itr = newPoints.begin(); itr != newPoints.end(); itr++) - { - out_cellData.m_mapData.m_vertexPositions.push_back(itr->second); - } -} - -//-------------------------------------------------------------------------------- - -void InterfaceReconstructor::generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData) -{ - // Calculate the vertex fractions at each vertex (use t value!) - // Make sure the output volume fractions containers are the proper size - out_cellData.m_mapData.m_vertexVolumeFractions.resize(tempMesh.m_numMaterials); - - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - int vID = itr->first; - for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) - { - if (vID == 0) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex]); - if (vID == 1) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex]); - if (vID == 2) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex]); - if (vID == 3) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], verticesClippingTValue[vID])); - if (vID == 4) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerLeftVertex], tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], verticesClippingTValue[vID])); - if (vID == 5) - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back(lerpFloat(tempMesh.m_materialVolumeFractionsVertex[matID][lowerRightVertex], tempMesh.m_materialVolumeFractionsVertex[matID][upperVertex], verticesClippingTValue[vID])); - } - } -} - -//-------------------------------------------------------------------------------- - -int InterfaceReconstructor::determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF) -{ - int dominantMaterial = matOne; - - axom::float64 matOneVF = -1.0; - axom::float64 matTwoVF = -1.0; - - if (elementShape == Shape::Triangle) + for (unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) { - for (unsigned long it = 0; it < vertexIDs.size(); ++it) - { - int vID = vertexIDs[it]; - if (vID == 0 || vID == 1 || vID == 2) - { - if (matOne != NULL_MAT) - { - matOneVF = vertexVF[matOne][vID]; - } - if (matTwo != NULL_MAT) - { - matTwoVF = vertexVF[matTwo][vID]; - } - - dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; - } - } - } - - if (elementShape == Shape::Quad) - { - for (unsigned long it = 0; it < vertexIDs.size(); ++it) - { - int vID = vertexIDs[it]; - if (vID == 0 || vID == 1 || vID == 2 || vID == 3) - { - if (matOne != NULL_MAT) - { - matOneVF = vertexVF[matOne][vID]; - } - if (matTwo != NULL_MAT) - { - matTwoVF = vertexVF[matTwo][vID]; - } - - dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; - } - } + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; } - return dominantMaterial; + // Memory management + delete[] tValues; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 646e1f284e..ed674af4a4 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -19,6 +19,9 @@ #include "MIRMesh.hpp" #include "CellData.hpp" #include "ZooClippingTables.hpp" +#include "MIRUtilities.hpp" +#include "CellClipper.hpp" +#include "CellGenerator.hpp" #include @@ -62,10 +65,10 @@ namespace mir * \brief Performs material interface reconstruction using the zoo-based algorithm. * * \param inputMesh The mesh composed of mixed cells. - * - * \return The mesh composed of clean cells. + * \param outputMesh The mesh composed of clean cells. */ - mir::MIRMesh computeReconstructedInterface(mir::MIRMesh& inputMesh); + void computeReconstructedInterface(mir::MIRMesh& inputMesh, + mir::MIRMesh& outputMesh); /** @@ -74,198 +77,27 @@ namespace mir * \param inputMesh The mesh made up of mixed cells. * \param numIterations The number of iterations for which to run the algorithm. * \param percent The percent of the difference to use when modifying the original element volume fractions. - * - * \return The mesh made up of clean cells. - */ - mir::MIRMesh computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, const int numIterations, const axom::float64 percent); - - // private: - - /** - * \brief A wrapper function that calls the appropriate splitting method based on the shape of the given element. - * - * \param eID The ID of the element to be split. - * \param matOneID The ID of the first material to use for splitting the element, - * \param matTwoID The ID of the second material to use for splitting the element. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the element. - */ - void computeClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Splits the specified quad element into clean cells of the two given material types - * based on the volume fractions of the two materials. - * - * \param eID The ID of the quad element. - * \param matOneID The ID of the first material to use for splitting the quad, - * \param matTwoID The ID of the second material to use for splitting the quad. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the quad. - */ - void computeQuadClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Splits the specified triangle element into clean cells of the two given material types - * based on the volume fractions of the two materials. - * - * \param eID The ID of the triangle element. - * \param matOneID The ID of the first material to use for splitting the triangle, - * \param matTwoID The ID of the second material to use for splitting the triangle. - * \param tempMesh A pointer to the intermediate mesh that is currently being processed. - * \param out_cellData Container to store the output of splitting the triangle. - */ - void computeTriangleClippingPoints(const int eID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh, CellData& out_cellData); - - - /** - * \brief Performs linear interpolation between the two vertex positions. - * - * \param vertexOnePos The position of the first vertex. - * \param vertexTwoPos The position of the second vertex. - * \param t The percent of the distance from vertex one to vertex two to interpolate at. - * - * \return The interpolated position. - */ - mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, const mir::Point2& vertexTwoPos, const float t); - - - /** - * \brief Performs linear interpolation between the two given float values. - * - * \param f0 The first float value. - * \param f1 The second float value. - * \param t The percent of the distance from the first float value to the second. - * - * \return The interpolated value. - */ - axom::float64 lerpFloat(const axom::float64 f0, const axom::float64 f1, const axom::float64 t); - - - /** - * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. - * - * \param vertexOneID The ID of the first vertex. - * \param vertexTwoID The ID of the second vertex. - * \param matOneID The ID of the first material to use for interpolating between. - * \param matTwoID The ID of the second material to use for interpolating between. - * \param tempMesh The intermediate mesh that is currently being processed. - * - * \return The t value, which is the percent distance from vertex one to vertex two where the two volume fractions are equal. - */ - axom::float64 computeClippingPointOnEdge(const int vertexOneID, const int vertexTwoID, const int matOneID, const int matTwoID, mir::MIRMesh& tempMesh); - - - /** - * \brief Generates the topology of the new elements resulting from a split. - * - * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param out_cellData Container to store the topology data of the generated elements. - */ - void generateTopologyData(const std::map >& newElements, const std::map >& newVertices, CellData& out_cellData); - - - /** - * \brief Calculates the bit map representing the clipping case for a quad. - * - * \param tempMesh The intermediate mesh that is currently being processed. - * \param matOneID The ID of the first material being used for splitting. - * \param matTwoID The ID of the second material being used for splitting. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * - * \return The bitmap representing the clipping case. - */ - unsigned int determineQuadClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex); - - /** - * \brief Generate the vertex position data for the new elements resulting from splitting a quad. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex position data of the generated elements. - */ - void generateVertexPositionsFromQuad(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); - - /** - * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a quad. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperLeftVertex The ID of the upper left vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param upperRightVertex The ID of the upper right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex volume fraction data of the generated elements. + * \param outputMesh The mesh composed of clean cells. */ - void generateVertexVolumeFractionsFromQuad(std::map >& newVertices, mir::MIRMesh&tempMesh, axom::float64* verticesClippingTValue, const int upperLeftVertex, const int lowerLeftVertex, const int lowerRightVertex, const int upperRightVertex, CellData& out_cellData); + void computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh); /** - * \brief Calculates the bit map representing the clipping case for a triangle. - * - * \param tempMesh The intermediate mesh that is currently being processed. - * \param matOneID The ID of the first material being used for splitting. - * \param matTwoID The ID of the second material being used for splitting. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * - * \return The bitmap representing the clipping case. - */ - unsigned int determineTriangleClippingCase(mir::MIRMesh& tempMesh, const int matOneID, const int matTwoID, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex); - - /** - * \brief Generate the vertex position data for the new elements resulting from splitting a triangle. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex position data of the generated elements. - */ - void generateVertexPositionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - - /** - * \brief Generate the vertex volume fraction data for the new vertices resulting from splitting a triangle. - * - * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. - * \param tempMesh The intermediate mesh that is currently being processed. - * \param verticesClippingTValue The set of t values where the quad should be split on each edge. - * \param upperVertex The ID of the upper vertex in the global frame of the original mesh. - * \param lowerLeftVertex The ID of the lower left vertex in the global frame of the original mesh. - * \param lowerRightVertex The ID of the lower right vertex in the global frame of the original mesh. - * \param out_cellData Container to store the vertex volume fraction data of the generated elements. - */ - void generateVertexVolumeFractionsFromTriangle(const std::map >& newVertices, mir::MIRMesh& tempMesh, axom::float64* verticesClippingTValue, const int upperVertex, const int lowerLeftVertex, const int lowerRightVertex, CellData& out_cellData); - - /** - * \brief Determines the mroe dominant material of the two given for the given element. - * - * \param elementShape An enumerator denoting the element's shape. - * \param vertexIDs A list of vertex IDs into the vertexVF param. - * \param matOne The ID of the first material. - * \param matTwo The ID of the second material. - * \param vertexVF The list of volume fractions associated with the given vertices in the vertexIDs param. - * - * \return The ID of the dominant material of the element. + * \brief Generates a set of clean cells by splitting the element with the two given materials. * - * \note The dominant element for the 2D cases will be the same as the material present at one of the - * original vertices that existed prior to the split. So, if you can find this vertex and its dominant - * material, then you know the dominant material of this new element. + * \param eID The ID of the element to be split. + * \param matOne The first material to split with. + * \param matTwo The second material to split with. + * \param tempMesh A reference to the mesh the element comes from. + * \param out_cellData Container to store the data of the generated elements. */ - int determineDominantMaterial(const Shape elementShape, const std::vector& vertexIDs, const int matOne, const int matTwo, const std::vector >& vertexVF); + void generateCleanCells(const int eID, + const int matOne, + const int matTwo, + mir::MIRMesh& tempMesh, + CellData& out_cellData); private: mir::MIRMesh m_originalMesh; diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 6e7c687dcf..c47fecf3bf 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -33,6 +33,7 @@ MIRMesh::MIRMesh(MIRMesh* _mesh) m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; m_elementParentIDs = _mesh->m_elementParentIDs; + m_shapeTypes = _mesh->m_shapeTypes; m_elementDominantMaterials = _mesh->m_elementDominantMaterials; m_numMaterials = _mesh->m_numMaterials; } @@ -46,7 +47,12 @@ MIRMesh::~MIRMesh() //-------------------------------------------------------------------------------- -void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF) +void MIRMesh::initializeMesh(const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector >& _elementVF) { // Initialize the vertex and element sets m_verts = _verts; @@ -64,9 +70,10 @@ void MIRMesh::initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, constructMeshRelations(); // Initialize the mesh's data maps - constructVertexPositionMap(_mapData.m_vertexPositions.data()); - constructElementParentMap(_mapData.m_elementParents.data()); + constructVertexPositionMap(_mapData.m_vertexPositions); + constructElementParentMap(_mapData.m_elementParents); constructElementDominantMaterialMap(_mapData.m_elementDominantMaterials); + constructElementShapeTypesMap(_mapData.m_shapeTypes); // Initialize the element and vertex volume fraction maps if (_elementVF.size() > 0) @@ -121,7 +128,7 @@ void MIRMesh::constructMeshRelations() //-------------------------------------------------------------------------------- -void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > elementVF) +void MIRMesh::constructMeshVolumeFractionsMaps(const std::vector >& elementVF) { // Clear the old maps m_materialVolumeFractionsElement.clear(); @@ -170,7 +177,7 @@ void MIRMesh::constructMeshVolumeFractionsMaps(std::vector > vertexVF) +void MIRMesh::constructMeshVolumeFractionsVertex(const std::vector >& vertexVF) { // Initialize the maps for all of the materials with the input volume fraction data for each vertex for (int matID = 0; matID < m_numMaterials; ++matID) @@ -190,7 +197,7 @@ void MIRMesh::constructMeshVolumeFractionsVertex(std::vector& data) { // construct the position map on the vertices m_vertexPositions = PointMap( &m_verts ); @@ -203,7 +210,7 @@ void MIRMesh::constructVertexPositionMap(Point2* data) //-------------------------------------------------------------------------------- -void MIRMesh::constructElementParentMap(int* elementParents) +void MIRMesh::constructElementParentMap(const std::vector& elementParents) { // Initialize the map for the elements' parent IDs m_elementParentIDs = IntMap( &m_elems ); @@ -217,7 +224,7 @@ void MIRMesh::constructElementParentMap(int* elementParents) //-------------------------------------------------------------------------------- -void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMaterials) +void MIRMesh::constructElementDominantMaterialMap(const std::vector& dominantMaterials) { // Initialize the map for the elements' dominant colors m_elementDominantMaterials = IntMap( &m_elems ); @@ -231,6 +238,20 @@ void MIRMesh::constructElementDominantMaterialMap(std::vector dominantMater //-------------------------------------------------------------------------------- +void MIRMesh::constructElementShapeTypesMap(const std::vector& shapeTypes) +{ + // Initialize the map for the elements' dominant colors + m_shapeTypes = IntMap( &m_elems ); + + // Copy the dat for the elements + for (int eID = 0; eID < m_elems.size(); ++eID) + m_shapeTypes[eID] = shapeTypes[eID]; + + SLIC_ASSERT_MSG( m_shapeTypes.isValid(), "Element dominant materials map is not valid."); +} + +//-------------------------------------------------------------------------------- + void MIRMesh::print() { printf("\n------------------------Printing Mesh Information:------------------------\n"); @@ -287,6 +308,13 @@ void MIRMesh::print() } printf("}\n"); + printf("shapeTypes: { "); + for (int i = 0; i < m_elems.size(); ++i) + { + printf("%d ", m_shapeTypes[i]); + } + printf("}\n"); + printf("vertexVolumeFractions: { \n"); for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { @@ -354,10 +382,9 @@ void MIRMesh::writeMeshToFile(std::string filename) meshfile << "\n\nCELL_TYPES " << m_elems.size() << "\n"; for (int i = 0; i < m_elems.size(); ++i) { - int nVerts = m_meshTopology.m_evBegins[i + 1] - m_meshTopology.m_evBegins[i]; - if (nVerts == 3) + if (m_shapeTypes[i] == mir::Shape::Triangle) meshfile << "5\n"; - else if (nVerts == 4) + else if (m_shapeTypes[i] == mir::Shape::Quad) meshfile << "9\n"; } @@ -423,7 +450,9 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) +axom::float64 MIRMesh::computeTriangleArea(Point2 p0, + Point2 p1, + Point2 p2) { axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 @@ -436,7 +465,10 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) +axom::float64 MIRMesh::computeQuadArea(Point2 p0, + Point2 p1, + Point2 p2, + Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); } diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 63b23d2ccc..984ada037e 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -76,7 +76,12 @@ namespace mir * \param _mapData The data used to initialized the maps associated with the vertex and element sets. * \param _elementVF The volume fractions of each element. Note that this is an optional parameter. */ - void initializeMesh(VertSet _verts, ElemSet _elems, int _numMaterials, CellTopologyData _topology, CellMapData _mapData, std::vector > _elementVF = {}); + void initializeMesh(const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector >& _elementVF = {}); /** * \brief Constructs the mesh boundary and coboundary relations. @@ -91,14 +96,14 @@ namespace mir * * \param elementVF The volume fractions of each element. */ - void constructMeshVolumeFractionsMaps(std::vector > elementVF); + void constructMeshVolumeFractionsMaps(const std::vector >& elementVF); /** * \brief Constructs the vertex volume fraction map. * * \param vertexVF The volume fractions of each vertex. */ - void constructMeshVolumeFractionsVertex(std::vector > vertexVF); + void constructMeshVolumeFractionsVertex(const std::vector >& vertexVF); /** * \brief Prints out the data contained within this mesh in a nice format. @@ -110,7 +115,7 @@ namespace mir * * \param filename The location where the mesh file will be read from. */ - void readMeshFromFile(std::string filename); + void readMeshFromFile(const std::string filename); /** * \brief Writes out the mesh to the given file. @@ -119,7 +124,7 @@ namespace mir * * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. */ - void writeMeshToFile(std::string filename); + void writeMeshToFile(const std::string filename); /** @@ -132,23 +137,30 @@ namespace mir /** * \brief Constucts the positions map on the vertices. * - * \param data The array of position data for each vertex. + * \param data The vector of position data for each vertex. */ - void constructVertexPositionMap(Point2* data); + void constructVertexPositionMap(const std::vector& data); /** * \brief Constructs the map of elements to their original element parent. * - * \param cellParents The array of parent IDs for each element of the mesh. + * \param cellParents The vector of parent IDs for each element of the mesh. */ - void constructElementParentMap(int* cellParents); + void constructElementParentMap(const std::vector& elementParents); /** * \brief Constructs the map of elements to their dominant materials. * * \param dominantMaterials A vector of material ids that are the dominant material of each element. */ - void constructElementDominantMaterialMap(std::vector dominantMaterials); + void constructElementDominantMaterialMap(const std::vector& dominantMaterials); + + /** + * \brief Constructs the map of elements to their shape types. + * + * \param shapeTypes A vector of shape enumerators that are the shape type of each element. + */ + void constructElementShapeTypesMap(const std::vector& shapeTypes); /** * \brief Computes the area of the triangle defined by the given three vertex positions using Heron's formula. @@ -157,7 +169,9 @@ namespace mir * \param p1 The position of the second vertex. * \param p2 The position of the third vertex. */ - axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); + axom::float64 computeTriangleArea(Point2 p0, + Point2 p1, + Point2 p2); /** * \brief Computes the area of the quad defined by the given four vertex positions. @@ -169,7 +183,10 @@ namespace mir * * \note It is assumed that the points are given in consecutive, counter-clockwise order. */ - axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); + axom::float64 computeQuadArea(Point2 p0, + Point2 p1, + Point2 p2, + Point2 p3); /**************************************************************** * VARIABLES @@ -189,6 +206,7 @@ namespace mir std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex IntMap m_elementParentIDs; // the ID of the parent element from the original mesh IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + IntMap m_shapeTypes; // the int enumerator of what type of shape each element is int m_numMaterials; // the number of materials present in the mesh CellTopologyData m_meshTopology; // the topology/connectivity of the mesh diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index b6fc18ec28..9daed29cbb 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -25,6 +25,17 @@ namespace axom { namespace mir { + + enum Shape + { + Triangle, + Quad, + Tetrahedron, + Triangular_Prism, + Pyramid, + Hexahedron + }; + /** * \brief Simple 2D Point class for example */ diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp new file mode 100644 index 0000000000..db7c537a6e --- /dev/null +++ b/src/axom/mir/MIRUtilities.hpp @@ -0,0 +1,209 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +/** + * \file MIRUtilities.hpp + * + * \brief Contains a set of functions that provide general utility + * within Axom's MIR component. + * + */ + +#ifndef __MIR_UTILITIES_H__ +#define __MIR_UTILITIES_H__ + +#include "ZooClippingTables.hpp" + +//-------------------------------------------------------------------------------- + +namespace axom +{ +namespace mir +{ +namespace utilities +{ + +//-------------------------------------------------------------------------------- + + /** + * \brief Determines the number of vertices of the given shape in the finite element zoo. + * + * \param shape The shape type from the finite element zoo. + * + * \return THe number of vertices of the shape. + */ + inline int numVerts(mir::Shape shape) + { + int numVertices = -1; + switch (shape) + { + case mir::Shape::Triangle: + numVertices = 3; + break; + case mir::Shape::Quad: + numVertices = 4; + break; + case mir::Shape::Tetrahedron: + numVertices = 4; + break; + case mir::Shape::Pyramid: + numVertices = 5; + break; + case mir::Shape::Triangular_Prism: + numVertices = 6; + break; + case mir::Shape::Hexahedron: + numVertices = 8; + break; + default: + printf("Invalid shape. Cannot determine numVerts().\n"); + } + return numVertices; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Determines the maximum number of possible vertices of the given shape in the finite element zoo. + * This number includes the midpoint vertices between each of the original shape's vertices. + * + * \param shape The shape type from the finite element zoo. + * + * \return THe number of vertices of the shape. + */ + inline int maxPossibleNumVerts(mir::Shape shape) + { + int numVertices = -1; + switch (shape) + { + case mir::Shape::Triangle: + numVertices = 6; + break; + case mir::Shape::Quad: + numVertices = 8; + break; + case mir::Shape::Tetrahedron: + numVertices = 10; + break; + case mir::Shape::Pyramid: + numVertices = 13; + break; + case mir::Shape::Triangular_Prism: + numVertices = 15; + break; + case mir::Shape::Hexahedron: + numVertices = 20; + break; + default: + printf("Invalid shape. Cannot determine maxPossibleNumVerts().\n"); + } + return numVertices; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Performs linear interpolation between the two given float values. + * + * \param f0 The first float value. + * \param f1 The second float value. + * \param t The percent of the distance from the first float value to the second. + * + * \return The interpolated value. + */ + inline axom::float64 lerpFloat(const axom::float64 f0, + const axom::float64 f1, + const axom::float64 t) + { + return (1 - t) * f0 + t * f1; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Performs linear interpolation between the two vertex positions. + * + * \param vertexOnePos The position of the first vertex. + * \param vertexTwoPos The position of the second vertex. + * \param t The percent of the distance from vertex one to vertex two to interpolate at. + * + * \return The interpolated position. + */ + inline mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, + const mir::Point2& vertexTwoPos, + const float t) + { + mir::Point2 interpolatedPoint; + interpolatedPoint.m_x = lerpFloat(vertexOnePos.m_x, vertexTwoPos.m_x, t); + interpolatedPoint.m_y = lerpFloat(vertexOnePos.m_y, vertexTwoPos.m_y, t); + return interpolatedPoint; + } + +//-------------------------------------------------------------------------------- + + /** + * \brief Returns the local vertex ID of the from/to vertex that is one of + * the two endpoints that the edge the given midpoint is on. + * + * \param shapeType The shape type from the finite element zoo. + * \param midpointVertexID The ID of the vertex between the two endpoints. + * \param isFromVertex A flag denoting which of the two edge endpoints to return. + * + * \return The vertex ID of one of the endpoints. + */ + inline int getEdgeEndpoint(const mir::Shape shapeType, + const int midpointVertexID, + const bool isFromVertex) + { + switch(shapeType) + { + case mir::Shape::Triangle: + if ( midpointVertexID == 3 && isFromVertex ) { return 0; } + if ( midpointVertexID == 3 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 4 && isFromVertex ) { return 1; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 5 && isFromVertex ) { return 2; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 0; } + break; + case mir::Shape::Quad: + if ( midpointVertexID == 4 && isFromVertex ) { return 0; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 0; } + break; + default: + printf("Edge endpoint case not implemented.\n"); + return -1; + break; + } + return -1; + } + +//-------------------------------------------------------------------------------- + +/** + * \brief Calculate the distance between the two given points. + * + * \param p0 The first point. + * \param p1 The second point. + * + * \return The distance between the two points. + */ +inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) +{ + return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); +} + +//-------------------------------------------------------------------------------- + +} +} +} + +#endif \ No newline at end of file diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 690c9ad35b..d4a7238aeb 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -116,6 +116,7 @@ MIRMesh MeshTester::initTestCaseOne() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -217,6 +218,7 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -289,6 +291,7 @@ mir::MIRMesh MeshTester::initTestCaseThree() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle}; // Build the mesh mir::MIRMesh testMesh; @@ -399,6 +402,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves mapData.m_vertexPositions = points; + mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -442,10 +446,12 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 std::vector elementParents;// For the base mesh, the parents are always themselves std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } CellTopologyData topology; @@ -471,10 +477,10 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(quadP0, circleCenter); - axom::float64 distP1 = distance(quadP1, circleCenter); - axom::float64 distP2 = distance(quadP2, circleCenter); - axom::float64 distP3 = distance(quadP3, circleCenter); + axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); + axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); + axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); + axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) { @@ -492,7 +498,7 @@ axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::P for (int x = 0; x < gridSize; ++x) { mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); - if (distance(samplePoint, circleCenter) < circleRadius) + if (mir::utilities::distance(samplePoint, circleCenter) < circleRadius) ++countOverlap; } } @@ -626,13 +632,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) //-------------------------------------------------------------------------------- -axom::float64 MeshTester::distance(mir::Point2 p0, mir::Point2 p1) -{ - return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); -} - -//-------------------------------------------------------------------------------- - mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) { @@ -700,7 +699,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) bool isPointSampled = false; for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) { - if (distance(samplePoint, circleCenter) < circleRadii[cID]) + if (mir::utilities::distance(samplePoint, circleCenter) < circleRadii[cID]) { materialCount[cID]++; isPointSampled = true; @@ -723,10 +722,12 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } CellTopologyData topology; @@ -739,6 +740,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) mapData.m_elementDominantMaterials = elementDominantMaterials; mapData.m_elementParents = elementParents; mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; + mapData.m_shapeTypes = elementShapeTypes; // Build the mesh mir::MIRMesh testMesh; @@ -752,10 +754,10 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = distance(quadP0, circleCenter); - axom::float64 distP1 = distance(quadP1, circleCenter); - axom::float64 distP2 = distance(quadP2, circleCenter); - axom::float64 distP3 = distance(quadP3, circleCenter); + axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); + axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); + axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); + axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); int numCorners = 0; @@ -791,14 +793,17 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() std::vector elementParents; std::vector elementDominantMaterials; + std::vector elementShapeTypes; for (int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Quad); } cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; cellData.m_mapData.m_elementParents = elementParents; + cellData.m_mapData.m_shapeTypes = elementShapeTypes; // Build the mesh mir::MIRMesh testMesh; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 9641fec6a1..37160ae894 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -17,6 +17,7 @@ #include "axom/slam.hpp" // unified header for slam classes and functions #include "MIRMesh.hpp" +#include "MIRUtilities.hpp" #include @@ -119,17 +120,6 @@ namespace mir mir::MIRMesh initQuadClippingTestMesh(); private: - - /** - * \brief Calculate the distance between the two given points. - * - * \param p0 The first point. - * \param p1 The second point. - * - * \return The distance between the two points. - */ - axom::float64 distance(mir::Point2 p0, mir::Point2 p1); - /** * \brief Generates a 2D uniform grid of n x n elements. * diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 05e96ea2d0..9f87edbe5d 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -44,6 +44,27 @@ namespace mir {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} }; + const std::vector > quadClipTableVec = + { + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, + {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, + {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, + {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, + {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} + }; + + // Triangle Vertex Local Indices // 0 @@ -66,5 +87,18 @@ namespace mir {4,0,1,4,5,3,5,4,2,-1}, {3,0,1,2,-1,-1,-1,-1,-1,-1} }; + + const std::vector > triangleClipTableVec = + { + {3,0,1,2,-1,-1,-1,-1,-1,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,3,1,2,5,3,0,3,5,-1}, + {4,0,3,4,2,3,3,1,4,-1}, + {4,0,1,4,5,3,5,4,2,-1}, + {3,0,1,2,-1,-1,-1,-1,-1,-1} + }; + } } \ No newline at end of file diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index 924b98792a..4802db44f7 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -13,23 +13,17 @@ * for the shape types in the zoo. */ +#include + namespace axom { namespace mir { - - enum Shape - { - Triangle, - Quad, - Tetrahedron, - Triangular_Prism, - Pyramid, - Hexahedron - }; - extern const int quadClipTable[16][19]; extern const int triangleClipTable[8][10]; + + extern const std::vector > triangleClipTableVec; + extern const std::vector > quadClipTableVec; } } #endif \ No newline at end of file diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 48d361400f..6eb3493bf6 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -48,7 +48,8 @@ int main( int argc, char** argv ) // Begin material interface reconstruction startTime = Clock::now(); mir::InterfaceReconstructor reconstructor; - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); + mir::MIRMesh processedMesh; + reconstructor.computeReconstructedInterface(testMesh, processedMesh); endTime = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index ad67317a6e..c1034b1937 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -27,10 +27,10 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) auto startTime = Clock::now(); mir::MeshTester tester; // mir::MIRMesh testMesh = tester.initTestCaseOne(); - // mir::MIRMesh testMesh = tester.initTestCaseTwo(); + mir::MIRMesh testMesh = tester.initTestCaseTwo(); // mir::MIRMesh testMesh = tester.initTestCaseThree(); // mir::MIRMesh testMesh = tester.initTestCaseFour(); - mir::MIRMesh testMesh = tester.initTestCaseFive(50, 25); + // mir::MIRMesh testMesh = tester.initTestCaseFive(25, 12); auto endTime = Clock::now(); std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; @@ -38,15 +38,33 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) // Begin material interface reconstruction startTime = Clock::now(); + mir::MIRMesh processedMesh; + mir::InterfaceReconstructor reconstructor; - // mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterface(testMesh); // Process once, with original Meredith algorithm - mir::MIRMesh processedMesh = reconstructor.computeReconstructedInterfaceIterative(testMesh, 5, 0.3); // 5 iterations, 30 percent with iterative Meredith algorithm + reconstructor.computeReconstructedInterface(testMesh, processedMesh); // Process once, with original Meredith algorithm + // reconstructor.computeReconstructedInterfaceIterative(testMesh, 100, 0.3, processedMesh); // 100 iterations, 20 percent with iterative Meredith algorithm endTime = Clock::now(); std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputIterative6.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputNewMARKONEW4.vtk"); + + std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); + + // // Print out the results + // printf("elementVolumeFractions: {\n"); + // for (int matID = 0; matID < processedMesh.m_numMaterials; ++matID) + // { + // printf("Material %d: {", matID); + // for (int eID = 0; eID < materialVolumeFractionsElement[matID].size(); ++eID) + // { + // printf(" %f,", materialVolumeFractionsElement[matID][eID]); + // } + // printf("}\n"); + // } + // printf("}\n"); + // // END TESTING return 0; } diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 15a42d39ff..5e969fd329 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -14,6 +14,9 @@ set(gtest_mir_tests mir_smoke.cpp mir_interface_reconstructor.cpp + mir_cell_clipper.cpp + mir_utilities.cpp + mir_cell_generator.cpp ) diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp new file mode 100644 index 0000000000..68f538e3f4 --- /dev/null +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -0,0 +1,451 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_CELL_CLIPPER_TEST_H_ +#define MIR_CELL_CLIPPER_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_zero) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 0 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_three) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 3 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_four) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {1.0, 0.0, 0.0}; + std::vector matTwoVF = {0.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 4 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, triangle_case_seven) +{ + mir::Shape shape = mir::Shape::Triangle; + std::vector matOneVF = {1.0, 1.0, 1.0}; + std::vector matTwoVF = {0.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ( 7 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_zero) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 0 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_one) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 1 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_two) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 2 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_three) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 3 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_five) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 5 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_ten) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {1.0, 0.0, 1.0, 0.0}; + std::vector matTwoVF = {0.0, 1.0, 0.0, 1.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 10 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, quad_case_fifteen) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {1.0, 1.0, 1.0, 1.0}; + std::vector matTwoVF = {0.0, 0.0, 0.0, 0.0}; + + mir::CellClipper clipper; + unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + EXPECT_EQ( 15 , clippingCase); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_when_mat_one_dominates) +{ + axom::float64 vfMatOneVertexOne = 0.0; + axom::float64 vfMatTwoVertexOne = 1.0; + + axom::float64 vfMatOneVertexTwo = 0.0; + axom::float64 vfMatTwoVertexTwo = 1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_when_mat_two_dominates) +{ + axom::float64 vfMatOneVertexOne = 1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = 1.0; + axom::float64 vfMatTwoVertexTwo = 0.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +// //---------------------------------------------------------------------- + + +TEST(mir_clipping_place, clip_edge_in_middle) +{ + axom::float64 vfMatOneVertexOne = 1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = 0.0; + axom::float64 vfMatTwoVertexTwo = 1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.5 ); +} + +// //---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_with_one_null_material) +{ + axom::float64 vfMatOneVertexOne = -1.0; + axom::float64 vfMatTwoVertexOne = 0.0; + + axom::float64 vfMatOneVertexTwo = -1.0; + axom::float64 vfMatTwoVertexTwo = 0.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +// //---------------------------------------------------------------------- + +TEST(mir_clipping_place, clip_edge_with_two_null_material) +{ + axom::float64 vfMatOneVertexOne = -1.0; + axom::float64 vfMatTwoVertexOne = -1.0; + + axom::float64 vfMatOneVertexTwo = -1.0; + axom::float64 vfMatTwoVertexTwo = -1.0; + + mir::CellClipper clipper; + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + + EXPECT_DOUBLE_EQ( tValue, 0.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_zero) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 1, newElements.size() ); + EXPECT_EQ( 4, newVertices.size() ); + + EXPECT_EQ( 0, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 2, newElements[0][2] ); + EXPECT_EQ( 3, newElements[0][3] ); + + EXPECT_EQ( 0, newVertices[0][0] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 0, newVertices[2][0] ); + EXPECT_EQ( 0, newVertices[3][0] ); + + for (int i = 0; i < 8; ++i) + { + EXPECT_DOUBLE_EQ( 0, tValues[i] ); + } +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_one) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 3, newElements.size() ); + EXPECT_EQ( 6, newVertices.size() ); + + // Check the first element + EXPECT_EQ( 3, newElements[0][0] ); + EXPECT_EQ( 7, newElements[0][1] ); + EXPECT_EQ( 6, newElements[0][2] ); + + // Check the second element + EXPECT_EQ( 7, newElements[1][0] ); + EXPECT_EQ( 0, newElements[1][1] ); + EXPECT_EQ( 2, newElements[1][2] ); + EXPECT_EQ( 6, newElements[1][3] ); + + // Check the third element + EXPECT_EQ( 0, newElements[2][0] ); + EXPECT_EQ( 1, newElements[2][1] ); + EXPECT_EQ( 2, newElements[2][2] ); + + // Check each vertex's associated elements + EXPECT_EQ( 1, newVertices[0][0] ); + EXPECT_EQ( 2, newVertices[0][1] ); + + EXPECT_EQ( 2, newVertices[1][0] ); + + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 2, newVertices[2][1] ); + + EXPECT_EQ( 0, newVertices[3][0] ); + + EXPECT_EQ( 0, newVertices[6][0] ); + EXPECT_EQ( 1, newVertices[6][1] ); + + EXPECT_EQ( 0, newVertices[7][0] ); + EXPECT_EQ( 1, newVertices[7][1] ); +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_three) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; + std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 2, newElements.size() ); + EXPECT_EQ( 6, newVertices.size() ); + + EXPECT_EQ( 0, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 5, newElements[0][2] ); + EXPECT_EQ( 7, newElements[0][3] ); + + EXPECT_EQ( 7, newElements[1][0] ); + EXPECT_EQ( 5, newElements[1][1] ); + EXPECT_EQ( 2, newElements[1][2] ); + EXPECT_EQ( 3, newElements[1][3] ); + + EXPECT_EQ( 0, newVertices[0][0] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 1, newVertices[3][0] ); + EXPECT_EQ( 0, newVertices[5][0] ); + EXPECT_EQ( 1, newVertices[5][1] ); + EXPECT_EQ( 0, newVertices[7][0] ); + EXPECT_EQ( 1, newVertices[7][1] ); + +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_five) +{ + mir::Shape shape = mir::Shape::Quad; + std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; + std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; + + std::vector > vertexVF; + vertexVF.push_back(matOneVF); + vertexVF.push_back(matTwoVF); + + std::map > newElements; + std::map > newVertices; + axom::float64 tValues[8] = { 0 }; + + mir::CellClipper clipper; + clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); + + EXPECT_EQ( 4, newElements.size() ); + EXPECT_EQ( 8, newVertices.size() ); + + EXPECT_EQ( 4, newElements[0][0] ); + EXPECT_EQ( 1, newElements[0][1] ); + EXPECT_EQ( 5, newElements[0][2] ); + + EXPECT_EQ( 0, newElements[1][0] ); + EXPECT_EQ( 4, newElements[1][1] ); + EXPECT_EQ( 5, newElements[1][2] ); + EXPECT_EQ( 2, newElements[1][3] ); + + EXPECT_EQ( 0, newElements[2][0] ); + EXPECT_EQ( 2, newElements[2][1] ); + EXPECT_EQ( 6, newElements[2][2] ); + EXPECT_EQ( 7, newElements[2][3] ); + + EXPECT_EQ( 7, newElements[3][0] ); + EXPECT_EQ( 6, newElements[3][1] ); + EXPECT_EQ( 3, newElements[3][2] ); + + EXPECT_EQ( 1, newVertices[0][0] ); + EXPECT_EQ( 2, newVertices[0][1] ); + EXPECT_EQ( 0, newVertices[1][0] ); + EXPECT_EQ( 1, newVertices[2][0] ); + EXPECT_EQ( 2, newVertices[2][1] ); + EXPECT_EQ( 3, newVertices[3][0] ); + EXPECT_EQ( 0, newVertices[4][0] ); + EXPECT_EQ( 1, newVertices[4][1] ); + EXPECT_EQ( 0, newVertices[5][0] ); + EXPECT_EQ( 1, newVertices[5][1] ); + EXPECT_EQ( 2, newVertices[6][0] ); + EXPECT_EQ( 3, newVertices[6][1] ); + EXPECT_EQ( 2, newVertices[7][0] ); + EXPECT_EQ( 3, newVertices[7][1] ); + +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_CELL_CLIPPER_TEST_H_ diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp new file mode 100644 index 0000000000..f03b480bda --- /dev/null +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -0,0 +1,231 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_CELL_GENERATOR_TEST_H_ +#define MIR_CELL_GENERATOR_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_quad_topology) +{ + EXPECT_EQ(true, 1); + // this function generates the evInds, evBegins, ... etc given the map of elements -> verts, and verts -> elements + std::map > elementMap; + elementMap[0] = { 4, 1, 5 }; + elementMap[1] = { 0, 4, 5, 2 }; + elementMap[2] = { 0, 2, 6, 7 }; + elementMap[3] = { 7, 6, 3 }; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + mir::CellData cellData; + mir::CellGenerator generator; + generator.generateTopologyData(elementMap, vertexMap, cellData); + + EXPECT_EQ( 4, cellData.m_topology.m_evInds[0] ); + EXPECT_EQ( 1, cellData.m_topology.m_evInds[1] ); + EXPECT_EQ( 5, cellData.m_topology.m_evInds[2] ); + EXPECT_EQ( 0, cellData.m_topology.m_evInds[3] ); + EXPECT_EQ( 4, cellData.m_topology.m_evInds[4] ); + EXPECT_EQ( 5, cellData.m_topology.m_evInds[5] ); + EXPECT_EQ( 2, cellData.m_topology.m_evInds[6] ); + EXPECT_EQ( 0, cellData.m_topology.m_evInds[7] ); + EXPECT_EQ( 2, cellData.m_topology.m_evInds[8] ); + EXPECT_EQ( 6, cellData.m_topology.m_evInds[9] ); + EXPECT_EQ( 7, cellData.m_topology.m_evInds[10] ); + EXPECT_EQ( 7, cellData.m_topology.m_evInds[11] ); + EXPECT_EQ( 6, cellData.m_topology.m_evInds[12] ); + EXPECT_EQ( 3, cellData.m_topology.m_evInds[13] ); + + EXPECT_EQ( 1, cellData.m_topology.m_veInds[0] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[1] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[2] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[3] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[4] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[5] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[6] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[7] ); + EXPECT_EQ( 0, cellData.m_topology.m_veInds[8] ); + EXPECT_EQ( 1, cellData.m_topology.m_veInds[9] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[10] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[11] ); + EXPECT_EQ( 2, cellData.m_topology.m_veInds[12] ); + EXPECT_EQ( 3, cellData.m_topology.m_veInds[13] ); + + EXPECT_EQ( 0, cellData.m_topology.m_evBegins[0] ); + EXPECT_EQ( 3, cellData.m_topology.m_evBegins[1] ); + EXPECT_EQ( 7, cellData.m_topology.m_evBegins[2] ); + EXPECT_EQ( 11, cellData.m_topology.m_evBegins[3] ); + + EXPECT_EQ( 0, cellData.m_topology.m_veBegins[0] ); + EXPECT_EQ( 2, cellData.m_topology.m_veBegins[1] ); + EXPECT_EQ( 3, cellData.m_topology.m_veBegins[2] ); + EXPECT_EQ( 5, cellData.m_topology.m_veBegins[3] ); + EXPECT_EQ( 6, cellData.m_topology.m_veBegins[4] ); + EXPECT_EQ( 8, cellData.m_topology.m_veBegins[5] ); + EXPECT_EQ( 10, cellData.m_topology.m_veBegins[6] ); + EXPECT_EQ( 12, cellData.m_topology.m_veBegins[7] ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_vertex_positions) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + std::vector originalVertexPositions; + originalVertexPositions.push_back( mir::Point2(0.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2(0.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2(1.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2(1.0, 1.0) ); + + axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + cellGenerator.generateVertexPositions(shapeType, vertexMap, originalVertexPositions, tValues, cellData); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_y, 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_y, 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_x, 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_y, 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_x, 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_y, 0.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_x, 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_y, 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_x, 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_y, 1.0, 0.00001 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, generate_vertex_volume_fractions) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::map > vertexMap; + vertexMap[0] = { 1, 2 }; + vertexMap[1] = { 0 }; + vertexMap[2] = { 1, 2 }; + vertexMap[3] = { 3 }; + vertexMap[4] = { 0, 1 }; + vertexMap[5] = { 0, 1 }; + vertexMap[6] = { 2, 3 }; + vertexMap[7] = { 2, 3 }; + + std::vector > originalVertexVF(2); + originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; + originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + + axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + cellGenerator.generateVertexVolumeFractions(shapeType, vertexMap, originalVertexVF, tValues, cellData); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][0], 0.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][0], 1.0, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][1], 0.33, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][1], 0.67, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][2], 0.67, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][2], 0.33, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][3], 1.0, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][3], 0.0, 0.00001 ); + + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][4], 0.165, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][4], 0.835, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][5], 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][5], 0.5, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][6], 0.835, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][6], 0.165, 0.00001 ); + + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][7], 0.5, 0.00001 ); + EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][7], 0.5, 0.00001 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_cell_generator, determine_clean_cell_material) +{ + mir::Shape shapeType = mir::Shape::Quad; + + std::vector vertexIDs = { 0, 1, 5, 7 }; + + int matOne = 0; + int matTwo = 1; + + std::vector > originalVertexVF(2); + originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; + originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + + mir::CellData cellData; + mir::CellGenerator cellGenerator; + + int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, vertexIDs, matOne, matTwo, originalVertexVF); + + EXPECT_EQ( dominantMaterial, 1 ); +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_CELL_GENERATOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp index 8a11c21e73..875c7365df 100644 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef MIR_SMOKE_H_ -#define MIR_SMOKE_H_ +#ifndef MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ +#define MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ #include "gtest/gtest.h" @@ -13,31 +13,6 @@ using namespace axom; -TEST(mir_quad_clipping, quad_clipping_case_zero) -{ - // Initialize a 3x3 mesh with 2 materials - mir::MeshTester meshGenerator; - mir::MIRMesh testMesh = meshGenerator.initQuadClippingTestMesh(); - - // Initialize its volume fractions to custom values to guarantee this clipping case - std::vector > vertexVF; - vertexVF.resize(2); - vertexVF[0] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - vertexVF[1] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - int upperLeftVertexID = 5; - int lowerLeftVertexID = 9; - int lowerRightVertexID = 10; - int upperRightVertexID = 6; - - mir::InterfaceReconstructor reconstructor; - unsigned int clippingCase = reconstructor.determineQuadClippingCase( testMesh, 0, 1, upperLeftVertexID, lowerLeftVertexID, lowerRightVertexID, upperRightVertexID ); - - EXPECT_EQ( clippingCase, 0 ); -} - - //---------------------------------------------------------------------- int main(int argc, char* argv[]) @@ -52,4 +27,4 @@ int main(int argc, char* argv[]) } -#endif // MIR_SMOKE_H_ +#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp new file mode 100644 index 0000000000..82e26b89a0 --- /dev/null +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_UTILITIES_TEST_H_ +#define MIR_UTILITIES_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +using namespace axom; + +TEST(mir_interpolation, float_linear_interpolation) +{ + axom::float64 f0 = 50.0; + axom::float64 f1 = 100.0; + + axom::float64 t = 0.0; + axom::float64 interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 50.0 ); + + t = 1.0; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 100.0 ); + + t = 0.5; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 75.0 ); + + t = 0.66; + interpolant = mir::utilities::lerpFloat(f0, f1, t); + EXPECT_DOUBLE_EQ( interpolant, 83.0 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_interpolation, point_linear_interpolation) +{ + mir::Point2 p0(0.25, 0.0); + mir::Point2 p1(1.25, 1.0); + + axom::float64 t = 0.0; + mir::Point2 interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 0.25); + EXPECT_DOUBLE_EQ( interpolant.m_y, 0.0); + + t = 1.0; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 1.25); + EXPECT_DOUBLE_EQ( interpolant.m_y, 1.0); + + t = 0.5; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_DOUBLE_EQ( interpolant.m_x, 0.75); + EXPECT_DOUBLE_EQ( interpolant.m_y, 0.5); + + t = 0.66; + interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); + EXPECT_NEAR( interpolant.m_x, 0.91, 0.00001); + EXPECT_NEAR( interpolant.m_y, 0.66, 0.00001); +} + +//---------------------------------------------------------------------- + +TEST(mir_distance_utility, compute_distance) +{ + mir::Point2 p0( 0.25, 0.0 ); + mir::Point2 p1( 1.25, 0.0 ); + axom::float64 dist = mir::utilities::distance(p0, p1); + EXPECT_DOUBLE_EQ( dist, 1.0 ); + + mir::Point2 p2( 0.25, 0.0 ); + mir::Point2 p3( 1.25, 1.0 ); + dist = mir::utilities::distance(p2, p3); + EXPECT_NEAR( dist, 1.4142, 0.001 ); + + mir::Point2 p4( 1.0, 1.0 ); + mir::Point2 p5( 1.0, 1.0 ); + dist = mir::utilities::distance(p4, p5); + EXPECT_DOUBLE_EQ( dist, 0.0 ); + +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_UTILITIES_TEST_H_ diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index 75ab74554e..cddf4cd4df 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,7 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; - mir -> {slic core}; + mir -> {slic core slam}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/index.rst b/src/index.rst index 9ac474a4b6..afcf6266ee 100644 --- a/src/index.rst +++ b/src/index.rst @@ -88,7 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic +- Mir depends on Slic and Slam - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From d811ce037e46ed95a5c56609c4ac7053b53cdca4 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Thu, 27 Jun 2019 09:03:42 -0700 Subject: [PATCH 034/290] Implemented function for determining the two endpoints of a given midpoint for the 3D cases. --- src/axom/mir/CellGenerator.cpp | 2 +- src/axom/mir/MIRUtilities.hpp | 74 +++++++++++++++++++ src/axom/mir/examples/mir_tutorial_simple.cpp | 2 +- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index 05b61aa030..c81c535f59 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -110,7 +110,7 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, { int vID = itr->first; - for (int matID = 0; matID < vertexVF.size(); ++matID) + for (unsigned long matID = 0; matID < vertexVF.size(); ++matID) { if ( vID < mir::utilities::numVerts(shapeType) ) { diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index db7c537a6e..0f3c75c084 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -177,6 +177,80 @@ namespace utilities if ( midpointVertexID == 7 && isFromVertex ) { return 3; } if ( midpointVertexID == 7 && !isFromVertex ) { return 0; } break; + case mir::Shape::Tetrahedron: + if ( midpointVertexID == 4 && isFromVertex ) { return 0; } + if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 5 && isFromVertex ) { return 0; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 7 && isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 9 && isFromVertex ) { return 3; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 1; } + case mir::Shape::Pyramid: + if ( midpointVertexID == 5 && isFromVertex ) { return 0; } + if ( midpointVertexID == 5 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 7 && isFromVertex ) { return 0; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 8 && isFromVertex ) { return 0; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 9 && isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 12 && isFromVertex ) { return 4; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 1; } + case mir::Shape::Triangular_Prism: + if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && isFromVertex ) { return 1; } + if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && isFromVertex ) { return 2; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 10 && isFromVertex ) { return 1; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 11 && isFromVertex ) { return 2; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 12 && isFromVertex ) { return 3; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && !isFromVertex ) { return 3; } + case mir::Shape::Hexahedron: + if ( midpointVertexID == 8 && isFromVertex ) { return 0; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && isFromVertex ) { return 1; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && isFromVertex ) { return 2; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && isFromVertex ) { return 3; } + if ( midpointVertexID == 11 && !isFromVertex ) { return 0; } + if ( midpointVertexID == 12 && isFromVertex ) { return 0; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 13 && isFromVertex ) { return 1; } + if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 14 && isFromVertex ) { return 2; } + if ( midpointVertexID == 14 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 15 && isFromVertex ) { return 3; } + if ( midpointVertexID == 15 && !isFromVertex ) { return 7; } + if ( midpointVertexID == 16 && isFromVertex ) { return 4; } + if ( midpointVertexID == 16 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 17 && isFromVertex ) { return 5; } + if ( midpointVertexID == 17 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 18 && isFromVertex ) { return 6; } + if ( midpointVertexID == 18 && !isFromVertex ) { return 7; } + if ( midpointVertexID == 19 && isFromVertex ) { return 7; } + if ( midpointVertexID == 19 && !isFromVertex ) { return 4; } default: printf("Edge endpoint case not implemented.\n"); return -1; diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index c1034b1937..2217f64c4b 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -48,7 +48,7 @@ int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/testOutputNewMARKONEW4.vtk"); + processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedMesh.vtk"); std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); From 062143149a50627c8b06de8b46b4783c4dda751b Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Mon, 1 Jul 2019 16:51:15 -0700 Subject: [PATCH 035/290] Updates to build system for mir component --- src/axom/mainpage.md | 2 +- src/axom/mir/CMakeLists.txt | 7 ++----- src/axom/mir/examples/CMakeLists.txt | 9 ++------- src/axom/mir/tests/CMakeLists.txt | 7 +++++-- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 60708dae52..84f42826ad 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -21,7 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic +- Mir depends on Slic and Slam - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index db7d2fda20..65104b39b1 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -10,7 +10,7 @@ # Check necessary dependencies #------------------------------------------------------------------------------ axom_component_requires(NAME MIR - COMPONENTS SLIC) + COMPONENTS SLIC SLAM) #------------------------------------------------------------------------------ # Specify all headers/sources @@ -40,7 +40,7 @@ set(mir_sources #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic) +set(mir_depends_on core slic slam) blt_add_library( NAME mir @@ -61,9 +61,6 @@ axom_install_component(NAME mir #------------------------------------------------------------------------------ if (AXOM_ENABLE_TESTS) add_subdirectory(tests) - if (ENABLE_BENCHMARKS) - # add_subdirectory(benchmarks) - endif() endif() if (AXOM_ENABLE_EXAMPLES) diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 16e15481a7..24878a8dde 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -9,16 +9,11 @@ set( mir_examples ) set( mir_example_dependencies - #core + core + slic mir - #slic ) -blt_list_append( TO mir_example_dependencies ELEMENTS sidre conduit IF AXOM_MIR_USE_SIDRE ) -blt_list_append( TO mir_example_dependencies ELEMENTS raja IF RAJA_FOUND ) -blt_list_append( TO mir_example_dependencies ELEMENTS openmp IF ENABLE_OPENMP ) -blt_list_append( TO mir_example_dependencies ELEMENTS cuda IF ENABLE_CUDA ) - foreach( example ${mir_examples} ) get_filename_component( example_name ${example} NAME_WE ) diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 5e969fd329..102b1234c7 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -19,8 +19,10 @@ set(gtest_mir_tests mir_cell_generator.cpp ) - -set(mir_tests_depends_on core slic mir gtest) +set(mir_tests_depends_on + slic + mir + gtest) #------------------------------------------------------------------------------ # Add gtest based tests @@ -32,6 +34,7 @@ foreach(test ${gtest_mir_tests}) OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} DEPENDS_ON ${mir_tests_depends_on} FOLDER axom/mir/tests ) + blt_add_test( NAME ${test_name} COMMAND ${test_name}_test ) endforeach() From 37e41385b76f0650f0f554e058348ac5b46fd357 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:18:55 -0700 Subject: [PATCH 036/290] Adds some improvements to MIRMesh * Adds copy constructor and copy assignment operator * Adds validity check predicate -- isValid() * Adds unit tests. Initially focused on exercising constructors and assignment operators. --- src/axom/mir/InterfaceReconstructor.cpp | 4 +- src/axom/mir/MIRMesh.cpp | 254 +++++++++++++++++++++--- src/axom/mir/MIRMesh.hpp | 29 ++- src/axom/mir/tests/CMakeLists.txt | 1 + src/axom/mir/tests/mir_mesh.cpp | 229 +++++++++++++++++++++ 5 files changed, 483 insertions(+), 34 deletions(-) create mode 100644 src/axom/mir/tests/mir_mesh.cpp diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index e4fdab75d8..b18c17cfdb 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -39,7 +39,7 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe for (int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) { // Copy the mesh to be split - mir::MIRMesh intermediateMesh(&finalMesh); + mir::MIRMesh intermediateMesh(finalMesh); // Create an array to store the output of each element being split. CellData temp_cellData[intermediateMesh.m_elems.size()]; @@ -271,4 +271,4 @@ void InterfaceReconstructor::generateCleanCells(const int eID, //-------------------------------------------------------------------------------- } -} \ No newline at end of file +} diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index c47fecf3bf..96130fed54 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -5,44 +5,64 @@ #include "MIRMesh.hpp" +#include "axom/core.hpp" + namespace axom { namespace mir { -//-------------------------------------------------------------------------------- MIRMesh::MIRMesh() -{ - -} + : m_verts(0) + , m_elems(0) + , m_numMaterials(0) +{} //-------------------------------------------------------------------------------- -MIRMesh::MIRMesh(MIRMesh* _mesh) +MIRMesh::MIRMesh(const MIRMesh& other) + : m_verts(other.m_verts) + , m_elems(other.m_elems) + , m_materialVolumeFractionsElement(other.m_materialVolumeFractionsElement) + , m_materialVolumeFractionsVertex(other.m_materialVolumeFractionsVertex) + , m_numMaterials(other.m_numMaterials) + , m_meshTopology(other.m_meshTopology) { - m_meshTopology.m_evInds = _mesh->m_meshTopology.m_evInds; - m_meshTopology.m_evBegins = _mesh->m_meshTopology.m_evBegins; - m_meshTopology.m_veInds = _mesh->m_meshTopology.m_veInds; - m_meshTopology.m_veBegins = _mesh->m_meshTopology.m_veBegins; - m_verts = _mesh->m_verts; - m_elems = _mesh->m_elems; - m_bdry = _mesh->m_bdry; - m_cobdry = _mesh->m_cobdry; - m_vertexPositions = _mesh->m_vertexPositions; - m_materialVolumeFractionsElement = _mesh->m_materialVolumeFractionsElement; - m_materialVolumeFractionsVertex = _mesh->m_materialVolumeFractionsVertex; - m_elementParentIDs = _mesh->m_elementParentIDs; - m_shapeTypes = _mesh->m_shapeTypes; - m_elementDominantMaterials = _mesh->m_elementDominantMaterials; - m_numMaterials = _mesh->m_numMaterials; -} + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); -//-------------------------------------------------------------------------------- + m_shapeTypes= IntMap( &m_elems ); + for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } -MIRMesh::~MIRMesh() +} + +MIRMesh& MIRMesh::operator=(const MIRMesh& other) { + if(this != &other) + { + m_verts = other.m_verts; + m_elems = other.m_elems; + + m_meshTopology = other.m_meshTopology; + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); + + + m_shapeTypes = IntMap( &m_elems ); + for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } + + m_materialVolumeFractionsElement = other.m_materialVolumeFractionsElement; + m_materialVolumeFractionsVertex = other.m_materialVolumeFractionsVertex; + + m_numMaterials = other.m_numMaterials; + } + return *this; } //-------------------------------------------------------------------------------- @@ -122,8 +142,8 @@ void MIRMesh::constructMeshRelations() SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_INFO("Elem-Vert relation has size " << m_bdry.totalSize()); - SLIC_INFO("Vert-Elem relation has size " << m_cobdry.totalSize()); + SLIC_DEBUG("Elem-Vert relation has size " << m_bdry.totalSize()); + SLIC_DEBUG("Vert-Elem relation has size " << m_cobdry.totalSize()); } //-------------------------------------------------------------------------------- @@ -243,7 +263,7 @@ void MIRMesh::constructElementShapeTypesMap(const std::vector& shape // Initialize the map for the elements' dominant colors m_shapeTypes = IntMap( &m_elems ); - // Copy the dat for the elements + // Copy the data for the elements for (int eID = 0; eID < m_elems.size(); ++eID) m_shapeTypes[eID] = shapeTypes[eID]; @@ -315,6 +335,18 @@ void MIRMesh::print() } printf("}\n"); + printf("elementVolumeFractions: { \n"); + for (unsigned long i = 0; i < m_materialVolumeFractionsElement.size(); ++i) + { + printf(" { "); + for (int j = 0; j < m_elems.size(); ++j) + { + printf("%.3f, ", m_materialVolumeFractionsElement[i][j]); + } + printf("}\n"); + } + printf("}\n"); + printf("vertexVolumeFractions: { \n"); for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { @@ -475,5 +507,173 @@ axom::float64 MIRMesh::computeQuadArea(Point2 p0, //-------------------------------------------------------------------------------- +bool MIRMesh::isValid(bool verbose) const +{ + bool bValid = true; + + // Check the topology data + bValid = bValid && isTopologyValid(verbose); + + // Check the volume fraction data + bValid = bValid && areVolumeFractionsValid(verbose); + + // Check the map data + bValid = bValid && areMapsValid(verbose); + + return bValid; +} + +bool MIRMesh::isTopologyValid(bool verbose) const +{ + bool bValid = true; + + // check the vertex and element sets + bValid = bValid && m_verts.isValid(verbose); + bValid = bValid && m_elems.isValid(verbose); + + // check the relations + if(!m_verts.empty() && !m_elems.empty() ) + { + bValid = bValid && m_bdry.isValid(verbose); + bValid = bValid && m_cobdry.isValid(verbose); + } + + if(!bValid && verbose) + { + SLIC_WARNING("MIRMesh invalid: Invalid topology"); + } + + return bValid; +} + +bool MIRMesh::areVolumeFractionsValid(bool verbose) const +{ + bool bValid = true; + + int vfElemSize = m_materialVolumeFractionsElement.size(); + if( vfElemSize != m_numMaterials) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Invalid number of materials in volume fraction array."); + } + return bValid; + } + + // Check the sizes of the volume fraction arrays + for(int i=0; i < m_numMaterials; ++i) + { + const auto& mats = m_materialVolumeFractionsElement[i]; + if( mats.size() != m_elems.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Material " << i <<" has wrong " + << " number of materials in volume fraction array." + << " Expected " << m_elems.size() + << ", got " << mats.size() << "."); + } + } + } + + if(!bValid) + { + return false; + } + + // Check that the volume fractions sum to 1.0 + for(auto el : m_elems.positions()) + { + double sum = 0; + for(int m=0; m < m_numMaterials; ++m) + { + sum += m_materialVolumeFractionsElement[m][el]; + } + + if(! axom::utilities::isNearlyEqual(sum, 1.)) + { + bValid = false; + if(verbose) + { + std::stringstream sstr; + + for(int m=0; m < m_numMaterials; ++m) + { + sstr << m_materialVolumeFractionsElement[m][el] << " "; + } + + SLIC_WARNING("MIRMesh invalid: Sum of materials in element" + << el << " was " << sum << ". Expected 1." + << " Values were: " << sstr.str() ); + } + } + } + + return bValid; +} + +bool MIRMesh::areMapsValid(bool verbose) const +{ + bool bValid = true; + + // Check the positions map + if( m_vertexPositions.size() != m_verts.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of vertex positions." + << " Expected " << m_verts.size() + << ". Got " << m_vertexPositions.size()); + } + } + + bValid = bValid && m_vertexPositions.isValid(verbose); + + // Check the element maps + bValid = bValid && m_elementParentIDs.isValid(verbose); + bValid = bValid && m_elementDominantMaterials.isValid(verbose); + bValid = bValid && m_shapeTypes.isValid(verbose); + + if( m_elementParentIDs.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem parent IDs." + << " Expected " << m_elems.size() + << ". Got " << m_elementParentIDs.size()); + } + } + + if( m_elementDominantMaterials.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem dominant mats." + << " Expected " << m_elems.size() + << ". Got " << m_elementDominantMaterials.size()); + } + } + + if( m_shapeTypes.size() != m_elems.size() ) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem shape types." + << " Expected " << m_elems.size() + << ". Got " << m_shapeTypes.size()); + } + } + + return bValid; + +} + + +} } -} \ No newline at end of file diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 984ada037e..57b1e7446d 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -57,14 +57,19 @@ namespace mir MIRMesh(); /** - * \brief Copy constrcutor. + * \brief Copy constructor. */ - MIRMesh(MIRMesh* _mesh); + MIRMesh(const MIRMesh& other); /** * \brief Default destructor. */ - ~MIRMesh(); + ~MIRMesh() = default; + + /** + * \brief Copy assignment. + */ + MIRMesh& operator=(const MIRMesh& other); /** * \brief Initializes a mesh with the provided data and topology. @@ -132,10 +137,24 @@ namespace mir */ std::vector > computeOriginalElementVolumeFractions(); + /** + * \brief Checks that this instance of MIRMesh is valid + * + * \return True if this instance is valid, false otherwise + */ + bool isValid(bool verbose) const; + private: + /// Utility predicate to check if the mesh's topology is valid + bool isTopologyValid(bool verbose) const; + /// Utility predicate to check if the mesh's volume fractions are valid + bool areVolumeFractionsValid(bool verbose) const; + /// Utility predicate to check if the mesh's maps are valid + bool areMapsValid(bool verbose) const; + /** - * \brief Constucts the positions map on the vertices. + * \brief Constructs the positions map on the vertices. * * \param data The vector of position data for each vertex. */ @@ -215,4 +234,4 @@ namespace mir //-------------------------------------------------------------------------------- } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 102b1234c7..d4b750967b 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -13,6 +13,7 @@ set(gtest_mir_tests mir_smoke.cpp + mir_mesh.cpp mir_interface_reconstructor.cpp mir_cell_clipper.cpp mir_utilities.cpp diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp new file mode 100644 index 0000000000..0d9797e16d --- /dev/null +++ b/src/axom/mir/tests/mir_mesh.cpp @@ -0,0 +1,229 @@ +// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef MIR_MESH_TEST_H_ +#define MIR_MESH_TEST_H_ + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + + +namespace mir = axom::mir; + +class MirMeshTest : public ::testing::Test +{ +public: + + template + using Vec = std::vector; + + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; + + enum { GREEN = 0, BLUE = 1 }; + +public: + + mir::MIRMesh& getMesh() { return m_mesh; } + const mir::MIRMesh& getMesh() const { return m_mesh; } + +protected: + void SetUp() override + { + m_verts= mir::VertSet(16); + m_elems= mir::ElemSet(9); + m_nMats = 2; + + // Create the mesh connectivity information + setupTopoData(); + setupMapData(); + setupVolumeFractions(); + + m_mesh.initializeMesh(this->m_verts, this->m_elems, this->m_nMats, + this->m_topoData, this->m_mapData, this->m_volFracs); + } + + // Set up the mesh's topological connectivity + void setupTopoData() + { + m_topoData.m_evInds = { + 0,4,5,1, // elem 0, card 4, start 0 + 1,5,6,2, // elem 1, card 4, start 4 + 2,6,7,3, // elem 2, card 4, start 8 + 4,8,9,5, // elem 3, card 4, start 12 + 5,9,10,6, // elem 4, card 4, start 16 + 6,10,11,7, // elem 5, card 4, start 20 + 8,12,13,9, // elem 6, card 4, start 24 + 9,13,14,10, // elem 7, card 4, start 28 + 10,14,15,11 // elem 8, card 4, start 32, end 36 + }; + + m_topoData.m_evBegins = { + 0,4,8,12,16,20,24,28,32,36 + }; + + m_topoData.m_veInds = { + 0, // vert 0, card 1, start 0 + 0,1, // vert 1, card 2, start 1 + 1,2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0,3, // vert 4, card 2, start 6 + 0,1,3,4, // vert 5, card 4, start 8 + 1,2,4,5, // vert 6, card 4, start 12 + 2,5, // vert 7, card 2, start 16 + 3,6, // vert 8, card 2, start 18 + 3,4,6,7, // vert 9, card 4, start 20 + 4,5,7,8, // vert 10, card 4, start 24 + 5,8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6,7, // vert 13, card 2, start 31 + 7,8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + + m_topoData.m_veBegins = { + 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 + }; + } + + // Set up the mesh's map data + void setupMapData() + { + m_mapData.m_vertexPositions = { + mir::Point2( 0.0, 3.0 ), + mir::Point2( 1.0, 3.0 ), + mir::Point2( 2.0, 3.0 ), + mir::Point2( 3.0, 3.0 ), + + mir::Point2( 0.0, 2.0 ), + mir::Point2( 1.0, 2.0 ), + mir::Point2( 2.0, 2.0 ), + mir::Point2( 3.0, 2.0 ), + + mir::Point2( 0.0, 1.0 ), + mir::Point2( 1.0, 1.0 ), + mir::Point2( 2.0, 1.0 ), + mir::Point2( 3.0, 1.0 ), + + mir::Point2( 0.0, 0.0 ), + mir::Point2( 1.0, 0.0 ), + mir::Point2( 2.0, 0.0 ), + mir::Point2( 3.0, 0.0 ) + }; + + m_mapData.m_elementDominantMaterials = Vec(m_elems.size(), mir::NULL_MAT); + m_mapData.m_elementParents = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + m_mapData.m_shapeTypes = Vec(m_elems.size(), mir::Shape::Quad); + } + + // Set up the mesh's volume fraction data + void setupVolumeFractions() + { + m_volFracs.resize(m_nMats); + m_volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + m_volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + } + +protected: + mir::VertSet m_verts; + mir::ElemSet m_elems; + int m_nMats; + + mir::CellTopologyData m_topoData; + mir::CellMapData m_mapData; + mir::CellData m_cellData; + VolumeFractions m_volFracs; + + mir::MIRMesh m_mesh; +}; + + +TEST_F(MirMeshTest,default_ctor) +{ + mir::MIRMesh mesh; + EXPECT_TRUE(mesh.isValid(true)); +} + + +TEST_F(MirMeshTest,initialize) +{ + mir::MIRMesh mesh; + mesh.initializeMesh( + this->m_verts, + this->m_elems, + this->m_nMats, + this->m_topoData, + this->m_mapData, + this->m_volFracs); + + EXPECT_TRUE(mesh.isValid(true)); + + mesh.print(); +} + +TEST_F(MirMeshTest,copy_ctor) +{ + // test copy constructor + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + + mir::MIRMesh mirCopy(mesh); + EXPECT_TRUE( mirCopy.isValid(true) ); + } + + // test const copy constructor + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + // test copy constructor + const mir::MIRMesh mirCopy(cmesh); + EXPECT_TRUE( mirCopy.isValid(true) ); + } +} + +TEST_F(MirMeshTest,copy_assign) +{ + // test copy assignment from a non-const MIRMesh + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + + mir::MIRMesh mirCopy; + mirCopy = mesh; + EXPECT_TRUE( mirCopy.isValid(true) ); + } + + // test copy assignment from a const MIRMesh + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + mir::MIRMesh mirCopy; + mirCopy = cmesh; + EXPECT_TRUE( mirCopy.isValid(true) ); + } +} + + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::UnitTestLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} + + +#endif // MIR_MESH_TEST_H_ From ad822e8c09e7fb541cf71a5917a3f8c6d4659bfb Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:21:47 -0700 Subject: [PATCH 037/290] Improves output of slam::Map's validity check and print functions --- src/axom/slam/Map.hpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/axom/slam/Map.hpp b/src/axom/slam/Map.hpp index 35d9c55090..60da43c560 100644 --- a/src/axom/slam/Map.hpp +++ b/src/axom/slam/Map.hpp @@ -15,7 +15,6 @@ #include #include -#include #include "axom/core.hpp" #include "axom/slic.hpp" @@ -483,23 +482,16 @@ bool Map::isValid(bool verboseOutput) const } - if(verboseOutput) + if(verboseOutput && !bValid) { std::stringstream sstr; sstr << "\n*** Detailed results of isValid on the map.\n"; - if(bValid) - { - sstr << "Map was valid." << std::endl; - } - else - { - sstr << "Map was NOT valid.\n" + sstr << "Map was NOT valid.\n" << sstr.str() << std::endl; - } - std::cout << sstr.str() << std::endl; + SLIC_DEBUG( sstr.str() ); } return bValid; @@ -510,10 +502,11 @@ template void Map::print() const { bool valid = isValid(true); - std::stringstream sstr; if (valid) { + std::stringstream sstr; + if (!m_set) { sstr << "** map is empty."; @@ -534,9 +527,14 @@ void Map::print() const } } } + + SLIC_INFO( sstr.str() ); + } + else + { + SLIC_INFO("Map was not valid."); } - std::cout << sstr.str() << std::endl; } From 1bfbecc5c1c362c0da049e863c35c8a2ddacf022 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:22:31 -0700 Subject: [PATCH 038/290] Refactors how test meshes are set up in mir's MeshTester class --- src/axom/mir/MeshTester.cpp | 292 ++++++++++++++++-------------------- src/axom/mir/MeshTester.hpp | 22 ++- 2 files changed, 141 insertions(+), 173 deletions(-) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index d4a7238aeb..6a8eb612e5 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -5,34 +5,30 @@ #include "MeshTester.hpp" +namespace numerics = axom::numerics; +namespace slam = axom::slam; + namespace axom { namespace mir { - -//-------------------------------------------------------------------------------- - -MeshTester::MeshTester() -{ - -} - -//-------------------------------------------------------------------------------- - -MeshTester::~MeshTester() -{ - -} //-------------------------------------------------------------------------------- MIRMesh MeshTester::initTestCaseOne() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -44,10 +40,10 @@ MIRMesh MeshTester::initTestCaseOne() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -65,25 +61,20 @@ MIRMesh MeshTester::initTestCaseOne() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - - int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); + + volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - std::vector greenVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - elementVF[GREEN] = greenVolumeFractions; - std::vector blueVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - elementVF[BLUE] = blueVolumeFractions; - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -106,21 +97,13 @@ MIRMesh MeshTester::initTestCaseOne() mir::Point2( 3.0, 0.0 ) }; - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -129,11 +112,18 @@ MIRMesh MeshTester::initTestCaseOne() mir::MIRMesh MeshTester::initTestCaseTwo() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -145,10 +135,10 @@ mir::MIRMesh MeshTester::initTestCaseTwo() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -166,26 +156,19 @@ mir::MIRMesh MeshTester::initTestCaseTwo() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - int numMaterials = 3; enum { BLUE = 0, RED = 1, ORANGE = 2 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); + volFracs[BLUE] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + volFracs[RED] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + volFracs[ORANGE] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - std::vector blueVolumeFractions = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - elementVF[BLUE] = blueVolumeFractions; - std::vector redVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; - elementVF[RED] = redVolumeFractions; - std::vector orangeVolumeFractions = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - elementVF[ORANGE] = orangeVolumeFractions; - - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -208,21 +191,13 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mir::Point2( 3.0, 0.0 ) }; - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -231,21 +206,29 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mir::MIRMesh MeshTester::initTestCaseThree() { - int numElements = 4; - int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + + int numElements = 4; + int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,1,2, // elem 0, card 3, start 0 1,3,4, // elem 1, card 3, start 3 1,4,2, // elem 2, card 3, start 6 2,4,5 // elem 3, card 3, start 9, end 12 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,3,6,9,12 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1,2, // vert 1, card 3, start 1 0,2,3, // vert 2, card 3, start 4 @@ -253,24 +236,19 @@ mir::MIRMesh MeshTester::initTestCaseThree() 1,2,3, // vert 4, card 3, start 8 3 // vert 5, card 1, start 11, end 12 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,4,7,8,11,12 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 24 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 8 elements int numMaterials = 2; enum { BLUE = 0, RED = 1, }; - std::vector > elementVF; elementVF.resize(numMaterials); - - std::vector blueVolumeFractions = {0.0, 0.5, 0.8, 0.5}; - elementVF[BLUE] = blueVolumeFractions; - std::vector redVolumeFractions = {1.0, 0.5, 0.2, 0.5}; - elementVF[RED] = redVolumeFractions; + volFracs.resize(numMaterials); + volFracs[BLUE] = {0.0, 0.5, 0.8, 0.5}; + volFracs[RED] = {1.0, 0.5, 0.2, 0.5}; - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 1.0, 2.0 ), mir::Point2( 0.5, 1.0 ), @@ -280,22 +258,13 @@ mir::MIRMesh MeshTester::initTestCaseThree() mir::Point2( 2.0, 0.0 ) }; - - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle, mir::Shape::Triangle}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Triangle); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -304,11 +273,18 @@ mir::MIRMesh MeshTester::initTestCaseThree() mir::MIRMesh MeshTester::initTestCaseFour() { + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; + int numElements = 9; int numVertices = 16; + mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information - std::vector evInds = { + topoData.m_evInds = { 0,4,5,1, // elem 0, card 4, start 0 1,5,6,2, // elem 1, card 4, start 4 2,6,7,3, // elem 2, card 4, start 8 @@ -320,10 +296,10 @@ mir::MIRMesh MeshTester::initTestCaseFour() 10,14,15,11 // elem 8, card 4, start 32, end 36 }; - std::vector evBegins = { + topoData.m_evBegins = { 0,4,8,12,16,20,24,28,32,36 }; - std::vector veInds = { + topoData.m_veInds = { 0, // vert 0, card 1, start 0 0,1, // vert 1, card 2, start 1 1,2, // vert 2, card 2, start 3 @@ -341,14 +317,12 @@ mir::MIRMesh MeshTester::initTestCaseFour() 7,8, // vert 14, card 2, start 33 8, // vert 15, card 1, start 35, end 36 }; - std::vector veBegins = { + topoData.m_veBegins = { 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 }; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements - std::vector points = + mapData.m_vertexPositions = { mir::Point2( 0.0, 3.0 ), mir::Point2( 1.0, 3.0 ), @@ -374,10 +348,15 @@ mir::MIRMesh MeshTester::initTestCaseFour() int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); + volFracs.resize(numMaterials); - std::vector greenVolumeFractions; greenVolumeFractions.resize(numElements); - std::vector blueVolumeFractions; blueVolumeFractions.resize(numElements); + auto& greenVolumeFractions = volFracs[GREEN]; + auto& blueVolumeFractions = volFracs[BLUE]; + const auto& points = mapData.m_vertexPositions; + const auto& evInds = topoData.m_evInds; + + greenVolumeFractions.resize(numElements); + blueVolumeFractions.resize(numElements); // Generate the element volume fractions for the circle mir::Point2 circleCenter(1.5, 1.5); @@ -385,28 +364,24 @@ mir::MIRMesh MeshTester::initTestCaseFour() int gridSize = 1000; for (int i = 0; i < numElements; ++i) { - greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(gridSize, circleCenter, circleRadius, points[evInds[i * 4 + 0]], points[evInds[i * 4 + 1]], points[evInds[i * 4 + 2]], points[evInds[i * 4 + 3]]); - blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + auto vf = calculatePercentOverlapMonteCarlo(gridSize, + circleCenter, + circleRadius, + points[evInds[i * 4 + 0]], + points[evInds[i * 4 + 1]], + points[evInds[i * 4 + 2]], + points[evInds[i * 4 + 3]]); + greenVolumeFractions[i] = vf; + blueVolumeFractions[i] = 1.0 - vf; } - elementVF[GREEN] = greenVolumeFractions; - elementVF[BLUE] = blueVolumeFractions; - - CellTopologyData topology; - topology.m_evInds = evInds; - topology.m_evBegins = evBegins; - topology.m_veInds = veInds; - topology.m_veBegins = veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = {NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT, NULL_MAT}; + mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad, mir::Shape::Quad}; + mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); return testMesh; } @@ -424,50 +399,39 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 int numMaterials = 2; enum { GREEN = 0, BLUE = 1 }; - std::vector > elementVF; elementVF.resize(numMaterials); - - std::vector greenVolumeFractions; greenVolumeFractions.resize(cellData.m_numElems); - std::vector blueVolumeFractions; blueVolumeFractions.resize(cellData.m_numElems); + VolumeFractions volFracs; + volFracs.resize(numMaterials); + volFracs[GREEN].resize(cellData.m_numElems); + volFracs[BLUE].resize(cellData.m_numElems); // Generate the element volume fractions for the circle - int numMonteCarloSamples = 100; + const int numMonteCarloSamples = 100; + auto& pos = cellData.m_mapData.m_vertexPositions; + const auto& evInds = cellData.m_topology.m_evInds; for (int i = 0; i < cellData.m_numElems; ++i) { - greenVolumeFractions[i] = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, circleCenter, circleRadius, - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 0]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 1]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 2]], - cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[i * 4 + 3]]); - blueVolumeFractions[i] = 1.0 - greenVolumeFractions[i]; + auto vf = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, + circleCenter, + circleRadius, + pos[evInds[i * 4 + 0]], + pos[evInds[i * 4 + 1]], + pos[evInds[i * 4 + 2]], + pos[evInds[i * 4 + 3]]); + volFracs[GREEN][i] = vf; + volFracs[BLUE][i] = 1.0 - vf; } - elementVF[GREEN] = greenVolumeFractions; - elementVF[BLUE] = blueVolumeFractions; - - std::vector elementParents;// For the base mesh, the parents are always themselves - std::vector elementDominantMaterials; - std::vector elementShapeTypes; - for (int i = 0; i < cellData.m_numElems; ++i) + cellData.m_mapData.m_elementDominantMaterials = Vec(cellData.m_numVerts, NULL_MAT); + cellData.m_mapData.m_shapeTypes = Vec(cellData.m_numVerts, mir::Shape::Quad); + cellData.m_mapData.m_elementParents.resize(cellData.m_numVerts); + for (auto i : elems.positions() ) { - elementParents.push_back(i); - elementDominantMaterials.push_back(NULL_MAT); - elementShapeTypes.push_back(mir::Shape::Quad); + cellData.m_mapData.m_elementParents[i] = i; } - CellTopologyData topology; - topology.m_evInds = cellData.m_topology.m_evInds; - topology.m_evBegins = cellData.m_topology.m_evBegins; - topology.m_veInds = cellData.m_topology.m_veInds; - topology.m_veBegins = cellData.m_topology.m_veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = elementDominantMaterials; - mapData.m_elementParents = elementParents; - mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; - // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, elementVF); + testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, volFracs); return testMesh; } @@ -519,8 +483,13 @@ mir::CellData MeshTester::generateGrid(int gridSize) int numElements = gridSize * gridSize; int numVertices = (gridSize + 1) * (gridSize + 1); + mir::CellData data; + + data.m_numVerts = numVertices; + data.m_numElems = numElements; + // Generate the evInds - std::vector evInds; + auto& evInds = data.m_topology.m_evInds; for (int eID = 0; eID < numElements; ++eID) { int row = eID / gridSize; // note the integer division @@ -534,7 +503,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the evBegins - std::vector evBegins; + auto& evBegins = data.m_topology.m_evBegins; evBegins.push_back(0); for (int i = 0; i < numElements; ++i) { @@ -542,8 +511,9 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the veInds + auto& veInds = data.m_topology.m_veInds; + auto& veBegins = data.m_topology.m_veBegins; std::map > veInds_data; - std::vector veInds; for (int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) { int currentElementID = evInd_itr / 4; // note the integer division @@ -561,7 +531,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the veBegins - std::vector veBegins; veBegins.push_back(0); int currentIndexCount = 0; for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) @@ -571,7 +540,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) } // Generate the vertex positions - std::vector points; + auto& points = data.m_mapData.m_vertexPositions; for (int y = gridSize; y > -1; --y) { for (int x = 0; x < gridSize + 1; ++x) @@ -580,15 +549,6 @@ mir::CellData MeshTester::generateGrid(int gridSize) } } - mir::CellData data; - data.m_numVerts = numVertices; - data.m_numElems = numElements; - data.m_topology.m_evInds = evInds; - data.m_topology.m_evBegins = evBegins; - data.m_topology.m_veInds = veInds; - data.m_topology.m_veBegins = veBegins; - data.m_mapData.m_vertexPositions = points; - // // Print out the results // printf("evInds: { "); // for (int i = 0; i < evInds.size(); i++) diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 37160ae894..7d4ea33c40 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -20,9 +20,7 @@ #include "MIRUtilities.hpp" #include - -namespace numerics = axom::numerics; -namespace slam = axom::slam; +#include namespace axom { @@ -37,16 +35,25 @@ namespace mir */ class MeshTester { +public: + template + using Vec = std::vector; + + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; + + public: /** * \brief Default constructor. */ - MeshTester(); + MeshTester() = default; /** * \brief Default destructor. */ - ~MeshTester(); + ~MeshTester() = default; public: /** @@ -56,7 +63,7 @@ namespace mir * * \return The generated mesh. */ - MIRMesh initTestCaseOne(); + mir::MIRMesh initTestCaseOne(); /** * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. @@ -110,7 +117,7 @@ namespace mir mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); /** - * \brief Intializes a mesh to be used for validating the results of quad clipping. + * \brief Initializes a mesh to be used for validating the results of quad clipping. * * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such * that the mesh would be split horizontally through the middle. @@ -155,6 +162,7 @@ namespace mir * \return The number of corners of the quad that are within the circle. */ int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + }; } } From de268cf21594f1fd7e5cf292369d42083bb5e8c3 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 15:45:08 -0700 Subject: [PATCH 039/290] Adds command line arguments to mir's tutorial example --- src/axom/mir/ZooClippingTables.hpp | 4 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 252 +++++++++++++++--- 2 files changed, 217 insertions(+), 39 deletions(-) diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index 4802db44f7..de2b8be83c 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -9,7 +9,7 @@ /** * \file ZooClippingTables.hpp * - * \brief Contains the defintions for the clipping cases and enumerator + * \brief Contains the definitions for the clipping cases and enumerator * for the shape types in the zoo. */ @@ -26,4 +26,4 @@ namespace mir extern const std::vector > quadClipTableVec; } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 2217f64c4b..460a076b80 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -3,70 +3,248 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/core.hpp" // for axom macros -#include "axom/mir.hpp" // for Mir classes & functions +#include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/slam.hpp" +#include "axom/mir.hpp" -#include -using Clock = std::chrono::high_resolution_clock; +#include // namespace aliases namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; +namespace fs = axom::utilities::filesystem; //-------------------------------------------------------------------------------- +enum InputStatus +{ + SUCCESS, + INVALID, + SHOWHELP +}; + +struct Input +{ + int m_test_case; // valid values 1,2,3,4,5 + bool m_should_iterate; + int m_iter_count; + double m_iter_percent; + bool m_verbose; + std::string m_output_dir; + InputStatus m_status; + + Input() + : m_test_case(2) + , m_should_iterate(false) + , m_iter_count(100) + , m_iter_percent(.3) + , m_verbose(false) + , m_status(SUCCESS) + { + m_output_dir = fs::joinPath(AXOM_BIN_DIR, "mir_examples"); + } + + Input(int argc, char** argv) : Input() + { + for (int i = 1 ; i < argc ; /* increment i in loop */) + { + std::string arg = argv[i]; + if (arg == "--test-case") + { + m_test_case = std::stoi(argv[++i]); + } + else if (arg == "--output-dir") + { + m_output_dir = argv[++i]; + } + else if (arg == "--iter-count") + { + m_iter_count = std::stoi(argv[++i]); + m_should_iterate = true; + } + else if (arg == "--iter-percent") + { + m_iter_percent = std::stod(argv[++i]); + m_should_iterate = true; + } + else if (arg == "--verbose") + { + m_verbose = true; + } + else // help or unknown parameter + { + if(arg != "--help" && arg != "-h") + { + SLIC_WARNING("Unrecognized parameter: " << arg); + m_status = INVALID; + } + else + { + m_status = SHOWHELP; + } + return; + } + ++i; + } + + checkTestCase(); + checkOutputDir(); + checkIterationParams(); + } + + + bool shouldIterate() const { return m_should_iterate; } + int numIterations() const { return m_iter_count; } + int iterPercentage() const { return m_iter_percent; } + + + void showhelp() + { + std::cout + << "Argument usage:" + "\n --help Show this help message." + "\n --test-case N Mesh test case. Default N = 2." + "\n Valid values {1,2,3,4,5}" + "\n --output-dir dir Directory for output mesh" + "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" + "\n --iter-count N Number of iterations for iterative algorithm" + "\n Defaults to 100." + "\n Setting a value triggers iterative algorithm" + "\n --iter-percent D Volume diff percentage for iterative algorithm" + "\n Must be between 0 and 1. Defaults to 0.3" + "\n Setting a value triggers iterative algorithm" + "\n --verbose Increases verbosity of output" + << std::endl << std::endl; + }; + +private: + void checkTestCase() + { + if(m_test_case < 1 || m_test_case > 5) + { + m_status = INVALID; + SLIC_WARNING("Invalid test case " << m_test_case); + } + } + + void checkOutputDir() + { + if(! fs::pathExists(m_output_dir)) + { + fs::makeDirsForPath(m_output_dir); + } + } + + void checkIterationParams() + { + if(m_should_iterate) + { + if(m_iter_count < 1) + { + m_status = INVALID; + SLIC_WARNING("Invalid iteration count " << m_iter_count); + } + + if(m_iter_percent <= 0. || m_iter_percent > 1.) + { + m_status = INVALID; + SLIC_WARNING("Invalid iteration percentage " << m_iter_percent); + } + } + } + +}; + /*! * \brief Tutorial main showing how to initialize test cases and perform mir. */ -int main( int AXOM_NOT_USED(argc), char** AXOM_NOT_USED(argv) ) +int main(int argc, char** argv) { + axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - // Intialize a mesh for testing MIR - auto startTime = Clock::now(); + // Parse arguments + Input params(argc, argv); + + if (params.m_status != SUCCESS) + { + if (params.m_status == SHOWHELP) + { + params.showhelp(); + return 0; + } + else if (params.m_status == INVALID) + { + params.showhelp(); + return 1; + } + } + + mir::MIRMesh testMesh; mir::MeshTester tester; - // mir::MIRMesh testMesh = tester.initTestCaseOne(); - mir::MIRMesh testMesh = tester.initTestCaseTwo(); - // mir::MIRMesh testMesh = tester.initTestCaseThree(); - // mir::MIRMesh testMesh = tester.initTestCaseFour(); - // mir::MIRMesh testMesh = tester.initTestCaseFive(25, 12); - auto endTime = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + auto timer = axom::utilities::Timer(true); + + switch(params.m_test_case) + { + case 1: testMesh = tester.initTestCaseOne(); break; + case 2: testMesh = tester.initTestCaseTwo(); break; + case 3: testMesh = tester.initTestCaseThree(); break; + case 4: testMesh = tester.initTestCaseFour(); break; + case 5: testMesh = tester.initTestCaseFive(25, 12); break; + } + + timer.stop(); + SLIC_INFO( "Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + + SLIC_INFO("Test mesh is " << (testMesh.isValid(true) ? "" : " NOT") << " valid."); + + if(params.m_verbose) + { + SLIC_INFO("Initial mesh:"); + testMesh.print(); + } // Begin material interface reconstruction - startTime = Clock::now(); + timer.start(); mir::MIRMesh processedMesh; - mir::InterfaceReconstructor reconstructor; - reconstructor.computeReconstructedInterface(testMesh, processedMesh); // Process once, with original Meredith algorithm - // reconstructor.computeReconstructedInterfaceIterative(testMesh, 100, 0.3, processedMesh); // 100 iterations, 20 percent with iterative Meredith algorithm + + if(! params.shouldIterate()) // Process once, with original Meredith algorithm + { + reconstructor.computeReconstructedInterface(testMesh, processedMesh); + } + else // use iterative algorithm + { + int n = params.numIterations(); + double p = params.iterPercentage(); + + reconstructor.computeReconstructedInterfaceIterative(testMesh, n, p, processedMesh); + } - endTime = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO( "Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results - processedMesh.writeMeshToFile("/Users/sterbentz3/Desktop/processedMesh.vtk"); + std::string dir = params.m_output_dir; + processedMesh.writeMeshToFile( fs::joinPath(dir, "processedMesh.vtk") ); - std::vector > materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); - - // // Print out the results - // printf("elementVolumeFractions: {\n"); - // for (int matID = 0; matID < processedMesh.m_numMaterials; ++matID) - // { - // printf("Material %d: {", matID); - // for (int eID = 0; eID < materialVolumeFractionsElement[matID].size(); ++eID) - // { - // printf(" %f,", materialVolumeFractionsElement[matID][eID]); - // } - // printf("}\n"); - // } - // printf("}\n"); - // // END TESTING + using VolFracs = std::vector >; + timer.start(); + VolFracs materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); + timer.stop(); + SLIC_INFO( "Computing volumes took: " << timer.elapsedTimeInMilliSec() << " ms."); + + if(params.m_verbose) + { + SLIC_INFO("Final mesh:"); + processedMesh.print(); + } return 0; } -//-------------------------------------------------------------------------------- \ No newline at end of file +//-------------------------------------------------------------------------------- From 654c51bc43b82d40e39371f36df9555217e0734d Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 16:04:21 -0700 Subject: [PATCH 040/290] Minor update to concentric circles example --- .../mir/examples/mir_concentric_circles.cpp | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 6eb3493bf6..1455d16f81 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -4,31 +4,30 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/core.hpp" // for axom macros +#include "axom/slic.hpp" #include "axom/mir.hpp" // for Mir classes & functions -#include "axom/slam.hpp" -#include #include -using Clock = std::chrono::high_resolution_clock; - // namespace aliases -namespace numerics = axom::numerics; -namespace slam = axom::slam; namespace mir = axom::mir; //-------------------------------------------------------------------------------- -/*! - * \brief Tutorial main - */ +std::string usageString() +{ + return "Args are "; +} + int main( int argc, char** argv ) { + axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); if (argc != 4) { - printf("Incorrect number of args. Args are \n"); - return 0; + SLIC_WARNING("Incorrect number of args. " << usageString() ); + return 1; } try @@ -38,20 +37,21 @@ int main( int argc, char** argv ) int numCircles = std::stoi(argv[2]); std::string outputFilePath = std::string(argv[3]); - // Intialize a mesh for testing MIR - auto startTime = Clock::now(); + // Initialize a mesh for testing MIR + auto timer = axom::utilities::Timer(true); mir::MeshTester tester; mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); - auto endTime = Clock::now(); - std::cout << "Mesh init time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); // Begin material interface reconstruction - startTime = Clock::now(); + timer.start(); mir::InterfaceReconstructor reconstructor; mir::MIRMesh processedMesh; reconstructor.computeReconstructedInterface(testMesh, processedMesh); - endTime = Clock::now(); - std::cout << "Material interface reconstruction time: " << std::chrono::duration_cast(endTime - startTime).count() << " ms" << std::endl; + timer.stop(); + SLIC_INFO("Material interface reconstruction time: " + << timer.elapsedTimeInMilliSec() << " ms."); // Output results processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); @@ -60,12 +60,12 @@ int main( int argc, char** argv ) } catch (std::invalid_argument const &e) { - printf("Bad input. Arguments are \n"); - return 0; + SLIC_WARNING("Bad input. " << usageString() ); + return 1; } catch (std::out_of_range const &e) { - printf("Integer overflow. Arguments are \n"); - return 0; + SLIC_WARNING("Integer overflow. " << usageString() ); + return 1; } -} \ No newline at end of file +} From 5979ad6ff3b53efefef755d492f7b372c559d030 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:22:27 -0700 Subject: [PATCH 041/290] Adds a linear interpolation function to axom::core::utilities Implementation and tests implemented by M. Sterbentz and moved from mir component. --- src/axom/core/tests/utils_utilities.cpp | 31 +++++++++++++++++++++++++ src/axom/core/utilities/Utilities.hpp | 17 +++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/axom/core/tests/utils_utilities.cpp b/src/axom/core/tests/utils_utilities.cpp index 85c092b7e8..1b11d57a25 100644 --- a/src/axom/core/tests/utils_utilities.cpp +++ b/src/axom/core/tests/utils_utilities.cpp @@ -116,3 +116,34 @@ TEST(core_Utilities,minmax) EXPECT_EQ(a, axom::utilities::max(a,b)); } } + +TEST(core_Utilities, lerp) +{ + std::cout<<"Testing linear interpolation (lerp) function."<< std::endl; + + double f0 = 50.0; + double f1 = 100.0; + + // Test end points + { + EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f0, f1, 0.) ); + EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f1, f0, 0.) ); + + EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f0, f1, 1.) ); + EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f1, f0, 1.) ); + } + + // Test midpoint + { + double t = 0.5; + double exp = 75.; + EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + } + + // Another test + { + double t = 0.66; + double exp = 83.; + EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + } +} diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index d891db13e2..522cea8509 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -96,6 +96,22 @@ inline T log2( T& val) return std::log2(val); } + +/*! + * \brief Linearly interpolates between two values + * \param [in] val0 The first value + * \param [in] val2 The second value + * \param [in] t The interpolation parameter. + * \return The interpolated value + */ +template < typename T > +inline AXOM_HOST_DEVICE +T lerp( T v0, T v1, T t) +{ + constexpr T one = T(1); + return (one-t)*v0 + t*v1; +} + /*! * \brief Clamps an input value to a given range. * \param [in] val The value to clamp. @@ -113,7 +129,6 @@ T clampVal( T val, T lower, T upper ) : (val > upper) ? upper : val; } - /*! * \brief Clamps the upper range on an input value * From ef0b27e40ed930f0ef927a3354665af8e95fb551 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:25:48 -0700 Subject: [PATCH 042/290] Mir component now depends on primal --- src/axom/mainpage.md | 2 +- src/axom/mir/CMakeLists.txt | 2 +- src/docs/dependencies.dot | 2 +- src/index.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/mainpage.md b/src/axom/mainpage.md index 84f42826ad..366e41642b 100644 --- a/src/axom/mainpage.md +++ b/src/axom/mainpage.md @@ -21,7 +21,7 @@ Dependencies between components are as follows: - Slic optionally depends on Lumberjack - Slam, Primal, Mint, Quest, Spin, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic and Slam +- Mir depends on Slic, Slam and Primal - Spin depends on Primal and Slam - Quest depends on Slam, Primal, Spin, and Mint diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 65104b39b1..ef829028f0 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -10,7 +10,7 @@ # Check necessary dependencies #------------------------------------------------------------------------------ axom_component_requires(NAME MIR - COMPONENTS SLIC SLAM) + COMPONENTS SLIC SLAM PRIMAL) #------------------------------------------------------------------------------ # Specify all headers/sources diff --git a/src/docs/dependencies.dot b/src/docs/dependencies.dot index cddf4cd4df..5a5b7fef92 100644 --- a/src/docs/dependencies.dot +++ b/src/docs/dependencies.dot @@ -2,7 +2,7 @@ digraph dependencies { quest -> {slam primal mint spin}; {quest slam primal mint spin} -> {slic core}; mint -> sidre [style="dashed"]; - mir -> {slic core slam}; + mir -> {slic core slam primal}; spin -> {slam primal}; sidre -> {slic core}; slic -> core; diff --git a/src/index.rst b/src/index.rst index afcf6266ee..393cba7c82 100644 --- a/src/index.rst +++ b/src/index.rst @@ -88,7 +88,7 @@ Dependencies between modules are as follows: - Slic optionally depends on Lumberjack - Slam, Spin, Primal, Mint, Quest, and Sidre depend on Slic - Mint optionally depends on Sidre -- Mir depends on Slic and Slam +- Mir depends on Slic, Slam and Primal - Quest depends on Slam, Spin, Primal, and Mint The figure below summarizes the dependencies between the modules. Solid links From 557e9109506cda4c22d027a6a853ba2f717398a4 Mon Sep 17 00:00:00 2001 From: Kenneth Weiss Date: Wed, 3 Jul 2019 21:29:54 -0700 Subject: [PATCH 043/290] Mir implementation now uses primal for its Point class and for cell areas This replaces the custom Point2 class and lerp functionality. --- src/axom/mir/CellGenerator.cpp | 8 +- src/axom/mir/MIRMesh.cpp | 13 +- src/axom/mir/MIRMeshTypes.hpp | 33 +--- src/axom/mir/MIRUtilities.hpp | 54 +---- src/axom/mir/MeshTester.cpp | 231 ++++++++++++---------- src/axom/mir/MeshTester.hpp | 20 +- src/axom/mir/tests/mir_cell_generator.cpp | 42 ++-- src/axom/mir/tests/mir_utilities.cpp | 71 ------- 8 files changed, 178 insertions(+), 294 deletions(-) diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index c81c535f59..3e5537325f 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -91,7 +91,8 @@ void CellGenerator::generateVertexPositions(const mir::Shape shapeType, int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); - out_cellData.m_mapData.m_vertexPositions.push_back( mir::utilities::interpolateVertexPosition( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); + out_cellData.m_mapData.m_vertexPositions.push_back( + mir::Point2::lerp( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); } } } @@ -123,7 +124,8 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, int vIDFrom = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( mir::utilities::lerpFloat( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( + axom::utilities::lerp( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); } } } @@ -215,4 +217,4 @@ mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType } -} \ No newline at end of file +} diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 96130fed54..60ad504887 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -6,6 +6,7 @@ #include "MIRMesh.hpp" #include "axom/core.hpp" +#include "axom/primal.hpp" namespace axom { @@ -310,7 +311,7 @@ void MIRMesh::print() printf("vertexPositions: { "); for (int i = 0; i < m_verts.size(); ++i) { - printf("{%.2f, %.2f} ", m_vertexPositions[i].m_x, m_vertexPositions[i].m_y); + printf("{%.2f, %.2f} ", m_vertexPositions[i][0], m_vertexPositions[i][1]); } printf("}\n"); @@ -396,7 +397,7 @@ void MIRMesh::writeMeshToFile(std::string filename) // write positions for (int vID = 0; vID < m_verts.size(); ++vID) { - meshfile << m_vertexPositions[vID].m_x << " " << m_vertexPositions[vID].m_y << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + meshfile << m_vertexPositions[vID][0] << " " << m_vertexPositions[vID][1] << " 0\n"; // must always set all 3 coords; set Z=0 for 2D } meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); @@ -486,13 +487,7 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { - axom::float64 a = sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) );// the distance from p0 to p1 - axom::float64 b = sqrt( ((p2.m_x - p1.m_x) * (p2.m_x - p1.m_x)) + ((p2.m_y - p1.m_y) * (p2.m_y - p1.m_y)) );// the distance from p1 to p2 - axom::float64 c = sqrt( ((p0.m_x - p2.m_x) * (p0.m_x - p2.m_x)) + ((p0.m_y - p2.m_y) * (p0.m_y - p2.m_y)) );// the distance from p2 to p0 - - axom::float64 s = (a + b + c) / 2; // the semi-perimeter of the triangle - - return sqrt(s * (s - a) * (s - b) * (s - c)); + return primal::Triangle(p0,p1,p2).area(); } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 9daed29cbb..7ec2c79ba2 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -14,13 +14,10 @@ #include "axom/core.hpp" // for axom macros -// #include "axom/mir.hpp" // for Mir classes & functions #include "axom/slam.hpp" +#include "axom/primal.hpp" -namespace numerics = axom::numerics; -namespace slam = axom::slam; - namespace axom { namespace mir @@ -36,32 +33,8 @@ namespace mir Hexahedron }; - /** - * \brief Simple 2D Point class for example - */ - struct Point2 - { - Point2(double x = 0., double y = 0.) : m_x(x), m_y(y) {} - - Point2(const Point2& other) : m_x(other.m_x), m_y(other.m_y) {} - - Point2& operator=(const Point2& other) - { m_x = other.m_x; m_y = other.m_y; return *this; } - - Point2& operator+=(const Point2& other) - { m_x += other.m_x; m_y += other.m_y; return *this; } - Point2& operator/=(double val) - { m_x /= val; m_y += val; return *this; } - - double& operator[] (int i) { return (i==0) ? m_x : m_y; } - const double& operator[] (int i) const { return (i==0) ? m_x : m_y; } - - friend std::ostream& operator<<(std::ostream& os, const Point2& pt) - { return os << "{x:" << pt.m_x << ", y:" << pt.m_y <<"}"; } - - double m_x, m_y; - }; + using Point2 = primal::Point; // SET TYPE ALIASES using PosType = slam::DefaultPositionType; @@ -88,4 +61,4 @@ namespace mir using IntMap = slam::Map< BaseSet, int >; } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index 0f3c75c084..69c5eb3c17 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -102,44 +102,6 @@ namespace utilities return numVertices; } -//-------------------------------------------------------------------------------- - - /** - * \brief Performs linear interpolation between the two given float values. - * - * \param f0 The first float value. - * \param f1 The second float value. - * \param t The percent of the distance from the first float value to the second. - * - * \return The interpolated value. - */ - inline axom::float64 lerpFloat(const axom::float64 f0, - const axom::float64 f1, - const axom::float64 t) - { - return (1 - t) * f0 + t * f1; - } - -//-------------------------------------------------------------------------------- - - /** - * \brief Performs linear interpolation between the two vertex positions. - * - * \param vertexOnePos The position of the first vertex. - * \param vertexTwoPos The position of the second vertex. - * \param t The percent of the distance from vertex one to vertex two to interpolate at. - * - * \return The interpolated position. - */ - inline mir::Point2 interpolateVertexPosition(const mir::Point2& vertexOnePos, - const mir::Point2& vertexTwoPos, - const float t) - { - mir::Point2 interpolatedPoint; - interpolatedPoint.m_x = lerpFloat(vertexOnePos.m_x, vertexTwoPos.m_x, t); - interpolatedPoint.m_y = lerpFloat(vertexOnePos.m_y, vertexTwoPos.m_y, t); - return interpolatedPoint; - } //-------------------------------------------------------------------------------- @@ -259,20 +221,6 @@ namespace utilities return -1; } -//-------------------------------------------------------------------------------- - -/** - * \brief Calculate the distance between the two given points. - * - * \param p0 The first point. - * \param p1 The second point. - * - * \return The distance between the two points. - */ -inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) -{ - return sqrt( ((p1.m_x - p0.m_x) * (p1.m_x - p0.m_x)) + ((p1.m_y - p0.m_y) * (p1.m_y - p0.m_y)) ); -} //-------------------------------------------------------------------------------- @@ -280,4 +228,4 @@ inline axom::float64 distance(mir::Point2 p0, mir::Point2 p1) } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 6a8eb612e5..f64e310c8c 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -76,25 +76,25 @@ MIRMesh MeshTester::initTestCaseOne() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -170,25 +170,25 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -250,12 +250,12 @@ mir::MIRMesh MeshTester::initTestCaseThree() mapData.m_vertexPositions = { - mir::Point2( 1.0, 2.0 ), - mir::Point2( 0.5, 1.0 ), - mir::Point2( 1.5, 1.0 ), - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ) + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 0.5, 1.0 ), + mir::Point2::make_point( 1.5, 1.0 ), + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ) }; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); @@ -324,25 +324,25 @@ mir::MIRMesh MeshTester::initTestCaseFour() mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) + mir::Point2::make_point( 0.0, 3.0 ), + mir::Point2::make_point( 1.0, 3.0 ), + mir::Point2::make_point( 2.0, 3.0 ), + mir::Point2::make_point( 3.0, 3.0 ), + + mir::Point2::make_point( 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 2.0 ), + mir::Point2::make_point( 2.0, 2.0 ), + mir::Point2::make_point( 3.0, 2.0 ), + + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0 ), + mir::Point2::make_point( 2.0, 1.0 ), + mir::Point2::make_point( 3.0, 1.0 ), + + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 2.0, 0.0 ), + mir::Point2::make_point( 3.0, 0.0 ) }; int numMaterials = 2; @@ -359,7 +359,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() blueVolumeFractions.resize(numElements); // Generate the element volume fractions for the circle - mir::Point2 circleCenter(1.5, 1.5); + auto circleCenter = mir::Point2::make_point(1.5, 1.5); axom::float64 circleRadius = 1.25; int gridSize = 1000; for (int i = 0; i < numElements; ++i) @@ -388,7 +388,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- -mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius) +mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, const mir::Point2& circleCenter, axom::float64 circleRadius) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); @@ -438,40 +438,55 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, mir::Point2 //-------------------------------------------------------------------------------- -axom::float64 MeshTester::calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) +axom::float64 MeshTester::calculatePercentOverlapMonteCarlo( + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); - axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); - axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); - axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); - - if (distP0 < circleRadius && distP1 < circleRadius && distP2 < circleRadius && distP3 < circleRadius) + auto d0Sq = primal::squared_distance(quadP0, circleCenter); + auto d1Sq = primal::squared_distance(quadP1, circleCenter); + auto d2Sq = primal::squared_distance(quadP2, circleCenter); + auto d3Sq = primal::squared_distance(quadP3, circleCenter); + auto dRSq = circleRadius * circleRadius; + + int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) + + ((d1Sq < dRSq) ? 1 << 1 : 0) + + ((d2Sq < dRSq) ? 1 << 2 : 0) + + ((d3Sq < dRSq) ? 1 << 3 : 0); + const int allFlags = 15; + const int noFlags = 0; + + if (inFlags == allFlags ) { // The entire quad overlaps the circle - return 1.0; + return 1.; + } + else if( inFlags == noFlags) + { + return 0.; } - else if (distP0 < circleRadius || distP1 < circleRadius || distP2 < circleRadius || distP3 < circleRadius) + else { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = abs(quadP2.m_x - quadP1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(quadP0.m_y - quadP1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / static_cast(gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / static_cast(gridSize - 1); int countOverlap = 0; for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + quadP1.m_x, delta_y * y + quadP1.m_y); - if (mir::utilities::distance(samplePoint, circleCenter) < circleRadius) + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + quadP1[0], + delta_y * y + quadP1[1]); + if (primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } } - return countOverlap / (double) (gridSize * gridSize); - } - else - { - // None of the quad overlaps the circle - return 0; + return countOverlap / static_cast(gridSize * gridSize); } } @@ -545,7 +560,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) { for (int x = 0; x < gridSize + 1; ++x) { - points.push_back(mir::Point2(x, y)); + points.push_back(mir::Point2::make_point(x, y)); } } @@ -583,7 +598,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) // printf("points: { "); // for (int i = 0; i < numVertices; ++i) // { - // printf("{%.2f, %.2f} ", points[i].m_x, points[i].m_y); + // printf("{%.2f, %.2f} ", points[i][0], points[i][1]); // } // printf("}\n"); @@ -605,7 +620,7 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) int numMaterials = numCircles + 1; int defaultMaterialID = numMaterials - 1; // default material is always the last index - mir::Point2 circleCenter(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point + mir::Point2 circleCenter = mir::Point2::make_point(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point // Initialize the radii of the circles std::vector circleRadii; @@ -633,12 +648,13 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } // Use the uniform sampling method to generate volume fractions for each material + // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation for (int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; - mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; - mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; - mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; + mir::Point2& v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; + mir::Point2& v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; + mir::Point2& v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; + //mir::Point2& v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; @@ -648,18 +664,19 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); } - axom::float64 delta_x = abs(v2.m_x - v1.m_x) / (double) (gridSize - 1); - axom::float64 delta_y = abs(v0.m_y - v1.m_y) / (double) (gridSize - 1); + axom::float64 delta_x = axom::utilities::abs(v2[0] - v1[1]) / (double) (gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(v0[1] - v1[1]) / (double) (gridSize - 1); for (int y = 0; y < gridSize; ++y) { for (int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint(delta_x * x + v1.m_x, delta_y * y + v1.m_y); + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], delta_y * y + v1[1]); bool isPointSampled = false; for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) { - if (mir::utilities::distance(samplePoint, circleCenter) < circleRadii[cID]) + const auto r = circleRadii[cID]; + if (primal::squared_distance(samplePoint, circleCenter) < r*r) { materialCount[cID]++; isPointSampled = true; @@ -711,23 +728,29 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) //-------------------------------------------------------------------------------- -int MeshTester::circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3) +int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle - axom::float64 distP0 = mir::utilities::distance(quadP0, circleCenter); - axom::float64 distP1 = mir::utilities::distance(quadP1, circleCenter); - axom::float64 distP2 = mir::utilities::distance(quadP2, circleCenter); - axom::float64 distP3 = mir::utilities::distance(quadP3, circleCenter); + auto d0Sq = primal::squared_distance(quadP0, circleCenter); + auto d1Sq = primal::squared_distance(quadP1, circleCenter); + auto d2Sq = primal::squared_distance(quadP2, circleCenter); + auto d3Sq = primal::squared_distance(quadP3, circleCenter); + auto dRSq = circleRadius * circleRadius; int numCorners = 0; - if (distP0 < circleRadius) + if (d0Sq < dRSq) numCorners++; - if (distP1 < circleRadius) + if (d1Sq < dRSq) numCorners++; - if (distP2 < circleRadius) + if (d2Sq < dRSq) numCorners++; - if (distP3 < circleRadius) + if (d3Sq < dRSq) numCorners++; return numCorners; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 7d4ea33c40..dcbc380460 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -114,7 +114,9 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius); + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius); /** * \brief Initializes a mesh to be used for validating the results of quad clipping. @@ -147,7 +149,14 @@ namespace mir * * /return The percent value overlap of the circle and the quad between [0, 1]. */ - axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + axom::float64 calculatePercentOverlapMonteCarlo( + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); /** * \brief Calculates the number of corners of the quad that are within the circle. @@ -161,7 +170,12 @@ namespace mir * * \return The number of corners of the quad that are within the circle. */ - int circleQuadCornersOverlaps(mir::Point2 circleCenter, axom::float64 circleRadius, mir::Point2 quadP0, mir::Point2 quadP1, mir::Point2 quadP2, mir::Point2 quadP3); + int circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); }; } diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp index f03b480bda..9505eae633 100644 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -101,10 +101,10 @@ TEST(mir_cell_generator, generate_vertex_positions) vertexMap[7] = { 2, 3 }; std::vector originalVertexPositions; - originalVertexPositions.push_back( mir::Point2(0.0, 1.0) ); - originalVertexPositions.push_back( mir::Point2(0.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2(1.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2(1.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(0.0, 1.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(0.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(1.0, 0.0) ); + originalVertexPositions.push_back( mir::Point2::make_point(1.0, 1.0) ); axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; @@ -112,30 +112,30 @@ TEST(mir_cell_generator, generate_vertex_positions) mir::CellGenerator cellGenerator; cellGenerator.generateVertexPositions(shapeType, vertexMap, originalVertexPositions, tValues, cellData); + const auto& positions = cellData.m_mapData.m_vertexPositions; + EXPECT_NEAR( positions[0][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[0][1], 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[0].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[1][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[1][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[1].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[2][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[2][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[2].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[3][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[3][1], 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[3].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[4][0], 0.0, 0.00001 ); + EXPECT_NEAR( positions[4][1], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_x, 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[4].m_y, 0.5, 0.00001 ); + EXPECT_NEAR( positions[5][0], 0.5, 0.00001 ); + EXPECT_NEAR( positions[5][1], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_x, 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[5].m_y, 0.0, 0.00001 ); + EXPECT_NEAR( positions[6][0], 1.0, 0.00001 ); + EXPECT_NEAR( positions[6][1], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_x, 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[6].m_y, 0.5, 0.00001 ); - - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_x, 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexPositions[7].m_y, 1.0, 0.00001 ); + EXPECT_NEAR( positions[7][0], 0.5, 0.00001 ); + EXPECT_NEAR( positions[7][1], 1.0, 0.00001 ); } //---------------------------------------------------------------------- diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 82e26b89a0..6d5a6c2dde 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -11,78 +11,7 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" -using namespace axom; -TEST(mir_interpolation, float_linear_interpolation) -{ - axom::float64 f0 = 50.0; - axom::float64 f1 = 100.0; - - axom::float64 t = 0.0; - axom::float64 interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 50.0 ); - - t = 1.0; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 100.0 ); - - t = 0.5; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 75.0 ); - - t = 0.66; - interpolant = mir::utilities::lerpFloat(f0, f1, t); - EXPECT_DOUBLE_EQ( interpolant, 83.0 ); -} - -//---------------------------------------------------------------------- - -TEST(mir_interpolation, point_linear_interpolation) -{ - mir::Point2 p0(0.25, 0.0); - mir::Point2 p1(1.25, 1.0); - - axom::float64 t = 0.0; - mir::Point2 interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 0.25); - EXPECT_DOUBLE_EQ( interpolant.m_y, 0.0); - - t = 1.0; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 1.25); - EXPECT_DOUBLE_EQ( interpolant.m_y, 1.0); - - t = 0.5; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_DOUBLE_EQ( interpolant.m_x, 0.75); - EXPECT_DOUBLE_EQ( interpolant.m_y, 0.5); - - t = 0.66; - interpolant = mir::utilities::interpolateVertexPosition(p0, p1, t); - EXPECT_NEAR( interpolant.m_x, 0.91, 0.00001); - EXPECT_NEAR( interpolant.m_y, 0.66, 0.00001); -} - -//---------------------------------------------------------------------- - -TEST(mir_distance_utility, compute_distance) -{ - mir::Point2 p0( 0.25, 0.0 ); - mir::Point2 p1( 1.25, 0.0 ); - axom::float64 dist = mir::utilities::distance(p0, p1); - EXPECT_DOUBLE_EQ( dist, 1.0 ); - - mir::Point2 p2( 0.25, 0.0 ); - mir::Point2 p3( 1.25, 1.0 ); - dist = mir::utilities::distance(p2, p3); - EXPECT_NEAR( dist, 1.4142, 0.001 ); - - mir::Point2 p4( 1.0, 1.0 ); - mir::Point2 p5( 1.0, 1.0 ); - dist = mir::utilities::distance(p4, p5); - EXPECT_DOUBLE_EQ( dist, 0.0 ); - -} //---------------------------------------------------------------------- From a2ca33c748c259e45bc10f17c2ab99a1ba0de92e Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 2 Jul 2019 08:35:17 -0700 Subject: [PATCH 044/290] Added clipping tables for 3D shapes (wedges and hexahedrons all decompose). Added tests for outputting a mesh for each triangle, quad, and tetrahedron clipping cases. Updated Point2 to have a third dimension. --- src/axom/mir/CellClipper.cpp | 10 +- src/axom/mir/CellGenerator.cpp | 32 +- src/axom/mir/InterfaceReconstructor.cpp | 2 +- src/axom/mir/MIRMesh.cpp | 17 +- src/axom/mir/MIRUtilities.hpp | 84 +++-- src/axom/mir/ZooClippingTables.cpp | 390 ++++++++++++++++++++- src/axom/mir/ZooClippingTables.hpp | 4 + src/axom/mir/tests/mir_cell_clipper.cpp | 444 +++++++++++++++++------- 8 files changed, 819 insertions(+), 164 deletions(-) diff --git a/src/axom/mir/CellClipper.cpp b/src/axom/mir/CellClipper.cpp index c5f25d4272..644b3f31c4 100644 --- a/src/axom/mir/CellClipper.cpp +++ b/src/axom/mir/CellClipper.cpp @@ -57,7 +57,7 @@ void CellClipper::computeClippingPoints(const mir::Shape shapeType, newElements[currentElementIndex].push_back(vID); newVertices[vID].push_back(currentElementIndex); - if ( vID >= mir::utilities::numVerts(shapeType) ) + if ( vID >= mir::utilities::numVerts(shapeType) && vID < (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) { int vertexOneID = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vertexTwoID = mir::utilities::getEdgeEndpoint(shapeType, vID, false); @@ -145,6 +145,14 @@ const std::vector >& CellClipper::getClipTable(const mir::Shape return triangleClipTableVec; case mir::Shape::Quad: return quadClipTableVec; + case mir::Shape::Tetrahedron: + return tetrahedronClipTableVec; + case mir::Shape::Pyramid: + return pyramidClipTableVec; + case mir::Shape::Triangular_Prism: + return triangularPrismClipTableVec; + case mir::Shape::Hexahedron: + return hexahedronClipTableVec; default: printf("No clipping table for this shape type.\n"); return triangleClipTableVec; diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index 3e5537325f..b60756c325 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -85,6 +85,20 @@ void CellGenerator::generateVertexPositions(const mir::Shape shapeType, // This vertex is one of the shape's original vertices out_cellData.m_mapData.m_vertexPositions.push_back( vertexPositions[vID] ); } + else if (mir::utilities::isShapeThreeDimensional(shapeType) && vID == (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) + { + // This vertex is the central vertex of a three dimensional shape being decomposed/tesselated (tetrahedrons don't decompose) + + // Average the vertex position values at the corners of the shape + mir::Point2 centroid; + for (auto i = 0u; i < vertexPositions.size(); ++i) + { + centroid.array() += vertexPositions[i].array(); + } + centroid.array() /= vertexPositions.size(); + + out_cellData.m_mapData.m_vertexPositions.push_back( centroid ); + } else { // This vertex is between two of the shape's original vertices @@ -118,6 +132,20 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, // This vertex is one of the shape's original vertices out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( vertexVF[matID][vID] ); } + else if (mir::utilities::isShapeThreeDimensional(shapeType) && vID == (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) + { + // This vertex is the central vertex of a three dimensional shape being decomposed/tesselated (tetrahedrons don't decompose) + + // Average the vertex volume fractions values at the corners of the shape + axom::float64 averageValue(0.0); + for (auto i = 0u; i < vertexVF[matID].size(); ++i) + { + averageValue += vertexVF[matID][i]; + } + averageValue /= (axom::float64) vertexVF[matID].size(); + + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( averageValue ); + } else { // This vertex is between two of the shape's original vertices @@ -185,7 +213,7 @@ mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType break; default: newShapeType = mir::Shape::Triangle; - printf("Invalid number of vertices in determineElementShapeType().\n"); + printf("2D Case: Invalid number of vertices in determineElementShapeType().\n"); break; } } @@ -208,7 +236,7 @@ mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType break; default: newShapeType = mir::Shape::Tetrahedron; - printf("Invalid number of vertices in determineElementShapeType().\n"); + printf("3D Case: Invalid number of vertices in determineElementShapeType().\n"); break; } } diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index b18c17cfdb..fce0865123 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -246,7 +246,7 @@ void InterfaceReconstructor::generateCleanCells(const int eID, } } - // Modify the evIndex values to account for the fact that perhaps not all 8 possible vertices are present + // Modify the evIndex values to account for the fact that perhaps not all possible vertices are present std::vector evIndexSubtract; evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 60ad504887..7f9de8422b 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -396,8 +396,13 @@ void MIRMesh::writeMeshToFile(std::string filename) // write positions for (int vID = 0; vID < m_verts.size(); ++vID) - { - meshfile << m_vertexPositions[vID][0] << " " << m_vertexPositions[vID][1] << " 0\n"; // must always set all 3 coords; set Z=0 for 2D + { + auto& pt = m_vertexPositions[vID]; + for(int i=0; i < pt.dimension(); ++i) + { + meshfile << pt[i] << " "; + } + meshfile << (pt.dimension()==2 ? "0" : "") << "\n"; } meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); @@ -419,6 +424,14 @@ void MIRMesh::writeMeshToFile(std::string filename) meshfile << "5\n"; else if (m_shapeTypes[i] == mir::Shape::Quad) meshfile << "9\n"; + else if (m_shapeTypes[i] == mir::Shape::Tetrahedron) + meshfile << "10\n"; + else if (m_shapeTypes[i] == mir::Shape::Triangular_Prism) + meshfile << "13\n"; + else if (m_shapeTypes[i] == mir::Shape::Pyramid) + meshfile << "14\n"; + else if (m_shapeTypes[i] == mir::Shape::Hexahedron) + meshfile << "12\n"; } // write element materials diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index 69c5eb3c17..be759591c5 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -79,22 +79,22 @@ namespace utilities switch (shape) { case mir::Shape::Triangle: - numVertices = 6; + numVertices = 6 + 1; // add one for the central vertex (not used) break; case mir::Shape::Quad: - numVertices = 8; + numVertices = 8 + 1; // add one for the central vertex (not used) break; case mir::Shape::Tetrahedron: - numVertices = 10; + numVertices = 10 + 1; // add one for the central vertex (not used) break; case mir::Shape::Pyramid: - numVertices = 13; + numVertices = 13 + 1; // add one for the central vertex break; case mir::Shape::Triangular_Prism: - numVertices = 15; + numVertices = 15 + 1; // add one for the central vertex break; case mir::Shape::Hexahedron: - numVertices = 20; + numVertices = 20 + 1; // add one for the central vertex break; default: printf("Invalid shape. Cannot determine maxPossibleNumVerts().\n"); @@ -155,20 +155,21 @@ namespace utilities case mir::Shape::Pyramid: if ( midpointVertexID == 5 && isFromVertex ) { return 0; } if ( midpointVertexID == 5 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 6 && isFromVertex ) { return 0; } + if ( midpointVertexID == 6 && isFromVertex ) { return 1; } if ( midpointVertexID == 6 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 7 && isFromVertex ) { return 0; } + if ( midpointVertexID == 7 && isFromVertex ) { return 2; } if ( midpointVertexID == 7 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 8 && isFromVertex ) { return 0; } - if ( midpointVertexID == 8 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 9 && isFromVertex ) { return 1; } - if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 10 && isFromVertex ) { return 2; } - if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 11 && isFromVertex ) { return 3; } + if ( midpointVertexID == 8 && isFromVertex ) { return 3; } + if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + + if ( midpointVertexID == 9 && isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 10 && isFromVertex ) { return 1; } + if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 11 && isFromVertex ) { return 2; } if ( midpointVertexID == 11 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 12 && isFromVertex ) { return 4; } - if ( midpointVertexID == 12 && !isFromVertex ) { return 1; } + if ( midpointVertexID == 12 && isFromVertex ) { return 3; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } case mir::Shape::Triangular_Prism: if ( midpointVertexID == 6 && isFromVertex ) { return 0; } if ( midpointVertexID == 6 && !isFromVertex ) { return 1; } @@ -176,12 +177,14 @@ namespace utilities if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } if ( midpointVertexID == 8 && isFromVertex ) { return 2; } if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + if ( midpointVertexID == 9 && isFromVertex ) { return 0; } if ( midpointVertexID == 9 && !isFromVertex ) { return 3; } if ( midpointVertexID == 10 && isFromVertex ) { return 1; } if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } if ( midpointVertexID == 11 && isFromVertex ) { return 2; } if ( midpointVertexID == 11 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 12 && isFromVertex ) { return 3; } if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } if ( midpointVertexID == 13 && isFromVertex ) { return 4; } @@ -197,22 +200,24 @@ namespace utilities if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } if ( midpointVertexID == 11 && isFromVertex ) { return 3; } if ( midpointVertexID == 11 && !isFromVertex ) { return 0; } - if ( midpointVertexID == 12 && isFromVertex ) { return 0; } - if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 13 && isFromVertex ) { return 1; } - if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } - if ( midpointVertexID == 14 && isFromVertex ) { return 2; } - if ( midpointVertexID == 14 && !isFromVertex ) { return 6; } - if ( midpointVertexID == 15 && isFromVertex ) { return 3; } - if ( midpointVertexID == 15 && !isFromVertex ) { return 7; } - if ( midpointVertexID == 16 && isFromVertex ) { return 4; } - if ( midpointVertexID == 16 && !isFromVertex ) { return 5; } - if ( midpointVertexID == 17 && isFromVertex ) { return 5; } - if ( midpointVertexID == 17 && !isFromVertex ) { return 6; } - if ( midpointVertexID == 18 && isFromVertex ) { return 6; } - if ( midpointVertexID == 18 && !isFromVertex ) { return 7; } - if ( midpointVertexID == 19 && isFromVertex ) { return 7; } - if ( midpointVertexID == 19 && !isFromVertex ) { return 4; } + + if ( midpointVertexID == 12 && isFromVertex ) { return 4; } + if ( midpointVertexID == 12 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 13 && isFromVertex ) { return 5; } + if ( midpointVertexID == 13 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 14 && isFromVertex ) { return 6; } + if ( midpointVertexID == 14 && !isFromVertex ) { return 7; } + if ( midpointVertexID == 15 && isFromVertex ) { return 7; } + if ( midpointVertexID == 15 && !isFromVertex ) { return 4; } + + if ( midpointVertexID == 16 && isFromVertex ) { return 0; } + if ( midpointVertexID == 16 && !isFromVertex ) { return 4; } + if ( midpointVertexID == 17 && isFromVertex ) { return 1; } + if ( midpointVertexID == 17 && !isFromVertex ) { return 5; } + if ( midpointVertexID == 18 && isFromVertex ) { return 2; } + if ( midpointVertexID == 18 && !isFromVertex ) { return 6; } + if ( midpointVertexID == 19 && isFromVertex ) { return 3; } + if ( midpointVertexID == 19 && !isFromVertex ) { return 7; } default: printf("Edge endpoint case not implemented.\n"); return -1; @@ -221,6 +226,19 @@ namespace utilities return -1; } +//-------------------------------------------------------------------------------- + +/** + * \brief Determines if the shape type is a three dimensional shape or not. + * + * \param shapeType The shape type from the finite element zoo. + * + * \return True, if the shape is a tetrahedron, pyramid, triangular prism, or a hexahedron. + */ +inline bool isShapeThreeDimensional(mir::Shape shapeType) +{ + return (shapeType == mir::Tetrahedron || shapeType == mir::Pyramid || shapeType == mir::Triangular_Prism || shapeType == mir::Hexahedron); +} //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 9f87edbe5d..85522a5027 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -99,6 +99,394 @@ namespace mir {4,0,1,4,5,3,5,4,2,-1}, {3,0,1,2,-1,-1,-1,-1,-1,-1} }; - + + const std::vector > tetrahedronClipTableVec = + { + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + {4,3,6,9,8,6,0,1,2,6,9,8,-1}, + {4,2,5,7,8,6,0,1,3,5,7,8,-1}, + {6,2,5,7,3,6,9,6,0,5,6,1,7,9,-1}, + {4,1,4,7,9,6,0,2,3,4,7,9,-1}, + {6,1,4,7,3,6,8,6,0,4,6,2,7,8,-1}, + {6,1,4,9,2,5,8,6,0,4,5,3,9,8,-1}, + {4,0,4,5,6,6,1,2,3,4,5,6,-1}, + + {4,0,4,5,6,6,1,2,3,4,5,6,-1}, + {6,1,4,9,2,5,8,6,0,4,5,3,9,8,-1}, + {6,1,4,7,3,6,8,6,0,4,6,2,7,8,-1}, + {4,1,4,7,9,6,0,2,3,4,7,9,-1}, + {6,2,5,7,3,6,9,6,0,5,6,1,7,9,-1}, + {4,2,5,7,8,6,0,1,3,5,7,8,-1}, + {4,3,6,9,8,6,0,1,2,6,9,8,-1}, + {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, + }; + + const std::vector > pyramidClipTableVec = + { + {5,0,1,2,3,4,-1}, + {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, + {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, + {6,9,10,11,0,1,2,4,9,10,11,4,6,4,9,11,13,8,7,6,9,0,8,11,2,7,-1}, + {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, + {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, + {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, + {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, + {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, + {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, + + {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, + {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, + {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, + {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, + {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, + {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, + {6,9,10,11,0,1,2,4,9,10,11,4,6,4,9,11,13,8,7,6,9,0,8,11,2,7,-1}, + {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, + {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, + {5,0,1,2,3,4,-1} + }; + + // currently set to decompose as if every clipping case were considered "tough" (2**6 = 64 cases total) + const std::vector > triangularPrismClipTableVec = + { + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1} + }; + + // currently set to decompose as if every clipping case were considered "tough" (2**8 = 256 cases total) + const std::vector > hexahedronClipTableVec = + { + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, + {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1} + }; + } } \ No newline at end of file diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index de2b8be83c..0b7629548e 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -24,6 +24,10 @@ namespace mir extern const std::vector > triangleClipTableVec; extern const std::vector > quadClipTableVec; + extern const std::vector > tetrahedronClipTableVec; + extern const std::vector > pyramidClipTableVec; + extern const std::vector > triangularPrismClipTableVec; + extern const std::vector > hexahedronClipTableVec; } } #endif diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp index 68f538e3f4..2c69cb4222 100644 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -11,152 +11,76 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" -using namespace axom; - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, triangle_case_zero) -{ - mir::Shape shape = mir::Shape::Triangle; - std::vector matOneVF = {0.0, 0.0, 0.0}; - std::vector matTwoVF = {1.0, 1.0, 1.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ( 0 , clippingCase); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, triangle_case_three) -{ - mir::Shape shape = mir::Shape::Triangle; - std::vector matOneVF = {0.0, 1.0, 1.0}; - std::vector matTwoVF = {1.0, 0.0, 0.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 3 , clippingCase); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, triangle_case_four) -{ - mir::Shape shape = mir::Shape::Triangle; - std::vector matOneVF = {1.0, 0.0, 0.0}; - std::vector matTwoVF = {0.0, 1.0, 1.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); +#include - EXPECT_EQ( 4 , clippingCase); -} +using namespace axom; //---------------------------------------------------------------------- -TEST(mir_clipping_case, triangle_case_seven) +TEST(mir_clipping_case, all_triangle_cases) { mir::Shape shape = mir::Shape::Triangle; - std::vector matOneVF = {1.0, 1.0, 1.0}; - std::vector matTwoVF = {0.0, 0.0, 0.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ( 7 , clippingCase); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, quad_case_zero) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; - std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 0 , clippingCase); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, quad_case_one) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; - std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 1 , clippingCase); -} + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); -//---------------------------------------------------------------------- + for (auto actualClippingCase = 0; actualClippingCase < numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; -TEST(mir_clipping_case, quad_case_two) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 1.0, 0.0}; - std::vector matTwoVF = {1.0, 1.0, 0.0, 1.0}; + for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 2 , clippingCase); -} + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } -//---------------------------------------------------------------------- + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } -TEST(mir_clipping_case, quad_case_three) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; - std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 3 , clippingCase); + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } } -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, quad_case_five) +TEST(mir_clipping_case, all_quad_cases) { mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; - std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; - - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 5 , clippingCase); -} + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); -//---------------------------------------------------------------------- + for (auto actualClippingCase = 0; actualClippingCase < numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; -TEST(mir_clipping_case, quad_case_ten) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {1.0, 0.0, 1.0, 0.0}; - std::vector matTwoVF = {0.0, 1.0, 0.0, 1.0}; + for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 10 , clippingCase); -} + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } -//---------------------------------------------------------------------- + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } -TEST(mir_clipping_case, quad_case_fifteen) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {1.0, 1.0, 1.0, 1.0}; - std::vector matTwoVF = {0.0, 0.0, 0.0, 0.0}; + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - mir::CellClipper clipper; - unsigned int clippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ( 15 , clippingCase); + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } } //---------------------------------------------------------------------- @@ -436,6 +360,278 @@ TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_five) //---------------------------------------------------------------------- +TEST(clipping_table_mesh_generation, triangle_meshes) +{ + mir::Shape shape = mir::Shape::Triangle; + int numVerts = mir::utilities::numVerts(shape); + //int numCases = pow(2, numVerts); + + for (auto actualClippingCase = 0u; actualClippingCase < mir::triangleClipTableVec.size(); ++actualClippingCase) + { + // Initialize the mesh + int numElements = 1; + int numVertices = 3; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2 }; + topology.m_evBegins = { 0,3 }; + topology.m_veInds = { 0,0,0 }; + topology.m_veBegins = { 0,1,2,3 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.5, 0.717 ), + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Triangle }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; + std::string filename = "mir_clippingcase_triangle_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(filepath + filename); + } +} + +//---------------------------------------------------------------------- + +TEST(clipping_table_mesh_generation, tetrahedron_meshes) +{ + mir::Shape shape = mir::Shape::Tetrahedron; + int numVerts = mir::utilities::numVerts(shape); + //int numCases = pow(2, numVerts); + + for (unsigned int actualClippingCase = 0; actualClippingCase < mir::tetrahedronClipTableVec.size(); ++actualClippingCase) + { + // Initialize the mesh + int numElements = 1; + int numVertices = 4; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2,3 }; + topology.m_evBegins = { 0,4 }; + topology.m_veInds = { 0,0,0,0 }; + topology.m_veBegins = { 0,1,2,3,4 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.5, 0.717, 0.0 ), + mir::Point2::make_point( 0.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0, 0.0 ), + mir::Point2::make_point( 0.5, 0.3585, 0.717 ) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Tetrahedron }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; + std::string filename = "mir_clippingcase_tetrahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(filepath + filename); + } +} + +//---------------------------------------------------------------------- + +// TEST(clipping_table_mesh_generation, quad_meshes) +// { +// mir::Shape shape = mir::Shape::Quad; +// int numVerts = mir::utilities::numVerts(shape); +// int numCases = pow(2, numVerts); + +// for (unsigned int actualClippingCase = 0; actualClippingCase < mir::quadClipTableVec.size(); ++actualClippingCase) +// { +// // Initialize the mesh +// int numElements = 1; +// int numVertices = 4; + +// // Create the mesh connectivity information +// mir::CellTopologyData topology; +// topology.m_evInds = { 0,1,2,3 }; +// topology.m_evBegins = { 0,4 }; +// topology.m_veInds = { 0,0,0,0 }; +// topology.m_veBegins = { 0,1,2,3,4 }; + +// mir::VertSet verts = mir::VertSet(numVertices); +// mir::ElemSet elems = mir::ElemSet(numElements); + +// // Calculate the vertex volume fractions needed to clip with the current case +// std::vector matOneVF; +// std::vector matTwoVF; +// std::string bitString(""); +// for (unsigned int bitIndex = 0; bitIndex < numVerts; ++bitIndex) +// { +// unsigned int shiftedBit = 1; +// shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + +// axom::float64 matOneValue; +// if (actualClippingCase & shiftedBit) +// { +// matOneValue = 1.0; +// bitString += "1"; +// } +// else +// { +// matOneValue = 0.0; +// bitString += "0"; +// } + +// matOneVF.push_back( matOneValue ); +// matTwoVF.push_back( 1.0 - matOneValue ); +// } + +// std::vector > vertexVF = { +// matOneVF, +// matTwoVF +// }; + +// std::vector > elementVF = { +// { 0.5 }, +// { 0.5 } +// }; + + +// std::vector points = +// { +// mir::Point2( 0.0, 1.0 ), +// mir::Point2( 0.0, 0.0 ), +// mir::Point2( 1.0, 0.0 ), +// mir::Point2( 1.0, 1.0 ) +// }; + +// mir::CellMapData mapData; +// mapData.m_elementDominantMaterials = { mir::NULL_MAT }; +// mapData.m_elementParents = { 0 }; +// mapData.m_vertexPositions = points; +// mapData.m_shapeTypes = { mir::Shape::Quad }; + +// // Build the mesh +// mir::MIRMesh testMesh; +// testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); +// testMesh.constructMeshVolumeFractionsVertex(vertexVF); + +// // Clip the mesh using the actualClippingCase index +// mir::MIRMesh outputMesh; +// mir::InterfaceReconstructor interfaceReconstructor; +// interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + +// // Write out the processed mesh +// std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; +// std::string filename = "mir_clippingcase_quad_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; +// outputMesh.writeMeshToFile(filepath + filename); +// } +// } + +//---------------------------------------------------------------------- + int main(int argc, char* argv[]) { int result = 0; From ee63b7bbe6b3a44f5c1d87101f197d7c606f47e3 Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Fri, 5 Jul 2019 15:01:56 -0700 Subject: [PATCH 045/290] Added ability to handle tough 3D clipping cases. Added tests for outputting pyramids, wedges, and hexahedrons. Meshes are now written to the meshes/ directory in build directory. Added new utility functions and associated unit tests. Added a concentric spheres test case. --- src/axom/mir/CellData.cpp | 15 +- src/axom/mir/CellData.hpp | 4 +- src/axom/mir/CellGenerator.cpp | 72 +-- src/axom/mir/CellGenerator.hpp | 18 - src/axom/mir/InterfaceReconstructor.cpp | 294 ++++++--- src/axom/mir/InterfaceReconstructor.hpp | 22 +- src/axom/mir/MIRMesh.cpp | 34 +- src/axom/mir/MIRMesh.hpp | 4 +- src/axom/mir/MIRMeshTypes.hpp | 2 +- src/axom/mir/MIRUtilities.hpp | 155 ++++- src/axom/mir/MeshTester.cpp | 224 +++++++ src/axom/mir/MeshTester.hpp | 23 +- src/axom/mir/ZooClippingTables.cpp | 30 +- .../mir/examples/mir_concentric_circles.cpp | 2 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 8 +- src/axom/mir/tests/CMakeLists.txt | 2 - src/axom/mir/tests/mir_cell_clipper.cpp | 611 +++++++++++++++--- .../mir/tests/mir_interface_reconstructor.cpp | 30 - src/axom/mir/tests/mir_smoke.cpp | 38 -- src/axom/mir/tests/mir_utilities.cpp | 83 +++ 20 files changed, 1263 insertions(+), 408 deletions(-) delete mode 100644 src/axom/mir/tests/mir_interface_reconstructor.cpp delete mode 100644 src/axom/mir/tests/mir_smoke.cpp diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index 176d1d6fc7..c9bd25ecb5 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -13,16 +13,9 @@ namespace mir //-------------------------------------------------------------------------------- CellData::CellData() - { - - } - - //-------------------------------------------------------------------------------- - - CellData::~CellData() - { - - } + : m_numVerts(0) + , m_numElems(0) + {} //-------------------------------------------------------------------------------- @@ -97,4 +90,4 @@ namespace mir //-------------------------------------------------------------------------------- } -} \ No newline at end of file +} diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index a73134374c..93caca8fb4 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -75,7 +75,7 @@ namespace mir /** * \brief Default destructor. */ - ~CellData(); + ~CellData() = default; /** * \brief Merges the cell data from the given cell into this cell. @@ -94,4 +94,4 @@ namespace mir } } -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index b60756c325..888bfb24a2 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -85,17 +85,10 @@ void CellGenerator::generateVertexPositions(const mir::Shape shapeType, // This vertex is one of the shape's original vertices out_cellData.m_mapData.m_vertexPositions.push_back( vertexPositions[vID] ); } - else if (mir::utilities::isShapeThreeDimensional(shapeType) && vID == (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) + else if ( mir::utilities::isCenterVertex(shapeType, vID) ) { - // This vertex is the central vertex of a three dimensional shape being decomposed/tesselated (tetrahedrons don't decompose) - // Average the vertex position values at the corners of the shape - mir::Point2 centroid; - for (auto i = 0u; i < vertexPositions.size(); ++i) - { - centroid.array() += vertexPositions[i].array(); - } - centroid.array() /= vertexPositions.size(); + mir::Point2 centroid = mir::utilities::computeAveragePoint(vertexPositions); out_cellData.m_mapData.m_vertexPositions.push_back( centroid ); } @@ -132,18 +125,11 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, // This vertex is one of the shape's original vertices out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( vertexVF[matID][vID] ); } - else if (mir::utilities::isShapeThreeDimensional(shapeType) && vID == (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) + else if ( mir::utilities::isCenterVertex(shapeType, vID) ) { - // This vertex is the central vertex of a three dimensional shape being decomposed/tesselated (tetrahedrons don't decompose) - // Average the vertex volume fractions values at the corners of the shape - axom::float64 averageValue(0.0); - for (auto i = 0u; i < vertexVF[matID].size(); ++i) - { - averageValue += vertexVF[matID][i]; - } - averageValue /= (axom::float64) vertexVF[matID].size(); - + axom::float64 averageValue = mir::utilities::computeAverageFloat( vertexVF[matID] ); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( averageValue ); } else @@ -196,53 +182,5 @@ int CellGenerator::determineCleanCellMaterial(const Shape elementShape, //-------------------------------------------------------------------------------- -mir::Shape CellGenerator::determineElementShapeType(const Shape parentShapeType, - const int numVerts) -{ - mir::Shape newShapeType; - if (parentShapeType == mir::Shape::Triangle || parentShapeType == mir::Shape::Quad) - { - // Handle the two-dimensional case - switch (numVerts) - { - case 3: - newShapeType = mir::Shape::Triangle; - break; - case 4: - newShapeType = mir::Shape::Quad; - break; - default: - newShapeType = mir::Shape::Triangle; - printf("2D Case: Invalid number of vertices in determineElementShapeType().\n"); - break; - } - } - else - { - // Handle the three-dimensional case - switch (numVerts) - { - case 4: - newShapeType = mir::Shape::Tetrahedron; - break; - case 5: - newShapeType = mir::Shape::Pyramid; - break; - case 6: - newShapeType = mir::Shape::Triangular_Prism; - break; - case 8: - newShapeType = mir::Shape::Hexahedron; - break; - default: - newShapeType = mir::Shape::Tetrahedron; - printf("3D Case: Invalid number of vertices in determineElementShapeType().\n"); - break; - } - } - return newShapeType; -} - - } } diff --git a/src/axom/mir/CellGenerator.hpp b/src/axom/mir/CellGenerator.hpp index ccf189537b..94df6a3f3e 100644 --- a/src/axom/mir/CellGenerator.hpp +++ b/src/axom/mir/CellGenerator.hpp @@ -122,24 +122,6 @@ namespace mir const int matOne, const int matTwo, const std::vector >& vertexVF); - - /** - * \brief Determine the shape type of an element. - * - * \param parentShapeType The shape of the element from which the new element is generated. - * \param numVerts The number of vertices of the new element. - * - * \note It is assumed that the given cell is one that results from splitting its parent cell. - */ - mir::Shape determineElementShapeType(const Shape parentShapeType, - const int numVerts); - - /** - * \brief Ensures that the element is dominated by a material that is actually present in the original parent cell. - * - * \param - */ - // void fixDominantMaterial(mir::MIRMesh& originalMesh, const std::map >& newElements, const int matOne, const int matTwo, CellData& out_cellData); }; //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index fce0865123..1c81c47589 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -51,7 +51,42 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; int matOne = matID; - generateCleanCells(eID, currentDominantMat, matOne, intermediateMesh, temp_cellData[eID]); + // Extract the data for the current element + std::vector elementVertices; + for (int vID = 0; vID < intermediateMesh.m_bdry[eID].size(); ++vID) + { + elementVertices.push_back(intermediateMesh.m_bdry[eID][vID]); + } + + // Extract the vertex volume fractions of the element to split + std::vector > originalElementVertexVF; + for (int matID = 0; matID < intermediateMesh.m_numMaterials; ++matID) + { + std::vector materialVertexVF; + for (unsigned long vID = 0; vID < elementVertices.size(); ++vID) + { + int originalVID = elementVertices[vID]; + materialVertexVF.push_back( intermediateMesh.m_materialVolumeFractionsVertex[matID][originalVID] ); + } + originalElementVertexVF.push_back( materialVertexVF ); + } + + // Extract the vertex positions of the element to split + std::vector originalElementVertexPositions; + for (unsigned long vID = 0; vID < elementVertices.size(); ++vID) + { + int originalVID = elementVertices[vID]; + originalElementVertexPositions.push_back( intermediateMesh.m_vertexPositions[originalVID] ); + } + + generateCleanCells( (mir::Shape) intermediateMesh.m_shapeTypes[eID], + intermediateMesh.m_elementParentIDs[eID], + currentDominantMat, + matOne, + elementVertices, + originalElementVertexVF, + originalElementVertexPositions, + temp_cellData[eID]); } // Merge each of the cells into the first CellData struct @@ -59,7 +94,7 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe { temp_cellData[0].mergeCell(temp_cellData[eID]); } - + mir::VertSet combined_verts(temp_cellData[0].m_numVerts); mir::ElemSet combined_elems(temp_cellData[0].m_numElems); @@ -75,7 +110,6 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe // TODO: Run post-processing on mesh to meld the mesh vertices that are within a certain epsilon outputMesh = finalMesh; - // return finalMesh; } //-------------------------------------------------------------------------------- @@ -124,50 +158,46 @@ void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh //-------------------------------------------------------------------------------- -void InterfaceReconstructor::generateCleanCells(const int eID, +void InterfaceReconstructor::generateCleanCells(mir::Shape shapeType, + const int parentElementID, const int matOne, const int matTwo, - mir::MIRMesh& tempMesh, + const std::vector& elementVertices, + const std::vector >& originalElementVertexVF, + const std::vector& originalElementVertexPositions, CellData& out_cellData) { - mir::Shape shapeType = (mir::Shape) tempMesh.m_shapeTypes[eID]; - auto elementVertices = tempMesh.m_bdry[eID]; - // Set up data structures needed to compute how element should be clipped std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets std::vector verticesPresent(mir::utilities::maxPossibleNumVerts(shapeType), 0); // Vector of flags denoting whether the vertex is present in the current case or not axom::float64* tValues = new axom::float64[ mir::utilities::maxPossibleNumVerts(shapeType) ]{0}; // Array of t values that denote the percent value of where the edge should be clipped - // Set up the volume fractions for the current element for the two materials currently being considered std::vector > vertexVF(2); - for (int vID = 0; vID < elementVertices.size(); ++vID) + if (matOne == NULL_MAT) { - int originalVID = elementVertices[vID]; - if (matOne == NULL_MAT) - { - vertexVF[0].push_back(-1.0); - } - else - { - vertexVF[0].push_back( tempMesh.m_materialVolumeFractionsVertex[matOne][originalVID] ); - } - - if (matTwo == NULL_MAT) - { - vertexVF[1].push_back(-1.0); - } - else - { - vertexVF[1].push_back( tempMesh.m_materialVolumeFractionsVertex[matTwo][originalVID] ); - } + std::vector nullVF(elementVertices.size(), -1); + vertexVF[0] = nullVF; + } + else + { + vertexVF[0] = originalElementVertexVF[matOne]; + } + if (matTwo == NULL_MAT) + { + std::vector nullVF(elementVertices.size(), -1); + vertexVF[1] = nullVF; + } + else + { + vertexVF[1] = originalElementVertexVF[matTwo]; } // Clip the element CellClipper clipper; clipper.computeClippingPoints(shapeType, vertexVF, newElements, newVertices, tValues); - + // Determine which vertices are present for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { @@ -175,95 +205,157 @@ void InterfaceReconstructor::generateCleanCells(const int eID, verticesPresent[vID] = 1; } - // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); + int centerVertexID = mir::utilities::getCenterVertex(shapeType); + if (centerVertexID != -1 && verticesPresent[centerVertexID] == 1) + { + // Set up the cell data struct for the results of the decomposed element being clipped + CellData decomposed_cellData[newElements.size()]; + int decompElementID = 0; + // The clipping is one a tough one, and the element needs to decompose further before splitting with the two materials + for (auto itr = newElements.begin(); itr != newElements.end(); itr++, decompElementID++) + { + // Determine the shape type of the decomposed element + mir::Shape decomposedShapeType = mir::utilities::determineElementShapeType(shapeType, itr->second.size()); - // Generate the topology and connectivity of the newly split element - CellGenerator cellGenerator; - cellGenerator.generateTopologyData(newElements, newVertices, out_cellData); + // Extract the data for the current element + std::vector decomposedElementVertices;// = itr->second; + for (int i = 0; i < mir::utilities::numVerts(decomposedShapeType); ++i) + { + decomposedElementVertices.push_back(i); + } - // Generate the vertex position values of the newly split element - std::vector originalElementVertexPositions; - for (int vID = 0; vID < elementVertices.size(); ++vID) - { - int originalVID = elementVertices[vID]; - originalElementVertexPositions.push_back( tempMesh.m_vertexPositions[originalVID] ); - } - cellGenerator.generateVertexPositions( shapeType, newVertices, originalElementVertexPositions, tValues, out_cellData); + // Extract the vertex volume fractions of the element to split + std::vector > decomposedElementVertexVF; + for (unsigned long matID = 0; matID < originalElementVertexVF.size(); ++matID) + { + std::vector materialVertexVF; + for (unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) + { + int originalVID = itr->second[vID]; + if ( mir::utilities::isCenterVertex(shapeType, originalVID) ) + { + // Compute the central vertex volume fraction as the average of the other + axom::float64 averageMaterialVF = mir::utilities::computeAverageFloat(originalElementVertexVF[matID]); + materialVertexVF.push_back( averageMaterialVF ); + } + else + { + // Use the values that is at one of the original local vertices + materialVertexVF.push_back( originalElementVertexVF[matID][originalVID] ); + } + } + decomposedElementVertexVF.push_back( materialVertexVF ); + } - // Generate the vertex volume fractions of the newly split elements - std::vector > originalElementVertexVF; - for (int matID = 0; matID < tempMesh.m_numMaterials; ++matID) - { - std::vector materialVertexVF; - for (int vID = 0; vID < elementVertices.size(); ++vID) - { - int originalVID = elementVertices[vID]; + // Extract the vertex positions of the element to split + std::vector decomposedElementVertexPositions; + for (unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) + { + int originalVID = itr->second[vID]; + if ( mir::utilities::isCenterVertex(shapeType, originalVID) ) + { + // Compute the central vertex position as the centroid of the other + mir::Point2 centroid = mir::utilities::computeAveragePoint(originalElementVertexPositions); + decomposedElementVertexPositions.push_back( centroid ); + } + else + { + // Use the vertex position that is at one of the original local vertices + decomposedElementVertexPositions.push_back( originalElementVertexPositions[originalVID] ); + } + + } - materialVertexVF.push_back( tempMesh.m_materialVolumeFractionsVertex[matID][originalVID] ); + generateCleanCells(decomposedShapeType, + parentElementID, + matOne, + matTwo, + decomposedElementVertices, + decomposedElementVertexVF, + decomposedElementVertexPositions, + decomposed_cellData[decompElementID] + ); } - originalElementVertexVF.push_back( materialVertexVF ); + + // Merge the decomposed cell data with the outermost cellData + out_cellData.m_mapData.m_vertexVolumeFractions.resize(originalElementVertexVF.size()); + out_cellData.m_topology.m_evBegins.push_back(0); + out_cellData.m_topology.m_veBegins.push_back(0); + for (int i = 0; i < decompElementID; i++) + out_cellData.mergeCell(decomposed_cellData[i]); } + else + { + // Calculate the total number of elements and vertices that were generated from splitting the current element + out_cellData.m_numElems = (int) newElements.size(); + out_cellData.m_numVerts = (int) newVertices.size(); - cellGenerator.generateVertexVolumeFractions( shapeType, newVertices, originalElementVertexVF, tValues, out_cellData); + // Generate the topology and connectivity of the newly split element + CellGenerator cellGenerator; + cellGenerator.generateTopologyData(newElements, newVertices, out_cellData); - // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, itr->second, matOne, matTwo, out_cellData.m_mapData.m_vertexVolumeFractions); - out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); - } + // Generate the vertex position values of the newly split element + cellGenerator.generateVertexPositions( shapeType, newVertices, originalElementVertexPositions, tValues, out_cellData); - // Determine and store the parent of this element - out_cellData.m_mapData.m_elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.m_mapData.m_elementParents[itr->first] = tempMesh.m_elementParentIDs[eID]; - } + // Generate the vertex volume fractions of the newly split elements + cellGenerator.generateVertexVolumeFractions( shapeType, newVertices, originalElementVertexVF, tValues, out_cellData); - // Determine the generated elements' shape types - out_cellData.m_mapData.m_shapeTypes.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - out_cellData.m_mapData.m_shapeTypes[itr->first] = cellGenerator.determineElementShapeType(shapeType, itr->second.size()); - } + // Determine and store the dominant material of this element + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, itr->second, matOne, matTwo, out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); + } - // TODO: Make a function that does this - // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; - int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; + // Determine and store the parent of this element + out_cellData.m_mapData.m_elementParents.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + out_cellData.m_mapData.m_elementParents[itr->first] = parentElementID; + } - if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + // Determine the generated elements' shape types + out_cellData.m_mapData.m_shapeTypes.resize(newElements.size()); + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) { - // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOne) - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwo; + out_cellData.m_mapData.m_shapeTypes[itr->first] = mir::utilities::determineElementShapeType(shapeType, itr->second.size()); + } + + // Check that each element is dominated by a material that is actually present in the original parent cell + for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; + int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; + + if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + { + // This material is not present in the original element from which the current element comes from + if (currentDominantMaterial == matOne) + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwo; + else + out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOne; + } + } + + // Modify the evIndex values to account for the fact that perhaps not all possible vertices are present + std::vector evIndexSubtract; + evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); + for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) + { + if (verticesPresent[i] == 1) + evIndexSubtract[i] = 0; else - out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOne; + evIndexSubtract[i] = 1; } - } - - // Modify the evIndex values to account for the fact that perhaps not all possible vertices are present - std::vector evIndexSubtract; - evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); - for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) - { - if (verticesPresent[i] == 1) - evIndexSubtract[i] = 0; - else - evIndexSubtract[i] = 1; - } - for (unsigned long i = 1; i < evIndexSubtract.size(); ++i) - evIndexSubtract[i] += evIndexSubtract[i - 1]; + for (unsigned long i = 1; i < evIndexSubtract.size(); ++i) + evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) - { - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; + for (unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) + { + out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; + } } - + // Memory management delete[] tValues; } diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index ed674af4a4..19363f0177 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -87,17 +87,23 @@ namespace mir /** * \brief Generates a set of clean cells by splitting the element with the two given materials. * - * \param eID The ID of the element to be split. + * \param shapeType The shape type of the element to be split. + * \param parentElementID The ID parent element. * \param matOne The first material to split with. * \param matTwo The second material to split with. - * \param tempMesh A reference to the mesh the element comes from. + * \param elementVertices The vertices of the element to be split. + * \param originalElementVertexVF The original vertex volume fractions associated with the vertices of the element to be split. + * \param originalElementVertexPositions The original vertex positions associated with the vertices of the element to be split. * \param out_cellData Container to store the data of the generated elements. - */ - void generateCleanCells(const int eID, - const int matOne, - const int matTwo, - mir::MIRMesh& tempMesh, - CellData& out_cellData); + */ + void generateCleanCells(mir::Shape shapeType, + const int parentElementID, + const int matOne, + const int matTwo, + const std::vector& elementVertices, + const std::vector >& originalElementVertexVF, + const std::vector& originalElementVertexPositions, + CellData& out_cellData); private: mir::MIRMesh m_originalMesh; diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 7f9de8422b..2da1b10f5b 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -8,6 +8,8 @@ #include "axom/core.hpp" #include "axom/primal.hpp" +#include + namespace axom { namespace mir @@ -109,7 +111,7 @@ void MIRMesh::initializeMesh(const VertSet _verts, void MIRMesh::constructMeshRelations() { - // construct boundary relation from elements to vertices using variable cardinality + // construct boundary relation from elements to vertices { using RelationBuilder = ElemToVertRelation::RelationBuilder; m_bdry = RelationBuilder() @@ -125,7 +127,6 @@ void MIRMesh::constructMeshRelations() { - // _quadmesh_example_construct_cobdry_relation_start // construct coboundary relation from vertices to elements using RelationBuilder = VertToElemRelation::RelationBuilder; m_cobdry = RelationBuilder() @@ -137,14 +138,13 @@ void MIRMesh::constructMeshRelations() .indices( RelationBuilder::IndicesSetBuilder() .size( m_meshTopology.m_veInds.size() ) .data( m_meshTopology.m_veInds.data() ) ); - // _quadmesh_example_construct_cobdry_relation_end } SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); - SLIC_DEBUG("Elem-Vert relation has size " << m_bdry.totalSize()); - SLIC_DEBUG("Vert-Elem relation has size " << m_cobdry.totalSize()); + // SLIC_DEBUG("Elem-Vert relation has size " << m_bdry.totalSize()); + // SLIC_DEBUG("Vert-Elem relation has size " << m_cobdry.totalSize()); } //-------------------------------------------------------------------------------- @@ -200,6 +200,8 @@ void MIRMesh::constructMeshVolumeFractionsMaps(const std::vector >& vertexVF) { + m_materialVolumeFractionsVertex.clear(); + // Initialize the maps for all of the materials with the input volume fraction data for each vertex for (int matID = 0; matID < m_numMaterials; ++matID) { @@ -311,7 +313,9 @@ void MIRMesh::print() printf("vertexPositions: { "); for (int i = 0; i < m_verts.size(); ++i) { - printf("{%.2f, %.2f} ", m_vertexPositions[i][0], m_vertexPositions[i][1]); + std::stringstream sstr; + sstr<< m_vertexPositions[i]; + printf("%s", sstr.str().c_str()); } printf("}\n"); @@ -381,10 +385,20 @@ void MIRMesh::readMeshFromFile(std::string filename) //-------------------------------------------------------------------------------- -void MIRMesh::writeMeshToFile(std::string filename) +void MIRMesh::writeMeshToFile(const std::string& dirName, + const std::string& fileName, + const std::string& separator) { + // Ensure that the directory to write to exists + if ( !axom::utilities::filesystem::pathExists( dirName ) ) + { + axom::utilities::filesystem::makeDirsForPath( dirName ); + } + + std::string outputLocation = axom::utilities::filesystem::joinPath(dirName, fileName, separator); + std::ofstream meshfile; - meshfile.open(filename); + meshfile.open(outputLocation); std::ostream_iterator out_it(meshfile, " "); // write header @@ -402,7 +416,7 @@ void MIRMesh::writeMeshToFile(std::string filename) { meshfile << pt[i] << " "; } - meshfile << (pt.dimension()==2 ? "0" : "") << "\n"; + meshfile << "\n"; } meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); @@ -500,7 +514,7 @@ axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { - return primal::Triangle(p0,p1,p2).area(); + return primal::Triangle(p0,p1,p2).area(); } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index 57b1e7446d..faccaa0b84 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -129,7 +129,9 @@ namespace mir * * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. */ - void writeMeshToFile(const std::string filename); + void writeMeshToFile(const std::string& dirName, + const std::string& fileName, + const std::string& separator = "/"); /** diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 7ec2c79ba2..3e875bbaf6 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -34,7 +34,7 @@ namespace mir }; - using Point2 = primal::Point; + using Point2 = primal::Point; // SET TYPE ALIASES using PosType = slam::DefaultPositionType; diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index be759591c5..f2a9314776 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -36,7 +36,7 @@ namespace utilities */ inline int numVerts(mir::Shape shape) { - int numVertices = -1; + int numVertices = 0; switch (shape) { case mir::Shape::Triangle: @@ -235,13 +235,164 @@ namespace utilities * * \return True, if the shape is a tetrahedron, pyramid, triangular prism, or a hexahedron. */ -inline bool isShapeThreeDimensional(mir::Shape shapeType) +inline bool isShapeThreeDimensional(const mir::Shape shapeType) { return (shapeType == mir::Tetrahedron || shapeType == mir::Pyramid || shapeType == mir::Triangular_Prism || shapeType == mir::Hexahedron); } //-------------------------------------------------------------------------------- +/** + * \brief Get the local vertex ID of the central vertex used when decomposing a 3D shape. + * + * \param shapeType The shape type from the finite element zoo. + * + * \return The local vertex ID of the central vertex of a 3D shape. + * + * \note 2D shapes do not have a central vertex because they do not decompose. + */ +inline int getCenterVertex(const mir::Shape shapeType) +{ + switch(shapeType) + { + case mir::Tetrahedron: + return 10; + case mir::Pyramid: + return 13; + case mir::Triangular_Prism: + return 15; + case mir::Hexahedron: + return 20; + default: + return -1; + } +} + +//-------------------------------------------------------------------------------- + +/** + * \brief Determines if the given vertex is the central vertex of a 3D shape. + * + * \param shapeType The shape type from the finite element zoo. + * \param vID The local vertex ID. + * + * \return True, if vertex ID is that of the central vertex of a 3D shape. + * + * \note 2D shapes do not have a central vertex because they do not decompose. + */ +inline bool isCenterVertex(const mir::Shape shapeType, const int vID) +{ + if (shapeType == mir::Tetrahedron && vID == 10) + return true; + else if (shapeType == mir::Pyramid && vID == 13) + return true; + else if (shapeType == mir::Triangular_Prism && vID == 15) + return true; + else if (shapeType == mir::Hexahedron && vID == 20) + return true; + else + return false; +} + +//-------------------------------------------------------------------------------- + +/** + * \brief Computes the average value of the float values given. + * + * \param values A vector of float values. + * + * \return The average value. + */ +inline axom::float64 computeAverageFloat(const std::vector& values) +{ + axom::float64 sum = 0.0; + for (unsigned long i = 0; i < values.size(); ++i) + { + sum += values[i]; + } + return sum / (axom::float64) values.size(); +} + +//-------------------------------------------------------------------------------- + +/** + * \brief Computes the centroid for the points given. + * + * \param A vector of points. + * + * \return The centroid point. + */ +inline mir::Point2 computeAveragePoint(const std::vector& points) +{ + mir::Point2 centroid; + for (auto i = 0u; i < points.size(); ++i) + { + centroid.array() += points[i].array(); + } + centroid.array() /= (axom::float64) points.size(); + + return centroid; +} + +//-------------------------------------------------------------------------------- + +/** + * \brief Determine the shape type of an element. + * + * \param parentShapeType The shape of the element from which the new element is generated. + * \param numVerts The number of vertices of the new element. + * + * \note It is assumed that the given cell is one that results from splitting its parent cell. + */ +inline mir::Shape determineElementShapeType(const Shape parentShapeType, + const int numVerts) +{ + mir::Shape newShapeType; + if (parentShapeType == mir::Shape::Triangle || parentShapeType == mir::Shape::Quad) + { + // Handle the two-dimensional case + switch (numVerts) + { + case 3: + newShapeType = mir::Shape::Triangle; + break; + case 4: + newShapeType = mir::Shape::Quad; + break; + default: + newShapeType = mir::Shape::Triangle; + printf("2D Case: Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + else + { + // Handle the three-dimensional case + switch (numVerts) + { + case 4: + newShapeType = mir::Shape::Tetrahedron; + break; + case 5: + newShapeType = mir::Shape::Pyramid; + break; + case 6: + newShapeType = mir::Shape::Triangular_Prism; + break; + case 8: + newShapeType = mir::Shape::Hexahedron; + break; + default: + newShapeType = mir::Shape::Tetrahedron; + printf("3D Case: Invalid number of vertices in determineElementShapeType().\n"); + break; + } + } + return newShapeType; +} + +//-------------------------------------------------------------------------------- + } } } diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index f64e310c8c..dfb570ba87 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -797,5 +797,229 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() //-------------------------------------------------------------------------------- +mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) +{ + // Generate the mesh topology + mir::CellData cellData = generateGrid3D(gridSize); + + mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + + // // Generate the element volume fractions with concentric spheres + int numMaterials = numSpheres + 1; + int defaultMaterialID = numMaterials - 1; // default material is always the last index + + mir::Point2 sphereCenter = mir::Point2::make_point(gridSize / 2.0, + gridSize / 2.0, + gridSize / 2.0); // all spheres are centered around the same point + + // Initialize the radii of the circles + std::vector sphereRadii; + axom::float64 maxRadius = gridSize / 2.0; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = gridSize / 4.0; // Note: The choice of divisor is arbitrary + + axom::float64 radiusDelta; + if (numSpheres <= 1) + radiusDelta = (maxRadius - minRadius); + else + radiusDelta = (maxRadius - minRadius) / (double) (numSpheres - 1); + + for (int i = 0; i < numSpheres; ++i) + { + auto rad = minRadius + (i * radiusDelta); + sphereRadii.push_back( rad * rad ); + } + + // Initialize all material volume fractions to 0 + std::vector > materialVolumeFractionsData; + for (int i = 0; i < numMaterials; ++i) + { + std::vector tempVec; + tempVec.resize(cellData.m_numElems, 0); + materialVolumeFractionsData.push_back(tempVec); + } + + // Use the uniform sampling method to generate volume fractions for each material + for (int eID = 0; eID < cellData.m_numElems; ++eID) + { + mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 0]]; + mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 1]]; + mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 2]]; + mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; + + mir::Point2 v4 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; + mir::Point2 v5 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 5]]; + mir::Point2 v6 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; + mir::Point2 v7 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; + for (int i = 0; i < numMaterials; ++i) + materialCount[i] = 0; + + axom::float64 delta_x = axom::utilities::abs(v2[0] - v1[0]) / (double) (gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(v0[1] - v1[1]) / (double) (gridSize - 1); + axom::float64 delta_z = axom::utilities::abs(v5[2] - v1[2]) / (double) (gridSize - 1); + + for (int z = 0; z < gridSize; ++z) + { + for (int y = 0; y < gridSize; ++y) + { + for (int x = 0; x < gridSize; ++x) + { + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], + delta_y * y + v1[1], + delta_z * z + v1[2]); + bool isPointSampled = false; + for (int cID = 0; cID < numSpheres && !isPointSampled; ++cID) + { + if (primal::squared_distance(samplePoint, sphereCenter) < sphereRadii[cID]) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if (!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; + } + } + } + } + + // Assign the element volume fractions based on the count of the samples in each circle + for (int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize * gridSize); + } + } + + std::vector elementParents; // For the base mesh, the parents are always themselves + std::vector elementDominantMaterials; + std::vector elementShapeTypes; + for (int i = 0; i < cellData.m_numElems; ++i) + { + elementParents.push_back(i); + elementDominantMaterials.push_back(NULL_MAT); + elementShapeTypes.push_back(mir::Shape::Hexahedron); + } + + CellTopologyData topology; + topology.m_evInds = cellData.m_topology.m_evInds; + topology.m_evBegins = cellData.m_topology.m_evBegins; + topology.m_veInds = cellData.m_topology.m_veInds; + topology.m_veBegins = cellData.m_topology.m_veBegins; + + CellMapData mapData; + mapData.m_elementDominantMaterials = elementDominantMaterials; + mapData.m_elementParents = elementParents; + mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; + mapData.m_shapeTypes = elementShapeTypes; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, materialVolumeFractionsData); + + return testMesh; +} + +//-------------------------------------------------------------------------------- + +mir::CellData MeshTester::generateGrid3D(int gridSize) +{ + // Generate the topology for a uniform quad mesh with n x n elements automatically + int numElements = gridSize * gridSize * gridSize; + int numVertices = (gridSize + 1) * (gridSize + 1) * (gridSize + 1); + + // Generate the evInds + std::vector evInds; + for (int eID = 0; eID < numElements; ++eID) + { + int vertsPerLayer = (gridSize + 1) * (gridSize + 1); + int elemsPerLayer = gridSize * gridSize; + int layer = eID / (gridSize * gridSize); // note the integer division + + int vertsPerRow = gridSize + 1; + int elemsPerRow = gridSize; + int row = (eID % elemsPerLayer) / gridSize; // note the integer division + + // front face of the grid cube + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 0 + (vertsPerLayer * layer) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * layer) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * layer) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 1 + (vertsPerLayer * layer) ); + + // back face of the grid cube + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 0 + (vertsPerLayer * (layer + 1)) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * (layer + 1)) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * (layer + 1)) ); + evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 1 + (vertsPerLayer * (layer + 1)) ); + } + + // Generate the evBegins + std::vector evBegins; + evBegins.push_back(0); + for (int i = 0; i < numElements; ++i) + { + evBegins.push_back((i + 1) * 8); + } + + // Generate the veInds + std::map > veInds_data; + std::vector veInds; + for (int evInd_itr = 0; evInd_itr < numElements * 8; ++evInd_itr) + { + int currentElementID = evInd_itr / 8; // note the integer division + veInds_data[evInds[evInd_itr]].push_back(currentElementID); + } + + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + // Sort the vector + std::sort(itr->second.begin(), itr->second.end()); + + // Add the elements associated with the current vertex to veInds + for (unsigned long i = 0; i < itr->second.size(); ++i) + veInds.push_back(itr->second[i]); + } + + // Generate the veBegins + std::vector veBegins; + veBegins.push_back(0); + int currentIndexCount = 0; + for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + { + currentIndexCount += itr->second.size(); + veBegins.push_back(currentIndexCount); + } + + // Generate the vertex positions + std::vector points; + for (int z = 0; z < gridSize + 1; ++z) + { + for (int y = gridSize; y > -1; --y) + { + for (int x = 0; x < gridSize + 1; ++x) + { + points.push_back(mir::Point2::make_point(x, y, z)); + } + } + } + + mir::CellData data; + data.m_numVerts = numVertices; + data.m_numElems = numElements; + data.m_topology.m_evInds = evInds; + data.m_topology.m_evBegins = evBegins; + data.m_topology.m_veInds = veInds; + data.m_topology.m_veBegins = veBegins; + data.m_mapData.m_vertexPositions = points; + + return data; +} + +//-------------------------------------------------------------------------------- + } } diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index dcbc380460..25d5336279 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -94,7 +94,7 @@ namespace mir mir::MIRMesh initTestCaseFour(); /** - * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform grid. + * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform 2D grid. * * \param gridSize The number of elements in the width and the height of the uniform grid. * \param numCircles The number of concentric circles that are centered in the grid. @@ -103,7 +103,19 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); // multiple materials, multiple concentric circles + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); + + /** + * \brief Initializes a mesh to be used for testing a set of concentric spheres centered in a uniform 3D grid. + * + * \param gridSize The number of elements in the width and the height of the uniform grid. + * \param numSpheres The number of concentric spheres that are centered in the grid. + * + * \note Each sphere is composed of a different material. + * + * \return The generated mesh. + */ + mir::MIRMesh initTestCaseSix(int gridSize, int numSpheres); /** * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. @@ -136,6 +148,13 @@ namespace mir */ mir::CellData generateGrid(int gridSize); + /** + * \brief Generates a 3D uniform grid of n x n x n elements. + * + * \param gridSize The number of elements in the width, height, and depth of the uniform grid. + */ + mir::CellData generateGrid3D(int gridSize); + /** * \brief Calculates the percent overlap between the given circle and quad. * diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 85522a5027..db9a4e742e 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -121,38 +121,40 @@ namespace mir {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, }; + // {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,-1}, // correct decomp + // {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1} // incorect decomp (currently used) const std::vector > pyramidClipTableVec = { {5,0,1,2,3,4,-1}, {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, - {6,9,10,11,0,1,2,4,9,10,11,4,6,4,9,11,13,8,7,6,9,0,8,11,2,7,-1}, + {6,0,1,2,9,10,11,4,9,10,11,4,6,4,9,11,3,8,7,6,0,8,9,2,7,11,-1}, {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, - {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,4,2,3,4,13,4,3,0,4,13,-1}, + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp + {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, - {6,9,10,11,0,1,2,4,9,10,11,4,6,4,9,11,13,8,7,6,9,0,8,11,2,7,-1}, + {6,0,1,2,9,10,11,4,9,10,11,4,6,4,9,11,3,8,7,6,0,8,9,2,7,11,-1}, {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, {5,0,1,2,3,4,-1} @@ -161,6 +163,7 @@ namespace mir // currently set to decompose as if every clipping case were considered "tough" (2**6 = 64 cases total) const std::vector > triangularPrismClipTableVec = { + {6,0,1,2,3,4,5,-1}, {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, @@ -223,13 +226,13 @@ namespace mir {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1} + {6,0,1,2,3,4,5,-1} }; // currently set to decompose as if every clipping case were considered "tough" (2**8 = 256 cases total) const std::vector > hexahedronClipTableVec = { + {8,0,1,2,3,4,5,6,7,-1}, {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, @@ -484,8 +487,7 @@ namespace mir {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1} + {8,0,1,2,3,4,5,6,7,-1} }; } diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 1455d16f81..66581fb830 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -54,7 +54,7 @@ int main( int argc, char** argv ) << timer.elapsedTimeInMilliSec() << " ms."); // Output results - processedMesh.writeMeshToFile(outputFilePath + "outputConcentricCircles.vtk"); + processedMesh.writeMeshToFile(outputFilePath, "outputConcentricCircles.vtk"); return 0; } diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 460a076b80..5ff100945b 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -106,7 +106,7 @@ struct Input << "Argument usage:" "\n --help Show this help message." "\n --test-case N Mesh test case. Default N = 2." - "\n Valid values {1,2,3,4,5}" + "\n Valid values {1,2,3,4,5,6}" "\n --output-dir dir Directory for output mesh" "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" "\n --iter-count N Number of iterations for iterative algorithm" @@ -122,7 +122,7 @@ struct Input private: void checkTestCase() { - if(m_test_case < 1 || m_test_case > 5) + if(m_test_case < 1 || m_test_case > 6) { m_status = INVALID; SLIC_WARNING("Invalid test case " << m_test_case); @@ -194,6 +194,7 @@ int main(int argc, char** argv) case 3: testMesh = tester.initTestCaseThree(); break; case 4: testMesh = tester.initTestCaseFour(); break; case 5: testMesh = tester.initTestCaseFive(25, 12); break; + case 6: testMesh = tester.initTestCaseSix(15, 3); break; } timer.stop(); @@ -229,8 +230,7 @@ int main(int argc, char** argv) SLIC_INFO( "Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results - std::string dir = params.m_output_dir; - processedMesh.writeMeshToFile( fs::joinPath(dir, "processedMesh.vtk") ); + processedMesh.writeMeshToFile(params.m_output_dir, "processedMesh.vtk"); using VolFracs = std::vector >; timer.start(); diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index d4b750967b..236c87c9e3 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -12,9 +12,7 @@ #------------------------------------------------------------------------------ set(gtest_mir_tests - mir_smoke.cpp mir_mesh.cpp - mir_interface_reconstructor.cpp mir_cell_clipper.cpp mir_utilities.cpp mir_cell_generator.cpp diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp index 2c69cb4222..9a073b138e 100644 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -50,6 +50,8 @@ TEST(mir_clipping_case, all_triangle_cases) } } +//---------------------------------------------------------------------- + TEST(mir_clipping_case, all_quad_cases) { mir::Shape shape = mir::Shape::Quad; @@ -85,6 +87,146 @@ TEST(mir_clipping_case, all_quad_cases) //---------------------------------------------------------------------- +TEST(mir_clipping_case, all_tetrahedron_cases) +{ + mir::Shape shape = mir::Shape::Tetrahedron; + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); + + for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; + + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, all_pyramid_cases) +{ + mir::Shape shape = mir::Shape::Pyramid; + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); + + for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; + + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, all_triangular_prism_cases) +{ + mir::Shape shape = mir::Shape::Triangular_Prism; + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); + + for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; + + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } +} + +//---------------------------------------------------------------------- + +TEST(mir_clipping_case, all_hexahedron_cases) +{ + mir::Shape shape = mir::Shape::Hexahedron; + int numVerts = mir::utilities::numVerts(shape); + int numCases = pow(2, numVerts); + + for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + { + std::vector matOneVF; + std::vector matTwoVF; + + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue = 0.0; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + mir::CellClipper clipper; + unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + + EXPECT_EQ ( computedClippingCase, actualClippingCase ); + } +} + +//---------------------------------------------------------------------- + TEST(mir_clipping_place, clip_edge_when_mat_one_dominates) { axom::float64 vfMatOneVertexOne = 0.0; @@ -364,7 +506,6 @@ TEST(clipping_table_mesh_generation, triangle_meshes) { mir::Shape shape = mir::Shape::Triangle; int numVerts = mir::utilities::numVerts(shape); - //int numCases = pow(2, numVerts); for (auto actualClippingCase = 0u; actualClippingCase < mir::triangleClipTableVec.size(); ++actualClippingCase) { @@ -442,9 +583,99 @@ TEST(clipping_table_mesh_generation, triangle_meshes) interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); // Write out the processed mesh - std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; - std::string filename = "mir_clippingcase_triangle_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(filepath + filename); + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_triangle_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); + } +} + +//---------------------------------------------------------------------- + +TEST(clipping_table_mesh_generation, quad_meshes) +{ + mir::Shape shape = mir::Shape::Quad; + int numVerts = mir::utilities::numVerts(shape); + + for (unsigned int actualClippingCase = 0; actualClippingCase < mir::quadClipTableVec.size(); ++actualClippingCase) + { + // Initialize the mesh + int numElements = 1; + int numVertices = 4; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2,3 }; + topology.m_evBegins = { 0,4 }; + topology.m_veInds = { 0,0,0,0 }; + topology.m_veBegins = { 0,1,2,3,4 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.0, 1.0 ), + mir::Point2::make_point( 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0 ), + mir::Point2::make_point( 1.0, 1.0 ) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Quad }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_quad_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); } } @@ -454,7 +685,6 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) { mir::Shape shape = mir::Shape::Tetrahedron; int numVerts = mir::utilities::numVerts(shape); - //int numCases = pow(2, numVerts); for (unsigned int actualClippingCase = 0; actualClippingCase < mir::tetrahedronClipTableVec.size(); ++actualClippingCase) { @@ -533,102 +763,291 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); // Write out the processed mesh - std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; - std::string filename = "mir_clippingcase_tetrahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(filepath + filename); + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_tetrahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); + } +} + +//---------------------------------------------------------------------- + +TEST(clipping_table_mesh_generation, pyramid_meshes) +{ + mir::Shape shape = mir::Shape::Pyramid; + int numVerts = mir::utilities::numVerts(shape); + + for (unsigned int actualClippingCase = 0; actualClippingCase < mir::pyramidClipTableVec.size(); ++actualClippingCase) + { + { + // Initialize the mesh + int numElements = 1; + int numVertices = 5; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2,3,4 }; + topology.m_evBegins = { 0,5 }; + topology.m_veInds = { 0,0,0,0,0 }; + topology.m_veBegins = { 0,1,2,3,4,5 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 1.0, 0.0 ), + mir::Point2::make_point( 0.0, 1.0, 0.0 ), + mir::Point2::make_point( 0.5, 0.5, 0.717) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Pyramid }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_pyramid_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); + } + } + +} + +//---------------------------------------------------------------------- + +TEST(clipping_table_mesh_generation, triangular_prism_meshes) +{ + mir::Shape shape = mir::Shape::Triangular_Prism; + int numVerts = mir::utilities::numVerts(shape); + + for (unsigned int actualClippingCase = 0; actualClippingCase < mir::triangularPrismClipTableVec.size(); ++actualClippingCase) + { + // Initialize the mesh + int numElements = 1; + int numVertices = 6; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2,3,4,5 }; + topology.m_evBegins = { 0,6 }; + topology.m_veInds = { 0,0,0,0,0,0 }; + topology.m_veBegins = { 0,1,2,3,4,5,6 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.5, 0.717, 0.0 ), + mir::Point2::make_point( 0.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0, 0.0 ), + mir::Point2::make_point( 0.5, 0.717, 2.0 ), + mir::Point2::make_point( 0.0, 0.0, 2.0 ), + mir::Point2::make_point( 1.0, 0.0, 2.0 ) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Triangular_Prism }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_triangular_prism_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); } } //---------------------------------------------------------------------- -// TEST(clipping_table_mesh_generation, quad_meshes) -// { -// mir::Shape shape = mir::Shape::Quad; -// int numVerts = mir::utilities::numVerts(shape); -// int numCases = pow(2, numVerts); - -// for (unsigned int actualClippingCase = 0; actualClippingCase < mir::quadClipTableVec.size(); ++actualClippingCase) -// { -// // Initialize the mesh -// int numElements = 1; -// int numVertices = 4; - -// // Create the mesh connectivity information -// mir::CellTopologyData topology; -// topology.m_evInds = { 0,1,2,3 }; -// topology.m_evBegins = { 0,4 }; -// topology.m_veInds = { 0,0,0,0 }; -// topology.m_veBegins = { 0,1,2,3,4 }; - -// mir::VertSet verts = mir::VertSet(numVertices); -// mir::ElemSet elems = mir::ElemSet(numElements); - -// // Calculate the vertex volume fractions needed to clip with the current case -// std::vector matOneVF; -// std::vector matTwoVF; -// std::string bitString(""); -// for (unsigned int bitIndex = 0; bitIndex < numVerts; ++bitIndex) -// { -// unsigned int shiftedBit = 1; -// shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - -// axom::float64 matOneValue; -// if (actualClippingCase & shiftedBit) -// { -// matOneValue = 1.0; -// bitString += "1"; -// } -// else -// { -// matOneValue = 0.0; -// bitString += "0"; -// } - -// matOneVF.push_back( matOneValue ); -// matTwoVF.push_back( 1.0 - matOneValue ); -// } - -// std::vector > vertexVF = { -// matOneVF, -// matTwoVF -// }; - -// std::vector > elementVF = { -// { 0.5 }, -// { 0.5 } -// }; - - -// std::vector points = -// { -// mir::Point2( 0.0, 1.0 ), -// mir::Point2( 0.0, 0.0 ), -// mir::Point2( 1.0, 0.0 ), -// mir::Point2( 1.0, 1.0 ) -// }; - -// mir::CellMapData mapData; -// mapData.m_elementDominantMaterials = { mir::NULL_MAT }; -// mapData.m_elementParents = { 0 }; -// mapData.m_vertexPositions = points; -// mapData.m_shapeTypes = { mir::Shape::Quad }; - -// // Build the mesh -// mir::MIRMesh testMesh; -// testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); -// testMesh.constructMeshVolumeFractionsVertex(vertexVF); - -// // Clip the mesh using the actualClippingCase index -// mir::MIRMesh outputMesh; -// mir::InterfaceReconstructor interfaceReconstructor; -// interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - -// // Write out the processed mesh -// std::string filepath = "/Users/sterbentz3/Desktop/clipping_test_cases/"; -// std::string filename = "mir_clippingcase_quad_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; -// outputMesh.writeMeshToFile(filepath + filename); -// } -// } +TEST(clipping_table_mesh_generation, hexahedron_meshes) +{ + mir::Shape shape = mir::Shape::Hexahedron; + int numVerts = mir::utilities::numVerts(shape); + + for (unsigned int actualClippingCase = 0; actualClippingCase < mir::hexahedronClipTableVec.size(); ++actualClippingCase) + { + // Initialize the mesh + int numElements = 1; + int numVertices = 8; + + // Create the mesh connectivity information + mir::CellTopologyData topology; + topology.m_evInds = { 0,1,2,3,4,5,6,7 }; + topology.m_evBegins = { 0,8 }; + topology.m_veInds = { 0,0,0,0,0,0,0,0 }; + topology.m_veBegins = { 0,1,2,3,4,5,6,7,8 }; + + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); + + // Calculate the vertex volume fractions needed to clip with the current case + std::vector matOneVF; + std::vector matTwoVF; + std::string bitString(""); + for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + { + unsigned int shiftedBit = 1; + shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); + + axom::float64 matOneValue; + if (actualClippingCase & shiftedBit) + { + matOneValue = 1.0; + bitString += "1"; + } + else + { + matOneValue = 0.0; + bitString += "0"; + } + + matOneVF.push_back( matOneValue ); + matTwoVF.push_back( 1.0 - matOneValue ); + } + + std::vector > vertexVF = { + matOneVF, + matTwoVF + }; + + std::vector > elementVF = { + { 0.5 }, + { 0.5 } + }; + + + std::vector points = + { + mir::Point2::make_point( 0.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 0.0, 0.0 ), + mir::Point2::make_point( 1.0, 1.0, 0.0 ), + mir::Point2::make_point( 0.0, 1.0, 0.0 ), + mir::Point2::make_point( 0.0, 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 0.0, 1.0 ), + mir::Point2::make_point( 1.0, 1.0, 1.0 ), + mir::Point2::make_point( 0.0, 1.0, 1.0 ) + }; + + mir::CellMapData mapData; + mapData.m_elementDominantMaterials = { mir::NULL_MAT }; + mapData.m_elementParents = { 0 }; + mapData.m_vertexPositions = points; + mapData.m_shapeTypes = { mir::Shape::Hexahedron }; + + // Build the mesh + mir::MIRMesh testMesh; + testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); + testMesh.constructMeshVolumeFractionsVertex(vertexVF); + + // Clip the mesh using the actualClippingCase index + mir::MIRMesh outputMesh; + mir::InterfaceReconstructor interfaceReconstructor; + interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); + + // Write out the processed mesh + std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; + std::string fileName = "mir_clippingcase_hexahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + outputMesh.writeMeshToFile(dirName, fileName, "/"); + } +} //---------------------------------------------------------------------- diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp deleted file mode 100644 index 875c7365df..0000000000 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ -#define MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -using namespace axom; - -//---------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::UnitTestLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - - -#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_smoke.cpp b/src/axom/mir/tests/mir_smoke.cpp deleted file mode 100644 index c71092b54d..0000000000 --- a/src/axom/mir/tests/mir_smoke.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_SMOKE_H_ -#define MIR_SMOKE_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - - -TEST(mir,smoke) -{ - SLIC_INFO("Running smoke test for MIR component"); - - EXPECT_TRUE( true ); - EXPECT_EQ( 0, 0 ); -} - - -//---------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::UnitTestLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - - -#endif // MIR_SMOKE_H_ diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 6d5a6c2dde..0c442d3070 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -11,7 +11,90 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" +namespace mir = axom::mir; +//---------------------------------------------------------------------- + +TEST(mir_shape_tests, shape_dimesionality) +{ + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Triangle), false ); + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Quad), false ); + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Tetrahedron), true ); + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Pyramid), true ); + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Triangular_Prism), true ); + EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Hexahedron), true ); +} + +//---------------------------------------------------------------------- + +TEST(mir_shape_tests, check_shape_central_vertex) +{ + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangle, 6), false); + + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Quad, 8), false); + + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 9), false); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 10), true); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 11), false); + + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 12), false); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 13), true); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 14), false); + + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 14), false); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 15), true); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 16), false); + + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 19), false); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 20), true); + EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 21), false); +} + +//---------------------------------------------------------------------- + +TEST(mir_shape_tests, determine_central_vertex) +{ + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Triangle), -1 ); + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Quad), -1 ); + + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Tetrahedron), 10 ); + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Pyramid), 13 ); + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Triangular_Prism), 15 ); + EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Hexahedron), 20 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_compute_averages, float_value) +{ + std::vector values = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; + + axom::float64 average = mir::utilities::computeAverageFloat( values ); + + EXPECT_DOUBLE_EQ( average, 2.5 ); +} + +//---------------------------------------------------------------------- + +TEST(mir_compute_averages, point_value) +{ + std::vector points = { + mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(0.0, 1.0, 0.0), + mir::Point2::make_point(0.0, 0.0, 1.0), + mir::Point2::make_point(1.0, 1.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 1.0), + mir::Point2::make_point(0.0, 1.0, 1.0), + mir::Point2::make_point(1.0, 1.0, 1.0) + }; + + mir::Point2 centroid = mir::utilities::computeAveragePoint( points ); + + EXPECT_DOUBLE_EQ( centroid[0], 0.5); + EXPECT_DOUBLE_EQ( centroid[1], 0.5); + EXPECT_DOUBLE_EQ( centroid[2], 0.5); +} //---------------------------------------------------------------------- From 76029b9288387cd9474bec9125f2182dadbb7aaa Mon Sep 17 00:00:00 2001 From: Sterbentz Date: Tue, 9 Jul 2019 15:49:22 -0700 Subject: [PATCH 046/290] Added auto-generated clipping tables for pyramids and triangular prisms. --- src/axom/mir/ZooClippingTables.cpp | 187 ++++++++++++++--------------- 1 file changed, 93 insertions(+), 94 deletions(-) diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index db9a4e742e..21a0c6ca61 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -126,106 +126,105 @@ namespace mir const std::vector > pyramidClipTableVec = { {5,0,1,2,3,4,-1}, - {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, - {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, - {6,0,1,2,9,10,11,4,9,10,11,4,6,4,9,11,3,8,7,6,0,8,9,2,7,11,-1}, - {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, - {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, - {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, - {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, - {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, - {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, - - {4,9,8,5,0,6,9,8,5,4,3,1,4,4,3,1,2,-1}, - {6,0,5,8,4,10,12,4,4,10,12,11,6,1,5,10,3,8,12,6,10,11,12,1,2,3,-1}, - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,9,8,5,0,6,9,8,5,4,3,1,6,4,3,1,11,7,6,4,11,7,6,2,-1}, - {4,10,5,6,1,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,12,8,7,3,-1}, - {6,1,5,6,4,9,11,4,4,9,11,12,6,0,5,9,2,6,11,6,0,2,3,9,11,12,-1}, - {4,10,5,6,1,6,10,5,6,4,0,2,4,4,0,2,3,-1}, - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1}, // decomp - {6,0,1,3,9,10,12,4,9,10,12,4,6,4,10,12,2,6,7,6,1,6,10,3,7,12,-1}, - {4,11,7,6,2,6,11,7,6,4,3,1,4,4,3,1,0,-1}, - {6,0,1,2,9,10,11,4,9,10,11,4,6,4,9,11,3,8,7,6,0,8,9,2,7,11,-1}, - {4,12,8,7,3,6,12,8,7,4,0,2,4,4,0,2,1,-1}, - {5,9,10,11,12,4,8,0,1,2,3,9,10,11,12,-1}, - {5,0,1,2,3,4,-1} + {5,9,10,11,12,4,8,9,10,11,12,0,1,2,3,-1}, + {4,3,8,7,12,6,8,7,12,0,2,4,4,0,1,2,4,-1}, + {6,3,8,7,4,9,11,4,4,9,10,11,6,9,8,0,11,7,2,6,9,10,11,0,1,2,-1}, + {4,2,6,7,11,6,6,7,11,1,3,4,4,0,1,3,4,-1}, + {6,2,6,7,4,10,12,4,4,9,10,12,6,10,6,1,12,7,3,6,9,10,12,0,1,3,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,1,5,6,10,6,5,6,10,0,2,4,4,0,2,3,4,-1}, + {6,1,5,6,4,9,11,4,4,9,11,12,6,9,5,0,11,6,2,6,9,11,12,0,2,3,-1}, + {4,1,10,5,6,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,3,12,8,7,-1}, + {4,0,9,5,8,6,9,5,8,4,1,3,6,4,1,3,11,6,7,4,2,11,6,7,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {6,0,5,8,4,10,12,4,4,10,11,12,6,10,5,1,12,8,3,6,10,11,12,1,2,3,-1}, + {4,0,5,8,9,6,5,8,9,1,3,4,4,1,2,3,4,-1}, + {4,0,5,8,9,6,5,8,9,1,3,4,4,1,2,3,4,-1}, + {6,0,5,8,4,10,12,4,4,10,11,12,6,10,5,1,12,8,3,6,10,11,12,1,2,3,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,0,9,5,8,6,9,5,8,4,1,3,6,4,1,3,11,6,7,4,2,11,6,7,-1}, + {4,1,10,5,6,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,3,12,8,7,-1}, + {6,1,5,6,4,9,11,4,4,9,11,12,6,9,5,0,11,6,2,6,9,11,12,0,2,3,-1}, + {4,1,5,6,10,6,5,6,10,0,2,4,4,0,2,3,4,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, + {6,2,6,7,4,10,12,4,4,9,10,12,6,10,6,1,12,7,3,6,9,10,12,0,1,3,-1}, + {4,2,6,7,11,6,6,7,11,1,3,4,4,0,1,3,4,-1}, + {6,3,8,7,4,9,11,4,4,9,10,11,6,9,8,0,11,7,2,6,9,10,11,0,1,2,-1}, + {4,3,8,7,12,6,8,7,12,0,2,4,4,0,1,2,4,-1}, + {5,9,10,11,12,4,8,9,10,11,12,0,1,2,3,-1}, + {5,0,1,2,3,4,-1}, }; // currently set to decompose as if every clipping case were considered "tough" (2**6 = 64 cases total) const std::vector > triangularPrismClipTableVec = { {6,0,1,2,3,4,5,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, - {4,0,1,2,15,4,3,4,5,15,5,0,2,5,3,15,5,1,2,5,4,15,5,0,1,4,3,15,-1}, + {4,5,14,13,11,6,14,13,11,3,4,2,5,4,1,0,3,2,-1}, + {4,4,12,13,10,6,12,13,10,3,5,1,5,5,2,0,3,1,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,3,12,14,9,6,12,14,9,4,5,0,5,5,2,1,4,0,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,0,1,2,9,10,11,6,9,10,11,3,4,5,-1}, + {4,2,8,7,11,6,8,7,11,0,1,5,5,1,4,3,0,5,-1}, + {6,2,8,7,5,14,13,8,0,1,4,3,8,7,13,14,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,1,6,7,10,6,6,7,10,0,2,4,5,2,5,3,0,4,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,1,6,7,4,12,13,8,0,2,5,3,6,7,13,12,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,0,6,8,3,12,14,8,1,2,5,4,6,8,14,12,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,6,8,9,6,6,8,9,1,2,3,5,2,5,4,1,3,-1}, + {4,0,6,8,9,6,6,8,9,1,2,3,5,2,5,4,1,3,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,0,6,8,3,12,14,8,1,2,5,4,6,8,14,12,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,1,6,7,4,12,13,8,0,2,5,3,6,7,13,12,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,1,6,7,10,6,6,7,10,0,2,4,5,2,5,3,0,4,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {6,2,8,7,5,14,13,8,0,1,4,3,8,7,13,14,-1}, + {4,2,8,7,11,6,8,7,11,0,1,5,5,1,4,3,0,5,-1}, + {6,3,4,5,9,10,11,6,9,10,11,0,1,2,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,3,12,14,9,6,12,14,9,4,5,0,5,5,2,1,4,0,-1}, + {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, + {4,4,12,13,10,6,12,13,10,3,5,1,5,5,2,0,3,1,-1}, + {4,5,14,13,11,6,14,13,11,3,4,2,5,4,1,0,3,2,-1}, {6,0,1,2,3,4,5,-1} }; From dcd008affa9505d6b15f6569677e744b353429a0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 4 Jun 2024 17:37:32 -0700 Subject: [PATCH 047/290] Updated mir so it builds. --- src/axom/mir/CMakeLists.txt | 5 ++--- src/axom/mir/MIRMeshTypes.hpp | 8 ++++---- src/axom/mir/examples/mir_concentric_circles.cpp | 2 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 2 +- src/axom/mir/tests/mir_cell_clipper.cpp | 2 +- src/axom/mir/tests/mir_cell_generator.cpp | 2 +- src/axom/mir/tests/mir_interface_reconstructor.cpp | 2 +- src/axom/mir/tests/mir_mesh.cpp | 2 +- src/axom/mir/tests/mir_smoke.cpp | 2 +- src/axom/mir/tests/mir_utilities.cpp | 2 +- 10 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index ef829028f0..bedba70057 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -40,15 +40,14 @@ set(mir_sources #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic slam) +set(mir_depends_on core slic slam primal) blt_add_library( NAME mir SOURCES ${mir_sources} HEADERS ${mir_headers} DEPENDS_ON ${mir_depends_on} - FOLDER axom/mir - OBJECT TRUE ) + FOLDER axom/mir) axom_write_unified_header(NAME mir HEADERS ${mir_headers} ) diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 7ec2c79ba2..95fa9ad369 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -40,7 +40,7 @@ namespace mir using PosType = slam::DefaultPositionType; using ElemType = slam::DefaultElementType; - using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + using ArrayIndir = slam::policies::CArrayIndirection< PosType, ElemType >; using VertSet = slam::PositionSet< PosType, ElemType >; using ElemSet = slam::PositionSet< PosType, ElemType >; @@ -56,9 +56,9 @@ namespace mir // MAP TYPE ALIASES using BaseSet = slam::Set< PosType, ElemType >; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; - using PointMap = slam::Map< BaseSet, Point2 >; - using IntMap = slam::Map< BaseSet, int >; + using ScalarMap = slam::Map< axom::float64, BaseSet >; + using PointMap = slam::Map< Point2, BaseSet >; + using IntMap = slam::Map< int, BaseSet >; } } #endif diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 1455d16f81..ca63c83a7f 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -21,7 +21,7 @@ std::string usageString() int main( int argc, char** argv ) { - axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); if (argc != 4) diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 460a076b80..8d40e92049 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -162,7 +162,7 @@ struct Input */ int main(int argc, char** argv) { - axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); // Parse arguments diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp index 68f538e3f4..f5480be32f 100644 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -441,7 +441,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp index 9505eae633..afb8c4c97a 100644 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -221,7 +221,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp index 875c7365df..56057d060e 100644 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp index 0d9797e16d..61e8a48a14 100644 --- a/src/axom/mir/tests/mir_mesh.cpp +++ b/src/axom/mir/tests/mir_mesh.cpp @@ -219,7 +219,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_smoke.cpp b/src/axom/mir/tests/mir_smoke.cpp index c71092b54d..06a8e09486 100644 --- a/src/axom/mir/tests/mir_smoke.cpp +++ b/src/axom/mir/tests/mir_smoke.cpp @@ -28,7 +28,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 6d5a6c2dde..9430ea0f33 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -20,7 +20,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; From b78570d91b80975e316dac27439845b7e62e27a6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 7 Jun 2024 11:52:26 -0700 Subject: [PATCH 048/290] Bring back up to date with develop changes. --- src/axom/core/utilities/Utilities.hpp | 17 ----------------- src/axom/mir/CMakeLists.txt | 5 ++--- src/axom/mir/MIRMeshTypes.hpp | 8 ++++---- .../mir/examples/mir_concentric_circles.cpp | 2 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 2 +- src/axom/mir/tests/mir_cell_clipper.cpp | 2 +- src/axom/mir/tests/mir_cell_generator.cpp | 2 +- src/axom/mir/tests/mir_mesh.cpp | 2 +- src/axom/mir/tests/mir_utilities.cpp | 2 +- src/axom/slam/Map.hpp | 2 +- 10 files changed, 13 insertions(+), 31 deletions(-) diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index ddba045d3b..5447052ee0 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -119,23 +119,6 @@ inline T log2(T val) return static_cast(std::log2(val)); } - -/*! - * \brief Linearly interpolates between two values - * \param [in] val0 The first value - * \param [in] val2 The second value - * \param [in] t The interpolation parameter. - * \return The interpolated value - */ -template < typename T > -inline AXOM_HOST_DEVICE -T lerp( T v0, T v1, T t) -{ - constexpr T one = T(1); - return (one-t)*v0 + t*v1; -} - - /*! * \brief Linearly interpolates between two values * \param [in] val0 The first value diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index ef829028f0..bedba70057 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -40,15 +40,14 @@ set(mir_sources #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic slam) +set(mir_depends_on core slic slam primal) blt_add_library( NAME mir SOURCES ${mir_sources} HEADERS ${mir_headers} DEPENDS_ON ${mir_depends_on} - FOLDER axom/mir - OBJECT TRUE ) + FOLDER axom/mir) axom_write_unified_header(NAME mir HEADERS ${mir_headers} ) diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 3e875bbaf6..96450cc1d4 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -40,7 +40,7 @@ namespace mir using PosType = slam::DefaultPositionType; using ElemType = slam::DefaultElementType; - using ArrayIndir = slam::policies::ArrayIndirection< PosType, ElemType >; + using ArrayIndir = slam::policies::CArrayIndirection< PosType, ElemType >; using VertSet = slam::PositionSet< PosType, ElemType >; using ElemSet = slam::PositionSet< PosType, ElemType >; @@ -56,9 +56,9 @@ namespace mir // MAP TYPE ALIASES using BaseSet = slam::Set< PosType, ElemType >; - using ScalarMap = slam::Map< BaseSet, axom::float64 >; - using PointMap = slam::Map< BaseSet, Point2 >; - using IntMap = slam::Map< BaseSet, int >; + using ScalarMap = slam::Map< axom::float64, BaseSet >; + using PointMap = slam::Map< Point2, BaseSet >; + using IntMap = slam::Map< int, BaseSet >; } } #endif diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 66581fb830..dd5f1393ab 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -21,7 +21,7 @@ std::string usageString() int main( int argc, char** argv ) { - axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); if (argc != 4) diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index 5ff100945b..f41c44d053 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -162,7 +162,7 @@ struct Input */ int main(int argc, char** argv) { - axom::slic::UnitTestLogger logger; // create & initialize test logger + axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); // Parse arguments diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp index 9a073b138e..7e39346973 100644 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -1056,7 +1056,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp index 9505eae633..afb8c4c97a 100644 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -221,7 +221,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp index 0d9797e16d..61e8a48a14 100644 --- a/src/axom/mir/tests/mir_mesh.cpp +++ b/src/axom/mir/tests/mir_mesh.cpp @@ -219,7 +219,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 0c442d3070..98df90fb0d 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -103,7 +103,7 @@ int main(int argc, char* argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); - axom::slic::UnitTestLogger logger; // create & initialize test logger, + axom::slic::SimpleLogger logger; // create & initialize test logger, result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/slam/Map.hpp b/src/axom/slam/Map.hpp index 5f8d0f8a66..0901b1a198 100644 --- a/src/axom/slam/Map.hpp +++ b/src/axom/slam/Map.hpp @@ -865,7 +865,7 @@ void Map::print() const { std::stringstream sstr; - if (!m_set) + if (!m_set.get()) { sstr << "** map is empty."; } From 5307644798f5d20eb0a0b2adcd1d6a917dc84215 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 10 Jun 2024 18:11:37 -0700 Subject: [PATCH 049/290] some incomplete code --- src/axom/mir/ClipCaseManager.hpp | 123 +++ src/axom/mir/views/ExplitCoordsetView.hpp | 158 ++++ src/axom/mir/views/NodeArrayView.hpp | 816 ++++++++++++++++++ .../mir/views/RectilinearCoordsetView.hpp | 255 ++++++ src/axom/mir/views/Shapes.hpp | 130 +++ src/axom/mir/views/StructuredTopologyView.hpp | 87 ++ src/axom/mir/views/UniformCoordsetView.hpp | 157 ++++ .../UnstructuredTopologyPolyhedralView.hpp | 153 ++++ .../UnstructuredTopologySingleShapeView.hpp | 79 ++ src/axom/mir/views/dispatch_coordset.hpp | 120 +++ 10 files changed, 2078 insertions(+) create mode 100644 src/axom/mir/ClipCaseManager.hpp create mode 100644 src/axom/mir/views/ExplitCoordsetView.hpp create mode 100644 src/axom/mir/views/NodeArrayView.hpp create mode 100644 src/axom/mir/views/RectilinearCoordsetView.hpp create mode 100644 src/axom/mir/views/Shapes.hpp create mode 100644 src/axom/mir/views/StructuredTopologyView.hpp create mode 100644 src/axom/mir/views/UniformCoordsetView.hpp create mode 100644 src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp create mode 100644 src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp create mode 100644 src/axom/mir/views/dispatch_coordset.hpp diff --git a/src/axom/mir/ClipCaseManager.hpp b/src/axom/mir/ClipCaseManager.hpp new file mode 100644 index 0000000000..5da05c56e8 --- /dev/null +++ b/src/axom/mir/ClipCaseManager.hpp @@ -0,0 +1,123 @@ +#define ST_TRI 0 +#define ST_QUAD 1 +#define ST_TET 2 +#define ST_PYR 3 +#define ST_WDG 4 +#define ST_HEX 5 +#define ST_MAX 6 + +template +struct ClipCaseBase +{ + using IntContainerType = ContainerType; + using UInt8ContainerType = ContainerType; + + // Q: Do I need to explicitly provide a constructor to get it marked as AXOM_HOST_DEVICE? + + size_t size() const { return m_shapes.size(); } + + size_t shapesForCase(size_t index) const + { + return m_shapes[index]; + } + + UInt8ContainerType getShape(size_t caseId, size_t shapeId) const + { + assert(caseId < m_shapes.size()); + assert(shapeId < shapesForCase(caseId)); + + const uint8 *shapeStart = m_table.data() + m_offsets[caseId]; + size_t shapeLen = 0; + for(int i = 0; i < shapeId; i++) + { + shapeLen = advance(*shapeStart); + shapeStart += shapeLen; + } + shapeLen = advance(*shapeStart); + return UInt8ContainerType(shapeStart, shapeLen); + } + + size_t advance(uint8 shape) const + { + if(shape == ST_TRI) + retval = 2 + 3; + else if(shape == ST_QUAD) + retval = 2 + 4; + else if(shape == ST_TET) + retval = 2 + 4; + else if(shape == ST_PYR) + retval = 2 + 5; + else if(shape == ST_WDG) + retval = 2 + 6; + else if(shape == ST_HEX) + retval = 2 + 8; + return retval; + } + + IntContainerType m_shapes; + IntContainerType m_offsets; + UInt8ContainerType m_table; +}; + +template +struct ClipCase : public ClipCaseBase +{ + using ClipCaseView = ClipCaseBase + + void load(size_t n, const int *shapes, const int *offsets, const uint8 *table, size_t tableLen) + { + m_shapes = IntContainerType(shapes, n); + m_offsets = IntContainerType(offsets, n); + m_table = UInt8ContainerType(table, tableLen); + } + + ClipCaseView view() const + { + ClipCaseView v; + v.m_shapes = m_shapes.view(); + v.m_offsets = m_offsets.view(); + v.m_table = m_table.view(); + return v; + } + +}; + + +class ClipCaseManager +{ +public: + ClipCaseManager() + { + for(int i = 0; i < ST_MAX; i++) + m_clipCases[i] = ClipCase(); + } + + const ClipCase &operator[](size_t shapeId) + { + assert(shapeID < ST_MAX); + if(m_clipCases[shapeId].size() == 0) + { + load(shapeId); + } + return m_clipCases[shapeId]; + } + +private: + void load(size_t shapeId) + { + if(shapeId == ST_TRI) + m_clipCases[shapeId].load(numClipCasesTri, numClipShapesTri, startClipShapesTri, clipShapesTri); + else if(shapeId == ST_QUAD) + m_clipCases[shapeId].load(numClipCasesQuad, numClipShapesQuad, startClipShapesQuad, clipShapesQuad); + else if(shapeId == ST_TET) + m_clipCases[shapeId].load(numClipCasesTet, numClipShapesTet, startClipShapesTet, clipShapesTet); + else if(shapeId == ST_PYR) + m_clipCases[shapeId].load(numClipCasesPyr, numClipShapesPyr, startClipShapesPyr, clipShapesPyr); + else if(shapeId == ST_WDG) + m_clipCases[shapeId].load(numClipCasesWdg, numClipShapesWdg, startClipShapesWdg, clipShapesWdg); + else if(shapeId == ST_HEX) + m_clipCases[shapeId].load(numClipCasesHex, numClipShapesHex, startClipShapesHex, clipShapesHex); + } + + ClipCase m_clipCases[ST_MAX]; +}; diff --git a/src/axom/mir/views/ExplitCoordsetView.hpp b/src/axom/mir/views/ExplitCoordsetView.hpp new file mode 100644 index 0000000000..4f1dcba503 --- /dev/null +++ b/src/axom/mir/views/ExplitCoordsetView.hpp @@ -0,0 +1,158 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_EXPLICIT_COORDSET_VIEW_HPP_ +#define AXOM_MIR_EXPLICIT_COORDSET_VIEW_HPP_ + +#include "axom/core/ArrayView.hpp" +#include "axom/primal/Point.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \class This class provides a view for Conduit/Blueprint explicit coordsets. + */ +template +class ExplicitCoordsetView2 +{ +public: + using IndexType = axom::IndexType; + using value_type = DataType; + using PointType = axom::primal::Point; + + /** + * \brief Constructor + * + * \param x The first coordinate component. + * \param y The second coordinate component. + */ + AXOM_HOST_DEVICE + ExplicitCoordsetView2(const axom::ArrayView &x, + const axom::ArrayView &y) : m_coordinates{x,y} + { + assert(x.size() == y.size()); + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const { return m_coordinates[0].size(); } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + getPoint(IndexType vertex_index) const + { + assert(vertex_index < size()); + return PointType(m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index]); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](IndexType vertex_index) const + { + return getPoint(vertex_index); + } + +private: + axom::ArrayView m_coordinates[2]; +}; + +/** + * \class This class provides a view for Conduit/Blueprint explicit coordsets. + */ +template +class ExplicitCoordsetView3 +{ +public: + using IndexType = axom::IndexType; + using value_type = DataType; + using PointType = axom::primal::Point; + + /** + * \brief Constructor + * + * \param x The first coordinate component. + * \param y The second coordinate component. + * \param z The third coordinate component. + */ + AXOM_HOST_DEVICE + ExplicitCoordsetView3(const axom::ArrayView &x, + const axom::ArrayView &y, + const axom::ArrayView &z) : m_coordinates{x,y,z} + { + assert(x.size() == y.size() && x.size() == z.size()); + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const { return m_coordinates[0].size(); } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + getPoint(IndexType vertex_index) const + { + return PointType(m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index], + m_coordinates[2][vertex_index]); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](IndexType vertex_index) const + { + return getPoint(vertex_index); + } + +private: + axom::ArrayView m_coordinates[3]; +}; + + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp new file mode 100644 index 0000000000..49e9d3a416 --- /dev/null +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -0,0 +1,816 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_NODE_ARRAY_VIEW_HPP_ +#define AXOM_MIR_VIEWS_NODE_ARRAY_VIEW_HPP_ + +#include "axom/slic/slic.hpp" + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ +/* +using Node = ::conduit::Node; +using int8 = ::conduit::int8; +using int16 = ::conduit::int16; +using int32 = ::conduit::int32; +using int64 = ::conduit::int64; +using uint8 = ::conduit::uint8; +using uint16 = ::conduit::uint16; +using uint32 = ::conduit::uint32; +using uint64 = ::conduit::uint64; +using float32 = ::conduit::float32; +using float64 = ::conduit::float64; +using index_t = ::conduit::index_t; +using DataType = ::conduit::DataType; +*/ + +namespace detail +{ + +struct Delimiter {}; + +/// Used to separate arguments. +constexpr Delimiter ArgumentDelimiter; + +template +constexpr int encode_types(Args... args) +{ + return (... | args); +} + +template +constexpr int select_types(Args... args) +{ + return encode_types((1 << args)...); +} + +constexpr bool type_selected(int flag, int bit) +{ + return flag & (1 << bit); +} + +constexpr int select_all_types() +{ + return -1; +} + +constexpr int select_index_types() +{ + return select_types(conduit::DataType::INT32_ID, conduit::DataType::INT64_ID, conduit::DataType::UINT32_ID, conduit::DataType::UINT64_ID); +} + +constexpr int select_float_types() +{ + return select_types(conduit::DataType::FLOAT32_ID, conduit::DataType::FLOAT64_ID); +} + +//------------------------------------------------------------------------------ +// General Node to ArrayView. Handle all types. +//------------------------------------------------------------------------------ +template std::enable_if_t +Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int8_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int8 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int8(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int8_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int8(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int8 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int16_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int16 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int16(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int16_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int16(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int16 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int32(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int32(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int64 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_int64(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_int64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_int64(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported int64 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint8_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint8 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint8_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint8 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint16_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint16 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint16(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint16_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint16(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint16 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint32(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint32(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint64 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_uint64(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_uint64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_uint64(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported uint64 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_float32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported float32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_float32(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_float32_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_float32(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported float32 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_float64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported float64 node." << std::endl; +} + +template std::enable_if_t +Node_to_ArrayView_single_float64(conduit::Node &n, FuncType &&func) +{ + const auto size = n.dtype().number_of_elements(); + axom::ArrayView view(n.as_float64_ptr(), size); + func(view); +} + +template std::enable_if_t +Node_to_ArrayView_single_float64(conduit::Node &n, FuncType &&func) +{ + std::cout << "Unsupported float64 node." << std::endl; +} + +template +void Node_to_ArrayView_single(const conduit::Node &n, FuncType &&func) +{ + /* Later, with C++17, we can do this: + if constexpr (type_selected(Types, conduit::DataType::INT8_ID)) + { + if(n.dtype().is_int8()) + { + axom::ArrayView view(n.as_int8_ptr(), size); + func(view); + } + } + */ + + if(n.dtype().is_int8()) + { + Node_to_ArrayView_single_int8(n, func); + } + else if(n.dtype().is_int16()) + { + Node_to_ArrayView_single_int16(n, func); + } + else if(n.dtype().is_int32()) + { + Node_to_ArrayView_single_int32(n, func); + } + else if(n.dtype().is_int64()) + { + Node_to_ArrayView_single_int64(n, func); + } + else if(n.dtype().is_uint8()) + { + Node_to_ArrayView_single_uint8(n, func); + } + else if(n.dtype().is_uint16()) + { + Node_to_ArrayView_single_uint16(n, func); + } + else if(n.dtype().is_uint32()) + { + Node_to_ArrayView_single_uint32(n, func); + } + else if(n.dtype().is_uint64()) + { + Node_to_ArrayView_single_uint64(n, func); + } + else if(n.dtype().is_float32()) + { + Node_to_ArrayView_single_float32(n, func); + } + else if(n.dtype().is_float64()) + { + Node_to_ArrayView_single_float64(n, func); + } +} + +template +void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) +{ + if(n.dtype().is_int8()) + { + Node_to_ArrayView_single_int8(n, func); + } + else if(n.dtype().is_int16()) + { + Node_to_ArrayView_single_int16(n, func); + } + else if(n.dtype().is_int32()) + { + Node_to_ArrayView_single_int32(n, func); + } + else if(n.dtype().is_int64()) + { + Node_to_ArrayView_single_int64(n, func); + } + else if(n.dtype().is_uint8()) + { + Node_to_ArrayView_single_uint8(n, func); + } + else if(n.dtype().is_uint16()) + { + Node_to_ArrayView_single_uint16(n, func); + } + else if(n.dtype().is_uint32()) + { + Node_to_ArrayView_single_uint32(n, func); + } + else if(n.dtype().is_uint64()) + { + Node_to_ArrayView_single_uint64(n, func); + } + else if(n.dtype().is_float32()) + { + Node_to_ArrayView_single_float32(n, func); + } + else if(n.dtype().is_float64()) + { + Node_to_ArrayView_single_float64(n, func); + } +} + +template +void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View&... views) +{ + func(views...); +} + +template +void Node_to_ArrayView_internal(const conduit::Node &first, Args&&... args) +{ + Node_to_ArrayView_single(first, [&](auto view) + { + Node_to_ArrayView_internal(args..., view); + }); +} + +template +void Node_to_ArrayView_internal(conduit::Node &first, Args&&... args) +{ + Node_to_ArrayView_single(first, [&](auto view) + { + Node_to_ArrayView_internal(args..., view); + }); +} + +//------------------------------------------------------------------------------ +template std::enable_if_t +Node_to_ArrayView_same_internal_int8(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_int8_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int8(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int16(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_int16_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int16(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int32(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_int32_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int32(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int64(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_int64_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_int64(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint8(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_uint8_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint8(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint16(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_uint16_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint16(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint32(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_uint32_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint32(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint64(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_uint64_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_uint64(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_float32(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_float32_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_float32(FuncType &&func, Args&&... args) +{ +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_float64(FuncType &&func, Args&&... args) +{ + func(axom::ArrayView(args.as_float64_ptr(), args.dtype().number_of_elements())...); +} + +template std::enable_if_t +Node_to_ArrayView_same_internal_float64(FuncType &&func, Args&&... args) +{ +} + +template +void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit::Node &first, Args&&... args) +{ + if(first.dtype().is_int8()) + { + Node_to_ArrayView_same_internal_int8(func, first, args...); + } + else if(first.dtype().is_int16()) + { + Node_to_ArrayView_same_internal_int16(func, first, args...); + } + else if(first.dtype().is_int32()) + { + Node_to_ArrayView_same_internal_int32(func, first, args...); + } + else if(first.dtype().is_int64()) + { + Node_to_ArrayView_same_internal_int64(func, first, args...); + } + else if(first.dtype().is_uint8()) + { + Node_to_ArrayView_same_internal_uint8(func, first, args...); + } + else if(first.dtype().is_uint16()) + { + Node_to_ArrayView_same_internal_uint16(func, first, args...); + } + else if(first.dtype().is_uint32()) + { + Node_to_ArrayView_same_internal_uint32(func, first, args...); + } + else if(first.dtype().is_uint64()) + { + Node_to_ArrayView_same_internal_uint64(func, first, args...); + } + else if(first.dtype().is_float32()) + { + Node_to_ArrayView_same_internal_float32(func, first, args...); + } + else if(first.dtype().is_float64()) + { + Node_to_ArrayView_same_internal_float64(func, first, args...); + } +} + +template +void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node &first, Args&&... args) +{ + if(first.dtype().is_int8()) + { + Node_to_ArrayView_same_internal_int8(func, first, args...); + } + else if(first.dtype().is_int16()) + { + Node_to_ArrayView_same_internal_int16(func, first, args...); + } + else if(first.dtype().is_int32()) + { + Node_to_ArrayView_same_internal_int32(func, first, args...); + } + else if(first.dtype().is_int64()) + { + Node_to_ArrayView_same_internal_int64(func, first, args...); + } + else if(first.dtype().is_uint8()) + { + Node_to_ArrayView_same_internal_uint8(func, first, args...); + } + else if(first.dtype().is_uint16()) + { + Node_to_ArrayView_same_internal_uint16(func, first, args...); + } + else if(first.dtype().is_uint32()) + { + Node_to_ArrayView_same_internal_uint32(func, first, args...); + } + else if(first.dtype().is_uint64()) + { + Node_to_ArrayView_same_internal_uint64(func, first, args...); + } + else if(first.dtype().is_float32()) + { + Node_to_ArrayView_same_internal_float32(func, first, args...); + } + else if(first.dtype().is_float64()) + { + Node_to_ArrayView_same_internal_float64(func, first, args...); + } +} + +/// Reorder args +template +void Node_to_ArrayView_same_internal(const conduit::Node &first, Args&&... args) +{ + Node_to_ArrayView_same_internal(args..., first); +} + +template +void Node_to_ArrayView_same_internal(conduit::Node &first, Args&&... args) +{ + Node_to_ArrayView_same_internal(args..., first); +} + +} // namespace detail + +//------------------------------------------------------------------------------ +// Node to ArrayView. Handle all types. +//------------------------------------------------------------------------------ + +/*! + * \brief Convert a series of Conduit nodes to axom::ArrayView and pass the concrete + * views to a lambda function passed as the last argument. + * + * \note This method handles all Conduit array types and will instantiate views + * of any type. In other words, mixed node types can be used. + * + * \param first A Conduit node to be convered to a view. + * \param args A sequence of Conduit nodes, followed by a lambda that can accept + * the same number of views of any type. + * + * Node_to_ArrayView(node1, node2, [](auto &view1, auto &view2) { }); + * + */ +template +void Node_to_ArrayView(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void Node_to_ArrayView(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +/*! + * \brief Convert a series of Conduit nodes to axom::ArrayView and pass the concrete + * views to a lambda function passed as the last argument. + * + * \note This method handles all Conduit array types. All nodes will be treated + * as the same type as the first Conduit node. Use this when all nodes + * are assumed to contain the same type since it results in less code. + * + * \param first A Conduit node to be convered to a view. + * \param args A sequence of Conduit nodes, followed by a lambda that can accept + * the same number of views of any type. + * + * Node_to_ArrayView_same(node1, node2, [](auto &view1, auto &view2) { }); + * + */ +template +void Node_to_ArrayView_same(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void Node_to_ArrayView_same(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +//------------------------------------------------------------------------------ +// Index Node to ArrayView. Handle types used for indexing. +//------------------------------------------------------------------------------ + +template +void IndexNode_to_ArrayView(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void IndexNode_to_ArrayView(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void IndexNode_to_ArrayView_same(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void IndexNode_to_ArrayView_same(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +//------------------------------------------------------------------------------ +// Float Node to ArrayView. Handle float types. +//------------------------------------------------------------------------------ +template +void FloatNode_to_ArrayView(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void FloatNode_to_ArrayView(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void FloatNode_to_ArrayView_same(const conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +template +void FloatNode_to_ArrayView_same(conduit::Node &first, Args&&... args) +{ + detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); +} + +} // namespace views +} // namespace mir +} // namespace axom + +#endif diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp new file mode 100644 index 0000000000..1e95de1674 --- /dev/null +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -0,0 +1,255 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_UNIFORm_coordinates_VIEW_HPP_ +#define AXOM_MIR_UNIFORm_coordinates_VIEW_HPP_ + +#include "axom/core/StackArray.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/primal/Point.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \class This class provides a view for Conduit/Blueprint 2D rectilinear coordsets. + */ +template +class RectilinearCoordsetView2 +{ +public: + using LogicalIndexType = axom::StackArray; + using IndexType = axom::IndexType; + using value_type = DataType; + using PointType = Point; + + /** + * \brief Constructor + * + * \param x The first coordinate component. + * \param y The second coordinate component. + */ + AXOM_HOST_DEVICE + RectilinearCoordsetView2(const axom::ArrayView &x, + const axom::ArrayView &y) : m_coordinates{x, y} + { + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + return m_coordinates[0].size() * m_coordinates[1].size(); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType getPoint(LogicalIndexType vertex_index) const + { + return PointType(m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]]); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType getPoint(IndexType vertex_index) const + { + return getPoint(IndexToLogicalIndex(vert_index)); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](LogicalIndexType vertex_index) const + { + return getPoint(vertex_index); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](IndexType vertex_index) const + { + return getPoint(IndexToLogicalIndex(vertex_index)); + } + +private: + RectilinearCoordsetView2() = delete; + + /** + * \brief Turn an index into a logical IJ index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + AXOM_HOST_DEVICE + LogicalIndexType IndexToLogicalIndex(IndexType index) const + { + LogicalIndexType logical; + const auto nx = m_coordinates[0].size(); + logical[0] = index % nx; + logical[1] = index / nx; + return logical; + } + + axom::ArrayView m_coordinates[2]; +}; + +/** + * \class This class provides a view for Conduit/Blueprint 3D rectilinear coordsets. + */ +template +class RectilinearCoordsetView3 +{ +public: + using LogicalIndexType = axom::StackArray; + using IndexType = axom::IndexType; + using value_type = DataType; + using PointType = Point; + + /** + * \brief Constructor + * + * \param x The first coordinate component. + * \param y The second coordinate component. + * \param z The third coordinate component. + */ + AXOM_HOST_DEVICE + RectilinearCoordsetView3(const axom::ArrayView &x, + const axom::ArrayView &y, + const axom::ArrayView &z) : m_coordinates{x, y, z} + { + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + return m_coordinates[0].size() * m_coordinates[1].size() * m_coordinates[2].size(); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType getPoint(LogicalIndexType vertex_index) const + { + return PointType(m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]], + m_coordinates[2][vertex_index[2]]); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType getPoint(IndexType vertex_index) const + { + return getPoint(IndexToLogicalIndex(vert_index)); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](LogicalIndexType vertex_index) const + { + return getPoint(vertex_index); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](IndexType vertex_index) const + { + return getPoint(IndexToLogicalIndex(vertex_index)); + } + +private: + RectilinearCoordsetView3() = delete; + + /** + * \brief Turn an index into a logical IJK index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + AXOM_HOST_DEVICE + LogicalIndexType IndexToLogicalIndex(IndexType index) const + { + const auto nx = m_coordinates[0].size(); + const auto nxy = nx * m_coordinates[1].size(); + LogicalIndexType logical; + logical[0] = index % nx; + logical[1] = (index % nxy) / nx; + logical[2] = index / nxy; + return logical; + } + + axom::ArrayView m_coordinates[3]; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp new file mode 100644 index 0000000000..50668aa2f6 --- /dev/null +++ b/src/axom/mir/views/Shapes.hpp @@ -0,0 +1,130 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_SHAPES_HPP_ +#define AXOM_MIR_VIEWS_SHAPES_HPP_ + +#include "axom/core/AxomArrayView.hpp" + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/* + 3 + * + /|\ face 0: 0,2,1 + / | \ face 1: 0,1,3 + / | \ face 2: 1,2,3 + / | \ face 3: 2,0,3 +0*----|----* 2 + \ | / edge 0: 0,2 + \ | / edge 1: 2,1 + \ | / edge 2: 1,0 + \|/ edge 3: 1,3 + * edge 4: 3,0 + 1 edge 5: 2,3 + + */ +template +struct TetShape +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 4; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 6; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,2}, {2,1}, {1,0}, {1,3}, {3,0}, {2,3}}; + + AXOM_HOST_DEVICE TetShape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() + { + } + + AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + + AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + { + m_faceIds[0] = m_ids[faces[faceIndex][0]]; + m_faceIds[1] = m_ids[faces[faceIndex][1]]; + m_faceIds[2] = m_ids[faces[faceIndex][2]]; + return axom::ArrayView(m_faceIds.m_data, 3); + } +private: + axom::ArrayView m_ids; + mutable axom::StackArray m_faceIds; +}; + +/* + 3*------------* 2 + /| /| + / | / | + / | / | + 7*------------*6 | + | | | | + | | | | + | 0*--------|---* 1 + | / | / + | / | / + |/ |/ + *------------* + 4 5 + + */ +template +struct HexShape +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 8; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 4; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 6; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 12; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = { + {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,3}, {3,2}, {2,1}, {1,0}, {1,5}, {5,4}, {4,0}, {2,6}, {6,5}, {3,7}, {7,6}, {7,4}}; + + + AXOM_HOST_DEVICE HexShape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() + { + } + + AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + + AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + { + m_faceIds[0] = m_ids[faces[faceIndex][0]]; + m_faceIds[1] = m_ids[faces[faceIndex][1]]; + m_faceIds[2] = m_ids[faces[faceIndex][2]]; + m_faceIds[3] = m_ids[faces[faceIndex][3]]; + return axom::ArrayView(m_faceIds.m_data, 4); + } +private: + axom::ArrayView m_ids; + mutable axom::StackArray m_faceIds; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp new file mode 100644 index 0000000000..3524617aa1 --- /dev/null +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -0,0 +1,87 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ +#define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ + +#include "axom/mir/views/Shapes.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. + * + * \tparam IndexT The index type that will be used for connectivity, etc. + * \tparam ShapeT The shape type. + */ +template +class StructuredTopologyView +{ +public: + using IndexType = IndexT; + using LogicalIndexType = StackArray; + + /** + * \brief Constructor + * + * \param conn The mesh connectivity. + */ + AXOM_HOST_VIEW + StructuredTopologyView(const LogicalIndexType &dims) : m_dimensions(dims) + { + } + + /** + * \brief Return the number of zones. + * + * \return The number of zones. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) + sz *= m_dimensions[i]; + return sz; + } + + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + AXOM_HOST + void for_all_zones(FuncType &&func) const + { + const auto nzones = numberOfZones(); + + axom::ArrayView connectivity(m_connectivity); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + func(zoneIndex, shape); + }); + } + +private: + StructuredTopologyView() = delete; + + LogicalIndexType m_dimensions; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp new file mode 100644 index 0000000000..e5e0610169 --- /dev/null +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -0,0 +1,157 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_UNIFORM_COORDSET_VIEW_HPP_ +#define AXOM_MIR_UNIFORM_COORDSET_VIEW_HPP_ + +#include "axom/core/StackArray.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/primal/Point.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \class This class provides a view for Conduit/Blueprint uniform coordsets. + */ +template +class UniformCoordsetView +{ +public: + using LogicalIndexType = axom::StackArray; + using ExtentsType = axom::StackArray; + using IndexType = axom::IndexType; + using value_type = DataType; + using PointType = axom::primal::Point; + + /** + * \brief Constructor + * + * \param dims The logical dimensions of the coordset. + * \param origin The origin of the coordset's coordinate system. + * \param spacing The spacing inbetween points. + */ + AXOM_HOST_DEVICE + UniformCoordsetView(const LogicalIndexType dims, + const ExtentsType origin, + const ExtentsType spacing) : m_dimensions{dims}, m_origin{origin}, m_spacing{spacing} + { + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) + sz *= m_dimensions[i]; + return sz; + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType getPoint(LogicalIndexType vertex_index) const + { + PointType pt; + for(int i = 0; i < NDIMS; i++) + pt.x[i] = m_origin[i] + vertex_index[i] * m_spacing[i]; + return pt; + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The logical index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](LogicalIndexType vertex_index) const + { + return getPoint(vertex_index); + } + + /** + * \brief Return the requested point from the coordset. + * + * \param vertex_index The index of the point to return. + * + * \return A point that corresponds to \a vertex_index. + */ + AXOM_HOST_DEVICE + PointType + operator[](IndexType vertex_index) const + { + return getPoint(IndexToLogicalIndex(vertex_index)); + } + + +private: + /** + * \brief Turn an index into a logical IJ index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, LogicalIndexType>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndexType logical; + const auto nx = m_dimensions[0]; + assert(index >= 0); + logical[0] = index % nx; + logical[1] = index / nx; + return logical; + } + + /** + * \brief Turn an index into a logical IJK index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, LogicalIndexType>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndexType logical; + const auto nx = m_dimensions[0]; + const auto nxy = nx * m_dimensions[1]; + logical[0] = index % nx; + logical[1] = (index % nxy) / nx; + logical[2] = index / nxy; + return logical; + } + + LogicalIndexType m_dimensions; + ExtentsType m_origin; + ExtentsType m_spacing; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp new file mode 100644 index 0000000000..cdcb78cc6b --- /dev/null +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -0,0 +1,153 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_POLYHEDRAL_VIEW_HPP_ +#define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_POLYHEDRAL_VIEW_HPP_ + +#include "axom/mir/views/Shapes.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +template +class UnstructuredTopologyPolyhedralView +{ +public: + using IndexType = IndexT; + + struct PolyhedronData + { + AXOM_HOST_DEVICE + PolyhedronData(axom::ArrayView subelement_conn, + axom::ArrayView subelement_sizes, + axom::ArrayView subelement_offsets, + axom::ArrayView element_conn, + axom::ArrayView element_sizes, + axom::ArrayView element_offsets) : + m_subelement_conn(subelement_conn), m_subelement_sizes(subelement_sizes), m_subelement_offsets(subelement_offsets), + m_element_conn(element_conn), m_element_sizes(element_sizes), m_element_offsets(element_offsets) + { + } + + AXOM_HOST_DEVICE + PolyhedronData(const PolyhedronData &obj) : + m_subelement_conn(obj.m_subelement_conn), m_subelement_sizes(obj.m_subelement_sizes), m_subelement_offsets(obj.m_subelement_offsets), + m_element_conn(obj.m_element_conn), m_element_sizes(obj.m_element_sizes), m_element_offsets(obj.m_element_offsets) + { + } + + axom::ArrayView m_subelement_conn; + axom::ArrayView m_subelement_sizes; + axom::ArrayView m_subelement_offsets; + axom::ArrayView m_element_conn; + axom::ArrayView m_element_sizes; + axom::ArrayView m_element_offsets; + }; + + // Can we provide a way to provide data about Zone i's shape? + struct PolyhedronShape + { + constexpr static IndexType MaximumNumberOfIds = 20 * 3; + + AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return true; } + + AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj), m_zoneIndex(zi), m_ids() + { + } + + /// This implementation does not return unique number of nodes. + AXOM_HOST_DEVICE IndexType numberOfNodes() const + { + axom::IndexType nnodes = 0; + const auto nFaces = numberOfFaces(); + for(axom::IndexType f = 0; f < nFaces; f++) + nnodes += getFace(f).size(); + } + + AXOM_HOST_DEVICE IndexType numberOfFaces() const + { + return m_data.m_element_sizes[m_zoneIndex]; + } + + AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const + { + return getFace(faceIndex).size(); + } + + AXOM_HOST_DEVICE axom::ArrayView getIds() const + { + axom::IndexType nnodes = 0; + const auto nFaces = numberOfFaces(); + for(axom::IndexType f = 0; f < nFaces; f++) + { + const auto faceIds = getFace(f); + for(axom::IndexType i = 0; i < faceIds.size(); i++) + { + if(nnodes < MaximumNumberOfIds) + m_ids[nnodes++] = faceIds[i]; + } + } + return axom::ArrayView(m_ids.m_data, nnodes); + } + + AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + { + const axom::ArrayView element_face_ids(m_data.m_element_conn.data() + m_data.m_element_offsets[m_zoneIndex], m_data.m_element_sizes[m_zoneIndex]); + const auto faceId = element_face_ids[faceIndex]; + + return axom::ArrayView(m_data.m_subelement_conn.data() + m_data.m_subelement_offsets[faceId], m_data.m_subelement_sizes[faceId]); + } + + private: + PolyhedronData m_data; + IndexType m_zoneIndex {0}; + mutable axom::StackArray m_ids; + }; + //---------------------------------------------------------------------------- + + using ShapeType = PolyhedronShape; + + UnstructuredTopologyPolyhedralView(const axom::ArrayView &subelement_conn, + const axom::ArrayView &subelement_sizes, + const axom::ArrayView &subelement_offsets, + const axom::ArrayView &element_conn, + const axom::ArrayView &element_sizes, + const axom::ArrayView &element_offsets) : + m_data(subelement_conn, subelement_sizes, subelement_offsets, + element_conn, element_sizes, element_offsets) + { + } + + IndexType numberOfZones() const + { + return m_data.m_element_sizes.size(); + } + + template + void for_all_zones(FuncType &&func) const + { + const auto nzones = numberOfZones(); + + const PolyhedronData sd(m_data); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + const PolyhedronShape shape(sd, zoneIndex); + func(zoneIndex, shape); + }); + } + +private: + PolyhedronData m_data; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp new file mode 100644 index 0000000000..c9e832c882 --- /dev/null +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -0,0 +1,79 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ +#define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ + +#include "axom/mir/views/Shapes.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. + * + * \tparam IndexT The index type that will be used for connectivity, etc. + * \tparam ShapeT The shape type. + */ +template +class UnstructuredTopologySingleShapeView +{ +public: + using IndexType = IndexT; + using ShapeType = ShapeT; + + /** + * \brief Constructor + * + * \param conn The mesh connectivity. + */ + UnstructuredTopologySingleShapeView(const axom::ArrayView &conn) : m_connectivity(conn) + { + } + + /** + * \brief Return the number of zones. + * + * \return The number of zones. + */ + IndexType numberOfZones() const + { + return m_connectivity.size() / ShapeType::numberOfNodes(); + } + + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + void for_all_zones(FuncType &&func) const + { + const auto nzones = numberOfZones(); + + axom::ArrayView connectivity(m_connectivity); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + func(zoneIndex, shape); + }); + } + +private: + axom::ArrayView m_connectivity; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp new file mode 100644 index 0000000000..d2c5d649ed --- /dev/null +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -0,0 +1,120 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_COORDSET_HPP_ +#define AXOM_MIR_DISPATCH_COORDSET_HPP_ + +#include "axom/mir/views/ExplicitCoordsetView.hpp" +#include "axom/mir/views/UniformCoordsetView.hpp" +#include "axom/mir/views/RectilinearCoordsetView.hpp" +#include "axom/mir/views/NodeArrayView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Given a Conduit/Blueprint coordset, create an appropriate view and + * call the supplied function, passing the coordset view to it. + * + * \tparam FuncType The type of the function / lambda to invoke. It is expected + * that the callable accepts an auto argument for a coordset view. + * + * \param coordset The Conduit node that contains the coordset. + * \param func The function/lambda to invoke using the coordset view. + */ +template +void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) +{ + const std::string cstype = coordset["type"].as_string(); + if(cstype == "uniform") + { + const conduit::Node &n_dims = coordset["dims"]; + const conduit::index_t ndims = n_dims.dtype().number_of_elements(); + if(ndims == 2) + { + axom::StackArray dims; + axom::StackArray origin{0., 0.}, spacing{1., 1.}; + for(int i = 0; i < ndims; i++) + { + dims[i] = n_dims.as_int_accessor()[i]; + if(coordset.has_child("origin")) + origin[i] = coordset["origin"].as_double_accessor()[i]; + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"].as_double_accessor()[i]; + } + + UniformCoordsetView coordView(dims, origin, spacing); + func(coordView); + } + else if(ndims == 3) + { + axom::StackArray dims; + axom::StackArray origin{0., 0., 0.}, spacing{1., 1., 1.}; + for(int i = 0; i < ndims; i++) + { + dims[i] = n_dims.as_int_accessor()[i]; + if(coordset.has_child("origin")) + origin[i] = coordset["origin"].as_double_accessor()[i]; + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"].as_double_accessor()[i]; + } + + UniformCoordsetView coordView(dims, origin, spacing); + func(coordView); + } + } + else if(cstype == "rectilinear") + { + const conduit::Node &values = coordset["values"]; + if(values.number_of_children() == 2) + { + axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], [&](auto xView, auto yView) + { + RectilinearCoordsetView2 coordView(xView, yView); + func(coordView); + }); + } + else if(values.number_of_children() == 3) + { + axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], values[2], [&](auto xView, auto yView, auto zView) + { + RectilinearCoordsetView3 coordView(xView, yView, zView); + func(coordView); + }); + } + } + else if(cstype == "explicit") + { + // TODO: get the axis names. + // TODO: support strided/structured. + const conduit::Node &values = coordset["values"]; + if(values.number_of_children() == 2) + { + axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], [&](auto xView, auto yView) + { + ExplicitCoordsetView2 coordView(xView, yView); + func(coordView); + }); + } + else if(values.number_of_children() == 3) + { + axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], values[2], [&](auto xView, auto yView, auto zView) + { + ExplicitCoordsetView3 coordView(xView, yView, zView); + func(coordView); + }); + } + } +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From 23b9087f1a5470a5f98c7311040a2bcbab421c17 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 11 Jun 2024 17:30:54 -0700 Subject: [PATCH 050/290] Added shapes --- src/axom/mir/ClipCaseManager.hpp | 123 ---- src/axom/mir/MIRAlgorithm.cpp | 371 +++++++++++ src/axom/mir/MIRAlgorithm.hpp | 618 ++++++++++++++++++ src/axom/mir/views/Shapes.hpp | 313 ++++++++- src/axom/mir/views/StructuredTopologyView.hpp | 26 +- .../UnstructuredTopologyPolyhedralView.hpp | 3 +- .../UnstructuredTopologySingleShapeView.hpp | 44 +- .../views/dispatch_unstructured_topology.hpp | 155 +++++ 8 files changed, 1491 insertions(+), 162 deletions(-) delete mode 100644 src/axom/mir/ClipCaseManager.hpp create mode 100644 src/axom/mir/MIRAlgorithm.cpp create mode 100644 src/axom/mir/MIRAlgorithm.hpp create mode 100644 src/axom/mir/views/dispatch_unstructured_topology.hpp diff --git a/src/axom/mir/ClipCaseManager.hpp b/src/axom/mir/ClipCaseManager.hpp deleted file mode 100644 index 5da05c56e8..0000000000 --- a/src/axom/mir/ClipCaseManager.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#define ST_TRI 0 -#define ST_QUAD 1 -#define ST_TET 2 -#define ST_PYR 3 -#define ST_WDG 4 -#define ST_HEX 5 -#define ST_MAX 6 - -template -struct ClipCaseBase -{ - using IntContainerType = ContainerType; - using UInt8ContainerType = ContainerType; - - // Q: Do I need to explicitly provide a constructor to get it marked as AXOM_HOST_DEVICE? - - size_t size() const { return m_shapes.size(); } - - size_t shapesForCase(size_t index) const - { - return m_shapes[index]; - } - - UInt8ContainerType getShape(size_t caseId, size_t shapeId) const - { - assert(caseId < m_shapes.size()); - assert(shapeId < shapesForCase(caseId)); - - const uint8 *shapeStart = m_table.data() + m_offsets[caseId]; - size_t shapeLen = 0; - for(int i = 0; i < shapeId; i++) - { - shapeLen = advance(*shapeStart); - shapeStart += shapeLen; - } - shapeLen = advance(*shapeStart); - return UInt8ContainerType(shapeStart, shapeLen); - } - - size_t advance(uint8 shape) const - { - if(shape == ST_TRI) - retval = 2 + 3; - else if(shape == ST_QUAD) - retval = 2 + 4; - else if(shape == ST_TET) - retval = 2 + 4; - else if(shape == ST_PYR) - retval = 2 + 5; - else if(shape == ST_WDG) - retval = 2 + 6; - else if(shape == ST_HEX) - retval = 2 + 8; - return retval; - } - - IntContainerType m_shapes; - IntContainerType m_offsets; - UInt8ContainerType m_table; -}; - -template -struct ClipCase : public ClipCaseBase -{ - using ClipCaseView = ClipCaseBase - - void load(size_t n, const int *shapes, const int *offsets, const uint8 *table, size_t tableLen) - { - m_shapes = IntContainerType(shapes, n); - m_offsets = IntContainerType(offsets, n); - m_table = UInt8ContainerType(table, tableLen); - } - - ClipCaseView view() const - { - ClipCaseView v; - v.m_shapes = m_shapes.view(); - v.m_offsets = m_offsets.view(); - v.m_table = m_table.view(); - return v; - } - -}; - - -class ClipCaseManager -{ -public: - ClipCaseManager() - { - for(int i = 0; i < ST_MAX; i++) - m_clipCases[i] = ClipCase(); - } - - const ClipCase &operator[](size_t shapeId) - { - assert(shapeID < ST_MAX); - if(m_clipCases[shapeId].size() == 0) - { - load(shapeId); - } - return m_clipCases[shapeId]; - } - -private: - void load(size_t shapeId) - { - if(shapeId == ST_TRI) - m_clipCases[shapeId].load(numClipCasesTri, numClipShapesTri, startClipShapesTri, clipShapesTri); - else if(shapeId == ST_QUAD) - m_clipCases[shapeId].load(numClipCasesQuad, numClipShapesQuad, startClipShapesQuad, clipShapesQuad); - else if(shapeId == ST_TET) - m_clipCases[shapeId].load(numClipCasesTet, numClipShapesTet, startClipShapesTet, clipShapesTet); - else if(shapeId == ST_PYR) - m_clipCases[shapeId].load(numClipCasesPyr, numClipShapesPyr, startClipShapesPyr, clipShapesPyr); - else if(shapeId == ST_WDG) - m_clipCases[shapeId].load(numClipCasesWdg, numClipShapesWdg, startClipShapesWdg, clipShapesWdg); - else if(shapeId == ST_HEX) - m_clipCases[shapeId].load(numClipCasesHex, numClipShapesHex, startClipShapesHex, clipShapesHex); - } - - ClipCase m_clipCases[ST_MAX]; -}; diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp new file mode 100644 index 0000000000..b521527bc3 --- /dev/null +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -0,0 +1,371 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/quest/MIRAlgorithm.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/slic.hpp" + +#include +#include + +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on + +namespace axom +{ + +namespace quest +{ + +void MIRAlgorithm::execute(const conduit::Node &root, + const conduit::Node &options, + conduit::Node &output) +{ + auto domains = conduit::blueprint::mesh::domains(root); + if(domains.size() > 1) + { + // Handle multiple domains + for(const auto &dom_ptr : domains) + { + const conduit::Node &dom = *dom_ptr; + const std::string topoName = topologyName(dom, options); + const std::string newTopoName = newTopologyName(dom, options); + const std::string newCSName = newCoordsetName(dom, options); + + conduit::Node &newDomain = output.append(); + const conduit::Node &topologies = dom.fetch_existing("topologies"); + const conduit::Node &topo = topologies.fetch_existing(topoName); + const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + + conduit::Node &newTopo = newDomain["topologies/" + newTopoName]; + conduit::Node &newCoordset = newDomain["coordsets/" + newCSName]; + copyState(dom, newDomain); + execute(topo, *cset, options, newTopo, newCoordset); + } + } + else if(domains.size() > 0) + { + // Handle single domain + const conduit::Node &dom = *domains[0]; + + const std::string topoName = topologyName(dom, options); + const std::string newTopoName = newTopologyName(dom, options); + const std::string newCSName = newCoordsetName(dom, options); + + const conduit::Node &topologies = dom.fetch_existing("topologies"); + const conduit::Node &topo = topologies.fetch_existing(topoName); + const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + + conduit::Node &newTopo = output["topologies/" + newTopoName]; + conduit::Node &newCoordset = output["coordsets/" + newCSName]; + copyState(dom, output); + execute(topo, *cset, options, newTopo, newCoordset); + } +} + +void +MIRAlgorithm::copyState(const conduit::Node &mesh, conduit::Node &destMesh) const +{ + if(mesh.has_path("state")) + destMesh["state"].set(mesh["state"]); +} + +std::string +MIRAlgorithm::topologyName(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::string topoName; + if(options.has_path("topology")) + topoName = options.fetch_existing("topology").as_string(); + else if(mesh.has_path("topologies")) + { + const conduit::Node &topologies = mesh.fetch_existing("topologies"); + topoName = topologies[0].name(); + } + return topoName; +} + +std::string +MIRAlgorithm::newTopologyName(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::string topoName; + if(options.has_path("new_topology")) + topoName = options.fetch_existing("new_topology").as_string(); + else + topoName = topologyName(mesh, options); + return topoName; +} + +std::string +MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::string csetName; + if(options.has_path("new_coordset")) + csetName = options.fetch_existing("new_coordset").as_string(); + else + { + std::string topoName = topologyName(mesh, options); + const conduit::Node &topologies = mesh.fetch_existing("topologies"); + const conduit::Node &topo = topologies.fetch_existing(topoName); + csetName = topo.fetch_existing("coordset").as_string(); + } + + return csetName; +} + +const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, const conduit::Node &options) const +{ + const std::string topoName = topologyName(mesh, options); + const conduit::Node &topologies = mesh.fetch_existing("topologies"); + return topologies.fetch_existing(topoName); +} + +const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, const conduit::Node &options) const +{ + const std::string topoName = topologyName(mesh, options); + const conduit::Node &matsets = mesh.fetch_existing("matsets"); + for(conduit::index_t i = 0; i < matsets.number_of_children(); i++) + { + const conduit::Node &matset = matsets[i]; + if(matset["topology"].as_string() == topoName) + return matset; + } + // We did not find one. TODO: throw exception. + // return first to eliminate compiler warning. + return matsets[0]; +} + +std::vector +MIRAlgorithm::fieldNames(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::vector names; + if(options.has_path("fields")) + { + const conduit::Node &fields = options["fields"]; + if(fields.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < fields.number_of_children(); i++) + names.push_back(fields[i].name()); + } + else + { + if(fields.dtype().is_char8_str()) + names.push_back(fields.as_string()); + } + } + else if(mesh.has_child("fields")) + { + const conduit::Node &fields = mesh.fetch_existing("fields"); + for(conduit::index_t i = 0; i < fields.number_of_children(); i++) + names.push_back(fields[i].name()); + } + return names; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +namespace elvira +{ +} // end namespace elvira + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void ElviraMIRAlgorithm::execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) +{ +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + switch(m_execPolicy) + { + #if defined(AXOM_USE_OPENMP) + case RuntimePolicy::omp: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + #if defined(AXOM_USE_CUDA) + case RuntimePolicy::cuda: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + #if defined(AXOM_USE_HIP) + case RuntimePolicy::hip: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + default: + // Falls through + case RuntimePolicy::seq: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + } +#endif +} + +template +void for_all_zones(const conduit::Node &topo, FuncType &&func) +{ + conduit::index_t dims[3] = {1, 1, 1}; + conduit::blueprint::mesh::utils::topology::logical_dims(topo, dims, 3); + const conduit::index_t dimension = conduit::blueprint::mesh::topology::dims(topo); + const conduit::index_t nzones = conduit::blueprint::mesh::topology::length(topo); + + if(dimension == 1) + { + // Line elements + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + conduit::index_t ids[2]; + ids[0] = zoneIndex; + ids[1] = zoneIndex + 1; + func(zoneIndex, ids, 2); + }); + } + else if(dimension == 2) + { + // Quad elements + const conduit::index_t nx = dims[0] + 1; + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // zoneIndex to i,j + const conduit::index_t i = zoneIndex % dims[0]; + const conduit::index_t j = zoneIndex / dims[0]; + // Make node ids + const conduit::index_t jnx = j * nx; + const conduit::index_t j1nx = jnx + nx; + conduit::index_t ids[4]; + ids[0] = jnx + i; + ids[1] = jnx + i + 1; + ids[2] = j1nx + i + 1; + ids[3] = j1nx + i; + func(zoneIndex, ids, 4); + }); + } + else if(dimension == 3) + { + // Hex elements + const conduit::index_t nx = dims[0] + 1; + const conduit::index_t ny = dims[1] + 1; + const conduit::index_t nz = dims[2] + 1; + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // zoneIndex to i,j,k + const conduit::index_t i = zoneIndex % dims[0]; + const conduit::index_t j = (zoneIndex % (dims[0] * dims[1])) / dims[0]; + const conduit::index_t k = zoneIndex / (dims[0] * dims[1]); + + // Make node ids + const conduit::index_t knxny = k * nx * ny; + const conduit::index_t k1nxny = (k + 1) * nx * ny; + const conduit::index_t jnx = j * nx; + const conduit::index_t j1nx = jnx + nx; + conduit::index_t ids[8]; + ids[0] = knxny + jnx + i; + ids[1] = knxny + jnx + i + 1; + ids[2] = knxny + j1nx + i + 1; + ids[3] = knxny + j1nx + i; + ids[4] = k1nxny + jnx + i; + ids[5] = k1nxny + jnx + i + 1; + ids[6] = k1nxny + j1nx + i + 1; + ids[7] = k1nxny + j1nx + i; + + func(zoneIndex, ids, 4); + }); + } + else + { +// CONDUIT_ERROR("Unsupported dimension given to traverse_structured " +// << dimension << "."); + } +} + +template +void ElviraMIRAlgorithm::executeImpl(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) +{ + // I'm not sure some parts of Elvira work for unstructured topologies. (stencils) + const auto type = topo["type"].as_string(); + if(type == "unstructured") + { + SLIC_ERROR("ElviraMIRAlgorithm cannot be applied to unstructured topologies."); + return; + } + + if(options.has_path("zones")) + { + const conduit::Node &zones = options.fetch_existing("zones"); + const auto nzones = zones.dtype().number_of_elements(); + detail::IndexNodeToArrayView(options["zones"], [&](auto zonesView) + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType i) { + // Do something with zonesView[i]. + + // Get number of materials for zone. + // for each material, make a plane eq for its interface. + + // for each material, intersect zone with the plane. Make PH fragment and volume, add fragment to new_topo + + }); + }); + } + else + { + foreach_coordset_type // rectilinear, structured, uniform - the problem becomse passing ArrayViews to the lambda. Could we pass a StackArray of ArrayViews? + { + // Foreach zone + for_all_zones(topo, AXOM_LAMBDA(conduit::index_t zoneIndex, const conduit::index_t *ids, conduit::index_t nids) + { + // on-device + + // might want to pass in the ijk coordinate to deal with rectilinear coordsets. Or perhaps it is best to pass in an array of the coordinate values that we pull out as part of the zone iteration. + + }); + }); + } +} + + +} // namespace quest +} // namespace axom diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp new file mode 100644 index 0000000000..d3d744f25c --- /dev/null +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -0,0 +1,618 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + +#ifndef AXOM_MIR_ALGORITHM_HPP_ +#define AXOM_MIR_ALGORITHM_HPP_ + +#include "axom/config.hpp" +#include "axom/core.hpp" +#include "axom/core/ArrayView.hpp" + +#include + +#include +#include + +namespace axom +{ + +namespace quest +{ + +/** + \brief Base class for Material Interface Reconstruction (MIR) algorithms. + */ +class MIRAlgorithm +{ +public: + MIRAlgorithm() = default; + virtual ~MIRAlgorithm() = default; + + /** + \brief Perform material interface reconstruction on the meshes supplied in the + root node. Root can either be a mesh domain or a node that contains multiple + domains. + + \param[in] root The root node that contains either a mesh or list of mesh + domains that contain a topology and matset to be used for MIR. + \param[in] options A node that contains options that help govern MIR execution. + +options: + topology: main + new_topology: mirtopo + new_coordset: mircoords + fields: + - temperature + - pressure + zones: [0,1,6,9] + mapping: 0 + + The "topology" option specifies which topology we'll reconstruct. It must have an associated matset. + "new_topology" is the name of the topology that will be created in the output node. + "new_coordset" is the name of the new coordset that will be created in the output node. If it is not provided then the name of the topology's coordset will be used. + "fields" is the name of the fields to map to the new topology. If fields is specified but empty, no fields will be mapped. If fields is not present then all fields will be mapped. + "zones" is a list of zone indices from the topology that need to be reconstructed. If not present then all zones will be considered. + "mapping" indicates whether we should include an original_element_numbers field on the new topology to indicate where each new zone came from in the original topology. + + \param[out] output A node that will contain the new entities. + + */ + virtual void execute(const conduit::Node &root, + const conduit::Node &options, + conduit::Node &output); +protected: + /** + \brief Perform material interface reconstruction on a single domain. Derived classes + must implement this method and any device-specific coding gets handled under it. + + \param[in] topo The Conduit node containing the topology that will be used for MIR. + \param[in] coordset The Conduit node containing the topology's coordset. + \param[in] options The Conduit node containing the options that help govern MIR execution. + + \param[out] new_topo A Conduit node that will contain the new topology. + \param[out] new_coordset A Conduit node that will contain the new coordset. + + */ + virtual void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) = 0; + + // Utility methods for derived types. + void copyState(const conduit::Node &mesh, conduit::Node &destMesh) const; + std::string topologyName(const conduit::Node &mesh, const conduit::Node &options) const; + std::string newTopologyName(const conduit::Node &mesh, const conduit::Node &options) const; + std::string newCoordsetName(const conduit::Node &mesh, const conduit::Node &options) const; + std::vector fieldNames(const conduit::Node &mesh, const conduit::Node &options) const; + + const conduit::Node &topology(const conduit::Node &input, const conduit::Node &options) const; + const conduit::Node &matset(const conduit::Node &input, const conduit::Node &options) const; + + + // TODO: method for mapping element field to new topo + // TODO: method for mapping vertex field to new topo +}; + +/** + I was just thinking why not template the class on ExecSpace. That would be nice since it would only instantiate the one(s) we want. + However, that would not work with inheritance. + */ + +// Goal: We need to take a mesh, matset, field and new mesh+originalElements field and + +namespace detail +{ + +/** + \brief This function wraps the Conduit node data in an axom::ArrayView and passes + that view to a lambda. The lambda can be generic so we can instantiate + various view types to handle the types of data we see in Conduit. This + function deals with indices which are most likely to be int32/int64 only. + + \param[in] node1 The Conduit node that we expect to contain index arrays. + \param[in] func A lamda that can operate on the supplied view. + */ +template +void IndexNodeToArrayView(conduit::Node &node1, FuncType &&func) +{ + const conduit::index_t size = node1.dtype().number_of_elements(); + if(node1.dtype().is_int32()) + { + axom::ArrayView view(node1.as_int32_ptr(), size); + func(view); + } + else if(node1.dtype().is_int64()) + { + axom::ArrayView view(node1.as_int64_ptr(), size); + func(view); + } +} + +/// const version of the function above. +template +void IndexNodeToArrayView(const conduit::Node &node1, FuncType &&func) +{ + const conduit::index_t size = node1.dtype().number_of_elements(); + if(node1.dtype().is_int32()) + { + axom::ArrayView view(const_cast(node1.as_int32_ptr()), size); + func(view); + } + else if(node1.dtype().is_int64()) + { + axom::ArrayView view(const_cast(node1.as_int64_ptr()), size); + func(view); + } +} + +/// Convert 2 Conduit nodes to views and pass them to a lambda +template +void IndexNodeToArrayView(conduit::Node &node1, conduit::Node &node2, FuncType &&func) +{ + IndexNodeToArrayView(node1, [&](auto node1_view) + { + IndexNodeToArrayView(node2, [&](auto node2_view) + { + func(node1_view, node2_view); + }); + }); +} + +template +void IndexNodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, FuncType &&func) +{ + IndexNodeToArrayView(node1, [&](auto node1_view) + { + IndexNodeToArrayView(node2, [&](auto node2_view) + { + func(node1_view, node2_view); + }); + }); +} + +/// Convert 3 Conduit nodes to views and pass them to a lambda +template +void IndexNodeToArrayView(conduit::Node &node1, conduit::Node &node2, conduit::Node &node3, FuncType &&func) +{ + IndexNodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) + { + IndexNodeToArrayView(node3, [&](auto node3_view) + { + func(node1_view, node2_view, node3_view); + }); + }); +} + +template +void IndexNodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, const conduit::Node &node3, FuncType &&func) +{ + IndexNodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) + { + IndexNodeToArrayView(node3, [&](auto node3_view) + { + func(node1_view, node2_view, node3_view); + }); + }); +} + +//------------------------------------------------------------------------------ + +/** + \brief This function wraps the Conduit node data in an axom::ArrayView and passes + that view to a lambda. The lambda can be generic so we can instantiate + various view types to handle the types of data we see in Conduit. Handle + the supported Conduit field types. + + \param[in] node1 The Conduit node that we expect to contain field values. + \param[in] func A lamda that can operate on the supplied view. + */ +template +void NodeToArrayView(conduit::Node &node1, FuncType &&func) +{ + const conduit::index_t size = node1.dtype().number_of_elements(); + + // TODO: If the node data are not contiguous, we can use some other ArrayView + // constructors to supply stride. + + if(node1.dtype().is_int8()) + { + axom::ArrayView view(node1.as_int8_ptr(), size); + func(view); + } + else if(node1.dtype().is_int16()) + { + axom::ArrayView view(node1.as_int16_ptr(), size); + func(view); + } + if(node1.dtype().is_int32()) + { + axom::ArrayView view(node1.as_int32_ptr(), size); + func(view); + } + else if(node1.dtype().is_int64()) + { + axom::ArrayView view(node1.as_int64_ptr(), size); + func(view); + } + else if(node1.dtype().is_uint8()) + { + axom::ArrayView view(node1.as_uint8_ptr(), size); + func(view); + } + else if(node1.dtype().is_uint16()) + { + axom::ArrayView view(node1.as_uint16_ptr(), size); + func(view); + } + if(node1.dtype().is_uint32()) + { + axom::ArrayView view(node1.as_uint32_ptr(), size); + func(view); + } + else if(node1.dtype().is_uint64()) + { + axom::ArrayView view(node1.as_uint64_ptr(), size); + func(view); + } + else if(node1.dtype().is_float32()) + { + axom::ArrayView view(node1.as_float_ptr(), size); + func(view); + } + else if(node1.dtype().is_float64()) + { + axom::ArrayView view(node1.as_double_ptr(), size); + func(view); + } + else + { + // TODO: Axom error, exception. + } +} + +template +void NodeToArrayView(const conduit::Node &node1, FuncType &&func) +{ + const conduit::index_t size = node1.dtype().number_of_elements(); + if(node1.dtype().is_int8()) + { + axom::ArrayView view(const_cast(node1.as_int8_ptr()), size); + func(view); + } + else if(node1.dtype().is_int16()) + { + axom::ArrayView view(const_cast(node1.as_int16_ptr()), size); + func(view); + } + if(node1.dtype().is_int32()) + { + axom::ArrayView view(const_cast(node1.as_int32_ptr()), size); + func(view); + } + else if(node1.dtype().is_int64()) + { + axom::ArrayView view(const_cast(node1.as_int64_ptr()), size); + func(view); + } + else if(node1.dtype().is_uint8()) + { + axom::ArrayView view(const_cast(node1.as_uint8_ptr()), size); + func(view); + } + else if(node1.dtype().is_uint16()) + { + axom::ArrayView view(const_cast(node1.as_uint16_ptr()), size); + func(view); + } + if(node1.dtype().is_uint32()) + { + axom::ArrayView view(const_cast(node1.as_uint32_ptr()), size); + func(view); + } + else if(node1.dtype().is_uint64()) + { + axom::ArrayView view(const_cast(node1.as_uint64_ptr()), size); + func(view); + } + else if(node1.dtype().is_float32()) + { + axom::ArrayView view(const_cast(node1.as_float_ptr()), size); + func(view); + } + else if(node1.dtype().is_float64()) + { + axom::ArrayView view(const_cast(node1.as_double_ptr()), size); + func(view); + } + else + { + // TODO: Axom error, exception. + } +} +template +void NodeToArrayView(conduit::Node &node1, conduit::Node &node2, FuncType &&func) +{ + NodeToArrayView(node1, [&](auto node1_view) + { + NodeToArrayView(node2, [&](auto node2_view) + { + func(node1_view, node2_view); + }); + }); +} + +template +void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, FuncType &&func) +{ + NodeToArrayView(node1, [&](auto node1_view) + { + NodeToArrayView(node2, [&](auto node2_view) + { + func(node1_view, node2_view); + }); + }); +} + +template +void NodeToArrayView(conduit::Node &node1, conduit::Node &node2, conduit::Node &node3, FuncType &&func) +{ + NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) + { + NodeToArrayView(node3, [&](auto node3_view) + { + func(node1_view, node2_view, node3_view); + }); + }); +} + +template +void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, const conduit::Node &node3, FuncType &&func) +{ + NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) + { + NodeToArrayView(node3, [&](auto node3_view) + { + func(node1_view, node2_view, node3_view); + }); + }); +} + +template +void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, conduit::Node &node3, FuncType &&func) +{ + NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) + { + NodeToArrayView(node3, [&](auto node3_view) + { + func(node1_view, node2_view, node3_view); + }); + }); +} + + +/** + \brief This method slices a Conduit field using a node that contains index value + and stores the data in another node. The data in the nodes are assumed to + be in the memory space suitable for the ExecSpace. + + \tparam ExecSpace The execution space. + + \param[in] field The Blueprint field to be sliced. + \param[in] indices A Conduit node that contains an array of index values into the field. + \param[out] output A node that contains the new sliced field. + + */ +template +void +sliceField(const conduit::Node &field, const conduit::Node &indices, conduit::Node &output) +{ + // Start making an output field. + output["topology"] = field["topology"].as_string(); + output["association"] = "element"; + conduit::Node &output_values = output["values"]; + + const conduit::Node &field_values = field["values"]; + const auto n_indices = static_cast(indices.dtype().number_of_elements()); + int execSpaceAllocatorID = axom::execution_space::allocatorID(); + + // Slice the field using the indices and store values in the output field. + if(field_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) + { + const conduit::Node &componentNode = field_values[i]; + const std::string componentName(componentNode.name()); + + // Allocate memory for the output view using the allocator for the ExecSpace. + conduit::Node &destNode = output_values[componentName]; + destNode.set_allocator(execSpaceAllocatorID); + destNode.set(conduit::DataType(componentNode.dtype().id(), n_indices)); + + IndexNodeToArrayView(indices, [&](auto indicesView) + { + NodeToArrayView(componentNode, destNode, [&](auto fieldValuesView, auto destView) + { + axom::for_all( + n_indices, + AXOM_LAMBDA(axom::IndexType i) { + destView[i] = fieldValuesView[indicesView[i]]; + }); + }); + }); + } + } + else + { + // Allocate memory for the output view using the allocator for the ExecSpace. + output_values.set_allocator(execSpaceAllocatorID); + output_values.set(conduit::DataType(field_values.dtype().id(), n_indices)); + + IndexNodeToArrayView(indices, [&](auto indicesView) + { + NodeToArrayView(field_values, output_values, [&](auto fieldValuesView, auto destView) + { + axom::for_all( + n_indices, + AXOM_LAMBDA(axom::IndexType i) { + destView[i] = fieldValuesView[indicesView[i]]; + }); + }); + }); + } +} + +template +void +blendField(const conduit::Node &field, const conduit::Node &indices, const conduit::Node &weights, const conduit::Node &sizes, const conduit::Node &offsets, conduit::Node &output) +{ + // Start making an output field. + output["topology"] = field["topology"].as_string(); + output["association"] = "element"; + conduit::Node &output_values = output["values"]; + + const conduit::Node &field_values = field["values"]; + const auto n_sizes = static_cast(sizes.dtype().number_of_elements()); + int execSpaceAllocatorID = axom::execution_space::allocatorID(); + + // Slice the field using the indices and store values in the output field. + if(field_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) + { + const conduit::Node &componentNode = field_values[i]; + const std::string componentName(componentNode.name()); + + // Allocate memory for the output view using the allocator for the ExecSpace. + conduit::Node &destNode = output_values[componentName]; + destNode.set_allocator(execSpaceAllocatorID); + destNode.set(conduit::DataType(componentNode.dtype().id(), n_sizes)); + + IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) + { + NodeToArrayView(componentNode, weights, destNode, [&](auto fieldValuesView, auto weightsView, auto destView) + { + axom::for_all( + n_sizes, + AXOM_LAMBDA(axom::IndexType i) { + const auto offset = offsetsView[i]; + const auto size = sizesView[i]; + destView[i] = 0; + for(int j = 0; j < size; j++) + { + const auto idx = indicesView[offset + j]; + destView[i] += fieldValuesView[idx] * weightsView[idx]; + } + }); + }); + }); + } + } + else + { + // Allocate memory for the output view using the allocator for the ExecSpace. + output_values.set_allocator(execSpaceAllocatorID); + output_values.set(conduit::DataType(field_values.dtype().id(), n_sizes)); + + IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) + { + NodeToArrayView(field_values, weights, output_values, [&](auto fieldValuesView, auto weightsView, auto destView) + { + axom::for_all( + n_sizes, + AXOM_LAMBDA(axom::IndexType i) { + const auto offset = offsetsView[i]; + const auto size = sizesView[i]; + destView[i] = 0; + for(int j = 0; j < size; j++) + { + const auto idx = indicesView[offset + j]; + destView[i] += fieldValuesView[idx] * weightsView[idx]; + } + }); + }); + }); + } +} + +#if 0 +void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_allocator) +{ + if(src.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < src.number_of_children() + { + conduit_move(src[i], dest[src[i].name()], allocator); + } + } + else + { + if(src.dtype().number_of_elements() > 1) + { + // Allocate the node's memory in the right place. + dest.reset(); + dest.set_allocator(allocator); + dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); + + // Copy the data to the destination node. Axom uses Umpire to manage that. + if(src.is_compact()) + axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); + else + { + // NOTE: this assumes that src is on the host. Why would we have strided data on device? + conduit::Node tmp; + src.compact_to(tmp); + axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); + } + } + else + { + // The node data fits in the node. It's on the host. + dest.set(src); + } + } +} +#endif + +} // end namespace detail + +class ElviraMIRAlgorithm : public MIRAlgorithm +{ +public: + using RuntimePolicy = axom::runtime_policy::Policy; + + ElviraMIRAlgorithm() = default; + virtual ~ElviraMIRAlgorithm() = default; + + void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } + +protected: + /// Implement the Elvira MIR algorithm on a single domain. + virtual void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) override; + + + /// Implement the Elvira MIR algorithm on a single domain for a given ExecSpace. + template + void executeImpl(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset); + + RuntimePolicy m_execPolicy{RuntimePolicy::seq}; +}; + +// class EquiZMIRAlgorithm : public MIRAlgorithml + + +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 50668aa2f6..225130e6b2 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -11,6 +11,17 @@ #include #include +/** + NOTES: I use the shapes for 2 things: + 1. As type traits to help the topology views traverse connectivity + 2. As a shape type that contains information for a given zone so we can do a few useful things with it. + + Q: I was going to ask whether a shape's getFace() should return a shape itself such as a tri/quad shape. + That won't work for zones like pyr, wdg that have faces with different shapes. + + Q: Is it worth templating the shape's index type? + */ + namespace axom { namespace mir @@ -18,6 +29,126 @@ namespace mir namespace views { +// TODO: PointTraits +// TODO: LineTraits + +/* + 3*-----------* 2 + | | + | | + | | + | | + | | + 0*-----------* 1 + + */ +template +struct TriTraits +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 3; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 3; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}}; +}; + +/* + 3*-----------* 2 + | | + | | + | | + | | + | | + 0*-----------* 1 + + */ +template +struct QuadTraits +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 4; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}}; +}; + +// TODO: PolygonTraits +/* + +n-1 *-... * 2 + | | + | | + 0 *-----* 1 + + */ +template +struct PolygonShape +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 5; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return true; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } + + /** + * \brief Construct a shape. + */ + AXOM_HOST_DEVICE PolygonShape(const axom::ArrayView &ids) : m_ids(ids) + { + assert(m_ids.size() == numberOfNodes()); + } + + /** + * \brief Get the ids that make up this shape. + * + * \return A view containing the ids that make up this shape. + */ + AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + + /** + * \brief Get the ids for the requested face. + * + * \param faceIndex The index of the desired face. + * + * \return An array view (wrapping m_faceIds) that contains the ids for the face. + */ + AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + { + return m_ids; + } +private: + axom::ArrayView m_ids; +}; + + /* 3 * @@ -35,38 +166,110 @@ namespace views */ template -struct TetShape +struct TetTraits { using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 6; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 3; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 6; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,2}, {2,1}, {1,0}, {1,3}, {3,0}, {2,3}}; +}; - AXOM_HOST_DEVICE TetShape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() - { - } +/* - AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } +3*-----------* 2 face 0: 3,2,1,0 + |\ /| face 1: 0,1,4 + | \ / | face 2: 1,2,4 + | \ / | face 3: 2,3,4 + | \ / | face 4: 3,0,4 + | \ / | + | * 4 | edge 0: 0,1 + | / \ | edge 1: 1,2 + | / \ | edge 2: 2,3 + | / \ | edge 3: 3,0 + | / \ | edge 4: 0,4 + |/ \| edge 5: 1,4 +0*-----------* 1 edge 6: 2,4 + edge 7: 3,4 - AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const - { - m_faceIds[0] = m_ids[faces[faceIndex][0]]; - m_faceIds[1] = m_ids[faces[faceIndex][1]]; - m_faceIds[2] = m_ids[faces[faceIndex][2]]; - return axom::ArrayView(m_faceIds.m_data, 3); - } -private: - axom::ArrayView m_ids; - mutable axom::StackArray m_faceIds; + */ +template +struct PyramidTraits +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr int id() { return 1 << 7; } + AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 5; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) { return faceIndex == 0 ? 4 : 3; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 5; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 8; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static int faces[][4] = {{3,2,1,0}, {0,1,4,-1}, {1,2,4,-1}, {2,3,4,-1}, {3,0,4,-1}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}, {0,4}, {1,4}, {2,4}, {3,4}}; +}; + +/* + +3*---------* 5 face 0: 0,2,1 + |\ /| face 1: 3,4,5 + | \ / | face 2: 0,1,4,3 + | \ / | face 3: 1,2,5,4 + | \ / | face 4: 2,0,3,5 + | *4 | + | | | edge 0: 0,1 +0*----|----* 2 edge 1: 1,2 + \ | / edge 2: 2,0 + \ | / edge 3: 3,4 + \ | / edge 4: 4,5 + \|/ edge 5: 5,3 + * 1 edge 6: 0,3 + edge 7: 1,4 + edge 8: 2,3 + + */ +template +struct WedgeTraits +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 8; } + + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 6; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) { return (faceIndex == 0 || faceIndex == 1) ? 3 : 4; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 5; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 9; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static int faces[][4] = {{0,2,1,-1}, {3,4,5,-1}, {0,1,4,3}, {1,2,5,4}, {2,0,3,5}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,3}}; }; /* @@ -86,14 +289,20 @@ struct TetShape */ template -struct HexShape +struct HexTraits { using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 9; } + + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 8; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 6; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 12; } @@ -102,27 +311,75 @@ struct HexShape AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = { {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,3}, {3,2}, {2,1}, {1,0}, {1,5}, {5,4}, {4,0}, {2,6}, {6,5}, {3,7}, {7,6}, {7,4}}; +}; - - AXOM_HOST_DEVICE HexShape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() +/** + * \brief This class extends the ShapeTraits with object state so it can represent a zone. + */ +template +struct Shape : public ShapeTraits +{ + /** + * \brief Construct a shape. + */ + AXOM_HOST_DEVICE Shape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() { + assert(m_ids.size() == numberOfNodes()); } + /** + * \brief Get the ids that make up this shape. + * + * \return A view containing the ids that make up this shape. + */ AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } - AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + /** + * \brief Get the ids for the requested face. + * + * \param faceIndex The index of the desired face. + * + * \return An array view (wrapping m_faceIds) that contains the ids for the face. + */ + AXOM_HOST_DEVICE std::enable_if> + getFace(int faceIndex) const + { + return m_ids; + } + + AXOM_HOST_DEVICE std::enable_if> + getFace(int faceIndex) const { - m_faceIds[0] = m_ids[faces[faceIndex][0]]; - m_faceIds[1] = m_ids[faces[faceIndex][1]]; - m_faceIds[2] = m_ids[faces[faceIndex][2]]; - m_faceIds[3] = m_ids[faces[faceIndex][3]]; - return axom::ArrayView(m_faceIds.m_data, 4); + const auto nnodes = numberOfNodesInFace(faceIndex); + for(IndexType i = 0; i < nnodes; i++) + m_faceIds[i] = m_ids[faces[faceIndex][i]]; + return axom::ArrayView(m_faceIds.m_data, nnodes); } private: axom::ArrayView m_ids; - mutable axom::StackArray m_faceIds; + mutable axom::StackArray m_faceIds; }; +// Make some concrete shape classes based on the shape traits. +template +using TriShape = Shape>; + +template +using QuadShape = Shape>; + +template +using TetShape = Shape>; + +template +using PyramidShape = Shape>; + +template +using WedgeShape = Shape>; + +template +using HexShape = Shape>; + + } // end namespace views } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 3524617aa1..ec975143d5 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -15,6 +15,8 @@ namespace mir namespace views { +// NOTE: we could subclass this one and make a strided structured view that lets one define zones where the zones do not span all of the nodes. + /** * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. * @@ -67,11 +69,27 @@ class StructuredTopologyView const auto nzones = numberOfZones(); axom::ArrayView connectivity(m_connectivity); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + if constexpr (NDIMS == 2) + { + // Q: Should we make a for_all() that iterates over multiple ranges? + // Q: Should the logical index be passed to the lambda? + + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + using ShapeType = QuadShape; + const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + func(zoneIndex, shape); + }); + } + if constexpr (NDIMS == 3) { - const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); - func(zoneIndex, shape); - }); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + using ShapeType = HexShape; + const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + func(zoneIndex, shape); + }); + } } private: diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index cdcb78cc6b..7deca002bc 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -55,7 +55,8 @@ class UnstructuredTopologyPolyhedralView { constexpr static IndexType MaximumNumberOfIds = 20 * 3; - AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return true; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return true; } + AXOM_HOST_DEVICE constexpr static bool id() { return 1 << 10; } AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj), m_zoneIndex(zi), m_ids() { diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index c9e832c882..4873131e1f 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -33,7 +33,20 @@ class UnstructuredTopologySingleShapeView * * \param conn The mesh connectivity. */ - UnstructuredTopologySingleShapeView(const axom::ArrayView &conn) : m_connectivity(conn) + UnstructuredTopologySingleShapeView(const axom::ArrayView &conn) : m_connectivity(conn), m_sizes(), m_offsets() + { + } + + /** + * \brief Constructor + * + * \param conn The mesh connectivity. + * \param sizes The number of nodes in each zone. + * \param offsets The offset to each zone in the connectivity. + */ + UnstructuredTopologySingleShapeView(const axom::ArrayView &conn, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) : m_connectivity(conn), m_sizes(sizes), m_offsets(offsets) { } @@ -44,7 +57,7 @@ class UnstructuredTopologySingleShapeView */ IndexType numberOfZones() const { - return m_connectivity.size() / ShapeType::numberOfNodes(); + return (m_sizes.size() != 0) ? m_sizes.size() : (m_connectivity.size() / ShapeType::numberOfNodes()); } /** @@ -61,15 +74,34 @@ class UnstructuredTopologySingleShapeView const auto nzones = numberOfZones(); axom::ArrayView connectivity(m_connectivity); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + if constexpr (ShapeType::is_variable_size()) + { + assert(m_sizes.size() != 0); + assert(m_offsets.size() != 0); + assert(m_offsets.size() == m_sizes.size()); + + axom::ArrayView sizes(m_sizes); + axom::ArrayView offsets(m_offsets); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + const ShapeType shape(axom::ArrayView(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex])); + func(zoneIndex, shape); + }); + } + else { - const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); - func(zoneIndex, shape); - }); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + func(zoneIndex, shape); + }); + } } private: axom::ArrayView m_connectivity; + axom::ArrayView m_sizes; + axom::ArrayView m_offsets; }; } // end namespace views diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp new file mode 100644 index 0000000000..e6973845bf --- /dev/null +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -0,0 +1,155 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_COORDSET_HPP_ +#define AXOM_MIR_DISPATCH_COORDSET_HPP_ + +#include "axom/mir/views/UnstructuredTopologySingleShapeView.hpp" +#include "axom/mir/views/UnstructuredTopologyPolyhedralView.hpp" +#include "axom/mir/views/dispatch_coordset.hpp" +#include "axom/mir/views/NodeArrayView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +constexpr int AnyShape = -1; + +/** + * \brief This function dispatches a Conduit topology to the right view type + * and passes that view to the supplied function/lambda. + * + * \tparam ShapeTypes Allows us to limit which shape types get compiled in. + * \tparam FuncType The function/lambda type that will be invoked on the view. + * + * \ + */ +template +void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) +{ + const std::string type = topo["type"].as_string(); + if(type == "unstructured") + { + const conduit::Node *coordset_ptr = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node &coordset = *coordset_ptr; + dispatch_coordset(coordset, [&](auto coordsetView) + { + const std::string shape = topo["elements/shape"].as_string(); + bool eligible = true; + + // Conditionally add polyhedron support. + if constexpr (ShapeTypes & UnstructuredTopologyPolyhedralView::PolyhedralShape::id()) + { + if(shape == "polyhedral") + { + IndexNode_to_ArrayView_same( + topo["subelements/connectivity"], topo["subelements/sizes"], topo["subelements/offsets"], + topo["elements/connectivity"], topo["elements/sizes"], topo["elements/offsets"], + [&](auto seConnView, auto seSizesView, auto seOffsetsView, auto connView, auto sizesView, auto offsetsView) + { + using IndexType = typename decltype(seConnView)::value_type; + UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); + func(shape, ugView, coordsetView); + }); + eligible = false; + } + } + + if constexpr (ShapeTypes & PolygonShape::id()) + { + if(eligible && shape == "polygon") + { + IndexNode_to_ArrayView_same(topo["elements/connectivity"], + topo["elements/sizes"], + topo["elements/offsets"], [&](auto connView, auto sizesView, auto offsetsView) + { + using IndexType = typename decltype(connView)::value_type; + UnstructuredTopologySingleShapeView > ugView(connView, sizesView, offsetsView); + func(shape, ugView, coordsetView); + }); + eligible = false; + } + } + + if constexpr (ShapeTypes & AnyShape) + { + if(eligible && shape == "mixed") + { + // TODO: handle mixed zone types. + eligible = false; + } + } + + IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) + { + using IndexType = typename decltype(connView)::value_type; + // TODO: points, lines + if constexpr (ShapeTypes & TriShape::id()) + { + if(eligible && shape == "tet") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + if constexpr (ShapeTypes & QuadShape::id()) + { + if(eligible && shape == "tet") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + if constexpr (ShapeTypes & TetShape::id()) + { + if(eligible && shape == "tet") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + if constexpr (ShapeTypes & PyramidShape::id()) + { + if(eligible && shape == "pyramid") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + if constexpr (ShapeTypes & WedgeShape::id()) + { + if(eligible && shape == "wedge") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + if constexpr (ShapeTypes & HexShape::id()) + { + if(eligible && shape == "hex") + { + UnstructuredTopologySingleShapeView > ugView(connView); + func(shape, ugView, coordsetView); + eligible = false; + } + } + }); + }); + } +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From a41d42dbcd1fa14acab5a47b2406a75e7601384a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 11 Jun 2024 18:04:40 -0700 Subject: [PATCH 051/290] cmake fixes, added clipping stuff --- src/axom/mir/CMakeLists.txt | 29 +- src/axom/mir/EquiZAlgorithm.cpp | 205 ++ src/axom/mir/EquiZAlgorithm.hpp | 56 + src/axom/mir/MIRAlgorithm.cpp | 213 +- src/axom/mir/MIRAlgorithm.hpp | 304 +- src/axom/mir/clipping/ClipCases.h | 168 + src/axom/mir/clipping/ClipCasesHex.cpp | 3505 ++++++++++++++++++++ src/axom/mir/clipping/ClipCasesPyr.cpp | 226 ++ src/axom/mir/clipping/ClipCasesQuad.cpp | 102 + src/axom/mir/clipping/ClipCasesTet.cpp | 94 + src/axom/mir/clipping/ClipCasesTri.cpp | 64 + src/axom/mir/clipping/ClipCasesWdg.cpp | 622 ++++ src/axom/mir/clipping/ClipTableManager.hpp | 262 ++ 13 files changed, 5338 insertions(+), 512 deletions(-) create mode 100644 src/axom/mir/EquiZAlgorithm.cpp create mode 100644 src/axom/mir/EquiZAlgorithm.hpp create mode 100644 src/axom/mir/clipping/ClipCases.h create mode 100644 src/axom/mir/clipping/ClipCasesHex.cpp create mode 100644 src/axom/mir/clipping/ClipCasesPyr.cpp create mode 100644 src/axom/mir/clipping/ClipCasesQuad.cpp create mode 100644 src/axom/mir/clipping/ClipCasesTet.cpp create mode 100644 src/axom/mir/clipping/ClipCasesTri.cpp create mode 100644 src/axom/mir/clipping/ClipCasesWdg.cpp create mode 100644 src/axom/mir/clipping/ClipTableManager.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index bedba70057..51caa54521 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -10,7 +10,8 @@ # Check necessary dependencies #------------------------------------------------------------------------------ axom_component_requires(NAME MIR - COMPONENTS SLIC SLAM PRIMAL) + COMPONENTS SLIC SLAM PRIMAL + TPLS Conduit) #------------------------------------------------------------------------------ # Specify all headers/sources @@ -25,6 +26,21 @@ set(mir_headers CellClipper.hpp MIRUtilities.hpp CellGenerator.hpp + + MIRAlgorithm.hpp + EquiZAlgorithm.hpp + views/dispatch_coordset.hpp + views/dispatch_unstructured_topology.hpp + views/ExplitCoordsetView.hpp + views/NodeArrayView.hpp + views/RectilinearCoordsetView.hpp + views/Shapes.hpp + views/StructuredTopologyView.hpp + views/UniformCoordsetView.hpp + views/UnstructuredTopologyPolyhedralView.hpp + views/UnstructuredTopologySingleShapeView.hpp + clipping/ClipCases.h + clipping/ClipTableManager.hpp ) set(mir_sources @@ -35,12 +51,21 @@ set(mir_sources MeshTester.cpp CellClipper.cpp CellGenerator.cpp + + MIRAlgorithm.cpp + EquiZAlgorithm.cpp + clipping/ClipCasesHex.cpp + clipping/ClipCasesPyr.cpp + clipping/ClipCasesQuad.cpp + clipping/ClipCasesTet.cpp + clipping/ClipCasesTri.cpp + clipping/ClipCasesWdg.cpp ) #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic slam primal) +set(mir_depends_on core slic slam primal conduit::conduit) blt_add_library( NAME mir diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp new file mode 100644 index 0000000000..7c688688fa --- /dev/null +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -0,0 +1,205 @@ +#include "axom/mir/EquiZAlgorithm.hpp" + + +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on + +namespace axom +{ +namespace mir +{ + +#if 0 +template +void for_all_zones(const conduit::Node &topo, FuncType &&func) +{ + conduit::index_t dims[3] = {1, 1, 1}; + conduit::blueprint::mesh::utils::topology::logical_dims(topo, dims, 3); + const conduit::index_t dimension = conduit::blueprint::mesh::topology::dims(topo); + const conduit::index_t nzones = conduit::blueprint::mesh::topology::length(topo); + + if(dimension == 1) + { + // Line elements + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + conduit::index_t ids[2]; + ids[0] = zoneIndex; + ids[1] = zoneIndex + 1; + func(zoneIndex, ids, 2); + }); + } + else if(dimension == 2) + { + // Quad elements + const conduit::index_t nx = dims[0] + 1; + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // zoneIndex to i,j + const conduit::index_t i = zoneIndex % dims[0]; + const conduit::index_t j = zoneIndex / dims[0]; + // Make node ids + const conduit::index_t jnx = j * nx; + const conduit::index_t j1nx = jnx + nx; + conduit::index_t ids[4]; + ids[0] = jnx + i; + ids[1] = jnx + i + 1; + ids[2] = j1nx + i + 1; + ids[3] = j1nx + i; + func(zoneIndex, ids, 4); + }); + } + else if(dimension == 3) + { + // Hex elements + const conduit::index_t nx = dims[0] + 1; + const conduit::index_t ny = dims[1] + 1; + const conduit::index_t nz = dims[2] + 1; + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // zoneIndex to i,j,k + const conduit::index_t i = zoneIndex % dims[0]; + const conduit::index_t j = (zoneIndex % (dims[0] * dims[1])) / dims[0]; + const conduit::index_t k = zoneIndex / (dims[0] * dims[1]); + + // Make node ids + const conduit::index_t knxny = k * nx * ny; + const conduit::index_t k1nxny = (k + 1) * nx * ny; + const conduit::index_t jnx = j * nx; + const conduit::index_t j1nx = jnx + nx; + conduit::index_t ids[8]; + ids[0] = knxny + jnx + i; + ids[1] = knxny + jnx + i + 1; + ids[2] = knxny + j1nx + i + 1; + ids[3] = knxny + j1nx + i; + ids[4] = k1nxny + jnx + i; + ids[5] = k1nxny + jnx + i + 1; + ids[6] = k1nxny + j1nx + i + 1; + ids[7] = k1nxny + j1nx + i; + + func(zoneIndex, ids, 4); + }); + } + else + { +// CONDUIT_ERROR("Unsupported dimension given to traverse_structured " +// << dimension << "."); + } +} +#endif + +void EquiZAlgorithm::execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) +{ +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + switch(m_execPolicy) + { + #if defined(AXOM_USE_OPENMP) + case RuntimePolicy::omp: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + #if defined(AXOM_USE_CUDA) + case RuntimePolicy::cuda: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + #if defined(AXOM_USE_HIP) + case RuntimePolicy::hip: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + #endif + default: + // Falls through + case RuntimePolicy::seq: + executeImpl(topo, coordset, options, new_topo, new_coordset); + break; + } +#endif +} + +template +void EquiZAlgorithm::executeImpl(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) +{ + if(options.has_path("zones")) + { + const conduit::Node &zones = options.fetch_existing("zones"); + const auto nzones = zones.dtype().number_of_elements(); +#if 0 + detail::IndexNodeToArrayView(options["zones"], [&](auto zonesView) + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType i) { + // Do something with zonesView[i]. + + // Get number of materials for zone. + // for each material, make a plane eq for its interface. + + // for each material, intersect zone with the plane. Make PH fragment and volume, add fragment to new_topo + + }); + }); +#endif + } + else + { +#if 0 + foreach_coordset_type // rectilinear, structured, uniform - the problem becomse passing ArrayViews to the lambda. Could we pass a StackArray of ArrayViews? + { + // Foreach zone + for_all_zones(topo, AXOM_LAMBDA(conduit::index_t zoneIndex, const conduit::index_t *ids, conduit::index_t nids) + { + // on-device + + // might want to pass in the ijk coordinate to deal with rectilinear coordsets. Or perhaps it is best to pass in an array of the coordinate values that we pull out as part of the zone iteration. + + }); + }); +#endif + } +} + +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp new file mode 100644 index 0000000000..058f285fb6 --- /dev/null +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -0,0 +1,56 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + + +#ifndef AXOM_MIR_EQUIZ_ALGORITHM_HPP_ +#define AXOM_MIR_EQUIZ_ALGORITHM_HPP_ + +#include "axom/config.hpp" +#include "axom/core.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/mir/MIRAlgorithm.hpp" + +#include + +namespace axom +{ + +namespace mir +{ + +class EquiZAlgorithm : public MIRAlgorithm +{ +public: + using RuntimePolicy = axom::runtime_policy::Policy; + + EquiZAlgorithm() = default; + virtual ~EquiZAlgorithm() = default; + + void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } + +protected: + /// Implement the EquiZ algorithm on a single domain. + virtual void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) override; + + + /// Implement the EquiZ algorithm on a single domain for a given ExecSpace. + template + void executeImpl(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset); + + RuntimePolicy m_execPolicy{RuntimePolicy::seq}; +}; + +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index b521527bc3..1e9c51f2d2 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -3,48 +3,17 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/quest/MIRAlgorithm.hpp" +#include "axom/mir/MIRAlgorithm.hpp" #include "axom/core/ArrayView.hpp" #include "axom/slic.hpp" #include #include -// RAJA -#if defined(AXOM_USE_RAJA) - #include "RAJA/RAJA.hpp" -#endif - -// clang-format off -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - using seq_exec = axom::SEQ_EXEC; - - #if defined(AXOM_USE_OPENMP) - using omp_exec = axom::OMP_EXEC; - #else - using omp_exec = seq_exec; - #endif - - #if defined(AXOM_USE_CUDA) - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - #else - using cuda_exec = seq_exec; - #endif - - #if defined(AXOM_USE_HIP) - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - #else - using hip_exec = seq_exec; - #endif -#endif -// clang-format on - namespace axom { -namespace quest +namespace mir { void MIRAlgorithm::execute(const conduit::Node &root, @@ -191,181 +160,5 @@ MIRAlgorithm::fieldNames(const conduit::Node &mesh, const conduit::Node &options return names; } -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -namespace elvira -{ -} // end namespace elvira - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -void ElviraMIRAlgorithm::execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset) -{ -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - switch(m_execPolicy) - { - #if defined(AXOM_USE_OPENMP) - case RuntimePolicy::omp: - executeImpl(topo, coordset, options, new_topo, new_coordset); - break; - #endif - #if defined(AXOM_USE_CUDA) - case RuntimePolicy::cuda: - executeImpl(topo, coordset, options, new_topo, new_coordset); - break; - #endif - #if defined(AXOM_USE_HIP) - case RuntimePolicy::hip: - executeImpl(topo, coordset, options, new_topo, new_coordset); - break; - #endif - default: - // Falls through - case RuntimePolicy::seq: - executeImpl(topo, coordset, options, new_topo, new_coordset); - break; - } -#endif -} - -template -void for_all_zones(const conduit::Node &topo, FuncType &&func) -{ - conduit::index_t dims[3] = {1, 1, 1}; - conduit::blueprint::mesh::utils::topology::logical_dims(topo, dims, 3); - const conduit::index_t dimension = conduit::blueprint::mesh::topology::dims(topo); - const conduit::index_t nzones = conduit::blueprint::mesh::topology::length(topo); - - if(dimension == 1) - { - // Line elements - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - conduit::index_t ids[2]; - ids[0] = zoneIndex; - ids[1] = zoneIndex + 1; - func(zoneIndex, ids, 2); - }); - } - else if(dimension == 2) - { - // Quad elements - const conduit::index_t nx = dims[0] + 1; - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // zoneIndex to i,j - const conduit::index_t i = zoneIndex % dims[0]; - const conduit::index_t j = zoneIndex / dims[0]; - // Make node ids - const conduit::index_t jnx = j * nx; - const conduit::index_t j1nx = jnx + nx; - conduit::index_t ids[4]; - ids[0] = jnx + i; - ids[1] = jnx + i + 1; - ids[2] = j1nx + i + 1; - ids[3] = j1nx + i; - func(zoneIndex, ids, 4); - }); - } - else if(dimension == 3) - { - // Hex elements - const conduit::index_t nx = dims[0] + 1; - const conduit::index_t ny = dims[1] + 1; - const conduit::index_t nz = dims[2] + 1; - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // zoneIndex to i,j,k - const conduit::index_t i = zoneIndex % dims[0]; - const conduit::index_t j = (zoneIndex % (dims[0] * dims[1])) / dims[0]; - const conduit::index_t k = zoneIndex / (dims[0] * dims[1]); - - // Make node ids - const conduit::index_t knxny = k * nx * ny; - const conduit::index_t k1nxny = (k + 1) * nx * ny; - const conduit::index_t jnx = j * nx; - const conduit::index_t j1nx = jnx + nx; - conduit::index_t ids[8]; - ids[0] = knxny + jnx + i; - ids[1] = knxny + jnx + i + 1; - ids[2] = knxny + j1nx + i + 1; - ids[3] = knxny + j1nx + i; - ids[4] = k1nxny + jnx + i; - ids[5] = k1nxny + jnx + i + 1; - ids[6] = k1nxny + j1nx + i + 1; - ids[7] = k1nxny + j1nx + i; - - func(zoneIndex, ids, 4); - }); - } - else - { -// CONDUIT_ERROR("Unsupported dimension given to traverse_structured " -// << dimension << "."); - } -} - -template -void ElviraMIRAlgorithm::executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset) -{ - // I'm not sure some parts of Elvira work for unstructured topologies. (stencils) - const auto type = topo["type"].as_string(); - if(type == "unstructured") - { - SLIC_ERROR("ElviraMIRAlgorithm cannot be applied to unstructured topologies."); - return; - } - - if(options.has_path("zones")) - { - const conduit::Node &zones = options.fetch_existing("zones"); - const auto nzones = zones.dtype().number_of_elements(); - detail::IndexNodeToArrayView(options["zones"], [&](auto zonesView) - { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType i) { - // Do something with zonesView[i]. - - // Get number of materials for zone. - // for each material, make a plane eq for its interface. - - // for each material, intersect zone with the plane. Make PH fragment and volume, add fragment to new_topo - - }); - }); - } - else - { - foreach_coordset_type // rectilinear, structured, uniform - the problem becomse passing ArrayViews to the lambda. Could we pass a StackArray of ArrayViews? - { - // Foreach zone - for_all_zones(topo, AXOM_LAMBDA(conduit::index_t zoneIndex, const conduit::index_t *ids, conduit::index_t nids) - { - // on-device - - // might want to pass in the ijk coordinate to deal with rectilinear coordsets. Or perhaps it is best to pass in an array of the coordinate values that we pull out as part of the zone iteration. - - }); - }); - } -} - - -} // namespace quest +} // namespace mir } // namespace axom diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index d3d744f25c..7bf3d0923d 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -11,7 +11,7 @@ #include "axom/core.hpp" #include "axom/core/ArrayView.hpp" -#include +#include #include #include @@ -19,7 +19,7 @@ namespace axom { -namespace quest +namespace mir { /** @@ -97,301 +97,8 @@ class MIRAlgorithm // TODO: method for mapping vertex field to new topo }; -/** - I was just thinking why not template the class on ExecSpace. That would be nice since it would only instantiate the one(s) we want. - However, that would not work with inheritance. - */ - -// Goal: We need to take a mesh, matset, field and new mesh+originalElements field and - -namespace detail -{ - -/** - \brief This function wraps the Conduit node data in an axom::ArrayView and passes - that view to a lambda. The lambda can be generic so we can instantiate - various view types to handle the types of data we see in Conduit. This - function deals with indices which are most likely to be int32/int64 only. - - \param[in] node1 The Conduit node that we expect to contain index arrays. - \param[in] func A lamda that can operate on the supplied view. - */ -template -void IndexNodeToArrayView(conduit::Node &node1, FuncType &&func) -{ - const conduit::index_t size = node1.dtype().number_of_elements(); - if(node1.dtype().is_int32()) - { - axom::ArrayView view(node1.as_int32_ptr(), size); - func(view); - } - else if(node1.dtype().is_int64()) - { - axom::ArrayView view(node1.as_int64_ptr(), size); - func(view); - } -} - -/// const version of the function above. -template -void IndexNodeToArrayView(const conduit::Node &node1, FuncType &&func) -{ - const conduit::index_t size = node1.dtype().number_of_elements(); - if(node1.dtype().is_int32()) - { - axom::ArrayView view(const_cast(node1.as_int32_ptr()), size); - func(view); - } - else if(node1.dtype().is_int64()) - { - axom::ArrayView view(const_cast(node1.as_int64_ptr()), size); - func(view); - } -} - -/// Convert 2 Conduit nodes to views and pass them to a lambda -template -void IndexNodeToArrayView(conduit::Node &node1, conduit::Node &node2, FuncType &&func) -{ - IndexNodeToArrayView(node1, [&](auto node1_view) - { - IndexNodeToArrayView(node2, [&](auto node2_view) - { - func(node1_view, node2_view); - }); - }); -} - -template -void IndexNodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, FuncType &&func) -{ - IndexNodeToArrayView(node1, [&](auto node1_view) - { - IndexNodeToArrayView(node2, [&](auto node2_view) - { - func(node1_view, node2_view); - }); - }); -} - -/// Convert 3 Conduit nodes to views and pass them to a lambda -template -void IndexNodeToArrayView(conduit::Node &node1, conduit::Node &node2, conduit::Node &node3, FuncType &&func) -{ - IndexNodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) - { - IndexNodeToArrayView(node3, [&](auto node3_view) - { - func(node1_view, node2_view, node3_view); - }); - }); -} - -template -void IndexNodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, const conduit::Node &node3, FuncType &&func) -{ - IndexNodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) - { - IndexNodeToArrayView(node3, [&](auto node3_view) - { - func(node1_view, node2_view, node3_view); - }); - }); -} - -//------------------------------------------------------------------------------ - -/** - \brief This function wraps the Conduit node data in an axom::ArrayView and passes - that view to a lambda. The lambda can be generic so we can instantiate - various view types to handle the types of data we see in Conduit. Handle - the supported Conduit field types. - - \param[in] node1 The Conduit node that we expect to contain field values. - \param[in] func A lamda that can operate on the supplied view. - */ -template -void NodeToArrayView(conduit::Node &node1, FuncType &&func) -{ - const conduit::index_t size = node1.dtype().number_of_elements(); - - // TODO: If the node data are not contiguous, we can use some other ArrayView - // constructors to supply stride. - - if(node1.dtype().is_int8()) - { - axom::ArrayView view(node1.as_int8_ptr(), size); - func(view); - } - else if(node1.dtype().is_int16()) - { - axom::ArrayView view(node1.as_int16_ptr(), size); - func(view); - } - if(node1.dtype().is_int32()) - { - axom::ArrayView view(node1.as_int32_ptr(), size); - func(view); - } - else if(node1.dtype().is_int64()) - { - axom::ArrayView view(node1.as_int64_ptr(), size); - func(view); - } - else if(node1.dtype().is_uint8()) - { - axom::ArrayView view(node1.as_uint8_ptr(), size); - func(view); - } - else if(node1.dtype().is_uint16()) - { - axom::ArrayView view(node1.as_uint16_ptr(), size); - func(view); - } - if(node1.dtype().is_uint32()) - { - axom::ArrayView view(node1.as_uint32_ptr(), size); - func(view); - } - else if(node1.dtype().is_uint64()) - { - axom::ArrayView view(node1.as_uint64_ptr(), size); - func(view); - } - else if(node1.dtype().is_float32()) - { - axom::ArrayView view(node1.as_float_ptr(), size); - func(view); - } - else if(node1.dtype().is_float64()) - { - axom::ArrayView view(node1.as_double_ptr(), size); - func(view); - } - else - { - // TODO: Axom error, exception. - } -} - -template -void NodeToArrayView(const conduit::Node &node1, FuncType &&func) -{ - const conduit::index_t size = node1.dtype().number_of_elements(); - if(node1.dtype().is_int8()) - { - axom::ArrayView view(const_cast(node1.as_int8_ptr()), size); - func(view); - } - else if(node1.dtype().is_int16()) - { - axom::ArrayView view(const_cast(node1.as_int16_ptr()), size); - func(view); - } - if(node1.dtype().is_int32()) - { - axom::ArrayView view(const_cast(node1.as_int32_ptr()), size); - func(view); - } - else if(node1.dtype().is_int64()) - { - axom::ArrayView view(const_cast(node1.as_int64_ptr()), size); - func(view); - } - else if(node1.dtype().is_uint8()) - { - axom::ArrayView view(const_cast(node1.as_uint8_ptr()), size); - func(view); - } - else if(node1.dtype().is_uint16()) - { - axom::ArrayView view(const_cast(node1.as_uint16_ptr()), size); - func(view); - } - if(node1.dtype().is_uint32()) - { - axom::ArrayView view(const_cast(node1.as_uint32_ptr()), size); - func(view); - } - else if(node1.dtype().is_uint64()) - { - axom::ArrayView view(const_cast(node1.as_uint64_ptr()), size); - func(view); - } - else if(node1.dtype().is_float32()) - { - axom::ArrayView view(const_cast(node1.as_float_ptr()), size); - func(view); - } - else if(node1.dtype().is_float64()) - { - axom::ArrayView view(const_cast(node1.as_double_ptr()), size); - func(view); - } - else - { - // TODO: Axom error, exception. - } -} -template -void NodeToArrayView(conduit::Node &node1, conduit::Node &node2, FuncType &&func) -{ - NodeToArrayView(node1, [&](auto node1_view) - { - NodeToArrayView(node2, [&](auto node2_view) - { - func(node1_view, node2_view); - }); - }); -} - -template -void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, FuncType &&func) -{ - NodeToArrayView(node1, [&](auto node1_view) - { - NodeToArrayView(node2, [&](auto node2_view) - { - func(node1_view, node2_view); - }); - }); -} - -template -void NodeToArrayView(conduit::Node &node1, conduit::Node &node2, conduit::Node &node3, FuncType &&func) -{ - NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) - { - NodeToArrayView(node3, [&](auto node3_view) - { - func(node1_view, node2_view, node3_view); - }); - }); -} - -template -void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, const conduit::Node &node3, FuncType &&func) -{ - NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) - { - NodeToArrayView(node3, [&](auto node3_view) - { - func(node1_view, node2_view, node3_view); - }); - }); -} - -template -void NodeToArrayView(const conduit::Node &node1, const conduit::Node &node2, conduit::Node &node3, FuncType &&func) -{ - NodeToArrayView(node1, node2, [&](auto node1_view, auto node2_view) - { - NodeToArrayView(node3, [&](auto node3_view) - { - func(node1_view, node2_view, node3_view); - }); - }); -} +#if 0 +// Find a home for this stuff. /** @@ -538,7 +245,6 @@ blendField(const conduit::Node &field, const conduit::Node &indices, const condu } } -#if 0 void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_allocator) { if(src.number_of_children() > 0) @@ -577,8 +283,6 @@ void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_alloca } #endif -} // end namespace detail - class ElviraMIRAlgorithm : public MIRAlgorithm { public: diff --git a/src/axom/mir/clipping/ClipCases.h b/src/axom/mir/clipping/ClipCases.h new file mode 100644 index 0000000000..33fb487484 --- /dev/null +++ b/src/axom/mir/clipping/ClipCases.h @@ -0,0 +1,168 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#ifndef AXOM_VISIT_CLIP_CASES_H +#define AXOM_VISIT_CLIP_CASES_H +//--------------------------------------------------------------------------- +// Axom modifications +//#include +#define VISIT_VTK_LIGHT_API +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : August 11, 2003 +// +// Modifications: +// Jeremy Meredith, Mon Sep 15 17:24:15 PDT 2003 +// Added NOCOLOR. +// +// Jeremy Meredith, Thu Sep 18 11:29:12 PDT 2003 +// Added quad and triangle cases and output shapes. +// +// Brad Whitlock, Tue Sep 23 09:59:23 PDT 2003 +// Added API so it builds on Windows. +// +// Jeremy Meredith, Wed Jun 23 15:39:58 PDT 2004 +// Added voxel and pixel cases. Not output shapes, though. +// +// Jeremy Meredith, Tue Aug 29 13:52:33 EDT 2006 +// Added line segments and vertexes. +// + +// Points of original cell (up to 8, for the hex) +// Note: we assume P0 is zero in several places. +// Note: we assume these values are contiguous and monotonic. +#define P0 0 +#define P1 1 +#define P2 2 +#define P3 3 +#define P4 4 +#define P5 5 +#define P6 6 +#define P7 7 + +// Edges of original cell (up to 12, for the hex) +// Note: we assume these values are contiguous and monotonic. +#define EA 20 +#define EB 21 +#define EC 22 +#define ED 23 +#define EE 24 +#define EF 25 +#define EG 26 +#define EH 27 +#define EI 28 +#define EJ 29 +#define EK 30 +#define EL 31 + +// New interpolated points (ST_PNT outputs) +// Note: we assume these values are contiguous and monotonic. +#define N0 40 +#define N1 41 +#define N2 42 +#define N3 43 + +// Shapes +#define ST_TET 100 +#define ST_PYR 101 +#define ST_WDG 102 +#define ST_HEX 103 +#define ST_TRI 104 +#define ST_QUA 105 +#define ST_VTX 106 +#define ST_LIN 107 +#define ST_PNT 108 + +// Colors +#define COLOR0 120 +#define COLOR1 121 +#define NOCOLOR 122 + +// Tables +extern VISIT_VTK_LIGHT_API int numClipCasesHex; +extern VISIT_VTK_LIGHT_API int numClipShapesHex[256]; +extern VISIT_VTK_LIGHT_API int startClipShapesHex[256]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesHex[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesVox; +extern VISIT_VTK_LIGHT_API int numClipShapesVox[256]; +extern VISIT_VTK_LIGHT_API int startClipShapesVox[256]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesVox[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesWdg; +extern VISIT_VTK_LIGHT_API int numClipShapesWdg[64]; +extern VISIT_VTK_LIGHT_API int startClipShapesWdg[64]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesWdg[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPyr; +extern VISIT_VTK_LIGHT_API int numClipShapesPyr[32]; +extern VISIT_VTK_LIGHT_API int startClipShapesPyr[32]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPyr[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesTet; +extern VISIT_VTK_LIGHT_API int numClipShapesTet[16]; +extern VISIT_VTK_LIGHT_API int startClipShapesTet[16]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesTet[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesQua; +extern VISIT_VTK_LIGHT_API int numClipShapesQua[16]; +extern VISIT_VTK_LIGHT_API int startClipShapesQua[16]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesQua[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPix; +extern VISIT_VTK_LIGHT_API int numClipShapesPix[16]; +extern VISIT_VTK_LIGHT_API int startClipShapesPix[16]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPix[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesTri; +extern VISIT_VTK_LIGHT_API int numClipShapesTri[8]; +extern VISIT_VTK_LIGHT_API int startClipShapesTri[8]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesTri[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesLin; +extern VISIT_VTK_LIGHT_API int numClipShapesLin[4]; +extern VISIT_VTK_LIGHT_API int startClipShapesLin[4]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesLin[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesVtx; +extern VISIT_VTK_LIGHT_API int numClipShapesVtx[2]; +extern VISIT_VTK_LIGHT_API int startClipShapesVtx[2]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesVtx[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPoly5; +extern VISIT_VTK_LIGHT_API int numClipShapesPoly5[32]; +extern VISIT_VTK_LIGHT_API int startClipShapesPoly5[32]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly5[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPoly6; +extern VISIT_VTK_LIGHT_API int numClipShapesPoly6[64]; +extern VISIT_VTK_LIGHT_API int startClipShapesPoly6[64]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly6[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPoly7; +extern VISIT_VTK_LIGHT_API int numClipShapesPoly7[128]; +extern VISIT_VTK_LIGHT_API int startClipShapesPoly7[128]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly7[]; + +extern VISIT_VTK_LIGHT_API int numClipCasesPoly8; +extern VISIT_VTK_LIGHT_API int numClipShapesPoly8[256]; +extern VISIT_VTK_LIGHT_API int startClipShapesPoly8[256]; +extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly8[]; + +//--------------------------------------------------------------------------- +// Axom modifications +#define ST_MIN ST_TET +#define ST_MAX (ST_PNT + 1) +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- + +#endif diff --git a/src/axom/mir/clipping/ClipCasesHex.cpp b/src/axom/mir/clipping/ClipCasesHex.cpp new file mode 100644 index 0000000000..0bec543099 --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesHex.cpp @@ -0,0 +1,3505 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : August 11, 2003 +// +// Modifications: +// Jeremy Meredith, Mon Sep 15 17:30:21 PDT 2003 +// Added ability for Centroid-Points to have an associated color. +// Fixed two inconsistent cases. + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesHex = 256; + +int numClipShapesHex[256] = { + 1, 10, 10, 3, 10, 18, 3, 15, // cases 0 - 7 + 10, 3, 18, 15, 3, 15, 15, 2, // cases 8 - 15 + 10, 3, 18, 15, 8, 18, 18, 17, // cases 16 - 23 + 18, 15, 11, 10, 18, 17, 16, 15, // cases 24 - 31 + 10, 18, 3, 15, 18, 11, 15, 10, // cases 32 - 39 + 8, 18, 18, 17, 18, 16, 17, 15, // cases 40 - 47 + 3, 15, 15, 2, 18, 16, 17, 15, // cases 48 - 55 + 18, 17, 16, 15, 4, 13, 13, 3, // cases 56 - 63 + 10, 8, 18, 18, 3, 18, 15, 17, // cases 64 - 71 + 18, 18, 11, 16, 15, 17, 10, 15, // cases 72 - 79 + 18, 18, 11, 16, 18, 4, 16, 13, // cases 80 - 87 + 11, 16, 9, 8, 16, 13, 8, 7, // cases 88 - 95 + 3, 18, 15, 17, 15, 16, 2, 15, // cases 96 - 103 + 18, 4, 16, 13, 17, 13, 15, 3, // cases 104 - 111 + 15, 17, 10, 15, 17, 13, 15, 3, // cases 112 - 119 + 16, 13, 8, 7, 13, 8, 7, 10, // cases 120 - 127 + 10, 18, 8, 18, 18, 11, 18, 16, // cases 128 - 135 + 3, 15, 18, 17, 15, 10, 17, 15, // cases 136 - 143 + 3, 15, 18, 17, 18, 16, 4, 13, // cases 144 - 151 + 15, 2, 16, 15, 17, 15, 13, 3, // cases 152 - 159 + 18, 11, 18, 16, 11, 9, 16, 8, // cases 160 - 167 + 18, 16, 4, 13, 16, 8, 13, 7, // cases 168 - 175 + 15, 10, 17, 15, 16, 8, 13, 7, // cases 176 - 183 + 17, 15, 13, 3, 13, 7, 8, 10, // cases 184 - 191 + 3, 18, 18, 4, 15, 16, 17, 13, // cases 192 - 199 + 15, 17, 16, 13, 2, 15, 15, 3, // cases 200 - 207 + 15, 17, 16, 13, 17, 13, 13, 8, // cases 208 - 215 + 10, 15, 8, 7, 15, 3, 7, 10, // cases 216 - 223 + 15, 16, 17, 13, 10, 8, 15, 7, // cases 224 - 231 + 17, 13, 13, 8, 15, 7, 3, 10, // cases 232 - 239 + 2, 15, 15, 3, 15, 7, 3, 10, // cases 240 - 247 + 15, 3, 7, 10, 3, 10, 10, 1 // cases 248 - 255 +}; + +int startClipShapesHex[256] = { + 0, 10, 80, 150, 176, 246, 361, 387, // cases 0 - 7 + 488, 558, 584, 699, 800, 826, 927, 1028, // cases 8 - 15 + 1048, 1118, 1144, 1259, 1360, 1412, 1531, 1650, // cases 16 - 23 + 1764, 1879, 1980, 2056, 2124, 2243, 2357, 2465, // cases 24 - 31 + 2566, 2636, 2751, 2777, 2878, 2993, 3069, 3170, // cases 32 - 39 + 3238, 3290, 3409, 3528, 3642, 3761, 3869, 3983, // cases 40 - 47 + 4084, 4110, 4211, 4312, 4332, 4451, 4559, 4673, // cases 48 - 55 + 4774, 4893, 5007, 5115, 5216, 5252, 5343, 5434, // cases 56 - 63 + 5460, 5530, 5582, 5697, 5816, 5842, 5961, 6062, // cases 64 - 71 + 6176, 6291, 6410, 6486, 6594, 6695, 6809, 6877, // cases 72 - 79 + 6978, 7093, 7212, 7288, 7396, 7515, 7551, 7659, // cases 80 - 87 + 7750, 7826, 7934, 7996, 8050, 8158, 8249, 8303, // cases 88 - 95 + 8350, 8376, 8495, 8596, 8710, 8811, 8919, 8939, // cases 96 - 103 + 9040, 9159, 9195, 9303, 9394, 9508, 9599, 9700, // cases 104 - 111 + 9726, 9827, 9941, 10009, 10110, 10224, 10315, 10416, // cases 112 - 119 + 10442, 10550, 10641, 10695, 10742, 10833, 10885, 10932, // cases 120 - 127 + 11002, 11072, 11187, 11239, 11358, 11473, 11549, 11668, // cases 128 - 135 + 11776, 11802, 11903, 12022, 12136, 12237, 12305, 12419, // cases 136 - 143 + 12520, 12546, 12647, 12766, 12880, 12999, 13107, 13143, // cases 144 - 151 + 13234, 13335, 13355, 13463, 13564, 13678, 13779, 13870, // cases 152 - 159 + 13896, 14011, 14087, 14206, 14314, 14390, 14452, 14560, // cases 160 - 167 + 14614, 14733, 14841, 14877, 14968, 15076, 15130, 15221, // cases 168 - 175 + 15268, 15369, 15437, 15551, 15652, 15760, 15814, 15905, // cases 176 - 183 + 15952, 16066, 16167, 16258, 16284, 16375, 16422, 16474, // cases 184 - 191 + 16544, 16570, 16689, 16808, 16844, 16945, 17053, 17167, // cases 192 - 199 + 17258, 17359, 17473, 17581, 17672, 17692, 17793, 17894, // cases 200 - 207 + 17920, 18021, 18135, 18243, 18334, 18448, 18539, 18630, // cases 208 - 215 + 18682, 18750, 18851, 18905, 18952, 19053, 19079, 19126, // cases 216 - 223 + 19196, 19297, 19405, 19519, 19610, 19678, 19732, 19833, // cases 224 - 231 + 19880, 19994, 20085, 20176, 20228, 20329, 20376, 20402, // cases 232 - 239 + 20472, 20492, 20593, 20694, 20720, 20821, 20868, 20894, // cases 240 - 247 + 20964, 21065, 21091, 21138, 21208, 21234, 21304, 21374 // cases 248 - 255 +}; + +unsigned char clipShapesHex[] = { + // Case #0: Unique case #1 + ST_HEX, COLOR0, P0, P1, P2, P3, P4, P5, P6, P7, + // Case #1: Unique case #2 + ST_PNT, 0, COLOR0, 7, P1, P2, P3, P4, P5, P6, P7, + ST_WDG, COLOR0, P1, P3, P4, EA, ED, EI, + ST_TET, COLOR0, P1, P3, P4, N0, + ST_TET, COLOR0, P1, P2, P3, N0, + ST_PYR, COLOR0, P6, P7, P3, P2, N0, + ST_PYR, COLOR0, P5, P6, P2, P1, N0, + ST_PYR, COLOR0, P4, P7, P6, P5, N0, + ST_TET, COLOR0, P3, P7, P4, N0, + ST_TET, COLOR0, P4, P5, P1, N0, + ST_TET, COLOR1, P0, EA, ED, EI, + // Case #2: (cloned #1) + ST_PNT, 0, COLOR0, 7, P5, P4, P0, P2, P6, P7, P3, + ST_WDG, COLOR0, EJ, EA, EB, P5, P0, P2, + ST_TET, COLOR0, P5, P2, P0, N0, + ST_TET, COLOR0, P5, P0, P4, N0, + ST_PYR, COLOR0, P7, P4, P0, P3, N0, + ST_PYR, COLOR0, P6, P5, P4, P7, N0, + ST_PYR, COLOR0, P2, P6, P7, P3, N0, + ST_TET, COLOR0, P0, P2, P3, N0, + ST_TET, COLOR0, P2, P5, P6, N0, + ST_TET, COLOR1, P1, EA, EJ, EB, + // Case #3: Unique case #3 + ST_HEX, COLOR0, EB, P2, P3, ED, EJ, P5, P4, EI, + ST_WDG, COLOR0, P2, P6, P5, P3, P7, P4, + ST_WDG, COLOR1, P1, EB, EJ, P0, ED, EI, + // Case #4: (cloned #1) + ST_PNT, 0, COLOR0, 7, P6, P5, P1, P3, P7, P4, P0, + ST_WDG, COLOR0, EL, EB, EC, P6, P1, P3, + ST_TET, COLOR0, P6, P3, P1, N0, + ST_TET, COLOR0, P6, P1, P5, N0, + ST_PYR, COLOR0, P4, P5, P1, P0, N0, + ST_PYR, COLOR0, P7, P6, P5, P4, N0, + ST_PYR, COLOR0, P3, P7, P4, P0, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_TET, COLOR0, P3, P6, P7, N0, + ST_TET, COLOR1, P2, EB, EL, EC, + // Case #5: Unique case #4 + ST_PNT, 0, NOCOLOR, 2, EI, EL, + ST_PYR, COLOR0, P4, P7, P6, P5, N0, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_TET, COLOR0, P4, P5, P1, N0, + ST_TET, COLOR0, P3, P7, P4, N0, + ST_TET, COLOR0, P6, P7, P3, N0, + ST_PYR, COLOR0, P6, P3, EC, EL, N0, + ST_PYR, COLOR0, P1, P6, EL, EB, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_PYR, COLOR0, P4, P1, EA, EI, N0, + ST_PYR, COLOR0, P4, EI, ED, P3, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_PYR, COLOR1, P0, P2, EC, ED, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_TET, COLOR1, EB, EL, P2, N0, + ST_TET, COLOR1, P2, EL, EC, N0, + ST_TET, COLOR1, EA, N0, P0, EI, + ST_TET, COLOR1, ED, EI, P0, N0, + // Case #6: (cloned #3) + ST_HEX, COLOR0, EC, P3, P0, EA, EL, P6, P5, EJ, + ST_WDG, COLOR0, P3, P7, P6, P0, P4, P5, + ST_WDG, COLOR1, P2, EC, EL, P1, EA, EJ, + // Case #7: Unique case #5 + ST_PNT, 0, NOCOLOR, 5, EI, EJ, ED, EC, EL, + ST_PYR, COLOR0, P4, P7, P6, P5, N0, + ST_TET, COLOR0, P6, P3, N0, P7, + ST_PYR, COLOR0, P5, P6, EL, EJ, N0, + ST_PYR, COLOR0, EI, P4, P5, EJ, N0, + ST_TET, COLOR0, P3, P7, P4, N0, + ST_PYR, COLOR0, P3, P4, EI, ED, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_PYR, COLOR0, EL, P6, P3, EC, N0, + ST_PYR, COLOR1, EJ, EL, P2, P1, N0, + ST_PYR, COLOR1, EI, EJ, P1, P0, N0, + ST_TET, COLOR1, ED, EI, P0, N0, + ST_TET, COLOR1, P0, P1, P2, N0, + ST_PYR, COLOR1, ED, P0, P2, EC, N0, + ST_TET, COLOR1, P2, EL, EC, N0, + // Case #8: (cloned #1) + ST_PNT, 0, COLOR0, 7, P2, P1, P0, P7, P6, P5, P4, + ST_WDG, COLOR0, EC, ED, EK, P2, P0, P7, + ST_TET, COLOR0, P2, P7, P0, N0, + ST_TET, COLOR0, P2, P0, P1, N0, + ST_PYR, COLOR0, P5, P1, P0, P4, N0, + ST_PYR, COLOR0, P6, P2, P1, P5, N0, + ST_PYR, COLOR0, P7, P6, P5, P4, N0, + ST_TET, COLOR0, P0, P7, P4, N0, + ST_TET, COLOR0, P7, P2, P6, N0, + ST_TET, COLOR1, P3, ED, EC, EK, + // Case #9: (cloned #3) + ST_HEX, COLOR0, EK, P7, P4, EI, EC, P2, P1, EA, + ST_WDG, COLOR0, P7, P6, P2, P4, P5, P1, + ST_WDG, COLOR1, P3, EK, EC, P0, EI, EA, + // Case #10: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EK, EJ, + ST_PYR, COLOR0, P7, P6, P5, P4, N0, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_TET, COLOR0, P7, P2, P6, N0, + ST_TET, COLOR0, P0, P7, P4, N0, + ST_TET, COLOR0, P5, P0, P4, N0, + ST_PYR, COLOR0, P5, EJ, EA, P0, N0, + ST_PYR, COLOR0, P2, EB, EJ, P5, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_PYR, COLOR0, P7, EK, EC, P2, N0, + ST_PYR, COLOR0, P7, P0, ED, EK, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_PYR, COLOR1, P3, ED, EA, P1, N0, + ST_PYR, COLOR1, EC, P3, P1, EB, N0, + ST_TET, COLOR1, EB, P1, EJ, N0, + ST_TET, COLOR1, P1, EA, EJ, N0, + ST_TET, COLOR1, EC, P3, N0, EK, + ST_TET, COLOR1, ED, P3, EK, N0, + // Case #11: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EJ, EI, EB, EC, EK, + ST_PYR, COLOR0, P5, P4, P7, P6, N0, + ST_TET, COLOR0, P7, N0, P2, P6, + ST_PYR, COLOR0, P4, EI, EK, P7, N0, + ST_PYR, COLOR0, EJ, EI, P4, P5, N0, + ST_TET, COLOR0, P2, P5, P6, N0, + ST_PYR, COLOR0, P2, EB, EJ, P5, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_PYR, COLOR0, EK, EC, P2, P7, N0, + ST_PYR, COLOR1, EI, P0, P3, EK, N0, + ST_PYR, COLOR1, EJ, P1, P0, EI, N0, + ST_TET, COLOR1, EB, P1, EJ, N0, + ST_TET, COLOR1, P1, P3, P0, N0, + ST_PYR, COLOR1, EB, EC, P3, P1, N0, + ST_TET, COLOR1, P3, EC, EK, N0, + // Case #12: (cloned #3) + ST_HEX, COLOR0, EL, P6, P7, EK, EB, P1, P0, ED, + ST_WDG, COLOR0, P0, P4, P7, P1, P5, P6, + ST_WDG, COLOR1, P3, ED, EK, P2, EB, EL, + // Case #13: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EI, EK, EA, EB, EL, + ST_PYR, COLOR0, P4, P7, P6, P5, N0, + ST_TET, COLOR0, P6, N0, P1, P5, + ST_PYR, COLOR0, P7, EK, EL, P6, N0, + ST_PYR, COLOR0, EI, EK, P7, P4, N0, + ST_TET, COLOR0, P1, P4, P5, N0, + ST_PYR, COLOR0, P1, EA, EI, P4, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_PYR, COLOR0, EL, EB, P1, P6, N0, + ST_PYR, COLOR1, EK, P3, P2, EL, N0, + ST_PYR, COLOR1, EI, P0, P3, EK, N0, + ST_TET, COLOR1, EA, P0, EI, N0, + ST_TET, COLOR1, P0, P2, P3, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_TET, COLOR1, P2, EB, EL, N0, + // Case #14: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EK, EL, ED, EA, EJ, + ST_PYR, COLOR0, P7, P6, P5, P4, N0, + ST_TET, COLOR0, P5, N0, P0, P4, + ST_PYR, COLOR0, P6, EL, EJ, P5, N0, + ST_PYR, COLOR0, EK, EL, P6, P7, N0, + ST_TET, COLOR0, P0, P7, P4, N0, + ST_PYR, COLOR0, P0, ED, EK, P7, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_PYR, COLOR0, EJ, EA, P0, P5, N0, + ST_PYR, COLOR1, EL, P2, P1, EJ, N0, + ST_PYR, COLOR1, EK, P3, P2, EL, N0, + ST_TET, COLOR1, ED, P3, EK, N0, + ST_TET, COLOR1, P3, P1, P2, N0, + ST_PYR, COLOR1, ED, EA, P1, P3, N0, + ST_TET, COLOR1, P1, EA, EJ, N0, + // Case #15: Unique case #6 + ST_HEX, COLOR0, EI, EJ, EL, EK, P4, P5, P6, P7, + ST_HEX, COLOR1, P0, P1, P2, P3, EI, EJ, EL, EK, + // Case #16: (cloned #1) + ST_PNT, 0, COLOR0, 7, P5, P1, P0, P7, P6, P2, P3, + ST_WDG, COLOR0, P5, P0, P7, EE, EI, EH, + ST_TET, COLOR0, P5, P0, P7, N0, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_PYR, COLOR0, P2, P3, P0, P1, N0, + ST_PYR, COLOR0, P6, P2, P1, P5, N0, + ST_PYR, COLOR0, P7, P3, P2, P6, N0, + ST_TET, COLOR0, P0, P3, P7, N0, + ST_TET, COLOR0, P7, P6, P5, N0, + ST_TET, COLOR1, P4, EE, EI, EH, + // Case #17: (cloned #3) + ST_HEX, COLOR0, EE, P5, P1, EA, EH, P7, P3, ED, + ST_WDG, COLOR0, P3, P2, P1, P7, P6, P5, + ST_WDG, COLOR1, P0, ED, EA, P4, EH, EE, + // Case #18: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EH, EB, + ST_PYR, COLOR0, P7, P3, P2, P6, N0, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_TET, COLOR0, P7, P6, P5, N0, + ST_TET, COLOR0, P0, P3, P7, N0, + ST_TET, COLOR0, P2, P3, P0, N0, + ST_PYR, COLOR0, P2, P0, EA, EB, N0, + ST_PYR, COLOR0, P5, P2, EB, EJ, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_PYR, COLOR0, P7, P5, EE, EH, N0, + ST_PYR, COLOR0, P7, EH, EI, P0, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_PYR, COLOR1, P4, P1, EA, EI, N0, + ST_PYR, COLOR1, EE, EJ, P1, P4, N0, + ST_TET, COLOR1, EJ, EB, P1, N0, + ST_TET, COLOR1, P1, EB, EA, N0, + ST_TET, COLOR1, EE, N0, P4, EH, + ST_TET, COLOR1, EI, EH, P4, N0, + // Case #19: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EB, ED, EJ, EE, EH, + ST_PYR, COLOR0, P2, P6, P7, P3, N0, + ST_TET, COLOR0, P7, P5, N0, P6, + ST_PYR, COLOR0, P3, P7, EH, ED, N0, + ST_PYR, COLOR0, EB, P2, P3, ED, N0, + ST_TET, COLOR0, P5, P6, P2, N0, + ST_PYR, COLOR0, P5, P2, EB, EJ, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_PYR, COLOR0, EH, P7, P5, EE, N0, + ST_PYR, COLOR1, ED, EH, P4, P0, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_TET, COLOR1, EJ, EB, P1, N0, + ST_TET, COLOR1, P1, P0, P4, N0, + ST_PYR, COLOR1, EJ, P1, P4, EE, N0, + ST_TET, COLOR1, P4, EH, EE, N0, + // Case #20: Unique case #7 + ST_WDG, COLOR0, EB, EC, EL, P1, P3, P6, + ST_WDG, COLOR0, P0, P7, P5, EI, EH, EE, + ST_TET, COLOR0, P3, P1, P6, P7, + ST_TET, COLOR0, P5, P7, P6, P1, + ST_TET, COLOR0, P0, P5, P1, P7, + ST_TET, COLOR0, P3, P7, P0, P1, + ST_TET, COLOR1, P4, EE, EI, EH, + ST_TET, COLOR1, P2, EC, EB, EL, + // Case #21: Unique case #8 + ST_PNT, 0, NOCOLOR, 4, EE, EH, EL, EL, + ST_PYR, COLOR0, P6, P3, EC, EL, N0, + ST_TET, COLOR0, EC, P3, ED, N0, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_TET, COLOR0, P6, P7, P3, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_PYR, COLOR0, P1, P6, EL, EB, N0, + ST_TET, COLOR0, P5, P7, P6, N0, + ST_PYR, COLOR0, P5, EE, EH, P7, N0, + ST_PYR, COLOR0, P5, P1, EA, EE, N0, + ST_PYR, COLOR1, P2, EC, ED, P0, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_TET, COLOR1, P2, EL, EC, N0, + ST_TET, COLOR1, EB, EL, P2, N0, + ST_PYR, COLOR1, ED, EH, P4, P0, N0, + ST_PYR, COLOR1, P0, P4, EE, EA, N0, + ST_TET, COLOR1, EE, P4, EH, N0, + // Case #22: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EL, EC, EH, EH, + ST_PYR, COLOR0, P7, EH, EI, P0, N0, + ST_TET, COLOR0, EI, EA, P0, N0, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_TET, COLOR0, P7, P0, P3, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_PYR, COLOR0, P5, EE, EH, P7, N0, + ST_TET, COLOR0, P6, P7, P3, N0, + ST_PYR, COLOR0, P6, P3, EC, EL, N0, + ST_PYR, COLOR0, P6, EL, EJ, P5, N0, + ST_PYR, COLOR1, P4, P1, EA, EI, N0, + ST_PYR, COLOR1, EJ, P1, P4, EE, N0, + ST_TET, COLOR1, P4, EI, EH, N0, + ST_TET, COLOR1, EE, P4, EH, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_PYR, COLOR1, P1, EJ, EL, P2, N0, + ST_TET, COLOR1, EL, EC, P2, N0, + // Case #23: Unique case #9 + ST_PNT, 0, NOCOLOR, 6, ED, EC, EL, EJ, EE, EH, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_PYR, COLOR0, P6, EL, EJ, P5, N0, + ST_TET, COLOR0, P3, P6, P7, N0, + ST_PYR, COLOR0, P3, EC, EL, P6, N0, + ST_TET, COLOR0, ED, EC, P3, N0, + ST_PYR, COLOR0, P3, P7, EH, ED, N0, + ST_PYR, COLOR0, EH, P7, P5, EE, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_TET, COLOR1, P0, P1, P2, N0, + ST_PYR, COLOR1, ED, P0, P2, EC, N0, + ST_PYR, COLOR1, ED, EH, P4, P0, N0, + ST_TET, COLOR1, P4, P1, P0, N0, + ST_TET, COLOR1, P4, EH, EE, N0, + ST_PYR, COLOR1, P4, EE, EJ, P1, N0, + ST_PYR, COLOR1, EJ, EL, P2, P1, N0, + ST_TET, COLOR1, EL, EC, P2, N0, + // Case #24: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EC, EE, + ST_PYR, COLOR0, P2, P1, P5, P6, N0, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_TET, COLOR0, P2, P6, P7, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_PYR, COLOR0, P5, P0, EI, EE, N0, + ST_PYR, COLOR0, P7, P5, EE, EH, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_PYR, COLOR0, P2, P7, EK, EC, N0, + ST_PYR, COLOR0, P2, EC, ED, P0, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_PYR, COLOR1, P3, P4, EI, ED, N0, + ST_PYR, COLOR1, EK, EH, P4, P3, N0, + ST_TET, COLOR1, EH, EE, P4, N0, + ST_TET, COLOR1, P4, EE, EI, N0, + ST_TET, COLOR1, EK, N0, P3, EC, + ST_TET, COLOR1, ED, EC, P3, N0, + // Case #25: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EE, EA, EH, EK, EC, + ST_PYR, COLOR0, P5, P6, P2, P1, N0, + ST_TET, COLOR0, P2, P7, N0, P6, + ST_PYR, COLOR0, P1, P2, EC, EA, N0, + ST_PYR, COLOR0, EE, P5, P1, EA, N0, + ST_TET, COLOR0, P7, P6, P5, N0, + ST_PYR, COLOR0, P7, P5, EE, EH, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_PYR, COLOR0, EC, P2, P7, EK, N0, + ST_PYR, COLOR1, EA, EC, P3, P0, N0, + ST_PYR, COLOR1, EE, EA, P0, P4, N0, + ST_TET, COLOR1, EH, EE, P4, N0, + ST_TET, COLOR1, P4, P0, P3, N0, + ST_PYR, COLOR1, EH, P4, P3, EK, N0, + ST_TET, COLOR1, P3, EC, EK, N0, + // Case #26: Unique case #10 + ST_TET, COLOR0, P0, EA, ED, EI, + ST_TET, COLOR0, P5, P7, P6, P2, + ST_PYR, COLOR0, EC, P2, P7, EK, EH, + ST_PYR, COLOR0, EB, EJ, P5, P2, EE, + ST_PYR, COLOR0, P7, P5, EE, EH, P2, + ST_PYR, COLOR0, EH, EE, EB, EC, P2, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_PYR, COLOR1, P3, EK, EH, P4, EC, + ST_PYR, COLOR1, EE, EJ, P1, P4, EB, + ST_PYR, COLOR1, EC, P3, P1, EB, P4, + ST_PYR, COLOR1, EC, EB, EE, EH, P4, + // Case #27: Unique case #11 + ST_TET, COLOR0, P5, P7, P6, P2, + ST_PYR, COLOR0, EC, P2, P7, EK, EH, + ST_PYR, COLOR0, EB, EJ, P5, P2, EE, + ST_PYR, COLOR0, P7, P5, EE, EH, P2, + ST_PYR, COLOR0, EH, EE, EB, EC, P2, + ST_TET, COLOR1, P0, P1, P3, P4, + ST_PYR, COLOR1, EH, P4, P3, EK, EC, + ST_PYR, COLOR1, EE, EJ, P1, P4, EB, + ST_PYR, COLOR1, P3, P1, EB, EC, P4, + ST_PYR, COLOR1, EH, EC, EB, EE, P4, + // Case #28: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EL, EB, EE, EE, + ST_PYR, COLOR0, P5, P0, EI, EE, N0, + ST_TET, COLOR0, EI, P0, ED, N0, + ST_PYR, COLOR0, P1, EB, ED, P0, N0, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_PYR, COLOR0, P7, P5, EE, EH, N0, + ST_TET, COLOR0, P6, P1, P5, N0, + ST_PYR, COLOR0, P6, EL, EB, P1, N0, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_PYR, COLOR1, P4, EI, ED, P3, N0, + ST_PYR, COLOR1, EK, EH, P4, P3, N0, + ST_TET, COLOR1, P4, EE, EI, N0, + ST_TET, COLOR1, EH, EE, P4, N0, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_PYR, COLOR1, P3, P2, EL, EK, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + // Case #29: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EA, EB, EL, EK, EH, EE, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_TET, COLOR0, P1, P5, P6, N0, + ST_PYR, COLOR0, P1, P6, EL, EB, N0, + ST_TET, COLOR0, EA, P1, EB, N0, + ST_PYR, COLOR0, P1, EA, EE, P5, N0, + ST_PYR, COLOR0, EE, EH, P7, P5, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR1, P0, P2, P3, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_PYR, COLOR1, EA, P0, P4, EE, N0, + ST_TET, COLOR1, P4, P0, P3, N0, + ST_TET, COLOR1, P4, EH, EE, N0, + ST_PYR, COLOR1, P4, P3, EK, EH, N0, + ST_PYR, COLOR1, EK, P3, P2, EL, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + // Case #30: Unique case #12 + ST_PNT, 0, NOCOLOR, 5, EL, EJ, EK, EH, EE, + ST_TET, COLOR0, P0, EA, ED, EI, + ST_PYR, COLOR0, P5, P6, EL, EJ, N0, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR0, P6, P5, P7, N0, + ST_PYR, COLOR0, P7, P5, EE, EH, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_TET, COLOR1, P1, P3, P4, N0, + ST_PYR, COLOR1, P3, EK, EH, P4, N0, + ST_TET, COLOR1, P2, P3, P1, N0, + ST_PYR, COLOR1, EJ, EL, P2, P1, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_PYR, COLOR1, P4, EE, EJ, P1, N0, + ST_TET, COLOR1, EH, EE, P4, N0, + // Case #31: Unique case #13 + ST_PNT, 0, NOCOLOR, 5, EJ, EL, EK, EE, EH, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_PYR, COLOR0, P5, P6, EL, EJ, N0, + ST_TET, COLOR0, EE, P5, EJ, N0, + ST_PYR, COLOR0, EH, P7, P5, EE, N0, + ST_TET, COLOR0, P7, P6, P5, N0, + ST_PYR, COLOR1, P0, P1, P2, P3, N0, + ST_TET, COLOR1, P3, P4, P0, N0, + ST_TET, COLOR1, P4, P1, P0, N0, + ST_PYR, COLOR1, P4, EE, EJ, P1, N0, + ST_PYR, COLOR1, EJ, EL, P2, P1, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_PYR, COLOR1, EK, EH, P4, P3, N0, + ST_TET, COLOR1, EE, P4, EH, N0, + // Case #32: (cloned #1) + ST_PNT, 0, COLOR0, 7, P6, P2, P1, P4, P7, P3, P0, + ST_WDG, COLOR0, P6, P1, P4, EF, EJ, EE, + ST_TET, COLOR0, P6, P1, P4, N0, + ST_TET, COLOR0, P6, P2, P1, N0, + ST_PYR, COLOR0, P3, P0, P1, P2, N0, + ST_PYR, COLOR0, P7, P3, P2, P6, N0, + ST_PYR, COLOR0, P4, P0, P3, P7, N0, + ST_TET, COLOR0, P1, P0, P4, N0, + ST_TET, COLOR0, P4, P7, P6, N0, + ST_TET, COLOR1, P5, EF, EJ, EE, + // Case #33: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, ED, EF, + ST_PYR, COLOR0, P3, P2, P6, P7, N0, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_TET, COLOR0, P3, P1, P2, N0, + ST_TET, COLOR0, P4, P3, P7, N0, + ST_TET, COLOR0, P6, P4, P7, N0, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_PYR, COLOR0, P1, EJ, EF, P6, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_PYR, COLOR0, P3, ED, EA, P1, N0, + ST_PYR, COLOR0, P3, P4, EI, ED, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_PYR, COLOR1, P0, EI, EE, P5, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_TET, COLOR1, EJ, P5, EF, N0, + ST_TET, COLOR1, P5, EE, EF, N0, + ST_TET, COLOR1, EA, P0, N0, ED, + ST_TET, COLOR1, EI, P0, ED, N0, + // Case #34: (cloned #3) + ST_HEX, COLOR0, EF, P6, P2, EB, EE, P4, P0, EA, + ST_WDG, COLOR0, P0, P3, P2, P4, P7, P6, + ST_WDG, COLOR1, P1, EA, EB, P5, EE, EF, + // Case #35: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, ED, EB, EI, EE, EF, + ST_PYR, COLOR0, P3, P2, P6, P7, N0, + ST_TET, COLOR0, P6, N0, P4, P7, + ST_PYR, COLOR0, P2, EB, EF, P6, N0, + ST_PYR, COLOR0, ED, EB, P2, P3, N0, + ST_TET, COLOR0, P4, P3, P7, N0, + ST_PYR, COLOR0, P4, EI, ED, P3, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_PYR, COLOR0, EF, EE, P4, P6, N0, + ST_PYR, COLOR1, EB, P1, P5, EF, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_TET, COLOR1, EI, P0, ED, N0, + ST_TET, COLOR1, P0, P5, P1, N0, + ST_PYR, COLOR1, EI, EE, P5, P0, N0, + ST_TET, COLOR1, P5, EE, EF, N0, + // Case #36: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EC, EE, + ST_PYR, COLOR0, P3, P7, P4, P0, N0, + ST_TET, COLOR0, P7, P6, P4, N0, + ST_TET, COLOR0, P3, P6, P7, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_TET, COLOR0, P4, P1, P0, N0, + ST_PYR, COLOR0, P4, EE, EJ, P1, N0, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_PYR, COLOR0, P3, EC, EL, P6, N0, + ST_PYR, COLOR0, P3, P1, EB, EC, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_PYR, COLOR1, P2, EB, EJ, P5, N0, + ST_PYR, COLOR1, EL, P2, P5, EF, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + ST_TET, COLOR1, EL, P2, N0, EC, + ST_TET, COLOR1, EB, P2, EC, N0, + // Case #37: (cloned #26) + ST_TET, COLOR0, P1, EA, EJ, EB, + ST_TET, COLOR0, P6, P7, P3, P4, + ST_PYR, COLOR0, EI, ED, P3, P4, EC, + ST_PYR, COLOR0, EE, P4, P6, EF, EL, + ST_PYR, COLOR0, P3, EC, EL, P6, P4, + ST_PYR, COLOR0, EC, EI, EE, EL, P4, + ST_WDG, COLOR1, P0, P5, P2, EA, EJ, EB, + ST_PYR, COLOR1, P0, P2, EC, ED, EI, + ST_PYR, COLOR1, EL, P2, P5, EF, EE, + ST_PYR, COLOR1, EI, EE, P5, P0, P2, + ST_PYR, COLOR1, EI, EC, EL, EE, P2, + // Case #38: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EE, EA, EF, EL, EC, + ST_PYR, COLOR0, P4, P0, P3, P7, N0, + ST_TET, COLOR0, P3, N0, P6, P7, + ST_PYR, COLOR0, P0, EA, EC, P3, N0, + ST_PYR, COLOR0, EE, EA, P0, P4, N0, + ST_TET, COLOR0, P6, P4, P7, N0, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_PYR, COLOR0, EC, EL, P6, P3, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + ST_TET, COLOR1, P5, P2, P1, N0, + ST_PYR, COLOR1, EF, EL, P2, P5, N0, + ST_TET, COLOR1, P2, EL, EC, N0, + // Case #39: (cloned #27) + ST_TET, COLOR0, P6, P7, P3, P4, + ST_PYR, COLOR0, EI, ED, P3, P4, EC, + ST_PYR, COLOR0, EE, P4, P6, EF, EL, + ST_PYR, COLOR0, P3, EC, EL, P6, P4, + ST_PYR, COLOR0, EC, EI, EE, EL, P4, + ST_TET, COLOR1, P1, P0, P5, P2, + ST_PYR, COLOR1, EC, ED, P0, P2, EI, + ST_PYR, COLOR1, EL, P2, P5, EF, EE, + ST_PYR, COLOR1, P0, EI, EE, P5, P2, + ST_PYR, COLOR1, EC, EL, EE, EI, P2, + // Case #40: (cloned #20) + ST_WDG, COLOR0, P1, P4, P6, EJ, EE, EF, + ST_WDG, COLOR0, ED, EK, EC, P0, P7, P2, + ST_TET, COLOR0, P4, P6, P1, P7, + ST_TET, COLOR0, P2, P6, P7, P1, + ST_TET, COLOR0, P0, P1, P2, P7, + ST_TET, COLOR0, P4, P0, P7, P1, + ST_TET, COLOR1, P3, ED, EC, EK, + ST_TET, COLOR1, P5, EJ, EE, EF, + // Case #41: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EC, EK, EF, EF, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_TET, COLOR0, EE, EI, P4, N0, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_TET, COLOR0, P6, P4, P7, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_PYR, COLOR0, P1, EJ, EF, P6, N0, + ST_TET, COLOR0, P2, P6, P7, N0, + ST_PYR, COLOR0, P2, P7, EK, EC, N0, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_PYR, COLOR1, P5, P0, EI, EE, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_TET, COLOR1, P5, EE, EF, N0, + ST_TET, COLOR1, EJ, P5, EF, N0, + ST_PYR, COLOR1, EI, P0, P3, EK, N0, + ST_PYR, COLOR1, P0, EA, EC, P3, N0, + ST_TET, COLOR1, EC, EK, P3, N0, + // Case #42: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EF, EE, EK, EK, + ST_PYR, COLOR0, P7, P0, ED, EK, N0, + ST_TET, COLOR0, ED, P0, EA, N0, + ST_PYR, COLOR0, P4, EE, EA, P0, N0, + ST_TET, COLOR0, P7, P4, P0, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_TET, COLOR0, P6, P7, P2, N0, + ST_PYR, COLOR0, P2, P7, EK, EC, N0, + ST_TET, COLOR0, P6, P4, P7, N0, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_PYR, COLOR0, P6, P2, EB, EF, N0, + ST_PYR, COLOR1, P3, ED, EA, P1, N0, + ST_PYR, COLOR1, EB, EC, P3, P1, N0, + ST_TET, COLOR1, P3, EK, ED, N0, + ST_TET, COLOR1, EC, EK, P3, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_PYR, COLOR1, P1, P5, EF, EB, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + // Case #43: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EI, EE, EF, EB, EC, EK, + ST_TET, COLOR0, P6, P7, P2, N0, + ST_PYR, COLOR0, P6, P2, EB, EF, N0, + ST_TET, COLOR0, P4, P7, P6, N0, + ST_PYR, COLOR0, P4, P6, EF, EE, N0, + ST_TET, COLOR0, EI, P4, EE, N0, + ST_PYR, COLOR0, P4, EI, EK, P7, N0, + ST_PYR, COLOR0, EK, EC, P2, P7, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_TET, COLOR1, P0, P5, P1, N0, + ST_PYR, COLOR1, EI, EE, P5, P0, N0, + ST_PYR, COLOR1, EI, P0, P3, EK, N0, + ST_TET, COLOR1, P3, P0, P1, N0, + ST_TET, COLOR1, P3, EC, EK, N0, + ST_PYR, COLOR1, P3, P1, EB, EC, N0, + ST_PYR, COLOR1, EB, P1, P5, EF, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + // Case #44: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EK, ED, EE, EE, + ST_PYR, COLOR0, P4, EE, EJ, P1, N0, + ST_TET, COLOR0, EJ, EB, P1, N0, + ST_PYR, COLOR0, P0, P1, EB, ED, N0, + ST_TET, COLOR0, P4, P1, P0, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_TET, COLOR0, P7, P6, P4, N0, + ST_PYR, COLOR0, P6, EF, EE, P4, N0, + ST_TET, COLOR0, P7, P4, P0, N0, + ST_PYR, COLOR0, P7, P0, ED, EK, N0, + ST_PYR, COLOR0, P7, EK, EL, P6, N0, + ST_PYR, COLOR1, P5, P2, EB, EJ, N0, + ST_PYR, COLOR1, EL, P2, P5, EF, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_PYR, COLOR1, P2, EL, EK, P3, N0, + ST_TET, COLOR1, EK, ED, P3, N0, + // Case #45: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EK, EL, EI, EE, EF, + ST_TET, COLOR0, P1, EB, EA, EJ, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_TET, COLOR0, P7, P6, P4, N0, + ST_PYR, COLOR0, P4, P6, EF, EE, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_WDG, COLOR1, EA, EB, EJ, P0, P2, P5, + ST_TET, COLOR1, P2, P0, P5, N0, + ST_PYR, COLOR1, P0, EI, EE, P5, N0, + ST_TET, COLOR1, P3, P0, P2, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_PYR, COLOR1, P5, EF, EL, P2, N0, + ST_TET, COLOR1, EE, EF, P5, N0, + // Case #46: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EA, ED, EK, EL, EF, EE, + ST_TET, COLOR0, P7, P6, P4, N0, + ST_PYR, COLOR0, P7, EK, EL, P6, N0, + ST_TET, COLOR0, P0, P7, P4, N0, + ST_PYR, COLOR0, P0, ED, EK, P7, N0, + ST_TET, COLOR0, EA, ED, P0, N0, + ST_PYR, COLOR0, P0, P4, EE, EA, N0, + ST_PYR, COLOR0, EE, P4, P6, EF, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_TET, COLOR1, P1, P2, P3, N0, + ST_PYR, COLOR1, EA, P1, P3, ED, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_TET, COLOR1, P5, P2, P1, N0, + ST_TET, COLOR1, P5, EE, EF, N0, + ST_PYR, COLOR1, P5, EF, EL, P2, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_TET, COLOR1, EK, ED, P3, N0, + // Case #47: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EL, EK, EI, EF, EE, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_PYR, COLOR0, P6, P7, EK, EL, N0, + ST_TET, COLOR0, EF, P6, EL, N0, + ST_PYR, COLOR0, EE, P4, P6, EF, N0, + ST_TET, COLOR0, P4, P7, P6, N0, + ST_PYR, COLOR1, P1, P2, P3, P0, N0, + ST_TET, COLOR1, P0, P5, P1, N0, + ST_TET, COLOR1, P5, P2, P1, N0, + ST_PYR, COLOR1, P5, EF, EL, P2, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_PYR, COLOR1, EI, EE, P5, P0, N0, + ST_TET, COLOR1, EF, P5, EE, N0, + // Case #48: (cloned #3) + ST_HEX, COLOR0, EJ, P1, P0, EI, EF, P6, P7, EH, + ST_WDG, COLOR0, P1, P2, P6, P0, P3, P7, + ST_WDG, COLOR1, P5, EJ, EF, P4, EI, EH, + // Case #49: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, ED, EH, EA, EJ, EF, + ST_PYR, COLOR0, P3, P2, P6, P7, N0, + ST_TET, COLOR0, P6, P1, N0, P2, + ST_PYR, COLOR0, P7, P6, EF, EH, N0, + ST_PYR, COLOR0, ED, P3, P7, EH, N0, + ST_TET, COLOR0, P1, P2, P3, N0, + ST_PYR, COLOR0, P1, P3, ED, EA, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_PYR, COLOR0, EF, P6, P1, EJ, N0, + ST_PYR, COLOR1, EH, EF, P5, P4, N0, + ST_PYR, COLOR1, ED, EH, P4, P0, N0, + ST_TET, COLOR1, EA, ED, P0, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_TET, COLOR1, P5, EF, EJ, N0, + // Case #50: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EH, EF, EI, EA, EB, + ST_PYR, COLOR0, P7, P3, P2, P6, N0, + ST_TET, COLOR0, P2, P0, N0, P3, + ST_PYR, COLOR0, P6, P2, EB, EF, N0, + ST_PYR, COLOR0, EH, P7, P6, EF, N0, + ST_TET, COLOR0, P0, P3, P7, N0, + ST_PYR, COLOR0, P0, P7, EH, EI, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_PYR, COLOR0, EB, P2, P0, EA, N0, + ST_PYR, COLOR1, EF, EB, P1, P5, N0, + ST_PYR, COLOR1, EH, EF, P5, P4, N0, + ST_TET, COLOR1, EI, EH, P4, N0, + ST_TET, COLOR1, P4, P5, P1, N0, + ST_PYR, COLOR1, EI, P4, P1, EA, N0, + ST_TET, COLOR1, P1, EB, EA, N0, + // Case #51: (cloned #15) + ST_HEX, COLOR0, P3, P2, P6, P7, ED, EB, EF, EH, + ST_HEX, COLOR1, ED, EB, EF, EH, P0, P1, P5, P4, + // Case #52: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EH, EI, EC, EC, + ST_PYR, COLOR0, P3, P1, EB, EC, N0, + ST_TET, COLOR0, EB, P1, EJ, N0, + ST_PYR, COLOR0, P0, EI, EJ, P1, N0, + ST_TET, COLOR0, P3, P0, P1, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_TET, COLOR0, P7, P3, P6, N0, + ST_PYR, COLOR0, P6, P3, EC, EL, N0, + ST_TET, COLOR0, P7, P0, P3, N0, + ST_PYR, COLOR0, P7, EH, EI, P0, N0, + ST_PYR, COLOR0, P7, P6, EF, EH, N0, + ST_PYR, COLOR1, P2, EB, EJ, P5, N0, + ST_PYR, COLOR1, EF, EL, P2, P5, N0, + ST_TET, COLOR1, P2, EC, EB, N0, + ST_TET, COLOR1, EL, EC, P2, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_PYR, COLOR1, P5, P4, EH, EF, N0, + ST_TET, COLOR1, EH, P4, EI, N0, + // Case #53: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EH, EF, ED, EC, EL, + ST_TET, COLOR0, P1, EA, EJ, EB, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_TET, COLOR0, P7, P3, P6, N0, + ST_PYR, COLOR0, P3, EC, EL, P6, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_WDG, COLOR1, P0, P5, P2, EA, EJ, EB, + ST_TET, COLOR1, P5, P2, P0, N0, + ST_PYR, COLOR1, P0, P2, EC, ED, N0, + ST_TET, COLOR1, P4, P5, P0, N0, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, P2, P5, EF, EL, N0, + ST_TET, COLOR1, EC, P2, EL, N0, + // Case #54: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EA, EI, EH, EF, EL, EC, + ST_TET, COLOR0, P7, P3, P6, N0, + ST_PYR, COLOR0, P7, P6, EF, EH, N0, + ST_TET, COLOR0, P0, P3, P7, N0, + ST_PYR, COLOR0, P0, P7, EH, EI, N0, + ST_TET, COLOR0, EA, P0, EI, N0, + ST_PYR, COLOR0, P0, EA, EC, P3, N0, + ST_PYR, COLOR0, EC, EL, P6, P3, N0, + ST_TET, COLOR0, P6, EL, EF, N0, + ST_TET, COLOR1, P1, P4, P5, N0, + ST_PYR, COLOR1, EA, EI, P4, P1, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_TET, COLOR1, P2, P1, P5, N0, + ST_TET, COLOR1, P2, EL, EC, N0, + ST_PYR, COLOR1, P2, P5, EF, EL, N0, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_TET, COLOR1, EH, P4, EI, N0, + // Case #55: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EF, EH, ED, EL, EC, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_TET, COLOR0, EL, EF, P6, N0, + ST_PYR, COLOR0, EC, EL, P6, P3, N0, + ST_TET, COLOR0, P3, P6, P7, N0, + ST_PYR, COLOR1, P1, P0, P4, P5, N0, + ST_TET, COLOR1, P0, P1, P2, N0, + ST_TET, COLOR1, P2, P1, P5, N0, + ST_PYR, COLOR1, P2, P5, EF, EL, N0, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, ED, P0, P2, EC, N0, + ST_TET, COLOR1, EL, EC, P2, N0, + // Case #56: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EF, EJ, EC, EC, + ST_PYR, COLOR0, P2, EC, ED, P0, N0, + ST_TET, COLOR0, ED, EI, P0, N0, + ST_PYR, COLOR0, P1, P0, EI, EJ, N0, + ST_TET, COLOR0, P2, P0, P1, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR0, P6, P7, P2, N0, + ST_PYR, COLOR0, P7, EK, EC, P2, N0, + ST_TET, COLOR0, P6, P2, P1, N0, + ST_PYR, COLOR0, P6, P1, EJ, EF, N0, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_PYR, COLOR1, P3, P4, EI, ED, N0, + ST_PYR, COLOR1, EH, P4, P3, EK, N0, + ST_TET, COLOR1, P3, ED, EC, N0, + ST_TET, COLOR1, EK, P3, EC, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_PYR, COLOR1, P4, EH, EF, P5, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + // Case #57: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EA, EJ, EF, EH, EK, EC, + ST_TET, COLOR0, P6, P7, P2, N0, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_TET, COLOR0, P1, P6, P2, N0, + ST_PYR, COLOR0, P1, EJ, EF, P6, N0, + ST_TET, COLOR0, EA, EJ, P1, N0, + ST_PYR, COLOR0, P1, P2, EC, EA, N0, + ST_PYR, COLOR0, EC, P2, P7, EK, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_PYR, COLOR1, EA, EC, P3, P0, N0, + ST_TET, COLOR1, P3, P4, P0, N0, + ST_TET, COLOR1, P3, EC, EK, N0, + ST_PYR, COLOR1, P3, EK, EH, P4, N0, + ST_PYR, COLOR1, EH, EF, P5, P4, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + // Case #58: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EF, EB, EH, EK, EC, + ST_TET, COLOR0, P0, EI, EA, ED, + ST_PYR, COLOR0, P2, EB, EF, P6, N0, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_TET, COLOR0, P6, P7, P2, N0, + ST_PYR, COLOR0, P7, EK, EC, P2, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_WDG, COLOR1, P4, P1, P3, EI, EA, ED, + ST_TET, COLOR1, P1, P3, P4, N0, + ST_PYR, COLOR1, P4, P3, EK, EH, N0, + ST_TET, COLOR1, P5, P1, P4, N0, + ST_PYR, COLOR1, EB, P1, P5, EF, N0, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_PYR, COLOR1, P3, P1, EB, EC, N0, + ST_TET, COLOR1, EK, P3, EC, N0, + // Case #59: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EB, EF, EH, EC, EK, + ST_PYR, COLOR0, P6, EF, EH, P7, N0, + ST_TET, COLOR0, P7, EH, EK, N0, + ST_PYR, COLOR0, P2, EB, EF, P6, N0, + ST_TET, COLOR0, EC, EB, P2, N0, + ST_PYR, COLOR0, EK, EC, P2, P7, N0, + ST_TET, COLOR0, P7, P2, P6, N0, + ST_PYR, COLOR1, P0, P4, P5, P1, N0, + ST_TET, COLOR1, P4, P0, P3, N0, + ST_TET, COLOR1, P3, P0, P1, N0, + ST_PYR, COLOR1, P3, P1, EB, EC, N0, + ST_PYR, COLOR1, EB, P1, P5, EF, N0, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_PYR, COLOR1, EH, P4, P3, EK, N0, + ST_TET, COLOR1, EC, EK, P3, N0, + // Case #60: Unique case #14 + ST_WDG, COLOR0, P1, EB, EJ, P0, ED, EI, + ST_WDG, COLOR0, P6, EF, EL, P7, EH, EK, + ST_HEX, COLOR1, P3, P4, P5, P2, EK, EH, EF, EL, + ST_HEX, COLOR1, ED, EI, EJ, EB, P3, P4, P5, P2, + // Case #61: Unique case #15 + ST_PNT, 0, COLOR1, 6, P0, P2, P3, P4, EF, EH, + ST_WDG, COLOR0, EH, P7, EK, EF, P6, EL, + ST_TET, COLOR0, EA, P1, EB, EJ, + ST_WDG, COLOR1, P0, P5, P2, EA, EJ, EB, + ST_PYR, COLOR1, EH, EF, P5, P4, N0, + ST_TET, COLOR1, P4, P5, P0, N0, + ST_TET, COLOR1, P4, P0, P3, N0, + ST_PYR, COLOR1, EK, EH, P4, P3, N0, + ST_PYR, COLOR1, EL, EK, P3, P2, N0, + ST_TET, COLOR1, P3, P0, P2, N0, + ST_PYR, COLOR1, EF, EH, EK, EL, N0, + ST_TET, COLOR1, P2, P0, P5, N0, + ST_PYR, COLOR1, EF, EL, P2, P5, N0, + // Case #62: (cloned #61) + ST_PNT, 0, COLOR1, 6, P1, P3, P2, P5, EH, EF, + ST_WDG, COLOR0, EH, P7, EK, EF, P6, EL, + ST_TET, COLOR0, EA, ED, P0, EI, + ST_WDG, COLOR1, EA, EI, ED, P1, P4, P3, + ST_PYR, COLOR1, EF, P5, P4, EH, N0, + ST_TET, COLOR1, P5, P1, P4, N0, + ST_TET, COLOR1, P5, P2, P1, N0, + ST_PYR, COLOR1, EL, P2, P5, EF, N0, + ST_PYR, COLOR1, EK, P3, P2, EL, N0, + ST_TET, COLOR1, P2, P3, P1, N0, + ST_PYR, COLOR1, EH, EK, EL, EF, N0, + ST_TET, COLOR1, P3, P4, P1, N0, + ST_PYR, COLOR1, EH, P4, P3, EK, N0, + // Case #63: Unique case #16 + ST_WDG, COLOR0, P7, EK, EH, P6, EL, EF, + ST_HEX, COLOR1, P3, P4, P5, P2, EK, EH, EF, EL, + ST_WDG, COLOR1, P1, P2, P5, P0, P3, P4, + // Case #64: (cloned #1) + ST_PNT, 0, COLOR0, 7, P7, P4, P5, P2, P3, P0, P1, + ST_WDG, COLOR0, EG, EF, EL, P7, P5, P2, + ST_TET, COLOR0, P7, P2, P5, N0, + ST_TET, COLOR0, P7, P5, P4, N0, + ST_PYR, COLOR0, P0, P4, P5, P1, N0, + ST_PYR, COLOR0, P3, P7, P4, P0, N0, + ST_PYR, COLOR0, P2, P3, P0, P1, N0, + ST_TET, COLOR0, P5, P2, P1, N0, + ST_TET, COLOR0, P2, P7, P3, N0, + ST_TET, COLOR1, P6, EF, EG, EL, + // Case #65: (cloned #20) + ST_WDG, COLOR0, P5, P7, P2, EF, EG, EL, + ST_WDG, COLOR0, EI, ED, EA, P4, P3, P1, + ST_TET, COLOR0, P7, P2, P5, P3, + ST_TET, COLOR0, P1, P2, P3, P5, + ST_TET, COLOR0, P4, P5, P1, P3, + ST_TET, COLOR0, P7, P4, P3, P5, + ST_TET, COLOR1, P0, EI, EA, ED, + ST_TET, COLOR1, P6, EF, EG, EL, + // Case #66: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EA, EG, + ST_PYR, COLOR0, P0, P3, P7, P4, N0, + ST_TET, COLOR0, P4, P7, P5, N0, + ST_TET, COLOR0, P0, P4, P5, N0, + ST_TET, COLOR0, P2, P3, P0, N0, + ST_TET, COLOR0, P7, P3, P2, N0, + ST_PYR, COLOR0, P7, P2, EL, EG, N0, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_PYR, COLOR0, P0, P5, EJ, EA, N0, + ST_PYR, COLOR0, P0, EA, EB, P2, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_PYR, COLOR1, P1, P6, EL, EB, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_TET, COLOR1, EF, EG, P6, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + ST_TET, COLOR1, EJ, N0, P1, EA, + ST_TET, COLOR1, EB, EA, P1, N0, + // Case #67: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EI, ED, EG, EG, + ST_PYR, COLOR0, P7, P2, EL, EG, N0, + ST_TET, COLOR0, EL, P2, EB, N0, + ST_PYR, COLOR0, P3, ED, EB, P2, N0, + ST_TET, COLOR0, P7, P3, P2, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_TET, COLOR0, P4, P7, P5, N0, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_TET, COLOR0, P4, P3, P7, N0, + ST_PYR, COLOR0, P4, EI, ED, P3, N0, + ST_PYR, COLOR0, P4, P5, EJ, EI, N0, + ST_PYR, COLOR1, P6, EL, EB, P1, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + ST_TET, COLOR1, EF, EG, P6, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_PYR, COLOR1, P1, P0, EI, EJ, N0, + ST_TET, COLOR1, EI, P0, ED, N0, + // Case #68: (cloned #3) + ST_HEX, COLOR0, EG, P7, P3, EC, EF, P5, P1, EB, + ST_WDG, COLOR0, P1, P0, P3, P5, P4, P7, + ST_WDG, COLOR1, P2, EB, EC, P6, EF, EG, + // Case #69: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EG, EF, EI, EI, + ST_PYR, COLOR0, P4, P1, EA, EI, N0, + ST_TET, COLOR0, EA, P1, EB, N0, + ST_PYR, COLOR0, P5, EF, EB, P1, N0, + ST_TET, COLOR0, P4, P5, P1, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_TET, COLOR0, P7, P4, P3, N0, + ST_PYR, COLOR0, P3, P4, EI, ED, N0, + ST_TET, COLOR0, P7, P5, P4, N0, + ST_PYR, COLOR0, P7, EG, EF, P5, N0, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_PYR, COLOR1, P0, EA, EB, P2, N0, + ST_PYR, COLOR1, EC, ED, P0, P2, N0, + ST_TET, COLOR1, P0, EI, EA, N0, + ST_TET, COLOR1, ED, EI, P0, N0, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_PYR, COLOR1, P2, P6, EG, EC, N0, + ST_TET, COLOR1, EG, P6, EF, N0, + // Case #70: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EA, EC, EJ, EF, EG, + ST_PYR, COLOR0, P0, P3, P7, P4, N0, + ST_TET, COLOR0, P7, N0, P5, P4, + ST_PYR, COLOR0, P3, EC, EG, P7, N0, + ST_PYR, COLOR0, EA, EC, P3, P0, N0, + ST_TET, COLOR0, P5, P0, P4, N0, + ST_PYR, COLOR0, P5, EJ, EA, P0, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_PYR, COLOR0, EG, EF, P5, P7, N0, + ST_PYR, COLOR1, EC, P2, P6, EG, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_TET, COLOR1, EJ, P1, EA, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_TET, COLOR1, P6, EF, EG, N0, + // Case #71: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EJ, EF, EG, EC, ED, EI, + ST_TET, COLOR0, P7, P4, P3, N0, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_TET, COLOR0, P5, P4, P7, N0, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_TET, COLOR0, EJ, P5, EF, N0, + ST_PYR, COLOR0, P5, EJ, EI, P4, N0, + ST_PYR, COLOR0, EI, ED, P3, P4, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_PYR, COLOR1, EJ, P1, P0, EI, N0, + ST_TET, COLOR1, P0, P1, P2, N0, + ST_TET, COLOR1, P0, ED, EI, N0, + ST_PYR, COLOR1, P0, P2, EC, ED, N0, + ST_PYR, COLOR1, EC, P2, P6, EG, N0, + ST_TET, COLOR1, EG, P6, EF, N0, + // Case #72: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, ED, EF, + ST_PYR, COLOR0, P0, P4, P5, P1, N0, + ST_TET, COLOR0, P1, P5, P2, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_TET, COLOR0, P7, P4, P0, N0, + ST_TET, COLOR0, P5, P4, P7, N0, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_PYR, COLOR0, P2, P5, EF, EL, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_PYR, COLOR0, P0, P2, EC, ED, N0, + ST_PYR, COLOR0, P0, ED, EK, P7, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_PYR, COLOR1, P3, P6, EG, EK, N0, + ST_PYR, COLOR1, EC, EL, P6, P3, N0, + ST_TET, COLOR1, EL, EF, P6, N0, + ST_TET, COLOR1, P6, EF, EG, N0, + ST_TET, COLOR1, EC, N0, P3, ED, + ST_TET, COLOR1, EK, ED, P3, N0, + // Case #73: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EA, EI, EF, EF, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_TET, COLOR0, EG, P7, EK, N0, + ST_PYR, COLOR0, P4, EI, EK, P7, N0, + ST_TET, COLOR0, P5, P4, P7, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_TET, COLOR0, P1, P5, P2, N0, + ST_PYR, COLOR0, P2, P5, EF, EL, N0, + ST_TET, COLOR0, P1, P4, P5, N0, + ST_PYR, COLOR0, P1, EA, EI, P4, N0, + ST_PYR, COLOR0, P1, P2, EC, EA, N0, + ST_PYR, COLOR1, P6, EG, EK, P3, N0, + ST_PYR, COLOR1, EC, EL, P6, P3, N0, + ST_TET, COLOR1, P6, EF, EG, N0, + ST_TET, COLOR1, EL, EF, P6, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_PYR, COLOR1, P3, P0, EA, EC, N0, + ST_TET, COLOR1, EA, P0, EI, N0, + // Case #74: (cloned #26) + ST_TET, COLOR0, P2, EB, EL, EC, + ST_TET, COLOR0, P7, P4, P0, P5, + ST_PYR, COLOR0, EJ, EA, P0, P5, ED, + ST_PYR, COLOR0, EF, P5, P7, EG, EK, + ST_PYR, COLOR0, P0, ED, EK, P7, P5, + ST_PYR, COLOR0, ED, EJ, EF, EK, P5, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_PYR, COLOR1, P1, P3, ED, EA, EJ, + ST_PYR, COLOR1, EK, P3, P6, EG, EF, + ST_PYR, COLOR1, EJ, EF, P6, P1, P3, + ST_PYR, COLOR1, EJ, ED, EK, EF, P3, + // Case #75: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EI, EK, EJ, EF, EG, + ST_TET, COLOR0, P2, EC, EB, EL, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_PYR, COLOR0, P4, P5, EJ, EI, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_TET, COLOR0, P4, P7, P5, N0, + ST_PYR, COLOR0, P5, P7, EG, EF, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_WDG, COLOR1, EB, EC, EL, P1, P3, P6, + ST_TET, COLOR1, P3, P1, P6, N0, + ST_PYR, COLOR1, P1, EJ, EF, P6, N0, + ST_TET, COLOR1, P0, P1, P3, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_PYR, COLOR1, EI, EJ, P1, P0, N0, + ST_PYR, COLOR1, P6, EG, EK, P3, N0, + ST_TET, COLOR1, EF, EG, P6, N0, + // Case #76: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, ED, EB, EK, EG, EF, + ST_PYR, COLOR0, P0, P4, P5, P1, N0, + ST_TET, COLOR0, P5, P7, N0, P4, + ST_PYR, COLOR0, P1, P5, EF, EB, N0, + ST_PYR, COLOR0, ED, P0, P1, EB, N0, + ST_TET, COLOR0, P7, P4, P0, N0, + ST_PYR, COLOR0, P7, P0, ED, EK, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_PYR, COLOR0, EF, P5, P7, EG, N0, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_TET, COLOR1, EK, ED, P3, N0, + ST_TET, COLOR1, P3, P2, P6, N0, + ST_PYR, COLOR1, EK, P3, P6, EG, N0, + ST_TET, COLOR1, P6, EF, EG, N0, + // Case #77: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EK, EG, EF, EB, EA, EI, + ST_TET, COLOR0, P5, P1, P4, N0, + ST_PYR, COLOR0, P5, EF, EB, P1, N0, + ST_TET, COLOR0, P7, P5, P4, N0, + ST_PYR, COLOR0, P7, EG, EF, P5, N0, + ST_TET, COLOR0, EK, EG, P7, N0, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_PYR, COLOR0, EI, P4, P1, EA, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_TET, COLOR1, P3, P2, P6, N0, + ST_PYR, COLOR1, EK, P3, P6, EG, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_TET, COLOR1, P0, P2, P3, N0, + ST_TET, COLOR1, P0, EI, EA, N0, + ST_PYR, COLOR1, P0, EA, EB, P2, N0, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_TET, COLOR1, EF, EG, P6, N0, + // Case #78: (cloned #27) + ST_TET, COLOR0, P7, P4, P0, P5, + ST_PYR, COLOR0, EJ, EA, P0, P5, ED, + ST_PYR, COLOR0, EF, P5, P7, EG, EK, + ST_PYR, COLOR0, P0, ED, EK, P7, P5, + ST_PYR, COLOR0, ED, EJ, EF, EK, P5, + ST_TET, COLOR1, P2, P1, P6, P3, + ST_PYR, COLOR1, ED, EA, P1, P3, EJ, + ST_PYR, COLOR1, EK, P3, P6, EG, EF, + ST_PYR, COLOR1, P1, EJ, EF, P6, P3, + ST_PYR, COLOR1, ED, EK, EF, EJ, P3, + // Case #79: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EK, EI, EJ, EG, EF, + ST_PYR, COLOR0, P4, P5, EJ, EI, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_PYR, COLOR0, P7, P4, EI, EK, N0, + ST_TET, COLOR0, EG, P7, EK, N0, + ST_PYR, COLOR0, EF, P5, P7, EG, N0, + ST_TET, COLOR0, P5, P4, P7, N0, + ST_PYR, COLOR1, P2, P3, P0, P1, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + ST_TET, COLOR1, P6, P3, P2, N0, + ST_PYR, COLOR1, P6, EG, EK, P3, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_PYR, COLOR1, EI, EJ, P1, P0, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_TET, COLOR1, EG, P6, EF, N0, + // Case #80: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EI, EL, + ST_PYR, COLOR0, P0, P1, P2, P3, N0, + ST_TET, COLOR0, P1, P5, P2, N0, + ST_TET, COLOR0, P0, P5, P1, N0, + ST_TET, COLOR0, P7, P0, P3, N0, + ST_TET, COLOR0, P2, P7, P3, N0, + ST_PYR, COLOR0, P2, EL, EG, P7, N0, + ST_PYR, COLOR0, P5, EF, EL, P2, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_PYR, COLOR0, P0, EI, EE, P5, N0, + ST_PYR, COLOR0, P0, P7, EH, EI, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_PYR, COLOR1, P4, EH, EG, P6, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_TET, COLOR1, EF, P6, EL, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + ST_TET, COLOR1, EE, P4, N0, EI, + ST_TET, COLOR1, EH, P4, EI, N0, + // Case #81: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EA, ED, EL, EL, + ST_PYR, COLOR0, P2, EL, EG, P7, N0, + ST_TET, COLOR0, EG, EH, P7, N0, + ST_PYR, COLOR0, P3, P7, EH, ED, N0, + ST_TET, COLOR0, P2, P7, P3, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_TET, COLOR0, P1, P5, P2, N0, + ST_PYR, COLOR0, P5, EF, EL, P2, N0, + ST_TET, COLOR0, P1, P2, P3, N0, + ST_PYR, COLOR0, P1, P3, ED, EA, N0, + ST_PYR, COLOR0, P1, EA, EE, P5, N0, + ST_PYR, COLOR1, P6, P4, EH, EG, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + ST_TET, COLOR1, EF, P6, EL, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, P4, EE, EA, P0, N0, + ST_TET, COLOR1, EA, ED, P0, N0, + // Case #82: (cloned #26) + ST_TET, COLOR0, P5, EF, EJ, EE, + ST_TET, COLOR0, P7, P0, P3, P2, + ST_PYR, COLOR0, EB, P2, P0, EA, EI, + ST_PYR, COLOR0, EL, EG, P7, P2, EH, + ST_PYR, COLOR0, P0, P7, EH, EI, P2, + ST_PYR, COLOR0, EI, EH, EL, EB, P2, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_PYR, COLOR1, P1, EA, EI, P4, EB, + ST_PYR, COLOR1, EH, EG, P6, P4, EL, + ST_PYR, COLOR1, EB, P1, P6, EL, P4, + ST_PYR, COLOR1, EB, EL, EH, EI, P4, + // Case #83: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, ED, EH, EB, EL, EG, + ST_TET, COLOR0, P5, EJ, EE, EF, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_PYR, COLOR0, P3, ED, EB, P2, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_TET, COLOR0, P3, P2, P7, N0, + ST_PYR, COLOR0, P2, EL, EG, P7, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_WDG, COLOR1, P1, P4, P6, EJ, EE, EF, + ST_TET, COLOR1, P4, P6, P1, N0, + ST_PYR, COLOR1, P1, P6, EL, EB, N0, + ST_TET, COLOR1, P0, P4, P1, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_PYR, COLOR1, P6, P4, EH, EG, N0, + ST_TET, COLOR1, EL, P6, EG, N0, + // Case #84: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EC, EB, EI, EI, + ST_PYR, COLOR0, P0, EI, EE, P5, N0, + ST_TET, COLOR0, EE, EF, P5, N0, + ST_PYR, COLOR0, P1, P5, EF, EB, N0, + ST_TET, COLOR0, P0, P5, P1, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_TET, COLOR0, P3, P7, P0, N0, + ST_PYR, COLOR0, P7, EH, EI, P0, N0, + ST_TET, COLOR0, P3, P0, P1, N0, + ST_PYR, COLOR0, P3, P1, EB, EC, N0, + ST_PYR, COLOR0, P3, EC, EG, P7, N0, + ST_PYR, COLOR1, P4, P6, EF, EE, N0, + ST_PYR, COLOR1, EG, P6, P4, EH, N0, + ST_TET, COLOR1, P4, EE, EI, N0, + ST_TET, COLOR1, EH, P4, EI, N0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_PYR, COLOR1, P6, EG, EC, P2, N0, + ST_TET, COLOR1, EC, EB, P2, N0, + // Case #85: (cloned #60) + ST_WDG, COLOR0, P7, EH, EG, P3, ED, EC, + ST_WDG, COLOR0, P5, EF, EE, P1, EB, EA, + ST_HEX, COLOR1, P0, P2, P6, P4, EA, EB, EF, EE, + ST_HEX, COLOR1, ED, EC, EG, EH, P0, P2, P6, P4, + // Case #86: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EC, EG, EA, EI, EH, + ST_TET, COLOR0, P5, EF, EJ, EE, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_TET, COLOR0, P3, P7, P0, N0, + ST_PYR, COLOR0, P0, P7, EH, EI, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_TET, COLOR1, P6, P1, P4, N0, + ST_PYR, COLOR1, P1, EA, EI, P4, N0, + ST_TET, COLOR1, P2, P1, P6, N0, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_PYR, COLOR1, P4, EH, EG, P6, N0, + ST_TET, COLOR1, EI, EH, P4, N0, + // Case #87: (cloned #61) + ST_PNT, 0, COLOR1, 6, P1, P4, P0, P2, EG, EC, + ST_WDG, COLOR0, EG, P7, EH, EC, P3, ED, + ST_TET, COLOR0, EJ, EE, P5, EF, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_PYR, COLOR1, EC, P2, P6, EG, N0, + ST_TET, COLOR1, P2, P1, P6, N0, + ST_TET, COLOR1, P2, P0, P1, N0, + ST_PYR, COLOR1, ED, P0, P2, EC, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_TET, COLOR1, P0, P4, P1, N0, + ST_PYR, COLOR1, EG, EH, ED, EC, N0, + ST_TET, COLOR1, P4, P6, P1, N0, + ST_PYR, COLOR1, EG, P6, P4, EH, N0, + // Case #88: (cloned #26) + ST_TET, COLOR0, P7, EG, EH, EK, + ST_TET, COLOR0, P2, P0, P1, P5, + ST_PYR, COLOR0, EE, P5, P0, EI, ED, + ST_PYR, COLOR0, EF, EL, P2, P5, EC, + ST_PYR, COLOR0, P0, P2, EC, ED, P5, + ST_PYR, COLOR0, ED, EC, EF, EE, P5, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_PYR, COLOR1, P4, EI, ED, P3, EE, + ST_PYR, COLOR1, EC, EL, P6, P3, EF, + ST_PYR, COLOR1, EE, P4, P6, EF, P3, + ST_PYR, COLOR1, EE, EF, EC, ED, P3, + // Case #89: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EA, EC, EE, EF, EL, + ST_TET, COLOR0, P7, EH, EK, EG, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_PYR, COLOR0, P1, EA, EE, P5, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_TET, COLOR0, P1, P5, P2, N0, + ST_PYR, COLOR0, P5, EF, EL, P2, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_WDG, COLOR1, P4, P3, P6, EH, EK, EG, + ST_TET, COLOR1, P3, P6, P4, N0, + ST_PYR, COLOR1, P4, P6, EF, EE, N0, + ST_TET, COLOR1, P0, P3, P4, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_PYR, COLOR1, EA, P0, P4, EE, N0, + ST_PYR, COLOR1, P6, P3, EC, EL, N0, + ST_TET, COLOR1, EF, P6, EL, N0, + // Case #90: Unique case #17 + ST_TET, COLOR0, EH, EG, EK, P7, + ST_TET, COLOR0, EI, ED, EA, P0, + ST_TET, COLOR0, EE, EJ, EF, P5, + ST_TET, COLOR0, EB, EC, EL, P2, + ST_WDG, COLOR1, EB, EC, EL, P1, P3, P6, + ST_TET, COLOR1, P1, P6, P3, P4, + ST_WDG, COLOR1, P4, P6, P1, EE, EF, EJ, + ST_WDG, COLOR1, P3, P4, P1, ED, EI, EA, + ST_WDG, COLOR1, P6, P4, P3, EG, EH, EK, + // Case #91: Unique case #18 + ST_TET, COLOR0, EE, EJ, EF, P5, + ST_TET, COLOR0, EH, EK, P7, EG, + ST_TET, COLOR0, EB, P2, EC, EL, + ST_WDG, COLOR1, P6, P3, P1, EL, EC, EB, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_WDG, COLOR1, P4, P6, P1, EE, EF, EJ, + ST_TET, COLOR1, P6, P3, P1, P4, + ST_TET, COLOR1, P4, P1, P0, P3, + // Case #92: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EB, EF, ED, EI, EE, + ST_TET, COLOR0, P7, EK, EG, EH, + ST_PYR, COLOR0, P5, EF, EB, P1, N0, + ST_PYR, COLOR0, P1, EB, ED, P0, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_TET, COLOR0, P1, P0, P5, N0, + ST_PYR, COLOR0, P0, EI, EE, P5, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_WDG, COLOR1, P3, P6, P4, EK, EG, EH, + ST_TET, COLOR1, P6, P4, P3, N0, + ST_PYR, COLOR1, P3, P4, EI, ED, N0, + ST_TET, COLOR1, P2, P6, P3, N0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_PYR, COLOR1, P4, P6, EF, EE, N0, + ST_TET, COLOR1, EI, P4, EE, N0, + // Case #93: (cloned #61) + ST_PNT, 0, COLOR1, 6, P3, P4, P0, P2, EF, EB, + ST_WDG, COLOR0, EB, P1, EA, EF, P5, EE, + ST_TET, COLOR0, EK, P7, EH, EG, + ST_WDG, COLOR1, P3, P6, P4, EK, EG, EH, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_TET, COLOR1, P2, P6, P3, N0, + ST_TET, COLOR1, P2, P3, P0, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_PYR, COLOR1, EE, EA, P0, P4, N0, + ST_TET, COLOR1, P0, P3, P4, N0, + ST_PYR, COLOR1, EF, EB, EA, EE, N0, + ST_TET, COLOR1, P4, P3, P6, N0, + ST_PYR, COLOR1, EF, EE, P4, P6, N0, + // Case #94: (cloned #91) + ST_TET, COLOR0, EK, EH, EG, P7, + ST_TET, COLOR0, ED, P0, EA, EI, + ST_TET, COLOR0, EF, EJ, P5, EE, + ST_WDG, COLOR1, EE, EJ, EF, P4, P1, P6, + ST_WDG, COLOR1, P3, P4, P1, ED, EI, EA, + ST_WDG, COLOR1, EK, EH, EG, P3, P4, P6, + ST_TET, COLOR1, P4, P6, P1, P3, + ST_TET, COLOR1, P3, P2, P6, P1, + // Case #95: Unique case #19 + ST_TET, COLOR0, EG, EK, EH, P7, + ST_TET, COLOR0, EF, EE, EJ, P5, + ST_WDG, COLOR1, P4, P6, P1, EE, EF, EJ, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_PYR, COLOR1, P0, P1, P2, P3, P4, + ST_TET, COLOR1, P6, P3, P2, P4, + ST_TET, COLOR1, P6, P2, P1, P4, + // Case #96: (cloned #3) + ST_HEX, COLOR0, EL, P2, P1, EJ, EG, P7, P4, EE, + ST_WDG, COLOR0, P2, P3, P7, P1, P0, P4, + ST_WDG, COLOR1, P6, EL, EG, P5, EJ, EE, + // Case #97: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EG, EL, ED, ED, + ST_PYR, COLOR0, P3, ED, EA, P1, N0, + ST_TET, COLOR0, EA, EJ, P1, N0, + ST_PYR, COLOR0, P2, P1, EJ, EL, N0, + ST_TET, COLOR0, P3, P1, P2, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_TET, COLOR0, P7, P4, P3, N0, + ST_PYR, COLOR0, P4, EI, ED, P3, N0, + ST_TET, COLOR0, P7, P3, P2, N0, + ST_PYR, COLOR0, P7, P2, EL, EG, N0, + ST_PYR, COLOR0, P7, EG, EE, P4, N0, + ST_PYR, COLOR1, P0, P5, EJ, EA, N0, + ST_PYR, COLOR1, EE, P5, P0, EI, N0, + ST_TET, COLOR1, P0, EA, ED, N0, + ST_TET, COLOR1, EI, P0, ED, N0, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_PYR, COLOR1, P5, EE, EG, P6, N0, + ST_TET, COLOR1, EG, EL, P6, N0, + // Case #98: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EA, EE, EB, EL, EG, + ST_PYR, COLOR0, P0, P3, P7, P4, N0, + ST_TET, COLOR0, P7, P2, N0, P3, + ST_PYR, COLOR0, P4, P7, EG, EE, N0, + ST_PYR, COLOR0, EA, P0, P4, EE, N0, + ST_TET, COLOR0, P2, P3, P0, N0, + ST_PYR, COLOR0, P2, P0, EA, EB, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_PYR, COLOR0, EG, P7, P2, EL, N0, + ST_PYR, COLOR1, EE, EG, P6, P5, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_TET, COLOR1, EB, EA, P1, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + ST_PYR, COLOR1, EB, P1, P6, EL, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + // Case #99: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EB, EL, EG, EE, EI, ED, + ST_TET, COLOR0, P7, P4, P3, N0, + ST_PYR, COLOR0, P7, EG, EE, P4, N0, + ST_TET, COLOR0, P2, P7, P3, N0, + ST_PYR, COLOR0, P2, EL, EG, P7, N0, + ST_TET, COLOR0, EB, EL, P2, N0, + ST_PYR, COLOR0, P2, P3, ED, EB, N0, + ST_PYR, COLOR0, ED, P3, P4, EI, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + ST_PYR, COLOR1, EB, P1, P6, EL, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_TET, COLOR1, P0, P5, P1, N0, + ST_TET, COLOR1, P0, ED, EI, N0, + ST_PYR, COLOR1, P0, EI, EE, P5, N0, + ST_PYR, COLOR1, EE, EG, P6, P5, N0, + ST_TET, COLOR1, EG, EL, P6, N0, + // Case #100: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EC, EG, EB, EJ, EE, + ST_PYR, COLOR0, P3, P7, P4, P0, N0, + ST_TET, COLOR0, P4, N0, P1, P0, + ST_PYR, COLOR0, P7, EG, EE, P4, N0, + ST_PYR, COLOR0, EC, EG, P7, P3, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_PYR, COLOR0, P1, EB, EC, P3, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_PYR, COLOR0, EE, EJ, P1, P4, N0, + ST_PYR, COLOR1, EG, P6, P5, EE, N0, + ST_PYR, COLOR1, EC, P2, P6, EG, N0, + ST_TET, COLOR1, EB, P2, EC, N0, + ST_TET, COLOR1, P2, P5, P6, N0, + ST_PYR, COLOR1, EB, EJ, P5, P2, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + // Case #101: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EG, EE, EC, ED, EI, + ST_TET, COLOR0, P1, EJ, EB, EA, + ST_PYR, COLOR0, P4, P7, EG, EE, N0, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_TET, COLOR0, P7, P4, P3, N0, + ST_PYR, COLOR0, P3, P4, EI, ED, N0, + ST_TET, COLOR0, P4, EE, EI, N0, + ST_WDG, COLOR1, EB, EJ, EA, P2, P5, P0, + ST_TET, COLOR1, P5, P2, P0, N0, + ST_PYR, COLOR1, P2, EC, ED, P0, N0, + ST_TET, COLOR1, P6, P2, P5, N0, + ST_PYR, COLOR1, EE, EG, P6, P5, N0, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_PYR, COLOR1, P0, EI, EE, P5, N0, + ST_TET, COLOR1, ED, EI, P0, N0, + // Case #102: (cloned #15) + ST_HEX, COLOR0, EA, EE, EG, EC, P0, P4, P7, P3, + ST_HEX, COLOR1, P1, P5, P6, P2, EA, EE, EG, EC, + // Case #103: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EE, EG, EC, EI, ED, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_TET, COLOR0, P3, ED, EC, N0, + ST_PYR, COLOR0, P4, P7, EG, EE, N0, + ST_TET, COLOR0, EI, P4, EE, N0, + ST_PYR, COLOR0, ED, P3, P4, EI, N0, + ST_TET, COLOR0, P3, P7, P4, N0, + ST_PYR, COLOR1, P1, P5, P6, P2, N0, + ST_TET, COLOR1, P2, P0, P1, N0, + ST_TET, COLOR1, P0, P5, P1, N0, + ST_PYR, COLOR1, P0, EI, EE, P5, N0, + ST_PYR, COLOR1, EE, EG, P6, P5, N0, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_PYR, COLOR1, EC, ED, P0, P2, N0, + ST_TET, COLOR1, EI, P0, ED, N0, + // Case #104: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EE, EJ, ED, ED, + ST_PYR, COLOR0, P0, P2, EC, ED, N0, + ST_TET, COLOR0, EC, P2, EL, N0, + ST_PYR, COLOR0, P1, EJ, EL, P2, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_TET, COLOR0, P4, P0, P7, N0, + ST_PYR, COLOR0, P7, P0, ED, EK, N0, + ST_TET, COLOR0, P4, P1, P0, N0, + ST_PYR, COLOR0, P4, EE, EJ, P1, N0, + ST_PYR, COLOR0, P4, P7, EG, EE, N0, + ST_PYR, COLOR1, P3, EC, EL, P6, N0, + ST_PYR, COLOR1, EG, EK, P3, P6, N0, + ST_TET, COLOR1, P3, ED, EC, N0, + ST_TET, COLOR1, EK, ED, P3, N0, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_PYR, COLOR1, P6, P5, EE, EG, N0, + ST_TET, COLOR1, EE, P5, EJ, N0, + // Case #105: (cloned #60) + ST_WDG, COLOR0, P4, EI, EE, P7, EK, EG, + ST_WDG, COLOR0, P1, EJ, EA, P2, EL, EC, + ST_HEX, COLOR1, EA, EJ, EL, EC, P0, P5, P6, P3, + ST_HEX, COLOR1, P0, P5, P6, P3, EI, EE, EG, EK, + // Case #106: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EE, EG, EA, ED, EK, + ST_TET, COLOR0, P2, EB, EL, EC, + ST_PYR, COLOR0, P7, EG, EE, P4, N0, + ST_PYR, COLOR0, P4, EE, EA, P0, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_TET, COLOR0, P4, P0, P7, N0, + ST_PYR, COLOR0, P0, ED, EK, P7, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_TET, COLOR1, P6, P3, P1, N0, + ST_PYR, COLOR1, P1, P3, ED, EA, N0, + ST_TET, COLOR1, P5, P6, P1, N0, + ST_PYR, COLOR1, EG, P6, P5, EE, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_PYR, COLOR1, P3, P6, EG, EK, N0, + ST_TET, COLOR1, ED, P3, EK, N0, + // Case #107: (cloned #61) + ST_PNT, 0, COLOR1, 6, P1, P3, P0, P5, EG, EE, + ST_WDG, COLOR0, EE, P4, EI, EG, P7, EK, + ST_TET, COLOR0, EB, P2, EC, EL, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_PYR, COLOR1, EE, EG, P6, P5, N0, + ST_TET, COLOR1, P5, P6, P1, N0, + ST_TET, COLOR1, P5, P1, P0, N0, + ST_PYR, COLOR1, EI, EE, P5, P0, N0, + ST_PYR, COLOR1, EK, EI, P0, P3, N0, + ST_TET, COLOR1, P0, P1, P3, N0, + ST_PYR, COLOR1, EG, EE, EI, EK, N0, + ST_TET, COLOR1, P3, P1, P6, N0, + ST_PYR, COLOR1, EG, EK, P3, P6, N0, + // Case #108: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EB, EJ, EE, EG, EK, ED, + ST_TET, COLOR0, P4, P0, P7, N0, + ST_PYR, COLOR0, P4, P7, EG, EE, N0, + ST_TET, COLOR0, P1, P0, P4, N0, + ST_PYR, COLOR0, P1, P4, EE, EJ, N0, + ST_TET, COLOR0, EB, P1, EJ, N0, + ST_PYR, COLOR0, P1, EB, ED, P0, N0, + ST_PYR, COLOR0, ED, EK, P7, P0, N0, + ST_TET, COLOR0, P7, EK, EG, N0, + ST_TET, COLOR1, P2, P5, P6, N0, + ST_PYR, COLOR1, EB, EJ, P5, P2, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_TET, COLOR1, P3, P2, P6, N0, + ST_TET, COLOR1, P3, EK, ED, N0, + ST_PYR, COLOR1, P3, P6, EG, EK, N0, + ST_PYR, COLOR1, EG, P6, P5, EE, N0, + ST_TET, COLOR1, EE, P5, EJ, N0, + // Case #109: (cloned #61) + ST_PNT, 0, COLOR1, 6, P2, P0, P3, P6, EE, EG, + ST_WDG, COLOR0, EE, P4, EI, EG, P7, EK, + ST_TET, COLOR0, EB, EA, P1, EJ, + ST_WDG, COLOR1, EB, EJ, EA, P2, P5, P0, + ST_PYR, COLOR1, EG, P6, P5, EE, N0, + ST_TET, COLOR1, P6, P2, P5, N0, + ST_TET, COLOR1, P6, P3, P2, N0, + ST_PYR, COLOR1, EK, P3, P6, EG, N0, + ST_PYR, COLOR1, EI, P0, P3, EK, N0, + ST_TET, COLOR1, P3, P0, P2, N0, + ST_PYR, COLOR1, EE, EI, EK, EG, N0, + ST_TET, COLOR1, P0, P5, P2, N0, + ST_PYR, COLOR1, EE, P5, P0, EI, N0, + // Case #110: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EG, EE, EA, EK, ED, + ST_PYR, COLOR0, P4, EE, EA, P0, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_PYR, COLOR0, P7, EG, EE, P4, N0, + ST_TET, COLOR0, EK, EG, P7, N0, + ST_PYR, COLOR0, ED, EK, P7, P0, N0, + ST_TET, COLOR0, P0, P7, P4, N0, + ST_PYR, COLOR1, P2, P1, P5, P6, N0, + ST_TET, COLOR1, P1, P2, P3, N0, + ST_TET, COLOR1, P3, P2, P6, N0, + ST_PYR, COLOR1, P3, P6, EG, EK, N0, + ST_PYR, COLOR1, EG, P6, P5, EE, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_PYR, COLOR1, EA, P1, P3, ED, N0, + ST_TET, COLOR1, EK, ED, P3, N0, + // Case #111: (cloned #63) + ST_WDG, COLOR0, P4, EI, EE, P7, EK, EG, + ST_HEX, COLOR1, P0, P5, P6, P3, EI, EE, EG, EK, + ST_WDG, COLOR1, P2, P3, P6, P1, P0, P5, + // Case #112: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EI, EJ, EH, EG, EL, + ST_PYR, COLOR0, P0, P1, P2, P3, N0, + ST_TET, COLOR0, P2, N0, P7, P3, + ST_PYR, COLOR0, P1, EJ, EL, P2, N0, + ST_PYR, COLOR0, EI, EJ, P1, P0, N0, + ST_TET, COLOR0, P7, P0, P3, N0, + ST_PYR, COLOR0, P7, EH, EI, P0, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_PYR, COLOR0, EL, EG, P7, P2, N0, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_TET, COLOR1, EH, P4, EI, N0, + ST_TET, COLOR1, P4, P6, P5, N0, + ST_PYR, COLOR1, EH, EG, P6, P4, N0, + ST_TET, COLOR1, P6, EG, EL, N0, + // Case #113: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EH, EG, EL, EJ, EA, ED, + ST_TET, COLOR0, P2, P3, P1, N0, + ST_PYR, COLOR0, P2, P1, EJ, EL, N0, + ST_TET, COLOR0, P7, P3, P2, N0, + ST_PYR, COLOR0, P7, P2, EL, EG, N0, + ST_TET, COLOR0, EH, P7, EG, N0, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_PYR, COLOR0, ED, EA, P1, P3, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_TET, COLOR1, P4, P6, P5, N0, + ST_PYR, COLOR1, EH, EG, P6, P4, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_TET, COLOR1, P0, EA, ED, N0, + ST_PYR, COLOR1, P0, P5, EJ, EA, N0, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_TET, COLOR1, EL, P6, EG, N0, + // Case #114: (cloned #27) + ST_TET, COLOR0, P7, P0, P3, P2, + ST_PYR, COLOR0, EB, P2, P0, EA, EI, + ST_PYR, COLOR0, EL, EG, P7, P2, EH, + ST_PYR, COLOR0, P0, P7, EH, EI, P2, + ST_PYR, COLOR0, EI, EH, EL, EB, P2, + ST_TET, COLOR1, P5, P6, P1, P4, + ST_PYR, COLOR1, EI, P4, P1, EA, EB, + ST_PYR, COLOR1, EH, EG, P6, P4, EL, + ST_PYR, COLOR1, P1, P6, EL, EB, P4, + ST_PYR, COLOR1, EI, EB, EL, EH, P4, + // Case #115: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EH, ED, EB, EG, EL, + ST_PYR, COLOR0, P3, ED, EB, P2, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_PYR, COLOR0, P7, EH, ED, P3, N0, + ST_TET, COLOR0, EG, EH, P7, N0, + ST_PYR, COLOR0, EL, EG, P7, P2, N0, + ST_TET, COLOR0, P2, P7, P3, N0, + ST_PYR, COLOR1, P5, P1, P0, P4, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + ST_TET, COLOR1, P6, P5, P4, N0, + ST_PYR, COLOR1, P6, P4, EH, EG, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_PYR, COLOR1, EB, P1, P6, EL, N0, + ST_TET, COLOR1, EG, EL, P6, N0, + // Case #116: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EJ, EB, EC, EG, EH, EI, + ST_TET, COLOR0, P3, P7, P0, N0, + ST_PYR, COLOR0, P3, EC, EG, P7, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_PYR, COLOR0, P1, EB, EC, P3, N0, + ST_TET, COLOR0, EJ, EB, P1, N0, + ST_PYR, COLOR0, P1, P0, EI, EJ, N0, + ST_PYR, COLOR0, EI, P0, P7, EH, N0, + ST_TET, COLOR0, P7, EG, EH, N0, + ST_TET, COLOR1, P5, P6, P2, N0, + ST_PYR, COLOR1, EJ, P5, P2, EB, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_TET, COLOR1, P4, P6, P5, N0, + ST_TET, COLOR1, P4, EI, EH, N0, + ST_PYR, COLOR1, P4, EH, EG, P6, N0, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_TET, COLOR1, EC, EB, P2, N0, + // Case #117: (cloned #61) + ST_PNT, 0, COLOR1, 6, P5, P0, P4, P6, EC, EG, + ST_WDG, COLOR0, EG, P7, EH, EC, P3, ED, + ST_TET, COLOR0, EJ, P1, EA, EB, + ST_WDG, COLOR1, P5, P2, P0, EJ, EB, EA, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_TET, COLOR1, P6, P2, P5, N0, + ST_TET, COLOR1, P6, P5, P4, N0, + ST_PYR, COLOR1, EH, EG, P6, P4, N0, + ST_PYR, COLOR1, ED, EH, P4, P0, N0, + ST_TET, COLOR1, P4, P5, P0, N0, + ST_PYR, COLOR1, EC, EG, EH, ED, N0, + ST_TET, COLOR1, P0, P5, P2, N0, + ST_PYR, COLOR1, EC, ED, P0, P2, N0, + // Case #118: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EG, EC, EA, EH, EI, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_PYR, COLOR0, P7, P3, EC, EG, N0, + ST_TET, COLOR0, EH, P7, EG, N0, + ST_PYR, COLOR0, EI, P0, P7, EH, N0, + ST_TET, COLOR0, P0, P3, P7, N0, + ST_PYR, COLOR1, P5, P6, P2, P1, N0, + ST_TET, COLOR1, P1, P4, P5, N0, + ST_TET, COLOR1, P4, P6, P5, N0, + ST_PYR, COLOR1, P4, EH, EG, P6, N0, + ST_PYR, COLOR1, EG, EC, P2, P6, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_PYR, COLOR1, EA, EI, P4, P1, N0, + ST_TET, COLOR1, EH, P4, EI, N0, + // Case #119: (cloned #63) + ST_WDG, COLOR0, P7, EH, EG, P3, ED, EC, + ST_HEX, COLOR1, ED, EC, EG, EH, P0, P2, P6, P4, + ST_WDG, COLOR1, P1, P0, P2, P5, P4, P6, + // Case #120: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EJ, EL, EI, ED, EC, + ST_TET, COLOR0, P7, EG, EH, EK, + ST_PYR, COLOR0, P2, P1, EJ, EL, N0, + ST_PYR, COLOR0, P1, P0, EI, EJ, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_TET, COLOR0, P1, P2, P0, N0, + ST_PYR, COLOR0, P0, P2, EC, ED, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_TET, COLOR1, P6, P4, P3, N0, + ST_PYR, COLOR1, P4, EI, ED, P3, N0, + ST_TET, COLOR1, P5, P4, P6, N0, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_PYR, COLOR1, P3, EC, EL, P6, N0, + ST_TET, COLOR1, ED, EC, P3, N0, + // Case #121: (cloned #61) + ST_PNT, 0, COLOR1, 6, P4, P3, P0, P5, EL, EJ, + ST_WDG, COLOR0, EL, P2, EC, EJ, P1, EA, + ST_TET, COLOR0, EH, EK, P7, EG, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_TET, COLOR1, P5, P4, P6, N0, + ST_TET, COLOR1, P5, P0, P4, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_TET, COLOR1, P0, P3, P4, N0, + ST_PYR, COLOR1, EL, EC, EA, EJ, N0, + ST_TET, COLOR1, P3, P6, P4, N0, + ST_PYR, COLOR1, EL, P6, P3, EC, N0, + // Case #122: (cloned #91) + ST_TET, COLOR0, EH, EG, EK, P7, + ST_TET, COLOR0, EI, EA, P0, ED, + ST_TET, COLOR0, EL, P2, EB, EC, + ST_WDG, COLOR1, P3, P1, P6, EC, EB, EL, + ST_WDG, COLOR1, EI, ED, EA, P4, P3, P1, + ST_WDG, COLOR1, P4, P3, P6, EH, EK, EG, + ST_TET, COLOR1, P3, P1, P6, P4, + ST_TET, COLOR1, P4, P6, P5, P1, + // Case #123: (cloned #95) + ST_TET, COLOR0, EG, EK, EH, P7, + ST_TET, COLOR0, EL, EB, EC, P2, + ST_WDG, COLOR1, EC, EL, EB, P3, P6, P1, + ST_WDG, COLOR1, P3, P6, P4, EK, EG, EH, + ST_PYR, COLOR1, P0, P4, P5, P1, P3, + ST_TET, COLOR1, P6, P5, P4, P3, + ST_TET, COLOR1, P6, P1, P5, P3, + // Case #124: (cloned #61) + ST_PNT, 0, COLOR1, 6, P6, P4, P5, P2, ED, EB, + ST_WDG, COLOR0, ED, P0, EI, EB, P1, EJ, + ST_TET, COLOR0, EG, EH, P7, EK, + ST_WDG, COLOR1, EG, EK, EH, P6, P3, P4, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_TET, COLOR1, P2, P6, P3, N0, + ST_TET, COLOR1, P2, P5, P6, N0, + ST_PYR, COLOR1, EJ, P5, P2, EB, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_TET, COLOR1, P5, P4, P6, N0, + ST_PYR, COLOR1, ED, EI, EJ, EB, N0, + ST_TET, COLOR1, P4, P3, P6, N0, + ST_PYR, COLOR1, ED, P3, P4, EI, N0, + // Case #125: Unique case #20 + ST_TET, COLOR0, EG, EK, EH, P7, + ST_TET, COLOR0, EJ, EA, EB, P1, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_WDG, COLOR1, EJ, EA, EB, P5, P0, P2, + ST_TET, COLOR1, P2, P3, P0, P5, + ST_TET, COLOR1, P5, P4, P6, P3, + ST_TET, COLOR1, P4, P5, P0, P3, + ST_TET, COLOR1, P5, P6, P2, P3, + // Case #126: (cloned #95) + ST_TET, COLOR0, EI, ED, EA, P0, + ST_TET, COLOR0, EH, EG, EK, P7, + ST_WDG, COLOR1, EK, EH, EG, P3, P4, P6, + ST_WDG, COLOR1, P3, P4, P1, ED, EI, EA, + ST_PYR, COLOR1, P2, P1, P5, P6, P3, + ST_TET, COLOR1, P4, P5, P1, P3, + ST_TET, COLOR1, P4, P6, P5, P3, + // Case #127: Unique case #21 + ST_PNT, 0, COLOR1, 7, P0, P1, P2, P3, P4, P5, P6, + ST_TET, COLOR0, EH, EG, EK, P7, + ST_WDG, COLOR1, EH, EG, EK, P4, P6, P3, + ST_TET, COLOR1, P4, P3, P6, N0, + ST_PYR, COLOR1, P5, P6, P2, P1, N0, + ST_TET, COLOR1, P6, P3, P2, N0, + ST_PYR, COLOR1, P0, P1, P2, P3, N0, + ST_TET, COLOR1, P0, P3, P4, N0, + ST_PYR, COLOR1, P0, P4, P5, P1, N0, + ST_TET, COLOR1, P4, P6, P5, N0, + // Case #128: (cloned #1) + ST_PNT, 0, COLOR0, 7, P6, P5, P4, P3, P2, P1, P0, + ST_WDG, COLOR0, P6, P4, P3, EG, EH, EK, + ST_TET, COLOR0, P6, P4, P3, N0, + ST_TET, COLOR0, P6, P5, P4, N0, + ST_PYR, COLOR0, P1, P0, P4, P5, N0, + ST_PYR, COLOR0, P2, P1, P5, P6, N0, + ST_PYR, COLOR0, P3, P0, P1, P2, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_TET, COLOR0, P3, P2, P6, N0, + ST_TET, COLOR1, P7, EG, EH, EK, + // Case #129: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EA, EG, + ST_PYR, COLOR0, P1, P5, P6, P2, N0, + ST_TET, COLOR0, P5, P4, P6, N0, + ST_TET, COLOR0, P1, P4, P5, N0, + ST_TET, COLOR0, P3, P1, P2, N0, + ST_TET, COLOR0, P6, P3, P2, N0, + ST_PYR, COLOR0, P6, EG, EK, P3, N0, + ST_PYR, COLOR0, P4, EH, EG, P6, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_PYR, COLOR0, P1, EA, EI, P4, N0, + ST_PYR, COLOR0, P1, P3, ED, EA, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_PYR, COLOR1, P0, ED, EK, P7, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_TET, COLOR1, EH, P7, EG, N0, + ST_TET, COLOR1, P7, EK, EG, N0, + ST_TET, COLOR1, EI, P0, N0, EA, + ST_TET, COLOR1, ED, P0, EA, N0, + // Case #130: (cloned #20) + ST_WDG, COLOR0, P4, P3, P6, EH, EK, EG, + ST_WDG, COLOR0, EA, EB, EJ, P0, P2, P5, + ST_TET, COLOR0, P3, P6, P4, P2, + ST_TET, COLOR0, P5, P6, P2, P4, + ST_TET, COLOR0, P0, P4, P5, P2, + ST_TET, COLOR0, P3, P0, P2, P4, + ST_TET, COLOR1, P1, EA, EJ, EB, + ST_TET, COLOR1, P7, EH, EK, EG, + // Case #131: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EJ, EB, EG, EG, + ST_PYR, COLOR0, P6, EG, EK, P3, N0, + ST_TET, COLOR0, EK, ED, P3, N0, + ST_PYR, COLOR0, P2, P3, ED, EB, N0, + ST_TET, COLOR0, P6, P3, P2, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_TET, COLOR0, P5, P4, P6, N0, + ST_PYR, COLOR0, P4, EH, EG, P6, N0, + ST_TET, COLOR0, P5, P6, P2, N0, + ST_PYR, COLOR0, P5, P2, EB, EJ, N0, + ST_PYR, COLOR0, P5, EJ, EI, P4, N0, + ST_PYR, COLOR1, P7, P0, ED, EK, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_TET, COLOR1, P7, EK, EG, N0, + ST_TET, COLOR1, EH, P7, EG, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_PYR, COLOR1, P0, EI, EJ, P1, N0, + ST_TET, COLOR1, EJ, EB, P1, N0, + // Case #132: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EH, EB, + ST_PYR, COLOR0, P4, P5, P1, P0, N0, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_TET, COLOR0, P4, P6, P5, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_PYR, COLOR0, P1, EB, EC, P3, N0, + ST_PYR, COLOR0, P6, EL, EB, P1, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_PYR, COLOR0, P4, EH, EG, P6, N0, + ST_PYR, COLOR0, P4, P3, EK, EH, N0, + ST_TET, COLOR0, P3, EC, EK, N0, + ST_PYR, COLOR1, P7, EK, EC, P2, N0, + ST_PYR, COLOR1, EG, P7, P2, EL, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + ST_TET, COLOR1, P2, EC, EB, N0, + ST_TET, COLOR1, EG, P7, N0, EH, + ST_TET, COLOR1, EK, P7, EH, N0, + // Case #133: (cloned #26) + ST_TET, COLOR0, P3, ED, EC, EK, + ST_TET, COLOR0, P6, P5, P4, P1, + ST_PYR, COLOR0, EA, EI, P4, P1, EH, + ST_PYR, COLOR0, EB, P1, P6, EL, EG, + ST_PYR, COLOR0, P4, EH, EG, P6, P1, + ST_PYR, COLOR0, EH, EA, EB, EG, P1, + ST_WDG, COLOR1, P0, P2, P7, ED, EC, EK, + ST_PYR, COLOR1, P0, P7, EH, EI, EA, + ST_PYR, COLOR1, EG, P7, P2, EL, EB, + ST_PYR, COLOR1, EA, EB, P2, P0, P7, + ST_PYR, COLOR1, EA, EH, EG, EB, P7, + // Case #134: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EJ, EA, EH, EH, + ST_PYR, COLOR0, P4, P3, EK, EH, N0, + ST_TET, COLOR0, EK, P3, EC, N0, + ST_PYR, COLOR0, P0, EA, EC, P3, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_TET, COLOR0, P5, P4, P6, N0, + ST_PYR, COLOR0, P6, P4, EH, EG, N0, + ST_TET, COLOR0, P5, P0, P4, N0, + ST_PYR, COLOR0, P5, EJ, EA, P0, N0, + ST_PYR, COLOR0, P5, P6, EL, EJ, N0, + ST_PYR, COLOR1, P7, EK, EC, P2, N0, + ST_PYR, COLOR1, EL, EG, P7, P2, N0, + ST_TET, COLOR1, P7, EH, EK, N0, + ST_TET, COLOR1, EG, EH, P7, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_PYR, COLOR1, P2, P1, EJ, EL, N0, + ST_TET, COLOR1, EJ, P1, EA, N0, + // Case #135: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EJ, EL, EI, EH, EG, + ST_TET, COLOR0, P3, ED, EC, EK, + ST_PYR, COLOR0, P6, EL, EJ, P5, N0, + ST_PYR, COLOR0, P5, EJ, EI, P4, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_TET, COLOR0, P5, P4, P6, N0, + ST_PYR, COLOR0, P4, EH, EG, P6, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_WDG, COLOR1, P0, P2, P7, ED, EC, EK, + ST_TET, COLOR1, P2, P7, P0, N0, + ST_PYR, COLOR1, P0, P7, EH, EI, N0, + ST_TET, COLOR1, P1, P2, P0, N0, + ST_PYR, COLOR1, EL, P2, P1, EJ, N0, + ST_PYR, COLOR1, EJ, P1, P0, EI, N0, + ST_PYR, COLOR1, P7, P2, EL, EG, N0, + ST_TET, COLOR1, EH, P7, EG, N0, + // Case #136: (cloned #3) + ST_HEX, COLOR0, EH, P4, P0, ED, EG, P6, P2, EC, + ST_WDG, COLOR0, P4, P5, P6, P0, P1, P2, + ST_WDG, COLOR1, P7, EH, EG, P3, ED, EC, + // Case #137: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EA, EC, EI, EH, EG, + ST_PYR, COLOR0, P1, P5, P6, P2, N0, + ST_TET, COLOR0, P6, P4, N0, P5, + ST_PYR, COLOR0, P2, P6, EG, EC, N0, + ST_PYR, COLOR0, EA, P1, P2, EC, N0, + ST_TET, COLOR0, P4, P5, P1, N0, + ST_PYR, COLOR0, P4, P1, EA, EI, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_PYR, COLOR0, EG, P6, P4, EH, N0, + ST_PYR, COLOR1, EC, EG, P7, P3, N0, + ST_PYR, COLOR1, EA, EC, P3, P0, N0, + ST_TET, COLOR1, EI, EA, P0, N0, + ST_TET, COLOR1, P0, P3, P7, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_TET, COLOR1, P7, EG, EH, N0, + // Case #138: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EG, EH, EJ, EJ, + ST_PYR, COLOR0, P5, EJ, EA, P0, N0, + ST_TET, COLOR0, EA, ED, P0, N0, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_TET, COLOR0, P5, P0, P4, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_PYR, COLOR0, P2, EB, EJ, P5, N0, + ST_TET, COLOR0, P6, P5, P4, N0, + ST_PYR, COLOR0, P6, P4, EH, EG, N0, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_PYR, COLOR1, P1, P3, ED, EA, N0, + ST_PYR, COLOR1, EC, P3, P1, EB, N0, + ST_TET, COLOR1, P1, EA, EJ, N0, + ST_TET, COLOR1, EB, P1, EJ, N0, + ST_PYR, COLOR1, ED, P3, P7, EH, N0, + ST_PYR, COLOR1, P3, EC, EG, P7, N0, + ST_TET, COLOR1, EG, EH, P7, N0, + // Case #139: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EI, EH, EG, EC, EB, EJ, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_TET, COLOR0, P4, P6, P5, N0, + ST_PYR, COLOR0, P4, EH, EG, P6, N0, + ST_TET, COLOR0, EI, EH, P4, N0, + ST_PYR, COLOR0, P4, P5, EJ, EI, N0, + ST_PYR, COLOR0, EJ, P5, P2, EB, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_TET, COLOR1, P0, P3, P7, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_PYR, COLOR1, EI, EJ, P1, P0, N0, + ST_TET, COLOR1, P1, P3, P0, N0, + ST_TET, COLOR1, P1, EJ, EB, N0, + ST_PYR, COLOR1, P1, EB, EC, P3, N0, + ST_PYR, COLOR1, EC, EG, P7, P3, N0, + ST_TET, COLOR1, EG, EH, P7, N0, + // Case #140: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EB, ED, EL, EG, EH, + ST_PYR, COLOR0, P1, P0, P4, P5, N0, + ST_TET, COLOR0, P4, N0, P6, P5, + ST_PYR, COLOR0, P0, ED, EH, P4, N0, + ST_PYR, COLOR0, EB, ED, P0, P1, N0, + ST_TET, COLOR0, P6, P1, P5, N0, + ST_PYR, COLOR0, P6, EL, EB, P1, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_PYR, COLOR0, EH, EG, P6, P4, N0, + ST_PYR, COLOR1, ED, P3, P7, EH, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + ST_TET, COLOR1, P2, P7, P3, N0, + ST_PYR, COLOR1, EL, EG, P7, P2, N0, + ST_TET, COLOR1, P7, EG, EH, N0, + // Case #141: (cloned #27) + ST_TET, COLOR0, P6, P5, P4, P1, + ST_PYR, COLOR0, EA, EI, P4, P1, EH, + ST_PYR, COLOR0, EB, P1, P6, EL, EG, + ST_PYR, COLOR0, P4, EH, EG, P6, P1, + ST_PYR, COLOR0, EH, EA, EB, EG, P1, + ST_TET, COLOR1, P3, P0, P2, P7, + ST_PYR, COLOR1, EH, EI, P0, P7, EA, + ST_PYR, COLOR1, EG, P7, P2, EL, EB, + ST_PYR, COLOR1, P0, EA, EB, P2, P7, + ST_PYR, COLOR1, EH, EG, EB, EA, P7, + // Case #142: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, ED, EA, EJ, EL, EG, EH, + ST_TET, COLOR0, P5, P4, P6, N0, + ST_PYR, COLOR0, P5, P6, EL, EJ, N0, + ST_TET, COLOR0, P0, P4, P5, N0, + ST_PYR, COLOR0, P0, P5, EJ, EA, N0, + ST_TET, COLOR0, ED, P0, EA, N0, + ST_PYR, COLOR0, P0, ED, EH, P4, N0, + ST_PYR, COLOR0, EH, EG, P6, P4, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_TET, COLOR1, P3, P1, P2, N0, + ST_PYR, COLOR1, ED, EA, P1, P3, N0, + ST_PYR, COLOR1, ED, P3, P7, EH, N0, + ST_TET, COLOR1, P7, P3, P2, N0, + ST_TET, COLOR1, P7, EG, EH, N0, + ST_PYR, COLOR1, P7, P2, EL, EG, N0, + ST_PYR, COLOR1, EL, P2, P1, EJ, N0, + ST_TET, COLOR1, EJ, P1, EA, N0, + // Case #143: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EL, EJ, EI, EG, EH, + ST_PYR, COLOR0, P5, EJ, EI, P4, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_PYR, COLOR0, P6, EL, EJ, P5, N0, + ST_TET, COLOR0, EG, EL, P6, N0, + ST_PYR, COLOR0, EH, EG, P6, P4, N0, + ST_TET, COLOR0, P4, P6, P5, N0, + ST_PYR, COLOR1, P3, P0, P1, P2, N0, + ST_TET, COLOR1, P0, P3, P7, N0, + ST_TET, COLOR1, P7, P3, P2, N0, + ST_PYR, COLOR1, P7, P2, EL, EG, N0, + ST_PYR, COLOR1, EL, P2, P1, EJ, N0, + ST_PYR, COLOR1, EJ, P1, P0, EI, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_TET, COLOR1, EG, EH, P7, N0, + // Case #144: (cloned #3) + ST_HEX, COLOR0, EG, P6, P5, EE, EK, P3, P0, EI, + ST_WDG, COLOR0, P0, P1, P5, P3, P2, P6, + ST_WDG, COLOR1, P4, EI, EE, P7, EK, EG, + // Case #145: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EA, EE, ED, EK, EG, + ST_PYR, COLOR0, P1, P5, P6, P2, N0, + ST_TET, COLOR0, P6, N0, P3, P2, + ST_PYR, COLOR0, P5, EE, EG, P6, N0, + ST_PYR, COLOR0, EA, EE, P5, P1, N0, + ST_TET, COLOR0, P3, P1, P2, N0, + ST_PYR, COLOR0, P3, ED, EA, P1, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_PYR, COLOR0, EG, EK, P3, P6, N0, + ST_PYR, COLOR1, EE, P4, P7, EG, N0, + ST_PYR, COLOR1, EA, P0, P4, EE, N0, + ST_TET, COLOR1, ED, P0, EA, N0, + ST_TET, COLOR1, P0, P7, P4, N0, + ST_PYR, COLOR1, ED, EK, P7, P0, N0, + ST_TET, COLOR1, P7, EK, EG, N0, + // Case #146: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EG, EK, EB, EB, + ST_PYR, COLOR0, P2, P0, EA, EB, N0, + ST_TET, COLOR0, EA, P0, EI, N0, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_TET, COLOR0, P2, P3, P0, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_PYR, COLOR0, P5, P2, EB, EJ, N0, + ST_TET, COLOR0, P6, P3, P2, N0, + ST_PYR, COLOR0, P6, EG, EK, P3, N0, + ST_PYR, COLOR0, P6, P5, EE, EG, N0, + ST_PYR, COLOR1, P1, EA, EI, P4, N0, + ST_PYR, COLOR1, EE, EJ, P1, P4, N0, + ST_TET, COLOR1, P1, EB, EA, N0, + ST_TET, COLOR1, EJ, EB, P1, N0, + ST_PYR, COLOR1, EI, EK, P7, P4, N0, + ST_PYR, COLOR1, P4, P7, EG, EE, N0, + ST_TET, COLOR1, EG, P7, EK, N0, + // Case #147: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, ED, EK, EG, EE, EJ, EB, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_PYR, COLOR0, P6, P5, EE, EG, N0, + ST_TET, COLOR0, P3, P2, P6, N0, + ST_PYR, COLOR0, P3, P6, EG, EK, N0, + ST_TET, COLOR0, ED, P3, EK, N0, + ST_PYR, COLOR0, P3, ED, EB, P2, N0, + ST_PYR, COLOR0, EB, EJ, P5, P2, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_TET, COLOR1, P0, P7, P4, N0, + ST_PYR, COLOR1, ED, EK, P7, P0, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_TET, COLOR1, P1, P0, P4, N0, + ST_TET, COLOR1, P1, EJ, EB, N0, + ST_PYR, COLOR1, P1, P4, EE, EJ, N0, + ST_PYR, COLOR1, EE, P4, P7, EG, N0, + ST_TET, COLOR1, EG, P7, EK, N0, + // Case #148: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EE, EI, EB, EB, + ST_PYR, COLOR0, P1, EB, EC, P3, N0, + ST_TET, COLOR0, EC, EK, P3, N0, + ST_PYR, COLOR0, P0, P3, EK, EI, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_PYR, COLOR0, P6, EL, EB, P1, N0, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_PYR, COLOR0, P5, P0, EI, EE, N0, + ST_PYR, COLOR0, P5, EE, EG, P6, N0, + ST_PYR, COLOR1, P2, P7, EK, EC, N0, + ST_PYR, COLOR1, EG, P7, P2, EL, N0, + ST_TET, COLOR1, P2, EC, EB, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_PYR, COLOR1, P7, EG, EE, P4, N0, + ST_TET, COLOR1, EE, EI, P4, N0, + // Case #149: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EE, EG, EA, EB, EL, + ST_TET, COLOR0, P3, EK, ED, EC, + ST_PYR, COLOR0, P6, P5, EE, EG, N0, + ST_PYR, COLOR0, P5, P1, EA, EE, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_PYR, COLOR0, P1, P6, EL, EB, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_WDG, COLOR1, ED, EK, EC, P0, P7, P2, + ST_TET, COLOR1, P7, P0, P2, N0, + ST_PYR, COLOR1, P0, EA, EB, P2, N0, + ST_TET, COLOR1, P4, P0, P7, N0, + ST_PYR, COLOR1, EG, EE, P4, P7, N0, + ST_PYR, COLOR1, EE, EA, P0, P4, N0, + ST_PYR, COLOR1, P2, EL, EG, P7, N0, + ST_TET, COLOR1, EB, EL, P2, N0, + // Case #150: (cloned #60) + ST_WDG, COLOR0, P3, EK, EC, P0, EI, EA, + ST_WDG, COLOR0, P6, EL, EG, P5, EJ, EE, + ST_HEX, COLOR1, P4, P1, P2, P7, EE, EJ, EL, EG, + ST_HEX, COLOR1, EI, EA, EC, EK, P4, P1, P2, P7, + // Case #151: (cloned #61) + ST_PNT, 0, COLOR1, 6, P0, P7, P4, P1, EL, EJ, + ST_WDG, COLOR0, EJ, P5, EE, EL, P6, EG, + ST_TET, COLOR0, ED, P3, EK, EC, + ST_WDG, COLOR1, P0, P2, P7, ED, EC, EK, + ST_PYR, COLOR1, EJ, EL, P2, P1, N0, + ST_TET, COLOR1, P1, P2, P0, N0, + ST_TET, COLOR1, P1, P0, P4, N0, + ST_PYR, COLOR1, EE, EJ, P1, P4, N0, + ST_PYR, COLOR1, EG, EE, P4, P7, N0, + ST_TET, COLOR1, P4, P0, P7, N0, + ST_PYR, COLOR1, EL, EJ, EE, EG, N0, + ST_TET, COLOR1, P7, P0, P2, N0, + ST_PYR, COLOR1, EL, EG, P7, P2, N0, + // Case #152: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EC, EG, ED, EI, EE, + ST_PYR, COLOR0, P2, P1, P5, P6, N0, + ST_TET, COLOR0, P5, P0, N0, P1, + ST_PYR, COLOR0, P6, P5, EE, EG, N0, + ST_PYR, COLOR0, EC, P2, P6, EG, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_PYR, COLOR0, P0, P2, EC, ED, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_PYR, COLOR0, EE, P5, P0, EI, N0, + ST_PYR, COLOR1, EG, EE, P4, P7, N0, + ST_PYR, COLOR1, EC, EG, P7, P3, N0, + ST_TET, COLOR1, ED, EC, P3, N0, + ST_TET, COLOR1, P3, P7, P4, N0, + ST_PYR, COLOR1, ED, P3, P4, EI, N0, + ST_TET, COLOR1, P4, EE, EI, N0, + // Case #153: (cloned #15) + ST_HEX, COLOR0, P1, P5, P6, P2, EA, EE, EG, EC, + ST_HEX, COLOR1, EA, EE, EG, EC, P0, P4, P7, P3, + // Case #154: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EG, EE, EC, EB, EJ, + ST_TET, COLOR0, P0, ED, EI, EA, + ST_PYR, COLOR0, P5, EE, EG, P6, N0, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_TET, COLOR0, P6, P2, P5, N0, + ST_PYR, COLOR0, P2, EB, EJ, P5, N0, + ST_TET, COLOR0, P5, EJ, EE, N0, + ST_WDG, COLOR1, P3, P4, P1, ED, EI, EA, + ST_TET, COLOR1, P4, P1, P3, N0, + ST_PYR, COLOR1, P3, P1, EB, EC, N0, + ST_TET, COLOR1, P7, P4, P3, N0, + ST_PYR, COLOR1, EE, P4, P7, EG, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_PYR, COLOR1, P1, P4, EE, EJ, N0, + ST_TET, COLOR1, EB, P1, EJ, N0, + // Case #155: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EE, EG, EC, EJ, EB, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_TET, COLOR0, P2, EC, EB, N0, + ST_PYR, COLOR0, P5, EE, EG, P6, N0, + ST_TET, COLOR0, EJ, EE, P5, N0, + ST_PYR, COLOR0, EB, EJ, P5, P2, N0, + ST_TET, COLOR0, P2, P5, P6, N0, + ST_PYR, COLOR1, P0, P3, P7, P4, N0, + ST_TET, COLOR1, P3, P0, P1, N0, + ST_TET, COLOR1, P1, P0, P4, N0, + ST_PYR, COLOR1, P1, P4, EE, EJ, N0, + ST_PYR, COLOR1, EE, P4, P7, EG, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_PYR, COLOR1, EC, P3, P1, EB, N0, + ST_TET, COLOR1, EJ, EB, P1, N0, + // Case #156: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, ED, EI, EE, EG, EL, EB, + ST_TET, COLOR0, P5, P6, P1, N0, + ST_PYR, COLOR0, P5, EE, EG, P6, N0, + ST_TET, COLOR0, P0, P5, P1, N0, + ST_PYR, COLOR0, P0, EI, EE, P5, N0, + ST_TET, COLOR0, ED, EI, P0, N0, + ST_PYR, COLOR0, P0, P1, EB, ED, N0, + ST_PYR, COLOR0, EB, P1, P6, EL, N0, + ST_TET, COLOR0, P6, EG, EL, N0, + ST_TET, COLOR1, P3, P7, P4, N0, + ST_PYR, COLOR1, ED, P3, P4, EI, N0, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_TET, COLOR1, P2, P7, P3, N0, + ST_TET, COLOR1, P2, EB, EL, N0, + ST_PYR, COLOR1, P2, EL, EG, P7, N0, + ST_PYR, COLOR1, EG, EE, P4, P7, N0, + ST_TET, COLOR1, EE, EI, P4, N0, + // Case #157: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EG, EE, EA, EL, EB, + ST_PYR, COLOR0, P5, P1, EA, EE, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_PYR, COLOR0, P6, P5, EE, EG, N0, + ST_TET, COLOR0, EL, P6, EG, N0, + ST_PYR, COLOR0, EB, P1, P6, EL, N0, + ST_TET, COLOR0, P1, P5, P6, N0, + ST_PYR, COLOR1, P3, P7, P4, P0, N0, + ST_TET, COLOR1, P0, P2, P3, N0, + ST_TET, COLOR1, P2, P7, P3, N0, + ST_PYR, COLOR1, P2, EL, EG, P7, N0, + ST_PYR, COLOR1, EG, EE, P4, P7, N0, + ST_PYR, COLOR1, EE, EA, P0, P4, N0, + ST_PYR, COLOR1, EA, EB, P2, P0, N0, + ST_TET, COLOR1, EL, P2, EB, N0, + // Case #158: (cloned #61) + ST_PNT, 0, COLOR1, 6, P3, P4, P7, P2, EJ, EL, + ST_WDG, COLOR0, EJ, P5, EE, EL, P6, EG, + ST_TET, COLOR0, ED, EI, P0, EA, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_PYR, COLOR1, EL, P2, P1, EJ, N0, + ST_TET, COLOR1, P2, P3, P1, N0, + ST_TET, COLOR1, P2, P7, P3, N0, + ST_PYR, COLOR1, EG, P7, P2, EL, N0, + ST_PYR, COLOR1, EE, P4, P7, EG, N0, + ST_TET, COLOR1, P7, P4, P3, N0, + ST_PYR, COLOR1, EJ, EE, EG, EL, N0, + ST_TET, COLOR1, P4, P1, P3, N0, + ST_PYR, COLOR1, EJ, P1, P4, EE, N0, + // Case #159: (cloned #63) + ST_WDG, COLOR0, P5, EE, EJ, P6, EG, EL, + ST_HEX, COLOR1, P4, P1, P2, P7, EE, EJ, EL, EG, + ST_WDG, COLOR1, P3, P7, P2, P0, P4, P1, + // Case #160: (cloned #5) + ST_PNT, 0, NOCOLOR, 2, EK, EJ, + ST_PYR, COLOR0, P3, P0, P1, P2, N0, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_TET, COLOR0, P3, P2, P6, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_TET, COLOR0, P1, P0, P4, N0, + ST_PYR, COLOR0, P1, P4, EE, EJ, N0, + ST_PYR, COLOR0, P6, P1, EJ, EF, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_PYR, COLOR0, P3, P6, EG, EK, N0, + ST_PYR, COLOR0, P3, EK, EH, P4, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_PYR, COLOR1, P7, P5, EE, EH, N0, + ST_PYR, COLOR1, EG, EF, P5, P7, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + ST_TET, COLOR1, EG, N0, P7, EK, + ST_TET, COLOR1, EH, EK, P7, N0, + // Case #161: (cloned #26) + ST_TET, COLOR0, P4, EE, EI, EH, + ST_TET, COLOR0, P6, P3, P2, P1, + ST_PYR, COLOR0, EA, P1, P3, ED, EK, + ST_PYR, COLOR0, EJ, EF, P6, P1, EG, + ST_PYR, COLOR0, P3, P6, EG, EK, P1, + ST_PYR, COLOR0, EK, EG, EJ, EA, P1, + ST_WDG, COLOR1, EI, EE, EH, P0, P5, P7, + ST_PYR, COLOR1, P0, ED, EK, P7, EA, + ST_PYR, COLOR1, EG, EF, P5, P7, EJ, + ST_PYR, COLOR1, EA, P0, P5, EJ, P7, + ST_PYR, COLOR1, EA, EJ, EG, EK, P7, + // Case #162: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EB, EA, EK, EK, + ST_PYR, COLOR0, P3, EK, EH, P4, N0, + ST_TET, COLOR0, EH, EE, P4, N0, + ST_PYR, COLOR0, P0, P4, EE, EA, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_TET, COLOR0, P2, P6, P3, N0, + ST_PYR, COLOR0, P6, EG, EK, P3, N0, + ST_TET, COLOR0, P2, P3, P0, N0, + ST_PYR, COLOR0, P2, P0, EA, EB, N0, + ST_PYR, COLOR0, P2, EB, EF, P6, N0, + ST_PYR, COLOR1, P7, P5, EE, EH, N0, + ST_PYR, COLOR1, EF, P5, P7, EG, N0, + ST_TET, COLOR1, P7, EH, EK, N0, + ST_TET, COLOR1, EG, P7, EK, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_PYR, COLOR1, P5, EF, EB, P1, N0, + ST_TET, COLOR1, EB, EA, P1, N0, + // Case #163: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EB, EF, ED, EK, EG, + ST_TET, COLOR0, P4, EE, EI, EH, + ST_PYR, COLOR0, P6, P2, EB, EF, N0, + ST_PYR, COLOR0, P2, P3, ED, EB, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_TET, COLOR0, P2, P6, P3, N0, + ST_PYR, COLOR0, P3, P6, EG, EK, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_WDG, COLOR1, EI, EE, EH, P0, P5, P7, + ST_TET, COLOR1, P5, P0, P7, N0, + ST_PYR, COLOR1, P0, ED, EK, P7, N0, + ST_TET, COLOR1, P1, P0, P5, N0, + ST_PYR, COLOR1, EF, EB, P1, P5, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_PYR, COLOR1, P7, EG, EF, P5, N0, + ST_TET, COLOR1, EK, EG, P7, N0, + // Case #164: (cloned #26) + ST_TET, COLOR0, P6, EF, EG, EL, + ST_TET, COLOR0, P3, P0, P1, P4, + ST_PYR, COLOR0, EE, EJ, P1, P4, EB, + ST_PYR, COLOR0, EH, P4, P3, EK, EC, + ST_PYR, COLOR0, P1, EB, EC, P3, P4, + ST_PYR, COLOR0, EB, EE, EH, EC, P4, + ST_WDG, COLOR1, P5, P7, P2, EF, EG, EL, + ST_PYR, COLOR1, P5, P2, EB, EJ, EE, + ST_PYR, COLOR1, EC, P2, P7, EK, EH, + ST_PYR, COLOR1, EE, EH, P7, P5, P2, + ST_PYR, COLOR1, EE, EB, EC, EH, P2, + // Case #165: (cloned #90) + ST_TET, COLOR0, EH, EI, EE, P4, + ST_TET, COLOR0, EK, EC, ED, P3, + ST_TET, COLOR0, EG, EF, EL, P6, + ST_TET, COLOR0, EB, EJ, EA, P1, + ST_WDG, COLOR1, P2, P0, P5, EB, EA, EJ, + ST_TET, COLOR1, P2, P0, P5, P7, + ST_WDG, COLOR1, EG, EF, EL, P7, P5, P2, + ST_WDG, COLOR1, ED, EK, EC, P0, P7, P2, + ST_WDG, COLOR1, EE, EH, EI, P5, P7, P0, + // Case #166: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EA, EC, EE, EH, EK, + ST_TET, COLOR0, P6, EL, EF, EG, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_PYR, COLOR0, P0, P4, EE, EA, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_TET, COLOR0, P0, P3, P4, N0, + ST_PYR, COLOR0, P4, P3, EK, EH, N0, + ST_TET, COLOR0, P3, EC, EK, N0, + ST_WDG, COLOR1, EF, EL, EG, P5, P2, P7, + ST_TET, COLOR1, P2, P5, P7, N0, + ST_PYR, COLOR1, P5, EE, EH, P7, N0, + ST_TET, COLOR1, P1, P5, P2, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_PYR, COLOR1, P7, EK, EC, P2, N0, + ST_TET, COLOR1, EH, EK, P7, N0, + // Case #167: (cloned #91) + ST_TET, COLOR0, EL, EG, EF, P6, + ST_TET, COLOR0, EC, P3, ED, EK, + ST_TET, COLOR0, EE, EI, P4, EH, + ST_WDG, COLOR1, EH, EI, EE, P7, P0, P5, + ST_WDG, COLOR1, P2, P7, P0, EC, EK, ED, + ST_WDG, COLOR1, EL, EG, EF, P2, P7, P5, + ST_TET, COLOR1, P7, P5, P0, P2, + ST_TET, COLOR1, P2, P1, P5, P0, + // Case #168: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EC, ED, EJ, EJ, + ST_PYR, COLOR0, P1, P4, EE, EJ, N0, + ST_TET, COLOR0, EE, P4, EH, N0, + ST_PYR, COLOR0, P0, ED, EH, P4, N0, + ST_TET, COLOR0, P1, P0, P4, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_PYR, COLOR0, P6, P1, EJ, EF, N0, + ST_TET, COLOR0, P2, P0, P1, N0, + ST_PYR, COLOR0, P2, EC, ED, P0, N0, + ST_PYR, COLOR0, P2, P6, EG, EC, N0, + ST_PYR, COLOR1, P5, EE, EH, P7, N0, + ST_PYR, COLOR1, EG, EF, P5, P7, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_PYR, COLOR1, P7, P3, EC, EG, N0, + ST_TET, COLOR1, EC, P3, ED, N0, + // Case #169: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EC, EG, EA, EJ, EF, + ST_TET, COLOR0, P4, EI, EH, EE, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_PYR, COLOR0, P1, EJ, EF, P6, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_WDG, COLOR1, P0, P7, P5, EI, EH, EE, + ST_TET, COLOR1, P7, P5, P0, N0, + ST_PYR, COLOR1, P0, P5, EJ, EA, N0, + ST_TET, COLOR1, P3, P7, P0, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_PYR, COLOR1, P5, P7, EG, EF, N0, + ST_TET, COLOR1, EJ, P5, EF, N0, + // Case #170: (cloned #60) + ST_WDG, COLOR0, P0, ED, EA, P4, EH, EE, + ST_WDG, COLOR0, P2, EB, EC, P6, EF, EG, + ST_HEX, COLOR1, EC, EB, EF, EG, P3, P1, P5, P7, + ST_HEX, COLOR1, P3, P1, P5, P7, ED, EA, EE, EH, + // Case #171: (cloned #61) + ST_PNT, 0, COLOR1, 6, P0, P7, P3, P1, EF, EB, + ST_WDG, COLOR0, EF, P6, EG, EB, P2, EC, + ST_TET, COLOR0, EI, EH, P4, EE, + ST_WDG, COLOR1, EI, EE, EH, P0, P5, P7, + ST_PYR, COLOR1, EB, P1, P5, EF, N0, + ST_TET, COLOR1, P1, P0, P5, N0, + ST_TET, COLOR1, P1, P3, P0, N0, + ST_PYR, COLOR1, EC, P3, P1, EB, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_TET, COLOR1, P3, P7, P0, N0, + ST_PYR, COLOR1, EF, EG, EC, EB, N0, + ST_TET, COLOR1, P7, P5, P0, N0, + ST_PYR, COLOR1, EF, P5, P7, EG, N0, + // Case #172: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, ED, EH, EB, EJ, EE, + ST_TET, COLOR0, P6, EG, EL, EF, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_PYR, COLOR0, P0, P1, EB, ED, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_TET, COLOR0, P0, P4, P1, N0, + ST_PYR, COLOR0, P1, P4, EE, EJ, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_WDG, COLOR1, EL, EG, EF, P2, P7, P5, + ST_TET, COLOR1, P7, P2, P5, N0, + ST_PYR, COLOR1, P2, EB, EJ, P5, N0, + ST_TET, COLOR1, P3, P2, P7, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_PYR, COLOR1, P5, EE, EH, P7, N0, + ST_TET, COLOR1, EJ, EE, P5, N0, + // Case #173: (cloned #91) + ST_TET, COLOR0, EG, EF, EL, P6, + ST_TET, COLOR0, EH, P4, EI, EE, + ST_TET, COLOR0, EB, EA, P1, EJ, + ST_WDG, COLOR1, EJ, EA, EB, P5, P0, P2, + ST_WDG, COLOR1, P7, P5, P0, EH, EE, EI, + ST_WDG, COLOR1, EG, EF, EL, P7, P5, P2, + ST_TET, COLOR1, P5, P2, P0, P7, + ST_TET, COLOR1, P7, P3, P2, P0, + // Case #174: (cloned #61) + ST_PNT, 0, COLOR1, 6, P2, P5, P1, P3, EH, ED, + ST_WDG, COLOR0, EH, P4, EE, ED, P0, EA, + ST_TET, COLOR0, EL, EF, P6, EG, + ST_WDG, COLOR1, EL, EG, EF, P2, P7, P5, + ST_PYR, COLOR1, ED, P3, P7, EH, N0, + ST_TET, COLOR1, P3, P2, P7, N0, + ST_TET, COLOR1, P3, P1, P2, N0, + ST_PYR, COLOR1, EA, P1, P3, ED, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_TET, COLOR1, P1, P5, P2, N0, + ST_PYR, COLOR1, EH, EE, EA, ED, N0, + ST_TET, COLOR1, P5, P7, P2, N0, + ST_PYR, COLOR1, EH, P7, P5, EE, N0, + // Case #175: (cloned #95) + ST_TET, COLOR0, EE, EH, EI, P4, + ST_TET, COLOR0, EF, EL, EG, P6, + ST_WDG, COLOR1, EG, EF, EL, P7, P5, P2, + ST_WDG, COLOR1, P7, P5, P0, EH, EE, EI, + ST_PYR, COLOR1, P3, P0, P1, P2, P7, + ST_TET, COLOR1, P5, P1, P0, P7, + ST_TET, COLOR1, P5, P2, P1, P7, + // Case #176: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EJ, EI, EF, EG, EK, + ST_PYR, COLOR0, P1, P2, P3, P0, N0, + ST_TET, COLOR0, P3, P6, N0, P2, + ST_PYR, COLOR0, P0, P3, EK, EI, N0, + ST_PYR, COLOR0, EJ, P1, P0, EI, N0, + ST_TET, COLOR0, P6, P2, P1, N0, + ST_PYR, COLOR0, P6, P1, EJ, EF, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_PYR, COLOR0, EK, P3, P6, EG, N0, + ST_PYR, COLOR1, EI, EK, P7, P4, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + ST_TET, COLOR1, P5, P4, P7, N0, + ST_PYR, COLOR1, EF, P5, P7, EG, N0, + ST_TET, COLOR1, P7, EK, EG, N0, + // Case #177: (cloned #27) + ST_TET, COLOR0, P6, P3, P2, P1, + ST_PYR, COLOR0, EA, P1, P3, ED, EK, + ST_PYR, COLOR0, EJ, EF, P6, P1, EG, + ST_PYR, COLOR0, P3, P6, EG, EK, P1, + ST_PYR, COLOR0, EK, EG, EJ, EA, P1, + ST_TET, COLOR1, P4, P5, P0, P7, + ST_PYR, COLOR1, EK, P7, P0, ED, EA, + ST_PYR, COLOR1, EG, EF, P5, P7, EJ, + ST_PYR, COLOR1, P0, P5, EJ, EA, P7, + ST_PYR, COLOR1, EK, EA, EJ, EG, P7, + // Case #178: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EI, EA, EB, EF, EG, EK, + ST_TET, COLOR0, P2, P6, P3, N0, + ST_PYR, COLOR0, P2, EB, EF, P6, N0, + ST_TET, COLOR0, P0, P2, P3, N0, + ST_PYR, COLOR0, P0, EA, EB, P2, N0, + ST_TET, COLOR0, EI, EA, P0, N0, + ST_PYR, COLOR0, P0, P3, EK, EI, N0, + ST_PYR, COLOR0, EK, P3, P6, EG, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_TET, COLOR1, P4, P5, P1, N0, + ST_PYR, COLOR1, EI, P4, P1, EA, N0, + ST_PYR, COLOR1, EI, EK, P7, P4, N0, + ST_TET, COLOR1, P7, P5, P4, N0, + ST_TET, COLOR1, P7, EK, EG, N0, + ST_PYR, COLOR1, P7, EG, EF, P5, N0, + ST_PYR, COLOR1, EF, EB, P1, P5, N0, + ST_TET, COLOR1, EB, EA, P1, N0, + // Case #179: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EF, EB, ED, EG, EK, + ST_PYR, COLOR0, P2, P3, ED, EB, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_PYR, COLOR0, P6, P2, EB, EF, N0, + ST_TET, COLOR0, EG, P6, EF, N0, + ST_PYR, COLOR0, EK, P3, P6, EG, N0, + ST_TET, COLOR0, P3, P2, P6, N0, + ST_PYR, COLOR1, P4, P5, P1, P0, N0, + ST_TET, COLOR1, P0, P7, P4, N0, + ST_TET, COLOR1, P7, P5, P4, N0, + ST_PYR, COLOR1, P7, EG, EF, P5, N0, + ST_PYR, COLOR1, EF, EB, P1, P5, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_PYR, COLOR1, ED, EK, P7, P0, N0, + ST_TET, COLOR1, EG, P7, EK, N0, + // Case #180: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EI, EK, EJ, EB, EC, + ST_TET, COLOR0, P6, EF, EG, EL, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_PYR, COLOR0, P0, EI, EJ, P1, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_TET, COLOR0, P0, P1, P3, N0, + ST_PYR, COLOR0, P1, EB, EC, P3, N0, + ST_TET, COLOR0, P3, EC, EK, N0, + ST_WDG, COLOR1, P5, P7, P2, EF, EG, EL, + ST_TET, COLOR1, P7, P2, P5, N0, + ST_PYR, COLOR1, P5, P2, EB, EJ, N0, + ST_TET, COLOR1, P4, P7, P5, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_PYR, COLOR1, P2, P7, EK, EC, N0, + ST_TET, COLOR1, EB, P2, EC, N0, + // Case #181: (cloned #91) + ST_TET, COLOR0, EG, EF, EL, P6, + ST_TET, COLOR0, EK, ED, P3, EC, + ST_TET, COLOR0, EJ, P1, EA, EB, + ST_WDG, COLOR1, P2, P0, P5, EB, EA, EJ, + ST_WDG, COLOR1, EK, EC, ED, P7, P2, P0, + ST_WDG, COLOR1, P7, P2, P5, EG, EL, EF, + ST_TET, COLOR1, P2, P0, P5, P7, + ST_TET, COLOR1, P7, P5, P4, P0, + // Case #182: (cloned #61) + ST_PNT, 0, COLOR1, 6, P5, P2, P1, P4, EK, EI, + ST_WDG, COLOR0, EI, P0, EA, EK, P3, EC, + ST_TET, COLOR0, EF, P6, EL, EG, + ST_WDG, COLOR1, P5, P7, P2, EF, EG, EL, + ST_PYR, COLOR1, EI, EK, P7, P4, N0, + ST_TET, COLOR1, P4, P7, P5, N0, + ST_TET, COLOR1, P4, P5, P1, N0, + ST_PYR, COLOR1, EA, EI, P4, P1, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_TET, COLOR1, P1, P5, P2, N0, + ST_PYR, COLOR1, EK, EI, EA, EC, N0, + ST_TET, COLOR1, P2, P5, P7, N0, + ST_PYR, COLOR1, EK, EC, P2, P7, N0, + // Case #183: (cloned #95) + ST_TET, COLOR0, EC, ED, EK, P3, + ST_TET, COLOR0, EL, EG, EF, P6, + ST_WDG, COLOR1, P7, P2, P5, EG, EL, EF, + ST_WDG, COLOR1, EK, EC, ED, P7, P2, P0, + ST_PYR, COLOR1, P4, P5, P1, P0, P7, + ST_TET, COLOR1, P2, P0, P1, P7, + ST_TET, COLOR1, P2, P1, P5, P7, + // Case #184: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EI, ED, EC, EG, EF, EJ, + ST_TET, COLOR0, P2, P1, P6, N0, + ST_PYR, COLOR0, P2, P6, EG, EC, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_PYR, COLOR0, P0, P2, EC, ED, N0, + ST_TET, COLOR0, EI, P0, ED, N0, + ST_PYR, COLOR0, P0, EI, EJ, P1, N0, + ST_PYR, COLOR0, EJ, EF, P6, P1, N0, + ST_TET, COLOR0, P6, EF, EG, N0, + ST_TET, COLOR1, P4, P3, P7, N0, + ST_PYR, COLOR1, EI, ED, P3, P4, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_TET, COLOR1, P5, P4, P7, N0, + ST_TET, COLOR1, P5, EF, EJ, N0, + ST_PYR, COLOR1, P5, P7, EG, EF, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_TET, COLOR1, EC, P3, ED, N0, + // Case #185: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EG, EC, EA, EF, EJ, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_PYR, COLOR0, P6, EG, EC, P2, N0, + ST_TET, COLOR0, EF, EG, P6, N0, + ST_PYR, COLOR0, EJ, EF, P6, P1, N0, + ST_TET, COLOR0, P1, P6, P2, N0, + ST_PYR, COLOR1, P4, P0, P3, P7, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_TET, COLOR1, P5, P4, P7, N0, + ST_PYR, COLOR1, P5, P7, EG, EF, N0, + ST_PYR, COLOR1, EG, P7, P3, EC, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_PYR, COLOR1, EA, P0, P5, EJ, N0, + ST_TET, COLOR1, EF, EJ, P5, N0, + // Case #186: (cloned #61) + ST_PNT, 0, COLOR1, 6, P4, P3, P7, P5, EB, EF, + ST_WDG, COLOR0, EF, P6, EG, EB, P2, EC, + ST_TET, COLOR0, EI, P0, ED, EA, + ST_WDG, COLOR1, P4, P1, P3, EI, EA, ED, + ST_PYR, COLOR1, EF, EB, P1, P5, N0, + ST_TET, COLOR1, P5, P1, P4, N0, + ST_TET, COLOR1, P5, P4, P7, N0, + ST_PYR, COLOR1, EG, EF, P5, P7, N0, + ST_PYR, COLOR1, EC, EG, P7, P3, N0, + ST_TET, COLOR1, P7, P4, P3, N0, + ST_PYR, COLOR1, EB, EF, EG, EC, N0, + ST_TET, COLOR1, P3, P4, P1, N0, + ST_PYR, COLOR1, EB, EC, P3, P1, N0, + // Case #187: (cloned #63) + ST_WDG, COLOR0, P6, EG, EF, P2, EC, EB, + ST_HEX, COLOR1, EC, EB, EF, EG, P3, P1, P5, P7, + ST_WDG, COLOR1, P0, P3, P1, P4, P7, P5, + // Case #188: (cloned #61) + ST_PNT, 0, COLOR1, 6, P7, P5, P4, P3, EB, ED, + ST_WDG, COLOR0, ED, P0, EI, EB, P1, EJ, + ST_TET, COLOR0, EG, P6, EF, EL, + ST_WDG, COLOR1, P7, P2, P5, EG, EL, EF, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_TET, COLOR1, P3, P2, P7, N0, + ST_TET, COLOR1, P3, P7, P4, N0, + ST_PYR, COLOR1, EI, ED, P3, P4, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_TET, COLOR1, P4, P7, P5, N0, + ST_PYR, COLOR1, EB, ED, EI, EJ, N0, + ST_TET, COLOR1, P5, P7, P2, N0, + ST_PYR, COLOR1, EB, EJ, P5, P2, N0, + // Case #189: (cloned #95) + ST_TET, COLOR0, EJ, EA, EB, P1, + ST_TET, COLOR0, EF, EL, EG, P6, + ST_WDG, COLOR1, P2, P5, P7, EL, EF, EG, + ST_WDG, COLOR1, EB, EJ, EA, P2, P5, P0, + ST_PYR, COLOR1, P3, P7, P4, P0, P2, + ST_TET, COLOR1, P5, P0, P4, P2, + ST_TET, COLOR1, P5, P4, P7, P2, + // Case #190: (cloned #125) + ST_TET, COLOR0, EA, EI, ED, P0, + ST_TET, COLOR0, EL, EG, EF, P6, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_WDG, COLOR1, EL, EG, EF, P2, P7, P5, + ST_TET, COLOR1, P5, P4, P7, P2, + ST_TET, COLOR1, P2, P3, P1, P4, + ST_TET, COLOR1, P3, P2, P7, P4, + ST_TET, COLOR1, P2, P1, P5, P4, + // Case #191: (cloned #127) + ST_PNT, 0, COLOR1, 7, P1, P0, P3, P2, P5, P4, P7, + ST_TET, COLOR0, EF, EL, EG, P6, + ST_WDG, COLOR1, P5, P7, P2, EF, EG, EL, + ST_TET, COLOR1, P5, P7, P2, N0, + ST_PYR, COLOR1, P4, P0, P3, P7, N0, + ST_TET, COLOR1, P7, P3, P2, N0, + ST_PYR, COLOR1, P1, P2, P3, P0, N0, + ST_TET, COLOR1, P1, P5, P2, N0, + ST_PYR, COLOR1, P1, P0, P4, P5, N0, + ST_TET, COLOR1, P5, P4, P7, N0, + // Case #192: (cloned #3) + ST_HEX, COLOR0, EF, P5, P4, EH, EL, P2, P3, EK, + ST_WDG, COLOR0, P5, P1, P2, P4, P0, P3, + ST_WDG, COLOR1, P6, EF, EL, P7, EH, EK, + // Case #193: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EL, EF, EA, EA, + ST_PYR, COLOR0, P1, EA, EI, P4, N0, + ST_TET, COLOR0, EI, EH, P4, N0, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_TET, COLOR0, P1, P4, P5, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_TET, COLOR0, P2, P3, P1, N0, + ST_PYR, COLOR0, P3, ED, EA, P1, N0, + ST_TET, COLOR0, P2, P1, P5, N0, + ST_PYR, COLOR0, P2, P5, EF, EL, N0, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_PYR, COLOR1, P0, P7, EH, EI, N0, + ST_PYR, COLOR1, EK, P7, P0, ED, N0, + ST_TET, COLOR1, P0, EI, EA, N0, + ST_TET, COLOR1, ED, P0, EA, N0, + ST_PYR, COLOR1, EH, P7, P6, EF, N0, + ST_PYR, COLOR1, P7, EK, EL, P6, N0, + ST_TET, COLOR1, EL, EF, P6, N0, + // Case #194: (cloned #21) + ST_PNT, 0, NOCOLOR, 4, EK, EH, EA, EA, + ST_PYR, COLOR0, P0, P5, EJ, EA, N0, + ST_TET, COLOR0, EJ, P5, EF, N0, + ST_PYR, COLOR0, P4, EH, EF, P5, N0, + ST_TET, COLOR0, P0, P4, P5, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_TET, COLOR0, P3, P0, P2, N0, + ST_PYR, COLOR0, P2, P0, EA, EB, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_PYR, COLOR0, P3, EK, EH, P4, N0, + ST_PYR, COLOR0, P3, P2, EL, EK, N0, + ST_PYR, COLOR1, P1, EJ, EF, P6, N0, + ST_PYR, COLOR1, EL, EB, P1, P6, N0, + ST_TET, COLOR1, P1, EA, EJ, N0, + ST_TET, COLOR1, EB, EA, P1, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_PYR, COLOR1, P6, P7, EK, EL, N0, + ST_TET, COLOR1, EK, P7, EH, N0, + // Case #195: (cloned #60) + ST_WDG, COLOR0, P3, ED, EK, P2, EB, EL, + ST_WDG, COLOR0, P4, EH, EI, P5, EF, EJ, + ST_HEX, COLOR1, EI, EH, EF, EJ, P0, P7, P6, P1, + ST_HEX, COLOR1, P0, P7, P6, P1, ED, EK, EL, EB, + // Case #196: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EH, EF, EK, EC, EB, + ST_PYR, COLOR0, P4, P5, P1, P0, N0, + ST_TET, COLOR0, P1, N0, P3, P0, + ST_PYR, COLOR0, P5, EF, EB, P1, N0, + ST_PYR, COLOR0, EH, EF, P5, P4, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_PYR, COLOR0, P3, EK, EH, P4, N0, + ST_TET, COLOR0, P3, EC, EK, N0, + ST_PYR, COLOR0, EB, EC, P3, P1, N0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_PYR, COLOR1, EH, P7, P6, EF, N0, + ST_TET, COLOR1, EK, P7, EH, N0, + ST_TET, COLOR1, P7, P2, P6, N0, + ST_PYR, COLOR1, EK, EC, P2, P7, N0, + ST_TET, COLOR1, P2, EC, EB, N0, + // Case #197: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EF, EB, EH, EI, EA, + ST_TET, COLOR0, P3, EC, EK, ED, + ST_PYR, COLOR0, P1, P5, EF, EB, N0, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_TET, COLOR0, P5, P1, P4, N0, + ST_PYR, COLOR0, P4, P1, EA, EI, N0, + ST_TET, COLOR0, P1, EB, EA, N0, + ST_WDG, COLOR1, EK, EC, ED, P7, P2, P0, + ST_TET, COLOR1, P2, P7, P0, N0, + ST_PYR, COLOR1, P7, EH, EI, P0, N0, + ST_TET, COLOR1, P6, P7, P2, N0, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_PYR, COLOR1, P0, EA, EB, P2, N0, + ST_TET, COLOR1, EI, EA, P0, N0, + // Case #198: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EC, EK, EH, EF, EJ, EA, + ST_TET, COLOR0, P4, P5, P0, N0, + ST_PYR, COLOR0, P4, EH, EF, P5, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_PYR, COLOR0, P3, EK, EH, P4, N0, + ST_TET, COLOR0, EC, EK, P3, N0, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_PYR, COLOR0, EA, P0, P5, EJ, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_TET, COLOR1, P2, P6, P7, N0, + ST_PYR, COLOR1, EC, P2, P7, EK, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + ST_TET, COLOR1, P1, EA, EJ, N0, + ST_PYR, COLOR1, P1, EJ, EF, P6, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_TET, COLOR1, EH, EK, P7, N0, + // Case #199: (cloned #61) + ST_PNT, 0, COLOR1, 6, P2, P0, P1, P6, EH, EF, + ST_WDG, COLOR0, EF, P5, EJ, EH, P4, EI, + ST_TET, COLOR0, EC, P3, ED, EK, + ST_WDG, COLOR1, P2, P7, P0, EC, EK, ED, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_TET, COLOR1, P6, P7, P2, N0, + ST_TET, COLOR1, P6, P2, P1, N0, + ST_PYR, COLOR1, EJ, EF, P6, P1, N0, + ST_PYR, COLOR1, EI, EJ, P1, P0, N0, + ST_TET, COLOR1, P1, P2, P0, N0, + ST_PYR, COLOR1, EH, EF, EJ, EI, N0, + ST_TET, COLOR1, P0, P2, P7, N0, + ST_PYR, COLOR1, EH, EI, P0, P7, N0, + // Case #200: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, ED, EH, EC, EL, EF, + ST_PYR, COLOR0, P0, P4, P5, P1, N0, + ST_TET, COLOR0, P5, N0, P2, P1, + ST_PYR, COLOR0, P4, EH, EF, P5, N0, + ST_PYR, COLOR0, ED, EH, P4, P0, N0, + ST_TET, COLOR0, P2, P0, P1, N0, + ST_PYR, COLOR0, P2, EC, ED, P0, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_PYR, COLOR0, EF, EL, P2, P5, N0, + ST_PYR, COLOR1, EH, P7, P6, EF, N0, + ST_PYR, COLOR1, ED, P3, P7, EH, N0, + ST_TET, COLOR1, EC, P3, ED, N0, + ST_TET, COLOR1, P3, P6, P7, N0, + ST_PYR, COLOR1, EC, EL, P6, P3, N0, + ST_TET, COLOR1, P6, EL, EF, N0, + // Case #201: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EC, EL, EF, EH, EI, EA, + ST_TET, COLOR0, P5, P1, P4, N0, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_TET, COLOR0, P2, P1, P5, N0, + ST_PYR, COLOR0, P2, P5, EF, EL, N0, + ST_TET, COLOR0, EC, P2, EL, N0, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_PYR, COLOR0, EA, EI, P4, P1, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_TET, COLOR1, P3, P6, P7, N0, + ST_PYR, COLOR1, EC, EL, P6, P3, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_TET, COLOR1, P0, P3, P7, N0, + ST_TET, COLOR1, P0, EI, EA, N0, + ST_PYR, COLOR1, P0, P7, EH, EI, N0, + ST_PYR, COLOR1, EH, P7, P6, EF, N0, + ST_TET, COLOR1, EF, P6, EL, N0, + // Case #202: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EH, EF, ED, EA, EJ, + ST_TET, COLOR0, P2, EL, EC, EB, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_TET, COLOR0, P4, P5, P0, N0, + ST_PYR, COLOR0, P0, P5, EJ, EA, N0, + ST_TET, COLOR0, P5, EF, EJ, N0, + ST_WDG, COLOR1, EC, EL, EB, P3, P6, P1, + ST_TET, COLOR1, P6, P3, P1, N0, + ST_PYR, COLOR1, P3, ED, EA, P1, N0, + ST_TET, COLOR1, P7, P3, P6, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_PYR, COLOR1, P1, EJ, EF, P6, N0, + ST_TET, COLOR1, EA, EJ, P1, N0, + // Case #203: (cloned #61) + ST_PNT, 0, COLOR1, 6, P3, P1, P0, P7, EF, EH, + ST_WDG, COLOR0, EF, P5, EJ, EH, P4, EI, + ST_TET, COLOR0, EC, EB, P2, EL, + ST_WDG, COLOR1, EC, EL, EB, P3, P6, P1, + ST_PYR, COLOR1, EH, P7, P6, EF, N0, + ST_TET, COLOR1, P7, P3, P6, N0, + ST_TET, COLOR1, P7, P0, P3, N0, + ST_PYR, COLOR1, EI, P0, P7, EH, N0, + ST_PYR, COLOR1, EJ, P1, P0, EI, N0, + ST_TET, COLOR1, P0, P1, P3, N0, + ST_PYR, COLOR1, EF, EJ, EI, EH, N0, + ST_TET, COLOR1, P1, P6, P3, N0, + ST_PYR, COLOR1, EF, P6, P1, EJ, N0, + // Case #204: (cloned #15) + ST_HEX, COLOR0, ED, EB, EF, EH, P0, P1, P5, P4, + ST_HEX, COLOR1, P3, P2, P6, P7, ED, EB, EF, EH, + // Case #205: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EB, EF, EH, EA, EI, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_TET, COLOR0, P4, EI, EH, N0, + ST_PYR, COLOR0, P1, P5, EF, EB, N0, + ST_TET, COLOR0, EA, P1, EB, N0, + ST_PYR, COLOR0, EI, P4, P1, EA, N0, + ST_TET, COLOR0, P4, P5, P1, N0, + ST_PYR, COLOR1, P3, P2, P6, P7, N0, + ST_TET, COLOR1, P7, P0, P3, N0, + ST_TET, COLOR1, P0, P2, P3, N0, + ST_PYR, COLOR1, P0, EA, EB, P2, N0, + ST_PYR, COLOR1, EB, EF, P6, P2, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_PYR, COLOR1, EH, EI, P0, P7, N0, + ST_TET, COLOR1, EA, P0, EI, N0, + // Case #206: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EF, EH, ED, EJ, EA, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_TET, COLOR0, P0, EA, ED, N0, + ST_PYR, COLOR0, P5, P4, EH, EF, N0, + ST_TET, COLOR0, EJ, P5, EF, N0, + ST_PYR, COLOR0, EA, P0, P5, EJ, N0, + ST_TET, COLOR0, P0, P4, P5, N0, + ST_PYR, COLOR1, P2, P6, P7, P3, N0, + ST_TET, COLOR1, P3, P1, P2, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + ST_PYR, COLOR1, P1, EJ, EF, P6, N0, + ST_PYR, COLOR1, EF, EH, P7, P6, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_PYR, COLOR1, ED, EA, P1, P3, N0, + ST_TET, COLOR1, EJ, P1, EA, N0, + // Case #207: (cloned #63) + ST_WDG, COLOR0, P5, EJ, EF, P4, EI, EH, + ST_HEX, COLOR1, EI, EH, EF, EJ, P0, P7, P6, P1, + ST_WDG, COLOR1, P3, P0, P7, P2, P1, P6, + // Case #208: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EI, EK, EE, EF, EL, + ST_PYR, COLOR0, P0, P1, P2, P3, N0, + ST_TET, COLOR0, P2, P5, N0, P1, + ST_PYR, COLOR0, P3, P2, EL, EK, N0, + ST_PYR, COLOR0, EI, P0, P3, EK, N0, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_PYR, COLOR0, P5, P0, EI, EE, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_PYR, COLOR0, EL, P2, P5, EF, N0, + ST_PYR, COLOR1, EK, EL, P6, P7, N0, + ST_PYR, COLOR1, EI, EK, P7, P4, N0, + ST_TET, COLOR1, EE, EI, P4, N0, + ST_TET, COLOR1, P4, P7, P6, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_TET, COLOR1, P6, EL, EF, N0, + // Case #209: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EE, EF, EL, EK, ED, EA, + ST_TET, COLOR0, P2, P3, P1, N0, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_TET, COLOR0, P5, P2, P1, N0, + ST_PYR, COLOR0, P5, EF, EL, P2, N0, + ST_TET, COLOR0, EE, EF, P5, N0, + ST_PYR, COLOR0, P5, P1, EA, EE, N0, + ST_PYR, COLOR0, EA, P1, P3, ED, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_TET, COLOR1, P4, P7, P6, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_PYR, COLOR1, EE, EA, P0, P4, N0, + ST_TET, COLOR1, P0, P7, P4, N0, + ST_TET, COLOR1, P0, EA, ED, N0, + ST_PYR, COLOR1, P0, ED, EK, P7, N0, + ST_PYR, COLOR1, EK, EL, P6, P7, N0, + ST_TET, COLOR1, EL, EF, P6, N0, + // Case #210: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EK, EL, EI, EA, EB, + ST_TET, COLOR0, P5, EE, EF, EJ, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_TET, COLOR0, P3, P0, P2, N0, + ST_PYR, COLOR0, P0, EA, EB, P2, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_WDG, COLOR1, P4, P6, P1, EE, EF, EJ, + ST_TET, COLOR1, P6, P1, P4, N0, + ST_PYR, COLOR1, P4, P1, EA, EI, N0, + ST_TET, COLOR1, P7, P6, P4, N0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_PYR, COLOR1, P1, P6, EL, EB, N0, + ST_TET, COLOR1, EA, P1, EB, N0, + // Case #211: (cloned #61) + ST_PNT, 0, COLOR1, 6, P4, P1, P0, P7, EL, EK, + ST_WDG, COLOR0, EK, P3, ED, EL, P2, EB, + ST_TET, COLOR0, EE, P5, EJ, EF, + ST_WDG, COLOR1, P4, P6, P1, EE, EF, EJ, + ST_PYR, COLOR1, EK, EL, P6, P7, N0, + ST_TET, COLOR1, P7, P6, P4, N0, + ST_TET, COLOR1, P7, P4, P0, N0, + ST_PYR, COLOR1, ED, EK, P7, P0, N0, + ST_PYR, COLOR1, EB, ED, P0, P1, N0, + ST_TET, COLOR1, P0, P4, P1, N0, + ST_PYR, COLOR1, EL, EK, ED, EB, N0, + ST_TET, COLOR1, P1, P4, P6, N0, + ST_PYR, COLOR1, EL, EB, P1, P6, N0, + // Case #212: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EK, EC, EB, EF, EE, EI, + ST_TET, COLOR0, P1, P0, P5, N0, + ST_PYR, COLOR0, P1, P5, EF, EB, N0, + ST_TET, COLOR0, P3, P0, P1, N0, + ST_PYR, COLOR0, P3, P1, EB, EC, N0, + ST_TET, COLOR0, EK, P3, EC, N0, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_PYR, COLOR0, EI, EE, P5, P0, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_TET, COLOR1, P7, P2, P6, N0, + ST_PYR, COLOR1, EK, EC, P2, P7, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_TET, COLOR1, P4, P7, P6, N0, + ST_TET, COLOR1, P4, EE, EI, N0, + ST_PYR, COLOR1, P4, P6, EF, EE, N0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_TET, COLOR1, EB, P2, EC, N0, + // Case #213: (cloned #61) + ST_PNT, 0, COLOR1, 6, P7, P0, P4, P6, EB, EF, + ST_WDG, COLOR0, EB, P1, EA, EF, P5, EE, + ST_TET, COLOR0, EK, ED, P3, EC, + ST_WDG, COLOR1, EK, EC, ED, P7, P2, P0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_TET, COLOR1, P6, P7, P2, N0, + ST_TET, COLOR1, P6, P4, P7, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_PYR, COLOR1, EA, P0, P4, EE, N0, + ST_TET, COLOR1, P4, P0, P7, N0, + ST_PYR, COLOR1, EB, EA, EE, EF, N0, + ST_TET, COLOR1, P0, P2, P7, N0, + ST_PYR, COLOR1, EB, P2, P0, EA, N0, + // Case #214: (cloned #61) + ST_PNT, 0, COLOR1, 6, P6, P1, P2, P7, EI, EK, + ST_WDG, COLOR0, EI, P0, EA, EK, P3, EC, + ST_TET, COLOR0, EF, EJ, P5, EE, + ST_WDG, COLOR1, EF, EE, EJ, P6, P4, P1, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_TET, COLOR1, P7, P6, P4, N0, + ST_TET, COLOR1, P7, P2, P6, N0, + ST_PYR, COLOR1, EC, P2, P7, EK, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_TET, COLOR1, P2, P1, P6, N0, + ST_PYR, COLOR1, EI, EA, EC, EK, N0, + ST_TET, COLOR1, P1, P4, P6, N0, + ST_PYR, COLOR1, EI, P4, P1, EA, N0, + // Case #215: (cloned #125) + ST_TET, COLOR0, EF, EE, EJ, P5, + ST_TET, COLOR0, EC, ED, EK, P3, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_WDG, COLOR1, EC, ED, EK, P2, P0, P7, + ST_TET, COLOR1, P7, P4, P0, P2, + ST_TET, COLOR1, P2, P1, P6, P4, + ST_TET, COLOR1, P1, P2, P0, P4, + ST_TET, COLOR1, P2, P6, P7, P4, + // Case #216: (cloned #27) + ST_TET, COLOR0, P2, P0, P1, P5, + ST_PYR, COLOR0, EE, P5, P0, EI, ED, + ST_PYR, COLOR0, EF, EL, P2, P5, EC, + ST_PYR, COLOR0, P0, P2, EC, ED, P5, + ST_PYR, COLOR0, ED, EC, EF, EE, P5, + ST_TET, COLOR1, P7, P6, P4, P3, + ST_PYR, COLOR1, ED, P3, P4, EI, EE, + ST_PYR, COLOR1, EC, EL, P6, P3, EF, + ST_PYR, COLOR1, P4, P6, EF, EE, P3, + ST_PYR, COLOR1, ED, EE, EF, EC, P3, + // Case #217: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EC, EA, EE, EL, EF, + ST_PYR, COLOR0, P1, EA, EE, P5, N0, + ST_TET, COLOR0, P5, EE, EF, N0, + ST_PYR, COLOR0, P2, EC, EA, P1, N0, + ST_TET, COLOR0, EL, EC, P2, N0, + ST_PYR, COLOR0, EF, EL, P2, P5, N0, + ST_TET, COLOR0, P5, P2, P1, N0, + ST_PYR, COLOR1, P7, P4, P0, P3, N0, + ST_TET, COLOR1, P4, P7, P6, N0, + ST_TET, COLOR1, P6, P7, P3, N0, + ST_PYR, COLOR1, P6, P3, EC, EL, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_PYR, COLOR1, EA, P0, P4, EE, N0, + ST_PYR, COLOR1, EE, P4, P6, EF, N0, + ST_TET, COLOR1, EL, EF, P6, N0, + // Case #218: (cloned #91) + ST_TET, COLOR0, EC, EL, EB, P2, + ST_TET, COLOR0, ED, EI, P0, EA, + ST_TET, COLOR0, EF, P5, EE, EJ, + ST_WDG, COLOR1, P1, P4, P6, EJ, EE, EF, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_WDG, COLOR1, P3, P1, P6, EC, EB, EL, + ST_TET, COLOR1, P1, P4, P6, P3, + ST_TET, COLOR1, P3, P6, P7, P4, + // Case #219: (cloned #95) + ST_TET, COLOR0, EL, EB, EC, P2, + ST_TET, COLOR0, EF, EE, EJ, P5, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_PYR, COLOR1, P0, P3, P7, P4, P1, + ST_TET, COLOR1, P6, P7, P3, P1, + ST_TET, COLOR1, P6, P4, P7, P1, + // Case #220: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EF, EB, ED, EE, EI, + ST_PYR, COLOR0, P1, EB, ED, P0, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_PYR, COLOR0, P5, EF, EB, P1, N0, + ST_TET, COLOR0, EE, EF, P5, N0, + ST_PYR, COLOR0, EI, EE, P5, P0, N0, + ST_TET, COLOR0, P0, P5, P1, N0, + ST_PYR, COLOR1, P7, P3, P2, P6, N0, + ST_TET, COLOR1, P3, P7, P4, N0, + ST_TET, COLOR1, P4, P7, P6, N0, + ST_PYR, COLOR1, P4, P6, EF, EE, N0, + ST_PYR, COLOR1, EF, P6, P2, EB, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_PYR, COLOR1, ED, P3, P4, EI, N0, + ST_TET, COLOR1, EE, EI, P4, N0, + // Case #221: (cloned #63) + ST_WDG, COLOR0, P1, EA, EB, P5, EE, EF, + ST_HEX, COLOR1, P0, P2, P6, P4, EA, EB, EF, EE, + ST_WDG, COLOR1, P7, P4, P6, P3, P0, P2, + // Case #222: (cloned #95) + ST_TET, COLOR0, EA, EI, ED, P0, + ST_TET, COLOR0, EJ, EF, EE, P5, + ST_WDG, COLOR1, EE, EJ, EF, P4, P1, P6, + ST_WDG, COLOR1, P4, P1, P3, EI, EA, ED, + ST_PYR, COLOR1, P7, P3, P2, P6, P4, + ST_TET, COLOR1, P1, P2, P3, P4, + ST_TET, COLOR1, P1, P6, P2, P4, + // Case #223: (cloned #127) + ST_PNT, 0, COLOR1, 7, P0, P3, P7, P4, P1, P2, P6, + ST_TET, COLOR0, EJ, EF, EE, P5, + ST_WDG, COLOR1, EJ, EF, EE, P1, P6, P4, + ST_TET, COLOR1, P1, P4, P6, N0, + ST_PYR, COLOR1, P2, P6, P7, P3, N0, + ST_TET, COLOR1, P6, P4, P7, N0, + ST_PYR, COLOR1, P0, P3, P7, P4, N0, + ST_TET, COLOR1, P0, P4, P1, N0, + ST_PYR, COLOR1, P0, P1, P2, P3, N0, + ST_TET, COLOR1, P1, P6, P2, N0, + // Case #224: (cloned #7) + ST_PNT, 0, NOCOLOR, 5, EK, EL, EH, EE, EJ, + ST_PYR, COLOR0, P3, P0, P1, P2, N0, + ST_TET, COLOR0, P1, P4, N0, P0, + ST_PYR, COLOR0, P2, P1, EJ, EL, N0, + ST_PYR, COLOR0, EK, P3, P2, EL, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_PYR, COLOR0, P4, P3, EK, EH, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_PYR, COLOR0, EJ, P1, P4, EE, N0, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_PYR, COLOR1, EK, EL, P6, P7, N0, + ST_TET, COLOR1, EH, EK, P7, N0, + ST_TET, COLOR1, P7, P6, P5, N0, + ST_PYR, COLOR1, EH, P7, P5, EE, N0, + ST_TET, COLOR1, P5, EJ, EE, N0, + // Case #225: (cloned #30) + ST_PNT, 0, NOCOLOR, 5, EL, EJ, EK, ED, EA, + ST_TET, COLOR0, P4, EH, EE, EI, + ST_PYR, COLOR0, P1, EJ, EL, P2, N0, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_TET, COLOR0, P2, P3, P1, N0, + ST_PYR, COLOR0, P3, ED, EA, P1, N0, + ST_TET, COLOR0, P1, EA, EJ, N0, + ST_WDG, COLOR1, P7, P5, P0, EH, EE, EI, + ST_TET, COLOR1, P5, P0, P7, N0, + ST_PYR, COLOR1, P7, P0, ED, EK, N0, + ST_TET, COLOR1, P6, P5, P7, N0, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_PYR, COLOR1, P0, P5, EJ, EA, N0, + ST_TET, COLOR1, ED, P0, EA, N0, + // Case #226: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EE, EH, EK, EL, EB, EA, + ST_TET, COLOR0, P3, P0, P2, N0, + ST_PYR, COLOR0, P3, P2, EL, EK, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_PYR, COLOR0, P4, P3, EK, EH, N0, + ST_TET, COLOR0, EE, P4, EH, N0, + ST_PYR, COLOR0, P4, EE, EA, P0, N0, + ST_PYR, COLOR0, EA, EB, P2, P0, N0, + ST_TET, COLOR0, P2, EB, EL, N0, + ST_TET, COLOR1, P5, P7, P6, N0, + ST_PYR, COLOR1, EE, EH, P7, P5, N0, + ST_PYR, COLOR1, EE, P5, P1, EA, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + ST_TET, COLOR1, P1, EB, EA, N0, + ST_PYR, COLOR1, P1, P6, EL, EB, N0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_TET, COLOR1, EK, P7, EH, N0, + // Case #227: (cloned #61) + ST_PNT, 0, COLOR1, 6, P5, P0, P1, P6, EK, EL, + ST_WDG, COLOR0, EK, P3, ED, EL, P2, EB, + ST_TET, COLOR0, EE, EI, P4, EH, + ST_WDG, COLOR1, EE, EH, EI, P5, P7, P0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_TET, COLOR1, P6, P5, P7, N0, + ST_TET, COLOR1, P6, P1, P5, N0, + ST_PYR, COLOR1, EB, P1, P6, EL, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_TET, COLOR1, P1, P0, P5, N0, + ST_PYR, COLOR1, EK, ED, EB, EL, N0, + ST_TET, COLOR1, P0, P7, P5, N0, + ST_PYR, COLOR1, EK, P7, P0, ED, N0, + // Case #228: (cloned #27) + ST_TET, COLOR0, P3, P0, P1, P4, + ST_PYR, COLOR0, EE, EJ, P1, P4, EB, + ST_PYR, COLOR0, EH, P4, P3, EK, EC, + ST_PYR, COLOR0, P1, EB, EC, P3, P4, + ST_PYR, COLOR0, EB, EE, EH, EC, P4, + ST_TET, COLOR1, P6, P5, P7, P2, + ST_PYR, COLOR1, EB, EJ, P5, P2, EE, + ST_PYR, COLOR1, EC, P2, P7, EK, EH, + ST_PYR, COLOR1, P5, EE, EH, P7, P2, + ST_PYR, COLOR1, EB, EC, EH, EE, P2, + // Case #229: (cloned #91) + ST_TET, COLOR0, EC, ED, EK, P3, + ST_TET, COLOR0, EB, P1, EJ, EA, + ST_TET, COLOR0, EH, EE, P4, EI, + ST_WDG, COLOR1, EI, EE, EH, P0, P5, P7, + ST_WDG, COLOR1, P2, P0, P5, EB, EA, EJ, + ST_WDG, COLOR1, EC, ED, EK, P2, P0, P7, + ST_TET, COLOR1, P0, P7, P5, P2, + ST_TET, COLOR1, P2, P6, P7, P5, + // Case #230: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EC, EA, EE, EK, EH, + ST_PYR, COLOR0, P0, P4, EE, EA, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_TET, COLOR0, EK, P3, EC, N0, + ST_PYR, COLOR0, EH, P4, P3, EK, N0, + ST_TET, COLOR0, P4, P0, P3, N0, + ST_PYR, COLOR1, P6, P2, P1, P5, N0, + ST_TET, COLOR1, P5, P7, P6, N0, + ST_TET, COLOR1, P7, P2, P6, N0, + ST_PYR, COLOR1, P7, EK, EC, P2, N0, + ST_PYR, COLOR1, EC, EA, P1, P2, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_PYR, COLOR1, EE, EH, P7, P5, N0, + ST_TET, COLOR1, EK, P7, EH, N0, + // Case #231: (cloned #95) + ST_TET, COLOR0, EK, EC, ED, P3, + ST_TET, COLOR0, EH, EI, EE, P4, + ST_WDG, COLOR1, P0, P7, P5, EI, EH, EE, + ST_WDG, COLOR1, ED, EK, EC, P0, P7, P2, + ST_PYR, COLOR1, P1, P5, P6, P2, P0, + ST_TET, COLOR1, P7, P2, P6, P0, + ST_TET, COLOR1, P7, P6, P5, P0, + // Case #232: (cloned #23) + ST_PNT, 0, NOCOLOR, 6, EH, EE, EJ, EL, EC, ED, + ST_TET, COLOR0, P1, P2, P0, N0, + ST_PYR, COLOR0, P1, EJ, EL, P2, N0, + ST_TET, COLOR0, P4, P1, P0, N0, + ST_PYR, COLOR0, P4, EE, EJ, P1, N0, + ST_TET, COLOR0, EH, EE, P4, N0, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_PYR, COLOR0, ED, P0, P2, EC, N0, + ST_TET, COLOR0, P2, EL, EC, N0, + ST_TET, COLOR1, P7, P6, P5, N0, + ST_PYR, COLOR1, EH, P7, P5, EE, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_TET, COLOR1, P3, P6, P7, N0, + ST_TET, COLOR1, P3, ED, EC, N0, + ST_PYR, COLOR1, P3, EC, EL, P6, N0, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_TET, COLOR1, EJ, EE, P5, N0, + // Case #233: (cloned #61) + ST_PNT, 0, COLOR1, 6, P7, P0, P3, P6, EJ, EL, + ST_WDG, COLOR0, EL, P2, EC, EJ, P1, EA, + ST_TET, COLOR0, EH, P4, EI, EE, + ST_WDG, COLOR1, P7, P5, P0, EH, EE, EI, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_TET, COLOR1, P6, P5, P7, N0, + ST_TET, COLOR1, P6, P7, P3, N0, + ST_PYR, COLOR1, EC, EL, P6, P3, N0, + ST_PYR, COLOR1, EA, EC, P3, P0, N0, + ST_TET, COLOR1, P3, P7, P0, N0, + ST_PYR, COLOR1, EJ, EL, EC, EA, N0, + ST_TET, COLOR1, P0, P7, P5, N0, + ST_PYR, COLOR1, EJ, EA, P0, P5, N0, + // Case #234: (cloned #61) + ST_PNT, 0, COLOR1, 6, P6, P1, P5, P7, ED, EH, + ST_WDG, COLOR0, EH, P4, EE, ED, P0, EA, + ST_TET, COLOR0, EL, P2, EB, EC, + ST_WDG, COLOR1, P6, P3, P1, EL, EC, EB, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_TET, COLOR1, P7, P3, P6, N0, + ST_TET, COLOR1, P7, P6, P5, N0, + ST_PYR, COLOR1, EE, EH, P7, P5, N0, + ST_PYR, COLOR1, EA, EE, P5, P1, N0, + ST_TET, COLOR1, P5, P6, P1, N0, + ST_PYR, COLOR1, ED, EH, EE, EA, N0, + ST_TET, COLOR1, P1, P6, P3, N0, + ST_PYR, COLOR1, ED, EA, P1, P3, N0, + // Case #235: (cloned #125) + ST_TET, COLOR0, EL, EB, EC, P2, + ST_TET, COLOR0, EE, EH, EI, P4, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_WDG, COLOR1, P5, P0, P7, EE, EI, EH, + ST_TET, COLOR1, P7, P0, P3, P5, + ST_TET, COLOR1, P5, P6, P1, P3, + ST_TET, COLOR1, P1, P0, P5, P3, + ST_TET, COLOR1, P5, P7, P6, P3, + // Case #236: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EH, ED, EB, EE, EJ, + ST_PYR, COLOR0, P0, P1, EB, ED, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_PYR, COLOR0, P4, P0, ED, EH, N0, + ST_TET, COLOR0, EE, P4, EH, N0, + ST_PYR, COLOR0, EJ, P1, P4, EE, N0, + ST_TET, COLOR0, P1, P0, P4, N0, + ST_PYR, COLOR1, P6, P7, P3, P2, N0, + ST_TET, COLOR1, P2, P5, P6, N0, + ST_TET, COLOR1, P5, P7, P6, N0, + ST_PYR, COLOR1, P5, EE, EH, P7, N0, + ST_PYR, COLOR1, EH, ED, P3, P7, N0, + ST_PYR, COLOR1, ED, EB, P2, P3, N0, + ST_PYR, COLOR1, EB, EJ, P5, P2, N0, + ST_TET, COLOR1, EE, P5, EJ, N0, + // Case #237: (cloned #95) + ST_TET, COLOR0, EE, EH, EI, P4, + ST_TET, COLOR0, EJ, EA, EB, P1, + ST_WDG, COLOR1, P0, P5, P2, EA, EJ, EB, + ST_WDG, COLOR1, EI, EE, EH, P0, P5, P7, + ST_PYR, COLOR1, P3, P2, P6, P7, P0, + ST_TET, COLOR1, P5, P7, P6, P0, + ST_TET, COLOR1, P5, P6, P2, P0, + // Case #238: (cloned #63) + ST_WDG, COLOR0, P4, EE, EH, P0, EA, ED, + ST_HEX, COLOR1, EA, ED, EH, EE, P1, P3, P7, P5, + ST_WDG, COLOR1, P2, P1, P3, P6, P5, P7, + // Case #239: (cloned #127) + ST_PNT, 0, COLOR1, 7, P3, P2, P1, P0, P7, P6, P5, + ST_TET, COLOR0, EH, EI, EE, P4, + ST_WDG, COLOR1, P7, P5, P0, EH, EE, EI, + ST_TET, COLOR1, P7, P5, P0, N0, + ST_PYR, COLOR1, P6, P2, P1, P5, N0, + ST_TET, COLOR1, P5, P1, P0, N0, + ST_PYR, COLOR1, P3, P0, P1, P2, N0, + ST_TET, COLOR1, P3, P7, P0, N0, + ST_PYR, COLOR1, P3, P2, P6, P7, N0, + ST_TET, COLOR1, P7, P6, P5, N0, + // Case #240: (cloned #15) + ST_HEX, COLOR0, P0, P1, P2, P3, EI, EJ, EL, EK, + ST_HEX, COLOR1, EI, EJ, EL, EK, P4, P5, P6, P7, + // Case #241: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EJ, EL, EK, EA, ED, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_TET, COLOR0, P3, EK, ED, N0, + ST_PYR, COLOR0, P1, EJ, EL, P2, N0, + ST_TET, COLOR0, EA, EJ, P1, N0, + ST_PYR, COLOR0, ED, EA, P1, P3, N0, + ST_TET, COLOR0, P3, P1, P2, N0, + ST_PYR, COLOR1, P4, P7, P6, P5, N0, + ST_TET, COLOR1, P7, P4, P0, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_PYR, COLOR1, P0, P5, EJ, EA, N0, + ST_PYR, COLOR1, EJ, P5, P6, EL, N0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_PYR, COLOR1, EK, P7, P0, ED, N0, + ST_TET, COLOR1, EA, ED, P0, N0, + // Case #242: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EL, EK, EI, EB, EA, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_TET, COLOR0, P0, EI, EA, N0, + ST_PYR, COLOR0, P2, EL, EK, P3, N0, + ST_TET, COLOR0, EB, EL, P2, N0, + ST_PYR, COLOR0, EA, EB, P2, P0, N0, + ST_TET, COLOR0, P0, P2, P3, N0, + ST_PYR, COLOR1, P5, P4, P7, P6, N0, + ST_TET, COLOR1, P4, P5, P1, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + ST_PYR, COLOR1, P1, P6, EL, EB, N0, + ST_PYR, COLOR1, EL, P6, P7, EK, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_PYR, COLOR1, EI, P4, P1, EA, N0, + ST_TET, COLOR1, EB, EA, P1, N0, + // Case #243: (cloned #63) + ST_WDG, COLOR0, P3, ED, EK, P2, EB, EL, + ST_HEX, COLOR1, P0, P7, P6, P1, ED, EK, EL, EB, + ST_WDG, COLOR1, P5, P1, P6, P4, P0, P7, + // Case #244: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EK, EI, EJ, EC, EB, + ST_PYR, COLOR0, P0, EI, EJ, P1, N0, + ST_TET, COLOR0, P1, EJ, EB, N0, + ST_PYR, COLOR0, P3, EK, EI, P0, N0, + ST_TET, COLOR0, EC, EK, P3, N0, + ST_PYR, COLOR0, EB, EC, P3, P1, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_PYR, COLOR1, P6, P5, P4, P7, N0, + ST_TET, COLOR1, P5, P6, P2, N0, + ST_TET, COLOR1, P2, P6, P7, N0, + ST_PYR, COLOR1, P2, P7, EK, EC, N0, + ST_PYR, COLOR1, EK, P7, P4, EI, N0, + ST_PYR, COLOR1, EI, P4, P5, EJ, N0, + ST_PYR, COLOR1, EJ, P5, P2, EB, N0, + ST_TET, COLOR1, EC, EB, P2, N0, + // Case #245: (cloned #95) + ST_TET, COLOR0, EC, ED, EK, P3, + ST_TET, COLOR0, EB, EJ, EA, P1, + ST_WDG, COLOR1, EA, EB, EJ, P0, P2, P5, + ST_WDG, COLOR1, P0, P2, P7, ED, EC, EK, + ST_PYR, COLOR1, P4, P7, P6, P5, P0, + ST_TET, COLOR1, P2, P6, P7, P0, + ST_TET, COLOR1, P2, P5, P6, P0, + // Case #246: (cloned #63) + ST_WDG, COLOR0, P0, EA, EI, P3, EC, EK, + ST_HEX, COLOR1, P1, P4, P7, P2, EA, EI, EK, EC, + ST_WDG, COLOR1, P6, P2, P7, P5, P1, P4, + // Case #247: (cloned #127) + ST_PNT, 0, COLOR1, 7, P4, P5, P1, P0, P7, P6, P2, + ST_TET, COLOR0, EK, EC, ED, P3, + ST_WDG, COLOR1, EK, EC, ED, P7, P2, P0, + ST_TET, COLOR1, P7, P0, P2, N0, + ST_PYR, COLOR1, P6, P2, P1, P5, N0, + ST_TET, COLOR1, P2, P0, P1, N0, + ST_PYR, COLOR1, P4, P5, P1, P0, N0, + ST_TET, COLOR1, P4, P0, P7, N0, + ST_PYR, COLOR1, P4, P7, P6, P5, N0, + ST_TET, COLOR1, P7, P2, P6, N0, + // Case #248: (cloned #31) + ST_PNT, 0, NOCOLOR, 5, EL, EJ, EI, EC, ED, + ST_PYR, COLOR0, P1, P0, EI, EJ, N0, + ST_TET, COLOR0, P0, ED, EI, N0, + ST_PYR, COLOR0, P2, P1, EJ, EL, N0, + ST_TET, COLOR0, EC, P2, EL, N0, + ST_PYR, COLOR0, ED, P0, P2, EC, N0, + ST_TET, COLOR0, P0, P1, P2, N0, + ST_PYR, COLOR1, P7, P6, P5, P4, N0, + ST_TET, COLOR1, P4, P3, P7, N0, + ST_TET, COLOR1, P3, P6, P7, N0, + ST_PYR, COLOR1, P3, EC, EL, P6, N0, + ST_PYR, COLOR1, EL, EJ, P5, P6, N0, + ST_PYR, COLOR1, EJ, EI, P4, P5, N0, + ST_PYR, COLOR1, EI, ED, P3, P4, N0, + ST_TET, COLOR1, EC, P3, ED, N0, + // Case #249: (cloned #63) + ST_WDG, COLOR0, P2, EC, EL, P1, EA, EJ, + ST_HEX, COLOR1, EA, EJ, EL, EC, P0, P5, P6, P3, + ST_WDG, COLOR1, P4, P0, P5, P7, P3, P6, + // Case #250: (cloned #95) + ST_TET, COLOR0, EA, EI, ED, P0, + ST_TET, COLOR0, EB, EC, EL, P2, + ST_WDG, COLOR1, P3, P1, P6, EC, EB, EL, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_PYR, COLOR1, P7, P6, P5, P4, P3, + ST_TET, COLOR1, P1, P4, P5, P3, + ST_TET, COLOR1, P1, P5, P6, P3, + // Case #251: (cloned #127) + ST_PNT, 0, COLOR1, 7, P0, P4, P7, P3, P1, P5, P6, + ST_TET, COLOR0, EB, EC, EL, P2, + ST_WDG, COLOR1, P1, P6, P3, EB, EL, EC, + ST_TET, COLOR1, P1, P6, P3, N0, + ST_PYR, COLOR1, P5, P4, P7, P6, N0, + ST_TET, COLOR1, P6, P7, P3, N0, + ST_PYR, COLOR1, P0, P3, P7, P4, N0, + ST_TET, COLOR1, P0, P1, P3, N0, + ST_PYR, COLOR1, P0, P4, P5, P1, N0, + ST_TET, COLOR1, P1, P5, P6, N0, + // Case #252: (cloned #63) + ST_WDG, COLOR0, P0, EI, ED, P1, EJ, EB, + ST_HEX, COLOR1, P4, P3, P2, P5, EI, ED, EB, EJ, + ST_WDG, COLOR1, P6, P5, P2, P7, P4, P3, + // Case #253: (cloned #127) + ST_PNT, 0, COLOR1, 7, P3, P7, P4, P0, P2, P6, P5, + ST_TET, COLOR0, EB, EJ, EA, P1, + ST_WDG, COLOR1, EB, EJ, EA, P2, P5, P0, + ST_TET, COLOR1, P2, P0, P5, N0, + ST_PYR, COLOR1, P6, P5, P4, P7, N0, + ST_TET, COLOR1, P5, P0, P4, N0, + ST_PYR, COLOR1, P3, P7, P4, P0, N0, + ST_TET, COLOR1, P3, P0, P2, N0, + ST_PYR, COLOR1, P3, P2, P6, P7, N0, + ST_TET, COLOR1, P2, P5, P6, N0, + // Case #254: (cloned #127) + ST_PNT, 0, COLOR1, 7, P7, P6, P5, P4, P3, P2, P1, + ST_TET, COLOR0, ED, EA, EI, P0, + ST_WDG, COLOR1, ED, EA, EI, P3, P1, P4, + ST_TET, COLOR1, P3, P4, P1, N0, + ST_PYR, COLOR1, P2, P1, P5, P6, N0, + ST_TET, COLOR1, P1, P4, P5, N0, + ST_PYR, COLOR1, P7, P6, P5, P4, N0, + ST_TET, COLOR1, P7, P4, P3, N0, + ST_PYR, COLOR1, P7, P3, P2, P6, N0, + ST_TET, COLOR1, P3, P1, P2, N0, + // Case #255: Unique case #22 + ST_HEX, COLOR1, P0, P1, P2, P3, P4, P5, P6, P7, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesPyr.cpp b/src/axom/mir/clipping/ClipCasesPyr.cpp new file mode 100644 index 0000000000..06bde5764b --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesPyr.cpp @@ -0,0 +1,226 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : August 11, 2003 +// +// Modifications: +// Jeremy Meredith, Mon Sep 15 17:30:21 PDT 2003 +// Added ability for Centroid-Points to have an associated color. + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesPyr = 32; + +int numClipShapesPyr[32] = { + 1, 3, 3, 8, 3, 4, 8, 4, // cases 0 - 7 + 3, 8, 4, 4, 8, 4, 4, 2, // cases 8 - 15 + 2, 4, 4, 8, 4, 4, 8, 3, // cases 16 - 23 + 4, 8, 4, 3, 8, 3, 3, 1 // cases 24 - 31 +}; + +int startClipShapesPyr[32] = { + 0, 7, 27, 47, 106, 126, 158, 217, // cases 0 - 7 + 247, 267, 326, 358, 388, 447, 477, 507, // cases 8 - 15 + 524, 541, 571, 601, 660, 690, 718, 777, // cases 16 - 23 + 797, 827, 886, 914, 934, 993, 1013, 1033 // cases 24 - 31 +}; + +unsigned char clipShapesPyr[] = { + // Case #0: Unique case #1 + ST_PYR, COLOR0, P0, P1, P2, P3, P4, + // Case #1: Unique case #2 + ST_WDG, COLOR0, EA, EE, ED, P1, P4, P3, + ST_TET, COLOR0, P1, P2, P3, P4, + ST_TET, COLOR1, P0, EA, ED, EE, + // Case #2: (cloned #1) + ST_WDG, COLOR0, EB, EF, EA, P2, P4, P0, + ST_TET, COLOR0, P2, P3, P0, P4, + ST_TET, COLOR1, P1, EB, EA, EF, + // Case #3: Unique case #3 + ST_PNT, 0, COLOR0, 7, P4, EF, EE, EB, ED, P2, P3, + ST_TET, COLOR0, EE, P4, EF, N0, + ST_PYR, COLOR0, EB, ED, EE, EF, N0, + ST_PYR, COLOR0, EB, EF, P4, P2, N0, + ST_TET, COLOR0, P2, P4, P3, N0, + ST_PYR, COLOR0, P3, P4, EE, ED, N0, + ST_PYR, COLOR0, P2, P3, ED, EB, N0, + ST_WDG, COLOR1, EB, EF, P1, ED, EE, P0, + // Case #4: (cloned #1) + ST_WDG, COLOR0, EC, EG, EB, P3, P4, P1, + ST_TET, COLOR0, P3, P0, P1, P4, + ST_TET, COLOR1, P2, EC, EB, EG, + // Case #5: Unique case #4 + ST_WDG, COLOR0, EE, P4, EG, EA, P1, EB, + ST_WDG, COLOR0, P4, EE, EG, P3, ED, EC, + ST_WDG, COLOR1, P0, EA, EE, P2, EB, EG, + ST_WDG, COLOR1, ED, P0, EE, EC, P2, EG, + // Case #6: (cloned #3) + ST_PNT, 0, COLOR0, 7, P4, EG, EF, EC, EA, P3, P0, + ST_TET, COLOR0, EF, P4, EG, N0, + ST_PYR, COLOR0, EC, EA, EF, EG, N0, + ST_PYR, COLOR0, EC, EG, P4, P3, N0, + ST_TET, COLOR0, P3, P4, P0, N0, + ST_PYR, COLOR0, P0, P4, EF, EA, N0, + ST_PYR, COLOR0, P3, P0, EA, EC, N0, + ST_WDG, COLOR1, EC, EG, P2, EA, EF, P1, + // Case #7: Unique case #5 + ST_TET, COLOR0, EE, EF, EG, P4, + ST_WDG, COLOR0, EC, ED, P3, EG, EE, P4, + ST_WDG, COLOR1, EE, EF, EG, P0, P1, P2, + ST_WDG, COLOR1, P2, EC, EG, P0, ED, EE, + // Case #8: (cloned #1) + ST_WDG, COLOR0, ED, EH, EC, P0, P4, P2, + ST_TET, COLOR0, P0, P1, P2, P4, + ST_TET, COLOR1, P3, ED, EC, EH, + // Case #9: (cloned #3) + ST_PNT, 0, COLOR0, 7, P4, EE, EH, EA, EC, P1, P2, + ST_TET, COLOR0, EH, P4, EE, N0, + ST_PYR, COLOR0, EA, EC, EH, EE, N0, + ST_PYR, COLOR0, EA, EE, P4, P1, N0, + ST_TET, COLOR0, P1, P4, P2, N0, + ST_PYR, COLOR0, P2, P4, EH, EC, N0, + ST_PYR, COLOR0, P1, P2, EC, EA, N0, + ST_WDG, COLOR1, EA, EE, P0, EC, EH, P3, + // Case #10: (cloned #5) + ST_WDG, COLOR0, EH, P4, EF, ED, P0, EA, + ST_WDG, COLOR0, P4, EH, EF, P2, EC, EB, + ST_WDG, COLOR1, P3, ED, EH, P1, EA, EF, + ST_WDG, COLOR1, EC, P3, EH, EB, P1, EF, + // Case #11: (cloned #7) + ST_TET, COLOR0, EH, EE, EF, P4, + ST_WDG, COLOR0, EB, EC, P2, EF, EH, P4, + ST_WDG, COLOR1, EH, EE, EF, P3, P0, P1, + ST_WDG, COLOR1, P1, EB, EF, P3, EC, EH, + // Case #12: (cloned #3) + ST_PNT, 0, COLOR0, 7, P4, EH, EG, ED, EB, P0, P1, + ST_TET, COLOR0, EG, P4, EH, N0, + ST_PYR, COLOR0, ED, EB, EG, EH, N0, + ST_PYR, COLOR0, ED, EH, P4, P0, N0, + ST_TET, COLOR0, P0, P4, P1, N0, + ST_PYR, COLOR0, P1, P4, EG, EB, N0, + ST_PYR, COLOR0, P0, P1, EB, ED, N0, + ST_WDG, COLOR1, ED, EH, P3, EB, EG, P2, + // Case #13: (cloned #7) + ST_TET, COLOR0, EG, EH, EE, P4, + ST_WDG, COLOR0, EA, EB, P1, EE, EG, P4, + ST_WDG, COLOR1, EG, EH, EE, P2, P3, P0, + ST_WDG, COLOR1, P0, EA, EE, P2, EB, EG, + // Case #14: (cloned #7) + ST_TET, COLOR0, EF, EG, EH, P4, + ST_WDG, COLOR0, ED, EA, P0, EH, EF, P4, + ST_WDG, COLOR1, EF, EG, EH, P1, P2, P3, + ST_WDG, COLOR1, P3, ED, EH, P1, EA, EF, + // Case #15: Unique case #6 + ST_PYR, COLOR0, EE, EF, EG, EH, P4, + ST_HEX, COLOR1, P0, P1, P2, P3, EE, EF, EG, EH, + // Case #16: Unique case #7 + ST_HEX, COLOR0, P0, P1, P2, P3, EE, EF, EG, EH, + ST_PYR, COLOR1, EE, EF, EG, EH, P4, + // Case #17: Unique case #8 + ST_WDG, COLOR0, ED, EH, P3, EA, EF, P1, + ST_WDG, COLOR0, EF, EG, EH, P1, P2, P3, + ST_WDG, COLOR1, P4, EF, EH, P0, EA, ED, + ST_TET, COLOR1, EF, EG, EH, P4, + // Case #18: (cloned #17) + ST_WDG, COLOR0, EA, EE, P0, EB, EG, P2, + ST_WDG, COLOR0, EG, EH, EE, P2, P3, P0, + ST_WDG, COLOR1, P4, EG, EE, P1, EB, EA, + ST_TET, COLOR1, EG, EH, EE, P4, + // Case #19: Unique case #9 + ST_PNT, 0, COLOR1, 7, EH, EG, ED, EB, P0, P1, P4, + ST_WDG, COLOR0, ED, EH, P3, EB, EG, P2, + ST_PYR, COLOR1, EG, EH, ED, EB, N0, + ST_PYR, COLOR1, ED, P0, P1, EB, N0, + ST_TET, COLOR1, P0, P4, P1, N0, + ST_TET, COLOR1, EH, EG, P4, N0, + ST_PYR, COLOR1, EH, P4, P0, ED, N0, + ST_PYR, COLOR1, P4, EG, EB, P1, N0, + // Case #20: (cloned #17) + ST_WDG, COLOR0, EB, EF, P1, EC, EH, P3, + ST_WDG, COLOR0, EH, EE, EF, P3, P0, P1, + ST_WDG, COLOR1, P4, EH, EF, P2, EC, EB, + ST_TET, COLOR1, EH, EE, EF, P4, + // Case #21: Unique case #10 + ST_TET, COLOR0, EA, P1, EB, EF, + ST_TET, COLOR0, P3, ED, EC, EH, + ST_WDG, COLOR1, EA, EB, EF, P0, P2, P4, + ST_WDG, COLOR1, EC, ED, EH, P2, P0, P4, + // Case #22: (cloned #19) + ST_PNT, 0, COLOR1, 7, EE, EH, EA, EC, P1, P2, P4, + ST_WDG, COLOR0, EA, EE, P0, EC, EH, P3, + ST_PYR, COLOR1, EH, EE, EA, EC, N0, + ST_PYR, COLOR1, EA, P1, P2, EC, N0, + ST_TET, COLOR1, P1, P4, P2, N0, + ST_TET, COLOR1, EE, EH, P4, N0, + ST_PYR, COLOR1, EE, P4, P1, EA, N0, + ST_PYR, COLOR1, P4, EH, EC, P2, N0, + // Case #23: Unique case #11 + ST_TET, COLOR0, P3, ED, EC, EH, + ST_WDG, COLOR1, P0, P2, P4, ED, EC, EH, + ST_TET, COLOR1, P0, P1, P2, P4, + // Case #24: (cloned #17) + ST_WDG, COLOR0, EC, EG, P2, ED, EE, P0, + ST_WDG, COLOR0, EE, EF, EG, P0, P1, P2, + ST_WDG, COLOR1, P4, EE, EG, P3, ED, EC, + ST_TET, COLOR1, EE, EF, EG, P4, + // Case #25: (cloned #19) + ST_PNT, 0, COLOR1, 7, EG, EF, EC, EA, P3, P0, P4, + ST_WDG, COLOR0, EC, EG, P2, EA, EF, P1, + ST_PYR, COLOR1, EF, EG, EC, EA, N0, + ST_PYR, COLOR1, EC, P3, P0, EA, N0, + ST_TET, COLOR1, P3, P4, P0, N0, + ST_TET, COLOR1, EG, EF, P4, N0, + ST_PYR, COLOR1, EG, P4, P3, EC, N0, + ST_PYR, COLOR1, P4, EF, EA, P0, N0, + // Case #26: (cloned #21) + ST_TET, COLOR0, ED, P0, EA, EE, + ST_TET, COLOR0, P2, EC, EB, EG, + ST_WDG, COLOR1, ED, EA, EE, P3, P1, P4, + ST_WDG, COLOR1, EB, EC, EG, P1, P3, P4, + // Case #27: (cloned #23) + ST_TET, COLOR0, P2, EC, EB, EG, + ST_WDG, COLOR1, P3, P1, P4, EC, EB, EG, + ST_TET, COLOR1, P3, P0, P1, P4, + // Case #28: (cloned #19) + ST_PNT, 0, COLOR1, 7, EF, EE, EB, ED, P2, P3, P4, + ST_WDG, COLOR0, EB, EF, P1, ED, EE, P0, + ST_PYR, COLOR1, EE, EF, EB, ED, N0, + ST_PYR, COLOR1, EB, P2, P3, ED, N0, + ST_TET, COLOR1, P2, P4, P3, N0, + ST_TET, COLOR1, EF, EE, P4, N0, + ST_PYR, COLOR1, EF, P4, P2, EB, N0, + ST_PYR, COLOR1, P4, EE, ED, P3, N0, + // Case #29: (cloned #23) + ST_TET, COLOR0, P1, EB, EA, EF, + ST_WDG, COLOR1, P2, P0, P4, EB, EA, EF, + ST_TET, COLOR1, P2, P3, P0, P4, + // Case #30: (cloned #23) + ST_TET, COLOR0, P0, EA, ED, EE, + ST_WDG, COLOR1, P1, P3, P4, EA, ED, EE, + ST_TET, COLOR1, P1, P2, P3, P4, + // Case #31: Unique case #12 + ST_PYR, COLOR1, P0, P1, P2, P3, P4, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesQuad.cpp b/src/axom/mir/clipping/ClipCasesQuad.cpp new file mode 100644 index 0000000000..d105f5dd93 --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesQuad.cpp @@ -0,0 +1,102 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : September 18, 2003 + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesQua = 16; + +int numClipShapesQua[16] = { + 1, 3, 3, 2, 3, 4, 2, 3, // cases 0 - 7 + 3, 2, 4, 3, 2, 3, 3, 1 // cases 8 - 15 +}; + +int startClipShapesQua[16] = { + 0, 6, 22, 38, 50, 66, 88, 100, // cases 0 - 7 + 116, 132, 144, 166, 182, 194, 210, 226 // cases 8 - 15 +}; + +unsigned char clipShapesQua[] = { + // Case #0: Unique case #1 + ST_QUA, COLOR0, P0, P1, P2, P3, + // Case #1: Unique case #2 + ST_QUA, COLOR0, ED, EA, P1, P3, + ST_TRI, COLOR0, P3, P1, P2, + ST_TRI, COLOR1, P0, EA, ED, + // Case #2: (cloned #1) + ST_QUA, COLOR0, EA, EB, P2, P0, + ST_TRI, COLOR0, P0, P2, P3, + ST_TRI, COLOR1, P1, EB, EA, + // Case #3: Unique case #3 + ST_QUA, COLOR0, ED, EB, P2, P3, + ST_QUA, COLOR1, P0, P1, EB, ED, + // Case #4: (cloned #1) + ST_QUA, COLOR0, EB, EC, P3, P1, + ST_TRI, COLOR0, P1, P3, P0, + ST_TRI, COLOR1, P2, EC, EB, + // Case #5: Unique case #4 + ST_TRI, COLOR0, ED, EC, P3, + ST_TRI, COLOR0, EB, EA, P1, + ST_QUA, COLOR1, P2, P0, EA, EB, + ST_QUA, COLOR1, P0, P2, EC, ED, + // Case #6: (cloned #3) + ST_QUA, COLOR0, EA, EC, P3, P0, + ST_QUA, COLOR1, P1, P2, EC, EA, + // Case #7: Unique case #5 + ST_TRI, COLOR0, ED, EC, P3, + ST_QUA, COLOR1, P0, P2, EC, ED, + ST_TRI, COLOR1, P1, P2, P0, + // Case #8: (cloned #1) + ST_QUA, COLOR0, EC, ED, P0, P2, + ST_TRI, COLOR0, P2, P0, P1, + ST_TRI, COLOR1, P3, ED, EC, + // Case #9: (cloned #3) + ST_QUA, COLOR0, EC, EA, P1, P2, + ST_QUA, COLOR1, P3, P0, EA, EC, + // Case #10: (cloned #5) + ST_TRI, COLOR0, EA, ED, P0, + ST_TRI, COLOR0, EC, EB, P2, + ST_QUA, COLOR1, P3, P1, EB, EC, + ST_QUA, COLOR1, P1, P3, ED, EA, + // Case #11: (cloned #7) + ST_TRI, COLOR0, EC, EB, P2, + ST_QUA, COLOR1, P3, P1, EB, EC, + ST_TRI, COLOR1, P0, P1, P3, + // Case #12: (cloned #3) + ST_QUA, COLOR0, EB, ED, P0, P1, + ST_QUA, COLOR1, P2, P3, ED, EB, + // Case #13: (cloned #7) + ST_TRI, COLOR0, EB, EA, P1, + ST_QUA, COLOR1, P2, P0, EA, EB, + ST_TRI, COLOR1, P3, P0, P2, + // Case #14: (cloned #7) + ST_TRI, COLOR0, EA, ED, P0, + ST_QUA, COLOR1, P1, P3, ED, EA, + ST_TRI, COLOR1, P2, P3, P1, + // Case #15: Unique case #6 + ST_QUA, COLOR1, P0, P1, P2, P3, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesTet.cpp b/src/axom/mir/clipping/ClipCasesTet.cpp new file mode 100644 index 0000000000..9b810cd5b6 --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesTet.cpp @@ -0,0 +1,94 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : August 11, 2003 +// +// Modifications: +// Jeremy Meredith, Mon Sep 15 17:30:21 PDT 2003 +// Added ability for Centroid-Points to have an associated color. + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesTet = 16; + +int numClipShapesTet[16] = { + 1, 2, 2, 2, 2, 2, 2, 2, // cases 0 - 7 + 2, 2, 2, 2, 2, 2, 2, 1 // cases 8 - 15 +}; + +int startClipShapesTet[16] = { + 0, 6, 20, 34, 50, 64, 80, 96, // cases 0 - 7 + 110, 124, 140, 156, 170, 186, 200, 214 // cases 8 - 15 +}; + +unsigned char clipShapesTet[] = { + // Case #0: Unique case #1 + ST_TET, COLOR0, P0, P1, P2, P3, + // Case #1: Unique case #2 + ST_WDG, COLOR0, EA, ED, EC, P1, P3, P2, + ST_TET, COLOR1, P0, EA, EC, ED, + // Case #2: (cloned #1) + ST_WDG, COLOR0, P0, P3, P2, EA, EE, EB, + ST_TET, COLOR1, P1, EB, EA, EE, + // Case #3: Unique case #3 + ST_WDG, COLOR0, P3, ED, EE, P2, EC, EB, + ST_WDG, COLOR1, P0, ED, EC, P1, EE, EB, + // Case #4: (cloned #1) + ST_WDG, COLOR0, EC, EF, EB, P0, P3, P1, + ST_TET, COLOR1, P2, EC, EB, EF, + // Case #5: (cloned #3) + ST_WDG, COLOR0, P1, EA, EB, P3, ED, EF, + ST_WDG, COLOR1, P2, EF, EB, P0, ED, EA, + // Case #6: (cloned #3) + ST_WDG, COLOR0, P3, EE, EF, P0, EA, EC, + ST_WDG, COLOR1, P1, EE, EA, P2, EF, EC, + // Case #7: Unique case #4 + ST_TET, COLOR0, ED, EE, EF, P3, + ST_WDG, COLOR1, ED, EE, EF, P0, P1, P2, + // Case #8: (cloned #1) + ST_WDG, COLOR0, P0, P2, P1, ED, EF, EE, + ST_TET, COLOR1, P3, EE, ED, EF, + // Case #9: (cloned #3) + ST_WDG, COLOR0, P2, EC, EF, P1, EA, EE, + ST_WDG, COLOR1, P0, EC, EA, P3, EF, EE, + // Case #10: (cloned #3) + ST_WDG, COLOR0, P0, EA, ED, P2, EB, EF, + ST_WDG, COLOR1, P3, EF, ED, P1, EB, EA, + // Case #11: (cloned #7) + ST_TET, COLOR0, EC, EF, EB, P2, + ST_WDG, COLOR1, P0, P1, P3, EC, EB, EF, + // Case #12: (cloned #3) + ST_WDG, COLOR0, P1, EB, EE, P0, EC, ED, + ST_WDG, COLOR1, P2, EB, EC, P3, EE, ED, + // Case #13: (cloned #7) + ST_TET, COLOR0, EA, EB, EE, P1, + ST_WDG, COLOR1, EA, EB, EE, P0, P2, P3, + // Case #14: (cloned #7) + ST_TET, COLOR0, EA, ED, EC, P0, + ST_WDG, COLOR1, P1, P2, P3, EA, EC, ED, + // Case #15: Unique case #5 + ST_TET, COLOR1, P0, P1, P2, P3, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesTri.cpp b/src/axom/mir/clipping/ClipCasesTri.cpp new file mode 100644 index 0000000000..f23a59717b --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesTri.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : September 18, 2003 + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesTri = 8; + +int numClipShapesTri[8] = { + 1, 2, 2, 2, 2, 2, 2, 1 // cases 0 - 7 +}; + +int startClipShapesTri[8] = { + 0, 5, 16, 27, 38, 49, 60, 71 // cases 0 - 7 +}; + +unsigned char clipShapesTri[] = { + // Case #0: Unique case #1 + ST_TRI, COLOR0, P0, P1, P2, + // Case #1: Unique case #2 + ST_QUA, COLOR0, P1, P2, EC, EA, + ST_TRI, COLOR1, P0, EA, EC, + // Case #2: (cloned #1) + ST_QUA, COLOR0, P2, P0, EA, EB, + ST_TRI, COLOR1, P1, EB, EA, + // Case #3: Unique case #3 + ST_TRI, COLOR0, EC, EB, P2, + ST_QUA, COLOR1, P0, P1, EB, EC, + // Case #4: (cloned #1) + ST_QUA, COLOR0, P0, P1, EB, EC, + ST_TRI, COLOR1, P2, EC, EB, + // Case #5: (cloned #3) + ST_TRI, COLOR0, EB, EA, P1, + ST_QUA, COLOR1, P2, P0, EA, EB, + // Case #6: (cloned #3) + ST_TRI, COLOR0, EA, EC, P0, + ST_QUA, COLOR1, P1, P2, EC, EA, + // Case #7: Unique case #4 + ST_TRI, COLOR1, P0, P1, P2, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesWdg.cpp b/src/axom/mir/clipping/ClipCasesWdg.cpp new file mode 100644 index 0000000000..7356138f07 --- /dev/null +++ b/src/axom/mir/clipping/ClipCasesWdg.cpp @@ -0,0 +1,622 @@ +// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt +// Project developers. See the top-level LICENSE file for dates and other +// details. No copyright assignment is required to contribute to VisIt. + +#include "ClipCases.h" +//--------------------------------------------------------------------------- +// Axom modifications +namespace axom { +namespace mir { +namespace clipping { +namespace visit { +//--------------------------------------------------------------------------- + +// Programmer: Jeremy Meredith +// Date : August 11, 2003 +// +// Modifications: +// Jeremy Meredith, Mon Sep 15 17:30:21 PDT 2003 +// Added ability for Centroid-Points to have an associated color. + +// This file is meant to be read and created by a program other than a +// compiler. If you must modify it by hand, at least be nice to the +// parser and don't add anything else to this file or rearrange it. + +int numClipCasesWdg = 64; + +int numClipShapesWdg[64] = { + 1, 3, 3, 9, 3, 9, 9, 2, // cases 0 - 7 + 3, 2, 15, 13, 15, 13, 10, 9, // cases 8 - 15 + 3, 15, 2, 13, 15, 10, 13, 9, // cases 16 - 23 + 9, 13, 13, 2, 10, 5, 5, 3, // cases 24 - 31 + 3, 15, 15, 10, 2, 13, 13, 9, // cases 32 - 39 + 9, 13, 10, 5, 13, 2, 5, 3, // cases 40 - 47 + 9, 10, 13, 5, 13, 5, 2, 3, // cases 48 - 55 + 2, 9, 9, 3, 9, 3, 3, 1 // cases 56 - 63 +}; + +int startClipShapesWdg[64] = { + 0, 8, 29, 50, 115, 136, 201, 266, // cases 0 - 7 + 282, 303, 321, 421, 508, 608, 695, 768, // cases 8 - 15 + 833, 854, 954, 972, 1059, 1159, 1232, 1319, // cases 16 - 23 + 1384, 1449, 1536, 1623, 1641, 1714, 1748, 1782, // cases 24 - 31 + 1803, 1824, 1924, 2024, 2097, 2115, 2202, 2289, // cases 32 - 39 + 2354, 2419, 2506, 2579, 2613, 2700, 2718, 2752, // cases 40 - 47 + 2773, 2838, 2911, 2998, 3032, 3119, 3153, 3171, // cases 48 - 55 + 3192, 3208, 3273, 3338, 3359, 3424, 3445, 3466 // cases 56 - 63 +}; + +unsigned char clipShapesWdg[] = { + // Case #0: Unique case #1 + ST_WDG, COLOR0, P0, P1, P2, P3, P4, P5, + // Case #1: Unique case #2 + ST_WDG, COLOR0, EA, EC, EG, P1, P2, P3, + ST_PYR, COLOR0, P1, P2, P5, P4, P3, + ST_TET, COLOR1, EG, EA, EC, P0, + // Case #2: (cloned #1) + ST_WDG, COLOR0, EB, EA, EH, P2, P0, P4, + ST_PYR, COLOR0, P2, P0, P3, P5, P4, + ST_TET, COLOR1, EH, EB, EA, P1, + // Case #3: Unique case #3 + ST_PNT, 0, COLOR0, 7, P2, P3, P4, EB, EC, EG, EH, + ST_TET, COLOR0, P4, P5, P3, P2, + ST_TET, COLOR0, P2, P3, P4, N0, + ST_PYR, COLOR0, EG, EH, P4, P3, N0, + ST_PYR, COLOR0, EB, EH, EG, EC, N0, + ST_TET, COLOR0, P2, EB, EC, N0, + ST_PYR, COLOR0, P2, EC, EG, P3, N0, + ST_PYR, COLOR0, EH, EB, P2, P4, N0, + ST_WDG, COLOR1, EC, EG, P0, EB, EH, P1, + // Case #4: (cloned #1) + ST_WDG, COLOR0, EC, EB, EI, P0, P1, P5, + ST_PYR, COLOR0, P0, P1, P4, P3, P5, + ST_TET, COLOR1, EI, EC, EB, P2, + // Case #5: (cloned #3) + ST_PNT, 0, COLOR0, 7, P1, P5, P3, EA, EB, EI, EG, + ST_TET, COLOR0, P3, P4, P5, P1, + ST_TET, COLOR0, P1, P5, P3, N0, + ST_PYR, COLOR0, EI, EG, P3, P5, N0, + ST_PYR, COLOR0, EA, EG, EI, EB, N0, + ST_TET, COLOR0, P1, EA, EB, N0, + ST_PYR, COLOR0, P1, EB, EI, P5, N0, + ST_PYR, COLOR0, EG, EA, P1, P3, N0, + ST_WDG, COLOR1, EB, EI, P2, EA, EG, P0, + // Case #6: (cloned #3) + ST_PNT, 0, COLOR0, 7, P0, P4, P5, EC, EA, EH, EI, + ST_TET, COLOR0, P5, P3, P4, P0, + ST_TET, COLOR0, P0, P4, P5, N0, + ST_PYR, COLOR0, EH, EI, P5, P4, N0, + ST_PYR, COLOR0, EC, EI, EH, EA, N0, + ST_TET, COLOR0, P0, EC, EA, N0, + ST_PYR, COLOR0, P0, EA, EH, P4, N0, + ST_PYR, COLOR0, EI, EC, P0, P5, N0, + ST_WDG, COLOR1, EA, EH, P1, EC, EI, P2, + // Case #7: Unique case #4 + ST_WDG, COLOR0, EG, EH, EI, P3, P4, P5, + ST_WDG, COLOR1, P0, P1, P2, EG, EH, EI, + // Case #8: (cloned #1) + ST_WDG, COLOR0, P4, P5, P0, ED, EF, EG, + ST_PYR, COLOR0, P4, P1, P2, P5, P0, + ST_TET, COLOR1, EG, EF, ED, P3, + // Case #9: Unique case #5 + ST_HEX, COLOR0, P1, P2, P5, P4, EA, EC, EF, ED, + ST_WDG, COLOR1, P0, EA, EC, P3, ED, EF, + // Case #10: Unique case #6 + ST_PNT, 0, NOCOLOR, 6, EA, EB, EH, ED, EF, EG, + ST_PYR, COLOR0, P5, P0, EG, EF, N0, + ST_TET, COLOR0, P0, EA, EG, N0, + ST_PYR, COLOR0, P0, P2, EB, EA, N0, + ST_TET, COLOR0, P5, P2, P0, N0, + ST_PYR, COLOR0, P4, EH, EB, P2, N0, + ST_TET, COLOR0, P5, P4, P2, N0, + ST_PYR, COLOR0, EF, ED, P4, P5, N0, + ST_TET, COLOR0, ED, EH, P4, N0, + ST_PYR, COLOR1, EG, EA, P1, P3, N0, + ST_PYR, COLOR1, P3, P1, EH, ED, N0, + ST_TET, COLOR1, P3, ED, EF, N0, + ST_TET, COLOR1, EF, EG, P3, N0, + ST_TET, COLOR1, P1, EB, EH, N0, + ST_TET, COLOR1, P1, EA, EB, N0, + // Case #11: Unique case #7 + ST_PNT, 0, NOCOLOR, 5, EB, EC, EF, ED, EH, + ST_PYR, COLOR0, P4, P5, EF, ED, N0, + ST_TET, COLOR0, ED, EH, P4, N0, + ST_PYR, COLOR0, EC, EF, P5, P2, N0, + ST_PYR, COLOR0, EB, P2, P4, EH, N0, + ST_TET, COLOR0, P4, P2, P5, N0, + ST_TET, COLOR0, P2, EB, EC, N0, + ST_TET, COLOR1, P0, P1, P3, N0, + ST_PYR, COLOR1, EC, EB, P1, P0, N0, + ST_PYR, COLOR1, EC, P0, P3, EF, N0, + ST_TET, COLOR1, EF, P3, ED, N0, + ST_PYR, COLOR1, P3, P1, EH, ED, N0, + ST_TET, COLOR1, P1, EB, EH, N0, + // Case #12: (cloned #10) + ST_PNT, 0, NOCOLOR, 6, EF, ED, EG, EC, EB, EI, + ST_PYR, COLOR0, P1, EB, EI, P5, N0, + ST_TET, COLOR0, P5, EI, EF, N0, + ST_PYR, COLOR0, P5, EF, ED, P4, N0, + ST_TET, COLOR0, P1, P5, P4, N0, + ST_PYR, COLOR0, P0, P4, ED, EG, N0, + ST_TET, COLOR0, P1, P4, P0, N0, + ST_PYR, COLOR0, EB, P1, P0, EC, N0, + ST_TET, COLOR0, EC, P0, EG, N0, + ST_PYR, COLOR1, EI, P2, P3, EF, N0, + ST_PYR, COLOR1, P2, EC, EG, P3, N0, + ST_TET, COLOR1, P2, EB, EC, N0, + ST_TET, COLOR1, EB, P2, EI, N0, + ST_TET, COLOR1, P3, EG, ED, N0, + ST_TET, COLOR1, P3, ED, EF, N0, + // Case #13: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EB, EA, ED, EF, EI, + ST_PYR, COLOR0, P5, EF, ED, P4, N0, + ST_TET, COLOR0, EF, P5, EI, N0, + ST_PYR, COLOR0, EA, P1, P4, ED, N0, + ST_PYR, COLOR0, EB, EI, P5, P1, N0, + ST_TET, COLOR0, P5, P4, P1, N0, + ST_TET, COLOR0, P1, EA, EB, N0, + ST_TET, COLOR1, P0, P3, P2, N0, + ST_PYR, COLOR1, EA, P0, P2, EB, N0, + ST_PYR, COLOR1, EA, ED, P3, P0, N0, + ST_TET, COLOR1, ED, EF, P3, N0, + ST_PYR, COLOR1, P3, EF, EI, P2, N0, + ST_TET, COLOR1, P2, EI, EB, N0, + // Case #14: Unique case #8 + ST_PNT, 0, COLOR1, 7, ED, EF, EI, EH, P3, P2, P1, + ST_TET, COLOR0, P0, EC, EA, EG, + ST_WDG, COLOR0, EF, EI, P5, ED, EH, P4, + ST_WDG, COLOR1, P2, P1, P3, EC, EA, EG, + ST_PYR, COLOR1, EF, ED, EH, EI, N0, + ST_PYR, COLOR1, EH, P1, P2, EI, N0, + ST_TET, COLOR1, P3, P2, P1, N0, + ST_TET, COLOR1, P3, ED, EF, N0, + ST_PYR, COLOR1, ED, P3, P1, EH, N0, + ST_PYR, COLOR1, EI, P2, P3, EF, N0, + // Case #15: Unique case #9 + ST_PNT, 0, COLOR1, 7, P1, P2, P3, EF, ED, EH, EI, + ST_WDG, COLOR0, ED, P4, EH, EF, P5, EI, + ST_TET, COLOR1, P0, P2, P1, P3, + ST_PYR, COLOR1, EF, ED, EH, EI, N0, + ST_PYR, COLOR1, EI, EH, P1, P2, N0, + ST_TET, COLOR1, P3, P2, P1, N0, + ST_TET, COLOR1, P3, ED, EF, N0, + ST_PYR, COLOR1, P3, P1, EH, ED, N0, + ST_PYR, COLOR1, P2, P3, EF, EI, N0, + // Case #16: (cloned #1) + ST_WDG, COLOR0, P5, P3, P1, EE, ED, EH, + ST_PYR, COLOR0, P5, P2, P0, P3, P1, + ST_TET, COLOR1, EH, ED, EE, P4, + // Case #17: (cloned #10) + ST_PNT, 0, NOCOLOR, 6, ED, EE, EH, EA, EC, EG, + ST_PYR, COLOR0, P2, EC, EG, P3, N0, + ST_TET, COLOR0, P3, EG, ED, N0, + ST_PYR, COLOR0, P3, ED, EE, P5, N0, + ST_TET, COLOR0, P2, P3, P5, N0, + ST_PYR, COLOR0, P1, P5, EE, EH, N0, + ST_TET, COLOR0, P2, P5, P1, N0, + ST_PYR, COLOR0, EC, P2, P1, EA, N0, + ST_TET, COLOR0, EA, P1, EH, N0, + ST_PYR, COLOR1, EG, P0, P4, ED, N0, + ST_PYR, COLOR1, P0, EA, EH, P4, N0, + ST_TET, COLOR1, P0, EC, EA, N0, + ST_TET, COLOR1, EC, P0, EG, N0, + ST_TET, COLOR1, P4, EH, EE, N0, + ST_TET, COLOR1, P4, EE, ED, N0, + // Case #18: (cloned #9) + ST_HEX, COLOR0, P2, P0, P3, P5, EB, EA, ED, EE, + ST_WDG, COLOR1, P1, EB, EA, P4, EE, ED, + // Case #19: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EC, EB, EE, ED, EG, + ST_PYR, COLOR0, P3, ED, EE, P5, N0, + ST_TET, COLOR0, ED, P3, EG, N0, + ST_PYR, COLOR0, EB, P2, P5, EE, N0, + ST_PYR, COLOR0, EC, EG, P3, P2, N0, + ST_TET, COLOR0, P3, P5, P2, N0, + ST_TET, COLOR0, P2, EB, EC, N0, + ST_TET, COLOR1, P1, P4, P0, N0, + ST_PYR, COLOR1, EB, P1, P0, EC, N0, + ST_PYR, COLOR1, EB, EE, P4, P1, N0, + ST_TET, COLOR1, EE, ED, P4, N0, + ST_PYR, COLOR1, P4, ED, EG, P0, N0, + ST_TET, COLOR1, P0, EG, EC, N0, + // Case #20: (cloned #10) + ST_PNT, 0, NOCOLOR, 6, EB, EC, EI, EE, ED, EH, + ST_PYR, COLOR0, P3, P1, EH, ED, N0, + ST_TET, COLOR0, P1, EB, EH, N0, + ST_PYR, COLOR0, P1, P0, EC, EB, N0, + ST_TET, COLOR0, P3, P0, P1, N0, + ST_PYR, COLOR0, P5, EI, EC, P0, N0, + ST_TET, COLOR0, P3, P5, P0, N0, + ST_PYR, COLOR0, ED, EE, P5, P3, N0, + ST_TET, COLOR0, EE, EI, P5, N0, + ST_PYR, COLOR1, EH, EB, P2, P4, N0, + ST_PYR, COLOR1, P4, P2, EI, EE, N0, + ST_TET, COLOR1, P4, EE, ED, N0, + ST_TET, COLOR1, ED, EH, P4, N0, + ST_TET, COLOR1, P2, EC, EI, N0, + ST_TET, COLOR1, P2, EB, EC, N0, + // Case #21: (cloned #14) + ST_PNT, 0, COLOR1, 7, EE, ED, EG, EI, P4, P0, P2, + ST_TET, COLOR0, P1, EA, EB, EH, + ST_WDG, COLOR0, ED, EG, P3, EE, EI, P5, + ST_WDG, COLOR1, P0, P2, P4, EA, EB, EH, + ST_PYR, COLOR1, ED, EE, EI, EG, N0, + ST_PYR, COLOR1, EI, P2, P0, EG, N0, + ST_TET, COLOR1, P4, P0, P2, N0, + ST_TET, COLOR1, P4, EE, ED, N0, + ST_PYR, COLOR1, EE, P4, P2, EI, N0, + ST_PYR, COLOR1, EG, P0, P4, ED, N0, + // Case #22: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EC, EA, ED, EE, EI, + ST_PYR, COLOR0, P5, P3, ED, EE, N0, + ST_TET, COLOR0, EE, EI, P5, N0, + ST_PYR, COLOR0, EA, ED, P3, P0, N0, + ST_PYR, COLOR0, EC, P0, P5, EI, N0, + ST_TET, COLOR0, P5, P0, P3, N0, + ST_TET, COLOR0, P0, EC, EA, N0, + ST_TET, COLOR1, P1, P2, P4, N0, + ST_PYR, COLOR1, EA, EC, P2, P1, N0, + ST_PYR, COLOR1, EA, P1, P4, ED, N0, + ST_TET, COLOR1, ED, P4, EE, N0, + ST_PYR, COLOR1, P4, P2, EI, EE, N0, + ST_TET, COLOR1, P2, EC, EI, N0, + // Case #23: (cloned #15) + ST_PNT, 0, COLOR1, 7, P2, P0, P4, ED, EE, EI, EG, + ST_WDG, COLOR0, EE, P5, EI, ED, P3, EG, + ST_TET, COLOR1, P1, P0, P2, P4, + ST_PYR, COLOR1, ED, EE, EI, EG, N0, + ST_PYR, COLOR1, EG, EI, P2, P0, N0, + ST_TET, COLOR1, P4, P0, P2, N0, + ST_TET, COLOR1, P4, EE, ED, N0, + ST_PYR, COLOR1, P4, P2, EI, EE, N0, + ST_PYR, COLOR1, P0, P4, ED, EG, N0, + // Case #24: (cloned #3) + ST_PNT, 0, COLOR0, 7, P5, P0, P1, EE, EF, EG, EH, + ST_TET, COLOR0, P1, P0, P2, P5, + ST_TET, COLOR0, P5, P1, P0, N0, + ST_PYR, COLOR0, EG, P0, P1, EH, N0, + ST_PYR, COLOR0, EE, EF, EG, EH, N0, + ST_TET, COLOR0, P5, EF, EE, N0, + ST_PYR, COLOR0, P5, P0, EG, EF, N0, + ST_PYR, COLOR0, EH, P1, P5, EE, N0, + ST_WDG, COLOR1, EE, EH, P4, EF, EG, P3, + // Case #25: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EE, EF, EC, EA, EH, + ST_PYR, COLOR0, P1, EA, EC, P2, N0, + ST_TET, COLOR0, EA, P1, EH, N0, + ST_PYR, COLOR0, EF, P5, P2, EC, N0, + ST_PYR, COLOR0, EE, EH, P1, P5, N0, + ST_TET, COLOR0, P1, P2, P5, N0, + ST_TET, COLOR0, P5, EF, EE, N0, + ST_TET, COLOR1, P3, P0, P4, N0, + ST_PYR, COLOR1, EF, P3, P4, EE, N0, + ST_PYR, COLOR1, EF, EC, P0, P3, N0, + ST_TET, COLOR1, EC, EA, P0, N0, + ST_PYR, COLOR1, P0, EA, EH, P4, N0, + ST_TET, COLOR1, P4, EH, EE, N0, + // Case #26: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EF, EE, EB, EA, EG, + ST_PYR, COLOR0, P0, P2, EB, EA, N0, + ST_TET, COLOR0, EA, EG, P0, N0, + ST_PYR, COLOR0, EE, EB, P2, P5, N0, + ST_PYR, COLOR0, EF, P5, P0, EG, N0, + ST_TET, COLOR0, P0, P5, P2, N0, + ST_TET, COLOR0, P5, EF, EE, N0, + ST_TET, COLOR1, P4, P3, P1, N0, + ST_PYR, COLOR1, EE, EF, P3, P4, N0, + ST_PYR, COLOR1, EE, P4, P1, EB, N0, + ST_TET, COLOR1, EB, P1, EA, N0, + ST_PYR, COLOR1, P1, P3, EG, EA, N0, + ST_TET, COLOR1, P3, EF, EG, N0, + // Case #27: Unique case #10 + ST_WDG, COLOR0, EF, P5, EE, EC, P2, EB, + ST_HEX, COLOR1, P3, P4, EE, EF, P0, P1, EB, EC, + // Case #28: (cloned #14) + ST_PNT, 0, COLOR1, 7, EC, EB, EH, EG, P2, P4, P3, + ST_TET, COLOR0, P5, EF, EE, EI, + ST_WDG, COLOR0, EC, EG, P0, EB, EH, P1, + ST_WDG, COLOR1, EE, EF, EI, P4, P3, P2, + ST_PYR, COLOR1, EB, EH, EG, EC, N0, + ST_PYR, COLOR1, EG, EH, P4, P3, N0, + ST_TET, COLOR1, P2, P3, P4, N0, + ST_TET, COLOR1, P2, EB, EC, N0, + ST_PYR, COLOR1, EC, EG, P3, P2, N0, + ST_PYR, COLOR1, EH, EB, P2, P4, N0, + // Case #29: Unique case #11 + ST_TET, COLOR0, P1, EA, EB, EH, + ST_TET, COLOR0, EF, EE, P5, EI, + ST_WDG, COLOR1, P2, P3, P4, EI, EF, EE, + ST_TET, COLOR1, P2, P3, P4, P0, + ST_WDG, COLOR1, P2, P4, P0, EB, EH, EA, + // Case #30: (cloned #29) + ST_TET, COLOR0, P5, EF, EE, EI, + ST_TET, COLOR0, EA, P0, EC, EG, + ST_WDG, COLOR1, EG, EA, EC, P3, P1, P2, + ST_TET, COLOR1, P3, P2, P1, P4, + ST_WDG, COLOR1, EF, EI, EE, P3, P2, P4, + // Case #31: Unique case #12 + ST_TET, COLOR0, EF, EI, EE, P5, + ST_WDG, COLOR1, EI, EE, EF, P2, P4, P3, + ST_PYR, COLOR1, P0, P1, P4, P3, P2, + // Case #32: (cloned #1) + ST_WDG, COLOR0, P3, P4, P2, EF, EE, EI, + ST_PYR, COLOR0, P3, P0, P1, P4, P2, + ST_TET, COLOR1, EI, EE, EF, P5, + // Case #33: (cloned #10) + ST_PNT, 0, NOCOLOR, 6, EC, EA, EG, EF, EE, EI, + ST_PYR, COLOR0, P4, P2, EI, EE, N0, + ST_TET, COLOR0, P2, EC, EI, N0, + ST_PYR, COLOR0, P2, P1, EA, EC, N0, + ST_TET, COLOR0, P4, P1, P2, N0, + ST_PYR, COLOR0, P3, EG, EA, P1, N0, + ST_TET, COLOR0, P4, P3, P1, N0, + ST_PYR, COLOR0, EE, EF, P3, P4, N0, + ST_TET, COLOR0, EF, EG, P3, N0, + ST_PYR, COLOR1, EI, EC, P0, P5, N0, + ST_PYR, COLOR1, P5, P0, EG, EF, N0, + ST_TET, COLOR1, P5, EF, EE, N0, + ST_TET, COLOR1, EE, EI, P5, N0, + ST_TET, COLOR1, P0, EA, EG, N0, + ST_TET, COLOR1, P0, EC, EA, N0, + // Case #34: (cloned #10) + ST_PNT, 0, NOCOLOR, 6, EE, EF, EI, EB, EA, EH, + ST_PYR, COLOR0, P0, EA, EH, P4, N0, + ST_TET, COLOR0, P4, EH, EE, N0, + ST_PYR, COLOR0, P4, EE, EF, P3, N0, + ST_TET, COLOR0, P0, P4, P3, N0, + ST_PYR, COLOR0, P2, P3, EF, EI, N0, + ST_TET, COLOR0, P0, P3, P2, N0, + ST_PYR, COLOR0, EA, P0, P2, EB, N0, + ST_TET, COLOR0, EB, P2, EI, N0, + ST_PYR, COLOR1, EH, P1, P5, EE, N0, + ST_PYR, COLOR1, P1, EB, EI, P5, N0, + ST_TET, COLOR1, P1, EA, EB, N0, + ST_TET, COLOR1, EA, P1, EH, N0, + ST_TET, COLOR1, P5, EI, EF, N0, + ST_TET, COLOR1, P5, EF, EE, N0, + // Case #35: (cloned #14) + ST_PNT, 0, COLOR1, 7, EF, EE, EH, EG, P5, P1, P0, + ST_TET, COLOR0, P2, EB, EC, EI, + ST_WDG, COLOR0, EE, EH, P4, EF, EG, P3, + ST_WDG, COLOR1, P1, P0, P5, EB, EC, EI, + ST_PYR, COLOR1, EE, EF, EG, EH, N0, + ST_PYR, COLOR1, EG, P0, P1, EH, N0, + ST_TET, COLOR1, P5, P1, P0, N0, + ST_TET, COLOR1, P5, EF, EE, N0, + ST_PYR, COLOR1, EF, P5, P0, EG, N0, + ST_PYR, COLOR1, EH, P1, P5, EE, N0, + // Case #36: (cloned #9) + ST_HEX, COLOR0, P0, P1, P4, P3, EC, EB, EE, EF, + ST_WDG, COLOR1, P2, EC, EB, P5, EF, EE, + // Case #37: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EA, EB, EE, EF, EG, + ST_PYR, COLOR0, P3, P4, EE, EF, N0, + ST_TET, COLOR0, EF, EG, P3, N0, + ST_PYR, COLOR0, EB, EE, P4, P1, N0, + ST_PYR, COLOR0, EA, P1, P3, EG, N0, + ST_TET, COLOR0, P3, P1, P4, N0, + ST_TET, COLOR0, P1, EA, EB, N0, + ST_TET, COLOR1, P2, P0, P5, N0, + ST_PYR, COLOR1, EB, EA, P0, P2, N0, + ST_PYR, COLOR1, EB, P2, P5, EE, N0, + ST_TET, COLOR1, EE, P5, EF, N0, + ST_PYR, COLOR1, P5, P0, EG, EF, N0, + ST_TET, COLOR1, P0, EA, EG, N0, + // Case #38: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EA, EC, EF, EE, EH, + ST_PYR, COLOR0, P4, EE, EF, P3, N0, + ST_TET, COLOR0, EE, P4, EH, N0, + ST_PYR, COLOR0, EC, P0, P3, EF, N0, + ST_PYR, COLOR0, EA, EH, P4, P0, N0, + ST_TET, COLOR0, P4, P3, P0, N0, + ST_TET, COLOR0, P0, EC, EA, N0, + ST_TET, COLOR1, P2, P5, P1, N0, + ST_PYR, COLOR1, EC, P2, P1, EA, N0, + ST_PYR, COLOR1, EC, EF, P5, P2, N0, + ST_TET, COLOR1, EF, EE, P5, N0, + ST_PYR, COLOR1, P5, EE, EH, P1, N0, + ST_TET, COLOR1, P1, EH, EA, N0, + // Case #39: (cloned #15) + ST_PNT, 0, COLOR1, 7, P0, P1, P5, EE, EF, EG, EH, + ST_WDG, COLOR0, EF, P3, EG, EE, P4, EH, + ST_TET, COLOR1, P2, P1, P0, P5, + ST_PYR, COLOR1, EE, EF, EG, EH, N0, + ST_PYR, COLOR1, EH, EG, P0, P1, N0, + ST_TET, COLOR1, P5, P1, P0, N0, + ST_TET, COLOR1, P5, EF, EE, N0, + ST_PYR, COLOR1, P5, P0, EG, EF, N0, + ST_PYR, COLOR1, P1, P5, EE, EH, N0, + // Case #40: (cloned #3) + ST_PNT, 0, COLOR0, 7, P4, P2, P0, ED, EE, EI, EG, + ST_TET, COLOR0, P0, P2, P1, P4, + ST_TET, COLOR0, P4, P0, P2, N0, + ST_PYR, COLOR0, EI, P2, P0, EG, N0, + ST_PYR, COLOR0, ED, EE, EI, EG, N0, + ST_TET, COLOR0, P4, EE, ED, N0, + ST_PYR, COLOR0, P4, P2, EI, EE, N0, + ST_PYR, COLOR0, EG, P0, P4, ED, N0, + ST_WDG, COLOR1, ED, EG, P3, EE, EI, P5, + // Case #41: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EE, ED, EA, EC, EI, + ST_PYR, COLOR0, P2, P1, EA, EC, N0, + ST_TET, COLOR0, EC, EI, P2, N0, + ST_PYR, COLOR0, ED, EA, P1, P4, N0, + ST_PYR, COLOR0, EE, P4, P2, EI, N0, + ST_TET, COLOR0, P2, P4, P1, N0, + ST_TET, COLOR0, P4, EE, ED, N0, + ST_TET, COLOR1, P3, P5, P0, N0, + ST_PYR, COLOR1, ED, EE, P5, P3, N0, + ST_PYR, COLOR1, ED, P3, P0, EA, N0, + ST_TET, COLOR1, EA, P0, EC, N0, + ST_PYR, COLOR1, P0, P5, EI, EC, N0, + ST_TET, COLOR1, P5, EE, EI, N0, + // Case #42: (cloned #14) + ST_PNT, 0, COLOR1, 7, EB, EA, EG, EI, P1, P3, P5, + ST_TET, COLOR0, P4, EE, ED, EH, + ST_WDG, COLOR0, EB, EI, P2, EA, EG, P0, + ST_WDG, COLOR1, ED, EE, EH, P3, P5, P1, + ST_PYR, COLOR1, EA, EG, EI, EB, N0, + ST_PYR, COLOR1, EI, EG, P3, P5, N0, + ST_TET, COLOR1, P1, P5, P3, N0, + ST_TET, COLOR1, P1, EA, EB, N0, + ST_PYR, COLOR1, EB, EI, P5, P1, N0, + ST_PYR, COLOR1, EG, EA, P1, P3, N0, + // Case #43: (cloned #29) + ST_TET, COLOR0, P4, EE, ED, EH, + ST_TET, COLOR0, EC, P2, EB, EI, + ST_WDG, COLOR1, EI, EC, EB, P5, P0, P1, + ST_TET, COLOR1, P5, P1, P0, P3, + ST_WDG, COLOR1, EE, EH, ED, P5, P1, P3, + // Case #44: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, ED, EE, EB, EC, EG, + ST_PYR, COLOR0, P0, EC, EB, P1, N0, + ST_TET, COLOR0, EC, P0, EG, N0, + ST_PYR, COLOR0, EE, P4, P1, EB, N0, + ST_PYR, COLOR0, ED, EG, P0, P4, N0, + ST_TET, COLOR0, P0, P1, P4, N0, + ST_TET, COLOR0, P4, EE, ED, N0, + ST_TET, COLOR1, P5, P2, P3, N0, + ST_PYR, COLOR1, EE, P5, P3, ED, N0, + ST_PYR, COLOR1, EE, EB, P2, P5, N0, + ST_TET, COLOR1, EB, EC, P2, N0, + ST_PYR, COLOR1, P2, EC, EG, P3, N0, + ST_TET, COLOR1, P3, EG, ED, N0, + // Case #45: (cloned #27) + ST_WDG, COLOR0, EE, P4, ED, EB, P1, EA, + ST_HEX, COLOR1, P5, P3, ED, EE, P2, P0, EA, EB, + // Case #46: (cloned #29) + ST_TET, COLOR0, P0, EC, EA, EG, + ST_TET, COLOR0, EE, ED, P4, EH, + ST_WDG, COLOR1, P1, P5, P3, EH, EE, ED, + ST_TET, COLOR1, P1, P5, P3, P2, + ST_WDG, COLOR1, P1, P3, P2, EA, EG, EC, + // Case #47: (cloned #31) + ST_TET, COLOR0, EE, EH, ED, P4, + ST_WDG, COLOR1, EH, ED, EE, P1, P3, P5, + ST_PYR, COLOR1, P2, P0, P3, P5, P1, + // Case #48: (cloned #3) + ST_PNT, 0, COLOR0, 7, P3, P1, P2, EF, ED, EH, EI, + ST_TET, COLOR0, P2, P1, P0, P3, + ST_TET, COLOR0, P3, P2, P1, N0, + ST_PYR, COLOR0, EH, P1, P2, EI, N0, + ST_PYR, COLOR0, EF, ED, EH, EI, N0, + ST_TET, COLOR0, P3, ED, EF, N0, + ST_PYR, COLOR0, P3, P1, EH, ED, N0, + ST_PYR, COLOR0, EI, P2, P3, EF, N0, + ST_WDG, COLOR1, EF, EI, P5, ED, EH, P4, + // Case #49: (cloned #14) + ST_PNT, 0, COLOR1, 7, EA, EC, EI, EH, P0, P5, P4, + ST_TET, COLOR0, P3, ED, EF, EG, + ST_WDG, COLOR0, EA, EH, P1, EC, EI, P2, + ST_WDG, COLOR1, EF, ED, EG, P5, P4, P0, + ST_PYR, COLOR1, EC, EI, EH, EA, N0, + ST_PYR, COLOR1, EH, EI, P5, P4, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_TET, COLOR1, P0, EC, EA, N0, + ST_PYR, COLOR1, EA, EH, P4, P0, N0, + ST_PYR, COLOR1, EI, EC, P0, P5, N0, + // Case #50: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, EF, ED, EA, EB, EI, + ST_PYR, COLOR0, P2, EB, EA, P0, N0, + ST_TET, COLOR0, EB, P2, EI, N0, + ST_PYR, COLOR0, ED, P3, P0, EA, N0, + ST_PYR, COLOR0, EF, EI, P2, P3, N0, + ST_TET, COLOR0, P2, P0, P3, N0, + ST_TET, COLOR0, P3, ED, EF, N0, + ST_TET, COLOR1, P4, P1, P5, N0, + ST_PYR, COLOR1, ED, P4, P5, EF, N0, + ST_PYR, COLOR1, ED, EA, P1, P4, N0, + ST_TET, COLOR1, EA, EB, P1, N0, + ST_PYR, COLOR1, P1, EB, EI, P5, N0, + ST_TET, COLOR1, P5, EI, EF, N0, + // Case #51: (cloned #29) + ST_TET, COLOR0, P2, EB, EC, EI, + ST_TET, COLOR0, ED, EF, P3, EG, + ST_WDG, COLOR1, P0, P4, P5, EG, ED, EF, + ST_TET, COLOR1, P0, P4, P5, P1, + ST_WDG, COLOR1, P0, P5, P1, EC, EI, EB, + // Case #52: (cloned #11) + ST_PNT, 0, NOCOLOR, 5, ED, EF, EC, EB, EH, + ST_PYR, COLOR0, P1, P0, EC, EB, N0, + ST_TET, COLOR0, EB, EH, P1, N0, + ST_PYR, COLOR0, EF, EC, P0, P3, N0, + ST_PYR, COLOR0, ED, P3, P1, EH, N0, + ST_TET, COLOR0, P1, P3, P0, N0, + ST_TET, COLOR0, P3, ED, EF, N0, + ST_TET, COLOR1, P5, P4, P2, N0, + ST_PYR, COLOR1, EF, ED, P4, P5, N0, + ST_PYR, COLOR1, EF, P5, P2, EC, N0, + ST_TET, COLOR1, EC, P2, EB, N0, + ST_PYR, COLOR1, P2, P4, EH, EB, N0, + ST_TET, COLOR1, P4, ED, EH, N0, + // Case #53: (cloned #29) + ST_TET, COLOR0, P3, ED, EF, EG, + ST_TET, COLOR0, EB, P1, EA, EH, + ST_WDG, COLOR1, EH, EB, EA, P4, P2, P0, + ST_TET, COLOR1, P4, P0, P2, P5, + ST_WDG, COLOR1, ED, EG, EF, P4, P0, P5, + // Case #54: (cloned #27) + ST_WDG, COLOR0, ED, P3, EF, EA, P0, EC, + ST_HEX, COLOR1, P4, P5, EF, ED, P1, P2, EC, EA, + // Case #55: (cloned #31) + ST_TET, COLOR0, ED, EG, EF, P3, + ST_WDG, COLOR1, EG, EF, ED, P0, P5, P4, + ST_PYR, COLOR1, P1, P2, P5, P4, P0, + // Case #56: (cloned #7) + ST_WDG, COLOR0, P0, P1, P2, EG, EH, EI, + ST_WDG, COLOR1, EG, EH, EI, P3, P4, P5, + // Case #57: (cloned #15) + ST_PNT, 0, COLOR1, 7, P4, P5, P0, EC, EA, EH, EI, + ST_WDG, COLOR0, EC, P2, EI, EA, P1, EH, + ST_TET, COLOR1, P3, P4, P5, P0, + ST_PYR, COLOR1, EC, EI, EH, EA, N0, + ST_PYR, COLOR1, EI, P5, P4, EH, N0, + ST_TET, COLOR1, P0, P4, P5, N0, + ST_TET, COLOR1, P0, EC, EA, N0, + ST_PYR, COLOR1, P0, EA, EH, P4, N0, + ST_PYR, COLOR1, P5, EI, EC, P0, N0, + // Case #58: (cloned #15) + ST_PNT, 0, COLOR1, 7, P5, P3, P1, EA, EB, EI, EG, + ST_WDG, COLOR0, EA, P0, EG, EB, P2, EI, + ST_TET, COLOR1, P4, P5, P3, P1, + ST_PYR, COLOR1, EA, EG, EI, EB, N0, + ST_PYR, COLOR1, EG, P3, P5, EI, N0, + ST_TET, COLOR1, P1, P5, P3, N0, + ST_TET, COLOR1, P1, EA, EB, N0, + ST_PYR, COLOR1, P1, EB, EI, P5, N0, + ST_PYR, COLOR1, P3, EG, EA, P1, N0, + // Case #59: (cloned #31) + ST_TET, COLOR0, EC, EB, EI, P2, + ST_WDG, COLOR1, P5, P1, P0, EI, EB, EC, + ST_PYR, COLOR1, P3, P0, P1, P4, P5, + // Case #60: (cloned #15) + ST_PNT, 0, COLOR1, 7, P3, P4, P2, EB, EC, EG, EH, + ST_WDG, COLOR0, EB, P1, EH, EC, P0, EG, + ST_TET, COLOR1, P5, P3, P4, P2, + ST_PYR, COLOR1, EB, EH, EG, EC, N0, + ST_PYR, COLOR1, EH, P4, P3, EG, N0, + ST_TET, COLOR1, P2, P3, P4, N0, + ST_TET, COLOR1, P2, EB, EC, N0, + ST_PYR, COLOR1, P2, EC, EG, P3, N0, + ST_PYR, COLOR1, P4, EH, EB, P2, N0, + // Case #61: (cloned #31) + ST_TET, COLOR0, EB, EA, EH, P1, + ST_WDG, COLOR1, P4, P0, P2, EH, EA, EB, + ST_PYR, COLOR1, P5, P2, P0, P3, P4, + // Case #62: (cloned #31) + ST_TET, COLOR0, EA, EC, EG, P0, + ST_WDG, COLOR1, P3, P2, P1, EG, EC, EA, + ST_PYR, COLOR1, P4, P1, P2, P5, P3, + // Case #63: Unique case #13 + ST_WDG, COLOR1, P0, P1, P2, P3, P4, P5, + // Dummy + 0 +}; + +//--------------------------------------------------------------------------- +// Axom modifications +} // namespace visit +} // namespace clipping +} // namespace mir +} // namespace axom +//--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp new file mode 100644 index 0000000000..d01b281f0a --- /dev/null +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -0,0 +1,262 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_CLIPPING_CLIP_TABLE_MANAGER_HPP_ +#define AXOM_MIR_CLIPPING_CLIP_TABLE_MANAGER_HPP_ + +#include "axom/core/Array.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/mir/clipping/ClipCases.hpp" + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + +/** + * \brief This struct contains data for a clipping table. + * + * \tparam ContainerType The container for the clipping data. + * \tparam SPACE The memory space for the clipping data. + */ +template +struct ClipTableBase +{ + using IntContainerType = ContainerType; + using UInt8ContainerType = ContainerType; + + // Q: Do I need to explicitly provide a constructor to get it marked as AXOM_HOST_DEVICE? + + /** + * \brief Return the number of cases for the clipping table. + * + * \return The number of cases for the clipping table. + */ + AXOM_HOST_DEVICE + size_t size() const { return m_shapes.size(); } + + /** + * \brief Return the number of shapes for a given clipping case. + * + * \param caseId The index of the clipping case. + * + * \return The number of shapes in the clipping case. + */ + AXOM_HOST_DEVICE + size_t shapesForCase(size_t caseId) const + { + assert(caseId < m_shapes.size()); + return m_shapes[caseId]; + } + + /** + * \brief Return data for the requested shape in the clipping case. + * + * \param index The index of the clipping case. + * + * \return A container that holds the shape data for the case, probably a view. + */ + AXOM_HOST_DEVICE + UInt8ContainerType getShape(size_t caseId, size_t shapeId) const + { + assert(caseId < m_shapes.size()); + assert(shapeId < shapesForCase(caseId)); + + const uint8 *shapeStart = m_table.data() + m_offsets[caseId]; + size_t shapeLen = 0; + for(int i = 0; i < shapeId; i++) + { + shapeLen = advance(*shapeStart); + shapeStart += shapeLen; + } + shapeLen = advance(*shapeStart); + return UInt8ContainerType(shapeStart, shapeLen); + } + + /** + * \brief Given the input shape, return how many values to advance to get to the next shape. + * + * \param shape The shape type. + * + * \return The number of values to advance. + */ + AXOM_HOST_DEVICE + size_t advance(uint8 shape) const + { + if(shape == ST_TRI) + retval = 2 + 3; + else if(shape == ST_QUAD) + retval = 2 + 4; + else if(shape == ST_TET) + retval = 2 + 4; + else if(shape == ST_PYR) + retval = 2 + 5; + else if(shape == ST_WDG) + retval = 2 + 6; + else if(shape == ST_HEX) + retval = 2 + 8; + return retval; + } + + IntContainerType m_shapes; + IntContainerType m_offsets; + UInt8ContainerType m_table; +}; + +/** + * \brief This class contains data arrays for the clipping table and can produce a view for the data. + */ +template +struct ClipTable : public ClipTableBase +{ + using ClipTableView = ClipTableBase + + /** + * \brief Load clipping data into the arrays, moving data as needed. + * + * \param n The number of cases in the clip table. + * \param shapes The number of shapes produced by clipping cases. + * \param offsets The offset into the table for each clipping case. + * \param table The clipping table data. + * \param tableLen The size of the clipping table data. + */ + void load(size_t n, const int *shapes, const int *offsets, const uint8 *table, size_t tableLen) + { + m_shapes = IntContainerType(shapes, n); + m_offsets = IntContainerType(offsets, n); + m_table = UInt8ContainerType(table, tableLen); + } + + /** + * \brief Create a view to access the table data. + * + * \return A view of the table data. + */ + ClipTableView view() const + { + ClipTableView v; + v.m_shapes = m_shapes.view(); + v.m_offsets = m_offsets.view(); + v.m_table = m_table.view(); + return v; + } + +}; + +/** + * \brief Manage several clipping tables. + */ +template +class ClipTableManager +{ +public: + /** + * \brief Constructor + */ + ClipTableManager() + { + for(size_t shape = ST_MIN; shape < ST_MAX; shape++) + m_clipTables[shapeToIndex(shape)] = ClipTable(); + } + + /** + * \brief Return a reference to the clipping table, which is loaded on demand. + * + * \param shape The shape type to be retrieved. + * + * \return A reference to the clipping table. + */ + const ClipTable &operator[](size_t shape) + { + assert(shape < ST_MAX); + const auto index = shapeToIndex(shape); + if(m_clipTables[index].size() == 0) + { + load(shape); + } + return m_clipTables[index]; + } + +private: + /** + * \brief Turn a shape into an table index. + * + * \param shape The shape type ST_XXX. + * + * \return An index into the m_clipTables array. + */ + size_t shapeToIndex(size_t shape) const + { + return shape - ST_MIN; + } + + /** + * \brief Load the clipping table for a shape. + * + * \param shape The shape whose table will be loaded. + */ + void load(size_t shape) + { + const auto index = shapeToIndex(shape); + if(shape == ST_TRI) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTri, + axom::mir::clipping::visit::numClipShapesTri, + axom::mir::clipping::visit::startClipShapesTri, + axom::mir::clipping::visit::clipShapesTri, + sizeof(axom::mir::clipping::visit::clipShapesTri) / sizeof(unsigned char)); + } + else if(shape == ST_QUAD) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesQua, + axom::mir::clipping::visit::numClipShapesQua, + axom::mir::clipping::visit::startClipShapesQua, + axom::mir::clipping::visit::clipShapesQua, + sizeof(axom::mir::clipping::visit::clipShapesQua) / sizeof(unsigned char)); + } + else if(shape == ST_TET) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTet, + axom::mir::clipping::visit::numClipShapesTet, + axom::mir::clipping::visit::startClipShapesTet, + axom::mir::clipping::visit::clipShapesTet, + sizeof(axom::mir::clipping::visit::clipShapesTet) / sizeof(unsigned char)); + } + else if(shape == ST_PYR) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesPyr, + axom::mir::clipping::visit::numClipShapesPyr, + axom::mir::clipping::visit::startClipShapesPyr, + axom::mir::clipping::visit::clipShapesPyr, + sizeof(axom::mir::clipping::visit::clipShapesTet) / sizeof(unsigned char)); + } + else if(shape == ST_WDG) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesWdg, + axom::mir::clipping::visit::numClipShapesWdg, + axom::mir::clipping::visit::startClipShapesWdg, + axom::mir::clipping::visit::clipShapesWdg, + sizeof(axom::mir::clipping::visit::clipShapesWdg) / sizeof(unsigned char)); + } + else if(shape == ST_HEX) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesHex, + axom::mir::clipping::visit::numClipShapesHex, + axom::mir::clipping::visit::startClipShapesHex, + axom::mir::clipping::visit::clipShapesHex, + sizeof(axom::mir::clipping::visit::clipShapesHex) / sizeof(unsigned char)); + } + } + + ClipTable m_clipTables[ST_MAX - ST_MIN]; +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif From 101cb942004e85889ce8eff06796bf051c69f3ba Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 14 Jun 2024 18:38:33 -0700 Subject: [PATCH 052/290] Starting material views --- src/axom/mir/views/MaterialView.hpp | 818 ++++++++++++++++++++++++++++ 1 file changed, 818 insertions(+) create mode 100644 src/axom/mir/views/MaterialView.hpp diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp new file mode 100644 index 0000000000..f624fc1714 --- /dev/null +++ b/src/axom/mir/views/MaterialView.hpp @@ -0,0 +1,818 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ +#define AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ + +#include "axom/mir/views/Shapes.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +template +class StaticArray +{ +public: + constexpr static size_t MaxElements = MAXELEM; + + AXOM_HOST_DEVICE + size_t size() const + { + return m_size; + } + + AXOM_HOST_DEVICE + void push_back(const ElementType &e) + { + if(m_size + 1 < MaxElements) + m_data[m_size++] = e; + } + + AXOM_HOST_DEVICE + void clear() + { + m_size = 0; + } + + AXOM_HOST_DEVICE + ElementType &operator[](size_t index) + { + return m_data[index]; + } + + AXOM_HOST_DEVICE + const ElementType &operator[](size_t index) const + { + return m_data[index]; + } + + AXOM_HOST_DEVICE + void fill(const ElementType &e) + { + for(size_t i = 0; i < MaxElements; i++) + m_data[i] = e; + } + +private: + axom::StackArray m_data{}; + size_t m_size{0}; +}; + +/** + * \brief This object contains information about the materials as provided by a Conduit node. + * + * \note This would only be used on the host. + */ +class MaterialInformation +{ +public: + void set(const conduit::Node &matset) + { + if(matset.has_child("material_map")) + { + m_namesToIds.clear(); + m_idsToNames.clear(); + m_ids.clear(); + m_names.clear(); + + const conduit::Node &mm = matset["material_map"]; + for(conduit::index_t i = 0; i < mm.number_of_children(); i++) + { + const auto id = mm[i].to_int32(); + m_namesToIds[mm[i].name()] = id; + m_idsToNames[id] = mm[i].name(); + m_ids.push_back(id); + m_names.push_back(mm[i].name()); + } + } + } + + const std::map &getNamesToIds() const { return m_namesToIds; } + const std::map &getIdsToNames() const { return m_idsToNames; } + const std::vector &getIds() const { return m_ids; } + const std::vector &getNames() const { return m_names; } + +private: + std::map m_namesToIds{}; + std::map m_idsToNames{}; + std::vector m_ids{}; + std::vector m_names{}; +}; + +/** + +matsets: + matset: + topology: topology + material_map: + a: 1 + b: 2 + c: 0 + material_ids: [0, 1, 2, 2, 2, 0, 1, 0] + volume_fractions: [0, a0, b2, b1, b0, 0, a1, 0] + sizes: [2, 2, 1] + offsets: [0, 2, 4] + indices: [1, 4, 6, 3, 2] + + */ +template +class UnibufferMaterialView +{ +public: + using MaterialIndex = IndexT; + using ZoneIndex = IndexT; + using IndexType = IndexT; + using IDList = StaticArray; + using VFList = StaticArray; + + constexpr static size_t MaxMaterials = MAXMATERIALS; + + void set(const axom::ArrayView &material_ids; + const axom::ArrayView &volume_fractions; + const axom::ArrayView &sizes; + const axom::ArrayView &offsets; + const axom::ArrayView &indices) + { + m_material_ids = material_ids; + m_volume_fractions = volume_fractions; + m_sizes = sizes; + m_offsets = offsets; + m_indices = indices; + } + + AXOM_HOST_DEVICE + size_t getNumberOfZones() const + { + return m_sizes.size(); + } + + AXOM_HOST_DEVICE + size_t getNumberOfMaterials(ZoneIndex zi) const + { + assert(zi < m_sizes.size()); + return m_sizes[zi]; + } + + AXOM_HOST_DEVICE + void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + { + assert(zi < m_sizes.size()); + + ids.clear(); + vfs.clear(); + + const auto sz = m_sizes[zi]; + const auto offset = m_offsets[zi]; + for(size_t i = 0; i < sz; i++) + { + const auto idx = m_indices[offset + i]; + + ids.push_back(m_material_ids[idx]); + vfs.push_back(m_volume_fractions[idx]); + } + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const + { + assert(zi < m_sizes.size()); + const auto sz = m_sizes[zi]; + const auto offset = m_offsets[zi]; + for(size_t i = 0; i < sz; i++) + { + const auto idx = m_indices[offset + i]; + + if(m_material_ids[idx] == mat) + return true; + } + return false; + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const + { + assert(zi < m_sizes.size()); + const auto sz = m_sizes[zi]; + const auto offset = m_offsets[zi]; + for(size_t i = 0; i < sz; i++) + { + const auto idx = m_indices[offset + i]; + + if(m_material_ids[idx] == mat) + { + vf = m_volume_fractions[idx]; + return true; + } + } + vf = 0; + return false; + } + +private: + axom::ArrayView m_material_ids; + axom::ArrayView m_volume_fractions; + axom::ArrayView m_sizes; + axom::ArrayView m_offsets; + axom::ArrayView m_indices; +}; + + +/** + +matsets: + matset: + topology: topology + volume_fractions: + a: + values: [0, 0, 0, a1, 0, a0] + indices: [5, 3] + b: + values: [0, b0, b2, b1, 0] + indices: [1, 3, 2] + material_map: # (optional) + a: 0 + b: 1 + */ + +// NOTE: I'm not sure I 100% get this one. + +template +class MultiBufferMaterialView +{ +public: + using MaterialIndex = IndexT; + using ZoneIndex = IndexT; + using IDList = StaticArray; + using VFList = StaticArray; + + constexpr static size_t MaxMaterials = MAXMATERIALS; + + void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) + { + assert(m_size + 1 < MaxMaterials); + + m_indices[m_size] = ids; + m_values[m_size] = vfs; + m_size++; + } + + AXOM_HOST_DEVICE + size_t getNumberOfZones() const + { + size_t nzones = 0; + for(int i = 0; i < m_size; i++) + nzones = axom::utilities::max(nzones, m_indices[i].size()); + return nzones; + } + + AXOM_HOST_DEVICE + size_t getNumberOfMaterials(ZoneIndex zi) const + { + assert(zi < m_sizes.size()); + + size_t nmats = 0; + for(size_t i = 0; i < m_size; i++) + { + if(zi < m_indices[i].size()) + { + const auto idx = m_indices[zi]; + if(m_values[i][idx] > 0.) + nmats++; + } + } + + return nmats; + } + + AXOM_HOST_DEVICE + void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + { + assert(zi < m_sizes.size()); + + ids.clear(); + vfs.clear(); + + for(size_t i = 0; i < m_size; i++) + { + if(zi < m_indices[i].size()) + { + const auto idx = m_indices[zi]; + if(m_values[i][idx] > 0.) + { + ids.push_back(i); + vfs.push_back(m_values[i][idx]); + } + } + } + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const + { + assert(mat < m_size); + assert(zi < m_indices[mat].size()); + + bool contains = false; + const auto idx = m_indices[mat][zi]; + return m_values[mat][zi] > 0.; + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const + { + assert(mat < m_size); + assert(zi < m_indices[mat].size()); + + bool contains = false; + const auto idx = m_indices[mat][zi]; + vf = m_values[mat][zi]; + return vf > 0.; + } +private: + axom::StackArray, MAXMATERIALS> m_values{}; + axom::StackArray, MAXMATERIALS> m_indices{}; + size_t m_size{0}; +}; + +/** +matsets: + matset: + topology: topology + volume_fractions: + a: [a0, a1, 0] + b: [b0, b1, b2] + c: [0, 0, c2] + material_map: # (optional) + a: 0 + b: 1 + c: 2 + */ +template +class ElementDominantMaterialView +{ +public: + using MaterialIndex = IndexT; + using ZoneIndex = IndexT; + using IDList = StaticArray; + using VFList = StaticArray; + + constexpr static size_t MaxMaterials = MAXMATERIALS; + + void add(const axom::ArrayView &vfs) + { + assert(m_size + 1 < MaxMaterials); + + m_indices[m_size] = ids; + m_values[m_size] = vfs; + m_size++; + } + + AXOM_HOST_DEVICE + size_t getNumberOfZones() const + { + return m_size > 0 ? m_volume_fractions[0].size() : 0; + } + + AXOM_HOST_DEVICE + size_t getNumberOfMaterials(ZoneIndex zi) const + { + size_t nmats = 0; + if(m_size > 0) + { + assert(zi < m_volume_fractions[0].size()); + for(size_t i = 0; i < m_size; i++) + nmats += m_volume_fractions[i][zi] > 0. ? 1 : 0; + } + return nmats; + } + + AXOM_HOST_DEVICE + void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + { + ids.clear(); + vfs.clear(); + + if(m_size > 0) + { + assert(zi < m_volume_fractions[0].size()); + for(size_t i = 0; i < m_size; i++) + { + if(m_volume_fractions[i][zi] > 0) + { + ids.push_back(i); + vfs.push_back(m_volume_fractions[i][zi]); + } + } + } + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const + { + bool contains = false; + if(m_size > 0) + { + assert(zi < m_volume_fractions[0].size()); + contains = m_volume_fractions[mat][zi] > 0; + } + return contains; + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const + { + bool contains = false; + vf = 0; + if(m_size > 0) + { + assert(zi < m_volume_fractions[0].size()); + vf = m_volume_fractions[mat][zi] > 0; + contains = vf > 0; + } + return contains; + } +private: + axom::StackArray> m_volume_fractions{}; + size_t m_size{0}; +}; + +/** +matsets: + matset: + topology: topology + volume_fractions: + a: [a0, a1] + b: [b0, b1, b2] + c: [c2] + element_ids: + a: [0, 1] + b: [0, 1, 2] + c: [2] + material_map: # (optional) + a: 0 + b: 1 + c: 2 + */ +template +class MaterialDominantMaterialView +{ +public: + using MaterialIndex = IndexT; + using ZoneIndex = IndexT; + using IDList = StaticArray; + using VFList = StaticArray; + + constexpr static size_t MaxMaterials = MAXMATERIALS; + + void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) + { + assert(m_size + 1 < MaxMaterials); + + m_element_ids[m_size] = ids; + m_volume_fractions[m_size] = vfs; + m_size++; + } + + size_t getNumberOfZones() + { + if(m_nzones == 0) + { + for(size_t mi = 0; mi < m_size; mi++) + { + const auto sz = m_element_ids[mi].size(); + for(size_t i = 0; i < sz; i++) + m_nzones = axom::utilties::max(m_nzones, m_element_ids[mi][i]); +#if 0 + // Eh, do this. + RAJA::ReduceMax rm(0); + axom::forall(0, sz, AXOM_LAMBDA(int i) + { + rm.max(m_element_ids[mi][i]); + } + m_nzones = axom::utilties::max(m_nzones, rm.get()); +#endif + } + } + return m_nzones; + } + + size_t getNumberOfMaterials(ZoneIndex zi) const + { + size_t nmats = 0; +// Q: Can we RAJA-ify this search? + for(size_t mi = 0; mi < m_size; mi++) + { + const auto sz = m_element_ids[mi].size(); +#if 1 + for(size_t i = 0; i < sz; i++) + { + if(m_element_ids[mi][i] == zi) + { + nmats++; + break; + } + } +#else + RAJA::ReduceMax rm(0); + axom::forall(0, sz, AXOM_LAMBDA(int i) + { + rm.max((m_element_ids[mi][i] == zi) ? 1 : 0); + } + m_nzones += rm.get(); +#endif + } + return nmats; + } + + void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + { + ids.clear(); + vfs.clear(); + + for(size_t mi = 0; mi < m_size; mi++) + { + const auto sz = m_element_ids[mi].size(); +#if 1 + for(size_t i = 0; i < sz; i++) + { + if(m_element_ids[mi][i] == zi) + { + ids.push_back(mi); + vfs.push_back(m_volume_fractions[mi][i]; + break; + } + } +#else + RAJA::ReduceMax rm(-1); + axom::forall(0, sz, AXOM_LAMBDA(int i) + { + rm.max((m_element_ids[mi][i] == zi) ? i : -1); + } + const auto index = rm.get(); + if(index != -1) + { + ids.push_back(mi); + vfs.push_back(m_volume_fractions[mi][index]); + } +#endif + } + } + + AXOM_HOST_DEVICE + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const + { + assert(zi < m_sizes.size()); + + return false; + } + + bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const + { + return false; + } + + axom::ArrayView selectZonesContainingMaterial(MaterialIndex mat) const + { + assert(mat < m_size); + return m_element_ids[mat]; + } + +private: + StackArray, MAXMATERIALS> m_element_ids{}; + StackArray, MAXMATERIALS> m_volume_fractions{}; + size_t m_size{0}; + size_t m_nzones{0}; +}; + +template +axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_t nzones) +{ + // Figure out the number of materials per zone. + axom::Array matsPerZone(nzones, nzones, axom::getAllocatorID()); + auto matsPerZone_view = matsPerZone.view(); + axom::forall(0, nzones, AXOM_LAMBDA(int i) + { + matsPerZone_view[i] = 0; + }); + for(size_t mi = 0; mi < m_size; mi++) + { + auto element_ids_view = m_element_ids[mi].view(); + axom::forall(0, nzones, AXOM_LAMBDA(int i) + { + matsPerZone_view[element_ids_view[i]]++; + }); + } + return matsPerZone; +} + +// NOTE: This needs to be a method of MaterialDominantMaterialView to access the view data. +template +axom::Array selectZones(const MaterialDominantMaterialView &view) +{ + const auto nzones = view.getNumberOfZones(); + + // Figure out the number of materials per zone. + axom::Array matsPerZone = makeMatsPerZone(view, nzones); + auto matsPerZone_view = matsPerZone.view(); + + // Count the clean zones. + RAJA::ReduceSum num_selected(0); + axom::forall(0, nzones, AXOM_LAMBDA(int i) + { + num_selected += Selection(matsPerZone_view[i]) ? 1 : 0; + }); + size_t outsize = num_selected.get(); + + // Make an offset array that records where each thread can write its data. + axom::Array offsets(nzones); + RAJA::inclusive_scan(RAJA::make_span(zones, zones.size()), + RAJA::make_span(offsets, offsets.size())); + + // Make a list of the selected output zones. + axom::Array zonelist(outsize, outsize, axom::getAllocatorID()); + auto zonelist_view = zonelist.view(); + axom::forall(0, nzones, AXOM_LAMBDA(int zi) + { + if(Selection(matsPerZone_view[zi])) + zonelist_view[offset[zi]] = zi; + }); + + return zonelist; +} + +template +axom::Array selectZonesContainingMaterial(const MaterialDominantMaterialView &view, MaterialIndex mat) +{ + const auto zones_view = view.selectZonesContainingMaterial(mat); + axom::Array zones(zones_view.size(), zones_view.size(), axom::getAllocatorID()); + axom::copy(zones.data(), zones_view.data(), zones_view.size() * sizeof(int)); + return zones; +} + +template +axom::Array selectCleanZones(const MaterialDominantMaterialView &view) +{ + auto selectFunc = [](int nmats) -> bool { return nmats == 1; }; + return selectZones(view); +} + +template +axom::Array selectMixedZones(const MaterialDominantMaterialView &view) +{ + auto selectFunc = [](int nmats) -> bool { return nmats > 1; }; + return selectZones(view); +} + +//--------------------------------------------------------------------------- + +#if 0 +template +axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex mat, FuncType &&func) const +{ +/** + NOTE: I really do not like the code below because it forces the main Axom algorithm to use RAJA directly. + In the case of the reducer, I'd prefer to do this: + + auto reducer = axom::execution_space::make_ReduceSum(0); + + Then we could write the algorithm so we do RAJA things but we could make a reducer object for the serial non-RAJA case that does nothing. + */ + const size_t nzones = view.getNumberOfZones(); + + using REDUCE_POL = typename axom::execution_space::reduce_policy; + RAJA::ReduceSum num_selected(0); + axom::Array zones(nzones); + auto zones_view = zones.view(); + axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) + { + const int haveMat = func(view, mat, zi) ? 1 : 0; + zones_view[zi] = haveMat; + num_selected += haveMat; + }); + size_t outsize = num_selected.get(); + + // Make an offset array that records where each thread can write its data. + axom::Array offsets(nzones); + RAJA::inclusive_scan(RAJA::make_span(zones, zones.size()), + RAJA::make_span(offsets, offsets.size())); + + // Make a list of the selected output zones. + axom::Array zonelist(outsize); + auto zonelist_view = zonelist.view(); + axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) + { + if(zones[zi] > 0) + zonelist_view[offset[zi]] = zi; + }); + + return zonelist; +} + +template +axom::Array selectZonesContainingMaterial(const UnibufferMaterialView &view, MaterialIndex mat) +{ + auto findMaterial = [](const UnibufferMaterialView &deviceView, MaterialIndex deviceMat, int zi) + { + return deviceView.zoneContainsMaterial(zi, deviceMat); + }; + return selectZones(view, mat, findMaterial); +} + +template +axom::Array selectCleanZones(const UnibufferMaterialView &view) +{ + auto zoneIsClean = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) + { + return view.getNumberOfMaterials(zi) == 1; + }; + return selectZones(view, 0, zoneIsClean); +} + +template +axom::Array selectMixedZones(const UnibufferMaterialView &view) +{ + auto zoneIsMixed = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) + { + return view.getNumberOfMaterials(zi) > 1; + }; + return selectZones(view, 0, zoneIsClean); +} + +//--------------------------------------------------------------------------- +template +void +dispatch_matset(const conduit::Node &matset, FuncType &&func) +{ + constexpr static size_t MaxMaterials = 20; + + if(conduit::mesh::matset::is_uni_buffer(matset)) + { + IndexNode_to_ArrayView_same(matset["material_ids"],matset["sizes"],matset["offsets"],matset["indices"], + [&](auto material_ids, auto sizes, auto offsets, auto indices) + { + FloatNode_to_ArrayView(matset["volume_fractions"], [&](auto volume_fractions) + { + using IndexType = typename decltype(material_ids)::value_type; + using FloatType = typename decltype(volume_fractions)::value_type; + + UnibufferMaterialView matsetView; + matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); + func(matsetView); + }); + }); + } + else if(conduit::mesh::matset::is_multi_buffer(matset)) + { + const conduit::Node &volume_fractions = matset["volume_fractions"]; + const conduit::Node &n_firstValues = volume_fractions[0]["values"]; + const conduit::Node &n_firstIndices = volume_fractions[0]["indices"]; + IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) + { + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using IntView = decltype(firstIndices); + using IntElement = typename IntView::value_type; + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + MultiBufferMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &values = volume_fractions[i]["values"]; + const conduit::Node &indices = volume_fractions[i]["indices"]; + + const IntElement *indices_ptr = indices.value(); + const FloatElement *values_ptr = values.value(); + + IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(indices_view, values_view); + } + + func(matsetView); + }); + }); + + } + else if(conduit::mesh::matset::is_material_dominant(matset)) + { + } + else if(conduit::mesh::matset::is_element_dominant(matset)) + { + } +} + +#endif + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From e0769025f1bac790401cedf70ec71117ab8793c5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 24 Jun 2024 15:58:39 -0700 Subject: [PATCH 053/290] Added some other material views. --- src/axom/mir/views/MaterialView.hpp | 68 +++++++++-- src/axom/mir/views/dispatch_material.hpp | 145 +++++++++++++++++++++++ 2 files changed, 204 insertions(+), 9 deletions(-) create mode 100644 src/axom/mir/views/dispatch_material.hpp diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index f624fc1714..73f02ed9cc 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -769,9 +769,9 @@ dispatch_matset(const conduit::Node &matset, FuncType &&func) } else if(conduit::mesh::matset::is_multi_buffer(matset)) { - const conduit::Node &volume_fractions = matset["volume_fractions"]; - const conduit::Node &n_firstValues = volume_fractions[0]["values"]; - const conduit::Node &n_firstIndices = volume_fractions[0]["indices"]; + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &n_firstValues = volume_fractions[0].fetch_existing("values"); + const conduit::Node &n_firstIndices = volume_fractions[0].fetch_existing("indices"); IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) { FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) @@ -785,8 +785,8 @@ dispatch_matset(const conduit::Node &matset, FuncType &&func) for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) { - const conduit::Node &values = volume_fractions[i]["values"]; - const conduit::Node &indices = volume_fractions[i]["indices"]; + const conduit::Node &values = volume_fractions[i].fetch_existing("values"); + const conduit::Node &indices = volume_fractions[i].fetch_existing("indices"); const IntElement *indices_ptr = indices.value(); const FloatElement *values_ptr = values.value(); @@ -799,14 +799,64 @@ dispatch_matset(const conduit::Node &matset, FuncType &&func) func(matsetView); }); }); - - } - else if(conduit::mesh::matset::is_material_dominant(matset)) - { } else if(conduit::mesh::matset::is_element_dominant(matset)) { + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &n_firstValues = volume_fractions[0]; + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + ElementDominantMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &values = volume_fractions[i]; + const FloatElement *values_ptr = values.value(); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(values_view); + } + + func(matsetView); + }); } + else if(conduit::mesh::matset::is_material_dominant(matset)) + { + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &element_ids = matset.fetch_existing("element_ids"); + const conduit::Node &n_firstValues = volume_fractions[0]; + const conduit::Node &n_firstIndices = element_ids[0]; + + IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) + { + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using IntView = decltype(firstIndices); + using IntElement = typename IntView::value_type; + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + MaterialDominantMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &indices = element_ids[i]; + const conduit::Node &values = volume_fractions[i]; + + const IntElement *indices_ptr = indices.value(); + const FloatElement *values_ptr = values.value(); + + IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(indices_view, values_view); + } + + func(matsetView); + }); + }); + } } #endif diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp new file mode 100644 index 0000000000..99f8fb8725 --- /dev/null +++ b/src/axom/mir/views/dispatch_material.hpp @@ -0,0 +1,145 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_MATERIAL_HPP_ +#define AXOM_MIR_DISPATCH_MATERIAL_HPP_ + +#include "axom/mir/views/MaterialView.hpp" +#include "axom/mir/views/NodeArrayView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Dispatch a Conduit node containing a matset to a function as the appropriate type of matset view. + * + * \tparam FuncType The function/lambda type that will take the matset. + * + * \param matset The node that contains the matset. + * \param func The function/lambda that will operate on the matset view. + */ +template +void +dispatch_material(const conduit::Node &matset, FuncType &&func) +{ + constexpr static size_t MaxMaterials = 20; + + if(conduit::mesh::matset::is_uni_buffer(matset)) + { + IndexNode_to_ArrayView_same(matset["material_ids"],matset["sizes"],matset["offsets"],matset["indices"], + [&](auto material_ids, auto sizes, auto offsets, auto indices) + { + FloatNode_to_ArrayView(matset["volume_fractions"], [&](auto volume_fractions) + { + using IndexType = typename decltype(material_ids)::value_type; + using FloatType = typename decltype(volume_fractions)::value_type; + + UnibufferMaterialView matsetView; + matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); + func(matsetView); + }); + }); + } + else if(conduit::mesh::matset::is_multi_buffer(matset)) + { + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &n_firstValues = volume_fractions[0].fetch_existing("values"); + const conduit::Node &n_firstIndices = volume_fractions[0].fetch_existing("indices"); + IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) + { + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using IntView = decltype(firstIndices); + using IntElement = typename IntView::value_type; + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + MultiBufferMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &values = volume_fractions[i].fetch_existing("values"); + const conduit::Node &indices = volume_fractions[i].fetch_existing("indices"); + + const IntElement *indices_ptr = indices.value(); + const FloatElement *values_ptr = values.value(); + + IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(indices_view, values_view); + } + + func(matsetView); + }); + }); + } + else if(conduit::mesh::matset::is_element_dominant(matset)) + { + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &n_firstValues = volume_fractions[0]; + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + ElementDominantMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &values = volume_fractions[i]; + const FloatElement *values_ptr = values.value(); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(values_view); + } + + func(matsetView); + }); + } + else if(conduit::mesh::matset::is_material_dominant(matset)) + { + const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); + const conduit::Node &element_ids = matset.fetch_existing("element_ids"); + const conduit::Node &n_firstValues = volume_fractions[0]; + const conduit::Node &n_firstIndices = element_ids[0]; + + IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) + { + FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) + { + using IntView = decltype(firstIndices); + using IntElement = typename IntView::value_type; + using FloatView = decltype(firstValues); + using FloatElement = typename Floatview::value_type; + + MaterialDominantMaterialView matsetView; + + for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) + { + const conduit::Node &indices = element_ids[i]; + const conduit::Node &values = volume_fractions[i]; + + const IntElement *indices_ptr = indices.value(); + const FloatElement *values_ptr = values.value(); + + IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); + FloatView values_view(values_ptr, values.dtype().number_of_elements()); + matsetView.add(indices_view, values_view); + } + + func(matsetView); + }); + }); + } +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From f3e904c2b630dd6ff99e8a8b23f525e51a5cf802 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 24 Jun 2024 17:39:50 -0700 Subject: [PATCH 054/290] start to make it compile --- src/axom/mir/EquiZAlgorithm.cpp | 130 +++++++++-- src/axom/mir/views/MaterialView.hpp | 212 ++++++------------ src/axom/mir/views/Shapes.hpp | 2 +- src/axom/mir/views/StructuredTopologyView.hpp | 7 +- 4 files changed, 193 insertions(+), 158 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 7c688688fa..a1fbaf87a1 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -1,5 +1,6 @@ #include "axom/mir/EquiZAlgorithm.hpp" - +#include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_coordset.hpp" // RAJA #if defined(AXOM_USE_RAJA) @@ -155,12 +156,117 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, #endif } +template +void dispatch_uniform(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const conduit::Node &n_dims = coordset["dims"]; + const conduit::index_t ndims = n_dims.dtype().number_of_elements(); + if(ndims == 1) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + StructuredTopologyView topoView; + func(topoView); + } + else if(axes.size() == 2) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + StructuredTopologyView topoView; + func(topoView); + } + else if(axes.size() == 3) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + dims[2] = n_dims.as_int_accessor()[2]; + StructuredTopologyView topoView; + func(topoView); + } +} + +template +void dispatch_rectilinear(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); + if(axes.size() == 1) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + StructuredTopologyView topoView; + func(topoView); + } + else if(axes.size() == 2) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + StructuredTopologyView topoView; + func(topoView); + } + else if(axes.size() == 3) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); + StructuredTopologyView topoView; + func(topoView); + } +} + +template +void dispatch_structured(const conduit::Node &topo, FuncType &&func) +{ + if(topo.has_path("elements/dims/k")) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + dims[2] = topo.fetch_existing("elements/dims/k").as_int(); + StructuredTopologyView topoView; + func(topoView); + } + else if(topo.has_path("elements/dims/j")) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + StructuredTopologyView topoView; + func(topoView); + } + else + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + StructuredTopologyView topoView; + func(topoView); + } +} + +template +void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const auto type = topo.fetch_existing("type").as_string(); + + if(type == "uniform") + dispatch_uniform(topo, coordset, func); + else if(type == "rectilinear") + dispatch_rectilinear(topo, coordset, func); + else if(type == "structured") + dispatch_structured(topo, func); + else if(type == "unstructured") + dispatch_unstructured(topo, func); +} + template void EquiZAlgorithm::executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset) + const conduit::Node &coordset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset) { if(options.has_path("zones")) { @@ -185,19 +291,17 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, } else { -#if 0 - foreach_coordset_type // rectilinear, structured, uniform - the problem becomse passing ArrayViews to the lambda. Could we pass a StackArray of ArrayViews? + dispatch_coordset(coordset, [&](auto coordsetView) { - // Foreach zone - for_all_zones(topo, AXOM_LAMBDA(conduit::index_t zoneIndex, const conduit::index_t *ids, conduit::index_t nids) + dispatch_topology(topo, [&](auto topoView) { - // on-device - - // might want to pass in the ijk coordinate to deal with rectilinear coordsets. Or perhaps it is best to pass in an array of the coordinate values that we pull out as part of the zone iteration. + topoView. template for_all_zones(AXOM_LAMBDA(int zoneIndex, const auto &zone) + { + }); }); }); -#endif + } } diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 73f02ed9cc..3aa1ed36cb 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -19,7 +19,11 @@ template class StaticArray { public: - constexpr static size_t MaxElements = MAXELEM; + AXOM_HOST_DEVICE + constexpr size_t capacity() const + { + return MAXELEM; + } AXOM_HOST_DEVICE size_t size() const @@ -30,7 +34,7 @@ class StaticArray AXOM_HOST_DEVICE void push_back(const ElementType &e) { - if(m_size + 1 < MaxElements) + if(m_size + 1 < capacity()) m_data[m_size++] = e; } @@ -55,7 +59,7 @@ class StaticArray AXOM_HOST_DEVICE void fill(const ElementType &e) { - for(size_t i = 0; i < MaxElements; i++) + for(size_t i = 0; i < capacity(); i++) m_data[i] = e; } @@ -105,6 +109,13 @@ class MaterialInformation std::vector m_names{}; }; +//--------------------------------------------------------------------------- +// Material views - These objects are meant to wrap Blueprint Matsets behind +// an interface that lets us query materials for a single +// zone. It is intended that these views will be used in +// device kernels. +//--------------------------------------------------------------------------- + /** matsets: @@ -459,6 +470,8 @@ class ElementDominantMaterialView b: 1 c: 2 */ +/// NOTES: This matset type does not seem so GPU friendly since there is some work to do for some of the queries. + template class MaterialDominantMaterialView { @@ -479,6 +492,7 @@ class MaterialDominantMaterialView m_size++; } + AXOM_HOST_DEVICE size_t getNumberOfZones() { if(m_nzones == 0) @@ -489,6 +503,7 @@ class MaterialDominantMaterialView for(size_t i = 0; i < sz; i++) m_nzones = axom::utilties::max(m_nzones, m_element_ids[mi][i]); #if 0 + // host-only // Eh, do this. RAJA::ReduceMax rm(0); axom::forall(0, sz, AXOM_LAMBDA(int i) @@ -502,10 +517,10 @@ class MaterialDominantMaterialView return m_nzones; } + AXOM_HOST_DEVICE size_t getNumberOfMaterials(ZoneIndex zi) const { size_t nmats = 0; -// Q: Can we RAJA-ify this search? for(size_t mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); @@ -519,6 +534,7 @@ class MaterialDominantMaterialView } } #else + // host-only RAJA::ReduceMax rm(0); axom::forall(0, sz, AXOM_LAMBDA(int i) { @@ -530,6 +546,7 @@ class MaterialDominantMaterialView return nmats; } + AXOM_HOST_DEVICE void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { ids.clear(); @@ -568,19 +585,43 @@ class MaterialDominantMaterialView bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { assert(zi < m_sizes.size()); + assert(mat < m_element_ids.size()); - return false; + bool found = false; +#if 1 + const auto element_ids = m_element_ids[mat]; + for(size_t i = 0; i < selectedIds.size(); i++) + { + if(element_ids[i] == zi) + { + found = true; + break; + } + } +#endif + return found; } + AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const { - return false; - } + assert(zi < m_sizes.size()); + assert(mat < m_element_ids.size()); - axom::ArrayView selectZonesContainingMaterial(MaterialIndex mat) const - { - assert(mat < m_size); - return m_element_ids[mat]; + bool found = false; +#if 1 + const auto element_ids = m_element_ids[mat]; + for(size_t i = 0; i < selectedIds.size(); i++) + { + if(element_ids[i] == zi) + { + found = true; + vf = m_volume_fractions[mat][i]; + break; + } + } +#endif + return found; } private: @@ -590,6 +631,13 @@ class MaterialDominantMaterialView size_t m_nzones{0}; }; +#if 0 +//--------------------------------------------------------------------------- +// Some host-algorithms on material views. +//--------------------------------------------------------------------------- + +/** + */ template axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_t nzones) { @@ -612,20 +660,20 @@ axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_ } // NOTE: This needs to be a method of MaterialDominantMaterialView to access the view data. -template -axom::Array selectZones(const MaterialDominantMaterialView &view) +template +axom::Array selectZones(const MaterialDominantMaterialView &view, Predicate &&pred) { const auto nzones = view.getNumberOfZones(); // Figure out the number of materials per zone. - axom::Array matsPerZone = makeMatsPerZone(view, nzones); + axom::Array matsPerZone = makeMatsPerZone(view, nzones); auto matsPerZone_view = matsPerZone.view(); // Count the clean zones. RAJA::ReduceSum num_selected(0); axom::forall(0, nzones, AXOM_LAMBDA(int i) { - num_selected += Selection(matsPerZone_view[i]) ? 1 : 0; + num_selected += pred(matsPerZone_view[i]) ? 1 : 0; }); size_t outsize = num_selected.get(); @@ -639,7 +687,7 @@ axom::Array selectZones(const MaterialDominantMaterialView &view) auto zonelist_view = zonelist.view(); axom::forall(0, nzones, AXOM_LAMBDA(int zi) { - if(Selection(matsPerZone_view[zi])) + if(pred(matsPerZone_view[zi])) zonelist_view[offset[zi]] = zi; }); @@ -658,22 +706,20 @@ axom::Array selectZonesContainingMaterial(const MaterialDominantMaterialVie template axom::Array selectCleanZones(const MaterialDominantMaterialView &view) { - auto selectFunc = [](int nmats) -> bool { return nmats == 1; }; - return selectZones(view); + auto predicate = [](int nmats) -> bool { return nmats == 1; }; + return selectZones(view, predicate); } template axom::Array selectMixedZones(const MaterialDominantMaterialView &view) { - auto selectFunc = [](int nmats) -> bool { return nmats > 1; }; - return selectZones(view); + auto predicate = [](int nmats) -> bool { return nmats > 1; }; + return selectZones(view, predicate); } //--------------------------------------------------------------------------- - -#if 0 -template -axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex mat, FuncType &&func) const +template +axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex mat, Predicate &&pred) const { /** NOTE: I really do not like the code below because it forces the main Axom algorithm to use RAJA directly. @@ -691,7 +737,7 @@ axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex ma auto zones_view = zones.view(); axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) { - const int haveMat = func(view, mat, zi) ? 1 : 0; + const int haveMat = pred(view, mat, zi) ? 1 : 0; zones_view[zi] = haveMat; num_selected += haveMat; }); @@ -743,122 +789,6 @@ axom::Array selectMixedZones(const UnibufferMaterialView &view) }; return selectZones(view, 0, zoneIsClean); } - -//--------------------------------------------------------------------------- -template -void -dispatch_matset(const conduit::Node &matset, FuncType &&func) -{ - constexpr static size_t MaxMaterials = 20; - - if(conduit::mesh::matset::is_uni_buffer(matset)) - { - IndexNode_to_ArrayView_same(matset["material_ids"],matset["sizes"],matset["offsets"],matset["indices"], - [&](auto material_ids, auto sizes, auto offsets, auto indices) - { - FloatNode_to_ArrayView(matset["volume_fractions"], [&](auto volume_fractions) - { - using IndexType = typename decltype(material_ids)::value_type; - using FloatType = typename decltype(volume_fractions)::value_type; - - UnibufferMaterialView matsetView; - matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); - func(matsetView); - }); - }); - } - else if(conduit::mesh::matset::is_multi_buffer(matset)) - { - const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); - const conduit::Node &n_firstValues = volume_fractions[0].fetch_existing("values"); - const conduit::Node &n_firstIndices = volume_fractions[0].fetch_existing("indices"); - IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) - { - FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) - { - using IntView = decltype(firstIndices); - using IntElement = typename IntView::value_type; - using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; - - MultiBufferMaterialView matsetView; - - for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) - { - const conduit::Node &values = volume_fractions[i].fetch_existing("values"); - const conduit::Node &indices = volume_fractions[i].fetch_existing("indices"); - - const IntElement *indices_ptr = indices.value(); - const FloatElement *values_ptr = values.value(); - - IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); - FloatView values_view(values_ptr, values.dtype().number_of_elements()); - matsetView.add(indices_view, values_view); - } - - func(matsetView); - }); - }); - } - else if(conduit::mesh::matset::is_element_dominant(matset)) - { - const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); - const conduit::Node &n_firstValues = volume_fractions[0]; - FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) - { - using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; - - ElementDominantMaterialView matsetView; - - for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) - { - const conduit::Node &values = volume_fractions[i]; - const FloatElement *values_ptr = values.value(); - FloatView values_view(values_ptr, values.dtype().number_of_elements()); - matsetView.add(values_view); - } - - func(matsetView); - }); - } - else if(conduit::mesh::matset::is_material_dominant(matset)) - { - const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); - const conduit::Node &element_ids = matset.fetch_existing("element_ids"); - const conduit::Node &n_firstValues = volume_fractions[0]; - const conduit::Node &n_firstIndices = element_ids[0]; - - IndexNode_To_ArrayView(n_firstIndices, [&](auto firstIndices) - { - FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) - { - using IntView = decltype(firstIndices); - using IntElement = typename IntView::value_type; - using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; - - MaterialDominantMaterialView matsetView; - - for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) - { - const conduit::Node &indices = element_ids[i]; - const conduit::Node &values = volume_fractions[i]; - - const IntElement *indices_ptr = indices.value(); - const FloatElement *values_ptr = values.value(); - - IntView indices_view(indices_ptr, indices.dtype().number_of_elements()); - FloatView values_view(values_ptr, values.dtype().number_of_elements()); - matsetView.add(indices_view, values_view); - } - - func(matsetView); - }); - }); - } -} - #endif } // end namespace views diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 225130e6b2..b7a006a9e4 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -6,7 +6,7 @@ #ifndef AXOM_MIR_VIEWS_SHAPES_HPP_ #define AXOM_MIR_VIEWS_SHAPES_HPP_ -#include "axom/core/AxomArrayView.hpp" +#include "axom/core/ArrayView.hpp" #include #include diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index ec975143d5..69e0f6c273 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -68,7 +68,6 @@ class StructuredTopologyView { const auto nzones = numberOfZones(); - axom::ArrayView connectivity(m_connectivity); if constexpr (NDIMS == 2) { // Q: Should we make a for_all() that iterates over multiple ranges? @@ -77,7 +76,8 @@ class StructuredTopologyView axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { using ShapeType = QuadShape; - const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + IndexType data[4]; + const ShapeType shape(axom::ArrayView(data, 4)); func(zoneIndex, shape); }); } @@ -86,7 +86,8 @@ class StructuredTopologyView axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { using ShapeType = HexShape; - const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + IndexType data[8]; + const ShapeType shape(axom::ArrayView(data, 8)); func(zoneIndex, shape); }); } From 1b6f23dda8a13d3cdeb6bd0494fe1823cd3035cc Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 25 Jun 2024 17:56:02 -0700 Subject: [PATCH 055/290] work on compiling --- src/axom/mir/EquiZAlgorithm.cpp | 90 ++++++----- src/axom/mir/EquiZAlgorithm.hpp | 15 +- ...rdsetView.hpp => ExplicitCoordsetView.hpp} | 2 +- src/axom/mir/views/NodeArrayView.hpp | 148 +++++++++--------- .../mir/views/RectilinearCoordsetView.hpp | 13 +- src/axom/mir/views/Shapes.hpp | 54 +++---- src/axom/mir/views/StructuredTopologyView.hpp | 21 ++- src/axom/mir/views/UniformCoordsetView.hpp | 6 +- .../UnstructuredTopologyPolyhedralView.hpp | 2 +- .../UnstructuredTopologySingleShapeView.hpp | 10 +- .../views/dispatch_unstructured_topology.hpp | 61 ++++---- 11 files changed, 238 insertions(+), 184 deletions(-) rename src/axom/mir/views/{ExplitCoordsetView.hpp => ExplicitCoordsetView.hpp} (98%) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index a1fbaf87a1..00490d06db 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -1,6 +1,15 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + #include "axom/mir/EquiZAlgorithm.hpp" +#include "axom/core/ArrayView.hpp" #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_coordset.hpp" +#include "axom/mir/views/dispatch_unstructured_topology.hpp" + +#include // RAJA #if defined(AXOM_USE_RAJA) @@ -157,33 +166,36 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, } template -void dispatch_uniform(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +void dispatch_uniform(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) { const conduit::Node &n_dims = coordset["dims"]; const conduit::index_t ndims = n_dims.dtype().number_of_elements(); - if(ndims == 1) + if(ndims == 3) { - axom::StackArray dims; + axom::StackArray dims; dims[0] = n_dims.as_int_accessor()[0]; - StructuredTopologyView topoView; - func(topoView); + dims[1] = n_dims.as_int_accessor()[1]; + dims[2] = n_dims.as_int_accessor()[2]; + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); } - else if(axes.size() == 2) + else if(ndims == 2) { axom::StackArray dims; dims[0] = n_dims.as_int_accessor()[0]; dims[1] = n_dims.as_int_accessor()[1]; - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); } - else if(axes.size() == 3) + else if(ndims == 1) { - axom::StackArray dims; + axom::StackArray dims; dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - dims[2] = n_dims.as_int_accessor()[2]; - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); } } @@ -191,29 +203,32 @@ template void dispatch_rectilinear(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) { const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); - if(axes.size() == 1) + if(axes.size() == 3) { - axom::StackArray dims; + axom::StackArray dims; dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - StructuredTopologyView topoView; - func(topoView); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); } else if(axes.size() == 2) { axom::StackArray dims; dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); } - else if(axes.size() == 3) + else if(axes.size() == 1) { - axom::StackArray dims; + axom::StackArray dims; dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); } } @@ -226,23 +241,26 @@ void dispatch_structured(const conduit::Node &topo, FuncType &&func) dims[0] = topo.fetch_existing("elements/dims/i").as_int(); dims[1] = topo.fetch_existing("elements/dims/j").as_int(); dims[2] = topo.fetch_existing("elements/dims/k").as_int(); - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); } else if(topo.has_path("elements/dims/j")) { axom::StackArray dims; dims[0] = topo.fetch_existing("elements/dims/i").as_int(); dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); } else { axom::StackArray dims; dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - StructuredTopologyView topoView; - func(topoView); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); } } @@ -258,7 +276,7 @@ void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, else if(type == "structured") dispatch_structured(topo, func); else if(type == "unstructured") - dispatch_unstructured(topo, func); + views::dispatch_unstructured_topology(topo, func); } template @@ -291,11 +309,11 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, } else { - dispatch_coordset(coordset, [&](auto coordsetView) + views::dispatch_coordset(coordset, [&](auto coordsetView) { - dispatch_topology(topo, [&](auto topoView) + dispatch_topology(topo, coordset, [&](const std::string &shape, auto topoView) { - topoView. template for_all_zones(AXOM_LAMBDA(int zoneIndex, const auto &zone) + topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { }); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 058f285fb6..8c5d2637d6 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -2,14 +2,11 @@ // other Axom Project Developers. See the top-level LICENSE file for details. // // SPDX-License-Identifier: (BSD-3-Clause) - - #ifndef AXOM_MIR_EQUIZ_ALGORITHM_HPP_ #define AXOM_MIR_EQUIZ_ALGORITHM_HPP_ #include "axom/config.hpp" #include "axom/core.hpp" -#include "axom/core/ArrayView.hpp" #include "axom/mir/MIRAlgorithm.hpp" #include @@ -20,6 +17,10 @@ namespace axom namespace mir { +/** + * \accelerated + * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. + */ class EquiZAlgorithm : public MIRAlgorithm { public: @@ -28,6 +29,14 @@ class EquiZAlgorithm : public MIRAlgorithm EquiZAlgorithm() = default; virtual ~EquiZAlgorithm() = default; + /** + * \brief Set the desired execution policy. + * + * \param policy The execution policy. + * + * \note The input Conduit nodes must have data located in a memory space + * that is compatible with the execution policy. + */ void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } protected: diff --git a/src/axom/mir/views/ExplitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp similarity index 98% rename from src/axom/mir/views/ExplitCoordsetView.hpp rename to src/axom/mir/views/ExplicitCoordsetView.hpp index 4f1dcba503..11e8153889 100644 --- a/src/axom/mir/views/ExplitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -7,7 +7,7 @@ #define AXOM_MIR_EXPLICIT_COORDSET_VIEW_HPP_ #include "axom/core/ArrayView.hpp" -#include "axom/primal/Point.hpp" +#include "axom/primal/geometry/Point.hpp" namespace axom { diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 49e9d3a416..f655057c13 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -6,7 +6,7 @@ #ifndef AXOM_MIR_VIEWS_NODE_ARRAY_VIEW_HPP_ #define AXOM_MIR_VIEWS_NODE_ARRAY_VIEW_HPP_ -#include "axom/slic/slic.hpp" +#include "axom/slic/interface/slic.hpp" #include #include @@ -80,14 +80,14 @@ template std::enable_if_t Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int8_ptr(), size); + axom::ArrayView view(n.as_int8_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int8(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int8 node." << std::endl; + SLIC_WARNING("Unsupported int8 node."); } template std::enable_if_t @@ -99,23 +99,23 @@ Node_to_ArrayView_single_int8(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_int8(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int8(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int8 node." << std::endl; + SLIC_WARNING("Unsupported int8 node."); } template std::enable_if_t Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int16_ptr(), size); + axom::ArrayView view(n.as_int16_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int16(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int16 node." << std::endl; + SLIC_WARNING("Unsupported int16 node."); } template std::enable_if_t @@ -127,23 +127,23 @@ Node_to_ArrayView_single_int16(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_int16(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int16(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int16 node." << std::endl; + SLIC_WARNING("Unsupported int16 node."); } template std::enable_if_t Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int32_ptr(), size); + axom::ArrayView view(n.as_int32_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int32(const conduit::Node & AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int32 node." << std::endl; + SLIC_WARNING("Unsupported int32 node."); } template std::enable_if_t @@ -155,23 +155,23 @@ Node_to_ArrayView_single_int32(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_int32(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int32 node." << std::endl; + SLIC_WARNING("Unsupported int32 node."); } template std::enable_if_t Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int64_ptr(), size); + axom::ArrayView view(n.as_int64_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int64 node." << std::endl; + SLIC_WARNING("Unsupported int64 node."); } template std::enable_if_t @@ -183,79 +183,79 @@ Node_to_ArrayView_single_int64(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_int64(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_int64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported int64 node." << std::endl; + SLIC_WARNING("Unsupported int64 node."); } template std::enable_if_t Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint8_ptr(), size); + axom::ArrayView view(n.as_uint8_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint8(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint8 node." << std::endl; + SLIC_WARNING("Unsupported uint8 node."); } template std::enable_if_t Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint8_ptr(), size); + axom::ArrayView view(n.as_uint8_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint8(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint8 node." << std::endl; + SLIC_WARNING("Unsupported uint8 node."); } template std::enable_if_t Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint16_ptr(), size); + axom::ArrayView view(n.as_uint16_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint16(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint16 node." << std::endl; + SLIC_WARNING("Unsupported uint16 node."); } template std::enable_if_t Node_to_ArrayView_single_uint16(conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint16_ptr(), size); + axom::ArrayView view(n.as_uint16_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint16(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint16(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint16 node." << std::endl; + SLIC_WARNING("Unsupported uint16 node."); } template std::enable_if_t Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint32_ptr(), size); + axom::ArrayView view(n.as_uint32_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint32(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint32 node." << std::endl; + SLIC_WARNING("Unsupported uint32 node."); } template std::enable_if_t @@ -267,23 +267,23 @@ Node_to_ArrayView_single_uint32(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_uint32(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint32 node." << std::endl; + SLIC_WARNING("Unsupported uint32 node."); } template std::enable_if_t Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint64_ptr(), size); + axom::ArrayView view(n.as_uint64_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint64 node." << std::endl; + SLIC_WARNING("Unsupported uint64 node."); } template std::enable_if_t @@ -295,23 +295,23 @@ Node_to_ArrayView_single_uint64(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_uint64(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_uint64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported uint64 node." << std::endl; + SLIC_WARNING("Unsupported uint64 node."); } template std::enable_if_t Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_float32_ptr(), size); + axom::ArrayView view(n.as_float32_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_float32(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported float32 node." << std::endl; + SLIC_WARNING("Unsupported float32 node."); } template std::enable_if_t @@ -323,23 +323,23 @@ Node_to_ArrayView_single_float32(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_float32(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_float32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported float32 node." << std::endl; + SLIC_WARNING("Unsupported float32 node."); } template std::enable_if_t Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_float64_ptr(), size); + axom::ArrayView view(n.as_float64_ptr(), size); func(view); } template std::enable_if_t -Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_float64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported float64 node." << std::endl; + SLIC_WARNING("Unsupported float64 node."); } template std::enable_if_t @@ -351,9 +351,9 @@ Node_to_ArrayView_single_float64(conduit::Node &n, FuncType &&func) } template std::enable_if_t -Node_to_ArrayView_single_float64(conduit::Node &n, FuncType &&func) +Node_to_ArrayView_single_float64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) { - std::cout << "Unsupported float64 node." << std::endl; + SLIC_WARNING("Unsupported float64 node."); } template @@ -482,113 +482,115 @@ void Node_to_ArrayView_internal(conduit::Node &first, Args&&... args) } //------------------------------------------------------------------------------ +/// NOTE: handle const conduit::Node& better. For now, const_cast. + template std::enable_if_t Node_to_ArrayView_same_internal_int8(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_int8_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_int8_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_int8(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_int8(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int16(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_int16_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_int16_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_int16(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_int16(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int32(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_int32_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_int32_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_int32(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_int32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int64(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_int64_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_int64_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_int64(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_int64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint8(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_uint8_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_uint8_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_uint8(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_uint8(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint16(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_uint16_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_uint16_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_uint16(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_uint16(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint32(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_uint32_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_uint32_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_uint32(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_uint32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint64(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_uint64_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_uint64_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_uint64(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_uint64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float32(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_float32_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_float32_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_float32(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_float32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float64(FuncType &&func, Args&&... args) { - func(axom::ArrayView(args.as_float64_ptr(), args.dtype().number_of_elements())...); + func(axom::ArrayView(const_cast(args.as_float64_ptr()), args.dtype().number_of_elements())...); } template std::enable_if_t -Node_to_ArrayView_same_internal_float64(FuncType &&func, Args&&... args) +Node_to_ArrayView_same_internal_float64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) { } diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index 1e95de1674..b10542b3a7 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -8,7 +8,7 @@ #include "axom/core/StackArray.hpp" #include "axom/core/ArrayView.hpp" -#include "axom/primal/Point.hpp" +#include "axom/primal/geometry/Point.hpp" namespace axom { @@ -17,6 +17,9 @@ namespace mir namespace views { +/// NOTE: The rectilinear coordset views could be combined into a single RectilinearCoordset +/// view that is templated on NDIMS but the resulting SFINAE would just overcomplicate it. + /** * \class This class provides a view for Conduit/Blueprint 2D rectilinear coordsets. */ @@ -27,7 +30,7 @@ class RectilinearCoordsetView2 using LogicalIndexType = axom::StackArray; using IndexType = axom::IndexType; using value_type = DataType; - using PointType = Point; + using PointType = axom::primal::Point; /** * \brief Constructor @@ -76,7 +79,7 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vert_index)); + return getPoint(IndexToLogicalIndex(vertex_index)); } /** @@ -140,7 +143,7 @@ class RectilinearCoordsetView3 using LogicalIndexType = axom::StackArray; using IndexType = axom::IndexType; using value_type = DataType; - using PointType = Point; + using PointType = axom::primal::Point; /** * \brief Constructor @@ -192,7 +195,7 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vert_index)); + return getPoint(IndexToLogicalIndex(vertex_index)); } /** diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index b7a006a9e4..14e89cf5e8 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -98,7 +98,6 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}}; }; -// TODO: PolygonTraits /* n-1 *-... * 2 @@ -123,9 +122,10 @@ struct PolygonShape */ AXOM_HOST_DEVICE PolygonShape(const axom::ArrayView &ids) : m_ids(ids) { - assert(m_ids.size() == numberOfNodes()); } + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } + /** * \brief Get the ids that make up this shape. * @@ -140,7 +140,7 @@ struct PolygonShape * * \return An array view (wrapping m_faceIds) that contains the ids for the face. */ - AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + AXOM_HOST_DEVICE axom::ArrayView getFace(int /*faceIndex*/) const { return m_ids; } @@ -211,8 +211,8 @@ struct PyramidTraits { using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr int id() { return 1 << 7; } - AXOM_HOST_DEVICE constexpr bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 7; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } @@ -316,15 +316,15 @@ struct HexTraits /** * \brief This class extends the ShapeTraits with object state so it can represent a zone. */ -template -struct Shape : public ShapeTraits +template +struct Shape : public ShapeTraits { /** * \brief Construct a shape. */ AXOM_HOST_DEVICE Shape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() { - assert(m_ids.size() == numberOfNodes()); + assert(m_ids.size() == ShapeTraits::numberOfNodes()); } /** @@ -341,44 +341,44 @@ struct Shape : public ShapeTraits * * \return An array view (wrapping m_faceIds) that contains the ids for the face. */ - AXOM_HOST_DEVICE std::enable_if> + AXOM_HOST_DEVICE + axom::ArrayView getFace(int faceIndex) const { - return m_ids; - } - - AXOM_HOST_DEVICE std::enable_if> - getFace(int faceIndex) const - { - const auto nnodes = numberOfNodesInFace(faceIndex); - for(IndexType i = 0; i < nnodes; i++) - m_faceIds[i] = m_ids[faces[faceIndex][i]]; - return axom::ArrayView(m_faceIds.m_data, nnodes); + if constexpr(ShapeTraits::dimension() == 2) + return m_ids; + else + { + const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); + for(IndexType i = 0; i < nnodes; i++) + m_faceIds[i] = m_ids[ShapeTraits::faces[faceIndex][i]]; + return axom::ArrayView(m_faceIds.m_data, nnodes); + } } private: axom::ArrayView m_ids; - mutable axom::StackArray m_faceIds; + mutable axom::StackArray m_faceIds; }; // Make some concrete shape classes based on the shape traits. template -using TriShape = Shape>; +using TriShape = Shape>; template -using QuadShape = Shape>; +using QuadShape = Shape>; template -using TetShape = Shape>; +using TetShape = Shape>; template -using PyramidShape = Shape>; +using PyramidShape = Shape>; template -using WedgeShape = Shape>; +using WedgeShape = Shape>; template -using HexShape = Shape>; - +using HexShape = Shape>; +//struct HexShape : public Shape> {}; } // end namespace views } // end namespace mir diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 69e0f6c273..9928db1c44 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ -#define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ +#ifndef AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ +#define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ #include "axom/mir/views/Shapes.hpp" @@ -29,13 +29,14 @@ class StructuredTopologyView public: using IndexType = IndexT; using LogicalIndexType = StackArray; + constexpr static int Dimensions = NDIMS; /** * \brief Constructor * * \param conn The mesh connectivity. */ - AXOM_HOST_VIEW + AXOM_HOST_DEVICE StructuredTopologyView(const LogicalIndexType &dims) : m_dimensions(dims) { } @@ -54,6 +55,20 @@ class StructuredTopologyView return sz; } + /** + * \brief Return the number of zones. + * + * \return The number of zones. + */ + AXOM_HOST_DEVICE IndexType numberOfZones() const { return size(); } + + /** + * \brief Return the mesh logical dimensions. + * + * \return The mesh logical dimensions. + */ + const LogicalIndexType &dimensions() const { return m_dimensions; } + /** * \brief Execute a function for each zone in the mesh. * diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index e5e0610169..dde015a57b 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -8,7 +8,7 @@ #include "axom/core/StackArray.hpp" #include "axom/core/ArrayView.hpp" -#include "axom/primal/Point.hpp" +#include "axom/primal/geometry/Point.hpp" namespace axom { @@ -19,6 +19,10 @@ namespace views /** * \class This class provides a view for Conduit/Blueprint uniform coordsets. + * + * \tparam DataType The underlying type used for the coordinates. + * \tparam NDIMS The number of dimensions in each point. + * */ template class UniformCoordsetView diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 7deca002bc..0fa6dbe348 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -56,7 +56,7 @@ class UnstructuredTopologyPolyhedralView constexpr static IndexType MaximumNumberOfIds = 20 * 3; AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return true; } - AXOM_HOST_DEVICE constexpr static bool id() { return 1 << 10; } + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 10; } AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj), m_zoneIndex(zi), m_ids() { diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 4873131e1f..2bde88c452 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -21,11 +21,11 @@ namespace views * \tparam IndexT The index type that will be used for connectivity, etc. * \tparam ShapeT The shape type. */ -template +template class UnstructuredTopologySingleShapeView { public: - using IndexType = IndexT; + using IndexType = typename ShapeT::IndexType; using ShapeType = ShapeT; /** @@ -84,7 +84,8 @@ class UnstructuredTopologySingleShapeView axom::ArrayView offsets(m_offsets); axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { - const ShapeType shape(axom::ArrayView(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex])); + const axom::ArrayView shapeData(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex]); + const ShapeType shape(shapeData); func(zoneIndex, shape); }); } @@ -92,7 +93,8 @@ class UnstructuredTopologySingleShapeView { axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { - const ShapeType shape(axom::ArrayView(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes())); + const axom::ArrayView shapeData(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const ShapeType shape(shapeData); func(zoneIndex, shape); }); } diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index e6973845bf..e1de36625d 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -3,13 +3,15 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef AXOM_MIR_DISPATCH_COORDSET_HPP_ -#define AXOM_MIR_DISPATCH_COORDSET_HPP_ +#ifndef AXOM_MIR_DISPATCH_UNSTRUCTURED_TOPOLOGY_HPP_ +#define AXOM_MIR_DISPATCH_UNSTRUCTURED_TOPOLOGY_HPP_ #include "axom/mir/views/UnstructuredTopologySingleShapeView.hpp" #include "axom/mir/views/UnstructuredTopologyPolyhedralView.hpp" -#include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/NodeArrayView.hpp" +#include "axom/mir/views/Shapes.hpp" + +#include namespace axom { @@ -35,10 +37,6 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) const std::string type = topo["type"].as_string(); if(type == "unstructured") { - const conduit::Node *coordset_ptr = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); - const conduit::Node &coordset = *coordset_ptr; - dispatch_coordset(coordset, [&](auto coordsetView) - { const std::string shape = topo["elements/shape"].as_string(); bool eligible = true; @@ -54,12 +52,13 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { using IndexType = typename decltype(seConnView)::value_type; UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); - func(shape, ugView, coordsetView); + func(shape, ugView); }); eligible = false; } } - +#if 0 +// TODO: Can't use polygon with single shape view because its sizes are not known at compile time. if constexpr (ShapeTypes & PolygonShape::id()) { if(eligible && shape == "polygon") @@ -70,12 +69,12 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { using IndexType = typename decltype(connView)::value_type; UnstructuredTopologySingleShapeView > ugView(connView, sizesView, offsetsView); - func(shape, ugView, coordsetView); + func(shape, ugView); }); eligible = false; } } - +#endif if constexpr (ShapeTypes & AnyShape) { if(eligible && shape == "mixed") @@ -89,62 +88,64 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { using IndexType = typename decltype(connView)::value_type; // TODO: points, lines - if constexpr (ShapeTypes & TriShape::id()) + if constexpr (ShapeTypes & TriShape::id()) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & QuadShape::id()) + if constexpr (ShapeTypes & QuadShape::id()) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & TetShape::id()) + if constexpr (ShapeTypes & TetShape::id()) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & PyramidShape::id()) + if constexpr (ShapeTypes & PyramidShape::id()) { if(eligible && shape == "pyramid") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & WedgeShape::id()) + if constexpr (ShapeTypes & WedgeShape::id()) { if(eligible && shape == "wedge") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & HexShape::id()) + if constexpr (ShapeTypes & HexShape::id()) { if(eligible && shape == "hex") { - UnstructuredTopologySingleShapeView > ugView(connView); - func(shape, ugView, coordsetView); + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); eligible = false; } } + + // TODO: handle mixed shapes. + }); - }); } } From 5a3a89367b2cb48f55c72556a97de7e2dfe1870a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 25 Jun 2024 18:26:26 -0700 Subject: [PATCH 056/290] compiled --- src/axom/mir/EquiZAlgorithm.cpp | 4 +-- src/axom/mir/views/NodeArrayView.hpp | 27 +++++++++++-------- src/axom/mir/views/Shapes.hpp | 2 ++ .../views/dispatch_unstructured_topology.hpp | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 00490d06db..7d084cf5f0 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -309,9 +309,9 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, } else { - views::dispatch_coordset(coordset, [&](auto coordsetView) + views::dispatch_coordset(coordset, [&](auto &coordsetView) { - dispatch_topology(topo, coordset, [&](const std::string &shape, auto topoView) + dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index f655057c13..2fd7aa5013 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -76,11 +76,16 @@ constexpr int select_float_types() //------------------------------------------------------------------------------ // General Node to ArrayView. Handle all types. //------------------------------------------------------------------------------ +/// NOTE: Some of these functions use const_cast to get data into the ArrayView. +/// Is there a better way that does not let const bleed all over? +/// +/// TODO: Handle strided data from the Conduit node. + template std::enable_if_t Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int8_ptr(), size); + axom::ArrayView view(const_cast(n.as_int8_ptr()), size); func(view); } @@ -108,7 +113,7 @@ template std::enable_if_t Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int16_ptr(), size); + axom::ArrayView view(const_cast(n.as_int16_ptr()), size); func(view); } @@ -136,7 +141,7 @@ template std::enable_if_t Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int32_ptr(), size); + axom::ArrayView view(const_cast(n.as_int32_ptr()), size); func(view); } @@ -164,7 +169,7 @@ template std::enable_if_t Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_int64_ptr(), size); + axom::ArrayView view(const_cast(n.as_int64_ptr()), size); func(view); } @@ -192,7 +197,7 @@ template std::enable_if_t Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint8_ptr(), size); + axom::ArrayView view(const_cast(n.as_uint8_ptr()), size); func(view); } @@ -220,7 +225,7 @@ template std::enable_if_t Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint16_ptr(), size); + axom::ArrayView view(const_cast(n.as_uint16_ptr()), size); func(view); } @@ -248,7 +253,7 @@ template std::enable_if_t Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint32_ptr(), size); + axom::ArrayView view(const_cast(n.as_uint32_ptr()), size); func(view); } @@ -276,7 +281,7 @@ template std::enable_if_t Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint64_ptr(), size); + axom::ArrayView view(const_cast(n.as_uint64_ptr()), size); func(view); } @@ -304,7 +309,7 @@ template std::enable_if_t Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_float32_ptr(), size); + axom::ArrayView view(const_cast(n.as_float32_ptr()), size); func(view); } @@ -332,7 +337,7 @@ template std::enable_if_t Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_float64_ptr(), size); + axom::ArrayView view(const_cast(n.as_float64_ptr()), size); func(view); } @@ -359,7 +364,7 @@ Node_to_ArrayView_single_float64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType & template void Node_to_ArrayView_single(const conduit::Node &n, FuncType &&func) { - /* Later, with C++17, we can do this: + /* Later, with C++17, we can do this instead of using all of the SFINAE functions above: if constexpr (type_selected(Types, conduit::DataType::INT8_ID)) { if(n.dtype().is_int8()) diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 14e89cf5e8..4ebd627146 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -319,6 +319,8 @@ struct HexTraits template struct Shape : public ShapeTraits { + using IndexType = typename ShapeTraits::IndexType; + /** * \brief Construct a shape. */ diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index e1de36625d..8d60c32c63 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -41,7 +41,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) bool eligible = true; // Conditionally add polyhedron support. - if constexpr (ShapeTypes & UnstructuredTopologyPolyhedralView::PolyhedralShape::id()) + if constexpr (ShapeTypes & UnstructuredTopologyPolyhedralView::PolyhedronShape::id()) { if(shape == "polyhedral") { From 7f3681f1f9e6dd917551fa6844329b5a880034a7 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 26 Jun 2024 16:46:45 -0700 Subject: [PATCH 057/290] Moved some indexing logic to StructuredIndexing. Added for_selected_zones to topo views. --- .../mir/views/RectilinearCoordsetView.hpp | 66 ++----- src/axom/mir/views/StructuredIndexing.hpp | 177 ++++++++++++++++++ src/axom/mir/views/StructuredTopologyView.hpp | 127 +++++++++++-- src/axom/mir/views/UniformCoordsetView.hpp | 65 +------ .../UnstructuredTopologyPolyhedralView.hpp | 15 ++ .../UnstructuredTopologySingleShapeView.hpp | 47 ++++- .../views/dispatch_unstructured_topology.hpp | 3 +- 7 files changed, 377 insertions(+), 123 deletions(-) create mode 100644 src/axom/mir/views/StructuredIndexing.hpp diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index b10542b3a7..7916a95e87 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -9,6 +9,7 @@ #include "axom/core/StackArray.hpp" #include "axom/core/ArrayView.hpp" #include "axom/primal/geometry/Point.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" namespace axom { @@ -42,6 +43,8 @@ class RectilinearCoordsetView2 RectilinearCoordsetView2(const axom::ArrayView &x, const axom::ArrayView &y) : m_coordinates{x, y} { + m_shape.m_dimensions[0] = m_coordinates[0].size(); + m_shape.m_dimensions[1] = m_coordinates[1].size(); } /** @@ -52,7 +55,7 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE IndexType size() const { - return m_coordinates[0].size() * m_coordinates[1].size(); + return m_shape.size(); } /** @@ -79,7 +82,7 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vertex_index)); + return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); } /** @@ -107,30 +110,12 @@ class RectilinearCoordsetView2 PointType operator[](IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vertex_index)); + return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); } private: - RectilinearCoordsetView2() = delete; - - /** - * \brief Turn an index into a logical IJ index. - * - * \param index The index to convert. - * - * \return The logical index that corresponds to the \a index. - */ - AXOM_HOST_DEVICE - LogicalIndexType IndexToLogicalIndex(IndexType index) const - { - LogicalIndexType logical; - const auto nx = m_coordinates[0].size(); - logical[0] = index % nx; - logical[1] = index / nx; - return logical; - } - - axom::ArrayView m_coordinates[2]; + axom::ArrayView m_coordinates[2]; + StructuredIndexing m_shape; }; /** @@ -155,8 +140,11 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE RectilinearCoordsetView3(const axom::ArrayView &x, const axom::ArrayView &y, - const axom::ArrayView &z) : m_coordinates{x, y, z} + const axom::ArrayView &z) : m_coordinates{x, y, z}, m_shape() { + m_shape.m_dimensions[0] = m_coordinates[0].size(); + m_shape.m_dimensions[1] = m_coordinates[1].size(); + m_shape.m_dimensions[2] = m_coordinates[2].size(); } /** @@ -167,7 +155,7 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE IndexType size() const { - return m_coordinates[0].size() * m_coordinates[1].size() * m_coordinates[2].size(); + return m_shape.size(); } /** @@ -195,7 +183,7 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vertex_index)); + return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); } /** @@ -223,32 +211,12 @@ class RectilinearCoordsetView3 PointType operator[](IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vertex_index)); + return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); } private: - RectilinearCoordsetView3() = delete; - - /** - * \brief Turn an index into a logical IJK index. - * - * \param index The index to convert. - * - * \return The logical index that corresponds to the \a index. - */ - AXOM_HOST_DEVICE - LogicalIndexType IndexToLogicalIndex(IndexType index) const - { - const auto nx = m_coordinates[0].size(); - const auto nxy = nx * m_coordinates[1].size(); - LogicalIndexType logical; - logical[0] = index % nx; - logical[1] = (index % nxy) / nx; - logical[2] = index / nxy; - return logical; - } - - axom::ArrayView m_coordinates[3]; + axom::ArrayView m_coordinates[3]; + StructuredIndexing m_shape; }; } // end namespace views diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp new file mode 100644 index 0000000000..79979147f8 --- /dev/null +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -0,0 +1,177 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_STRUCTURED_INDEXING_HPP_ +#define AXOM_MIR_STRUCTURED_INDEXING_HPP_ + +#include "axom/core/StackArray.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/primal/geometry/Point.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief This class encapsulates a structured mesh size and contains methods to + * help with indexing into it. + * + * \tparam NDIMS The number of dimensions. + */ +template +struct StructuredIndexing +{ + using IndexType = IndexT; + using LogicalIndex = axom::StackArray; + + AXOM_HOST_DEVICE constexpr static int dimensions() { return NDIMS; } + + /** + * \brief constructor + * + * \param dims The dimensions we're indexing. + */ + AXOM_HOST_DEVICE + StructuredIndexing() : m_dimensions() + { + } + + AXOM_HOST_DEVICE + StructuredIndexing(const LogicalIndex &dims) : m_dimensions(dims) + { + } + + /** + * \brief Return the number of points in the coordset. + * + * \return The number of points in the coordset. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) + sz *= m_dimensions[i]; + return sz; + } + + /** + * \brief Return the j stride. + * + * \return The j stride to move up a row. + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims >= 2, IndexType>::type + jStride() const + { + return m_dimensions[0]; + } + + /** + * \brief Return the k stride. + * + * \return The k stride to move forward a "page". + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, IndexType>::type + kStride() const + { + return m_dimensions[0] * m_dimensions[1]; + } + + /** + * \brief Turn an index into a logical index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + /// @{ + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 1, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + logical[0] = index; + return logical; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + const auto nx = m_dimensions[0]; + logical[0] = index % nx; + logical[1] = index / nx; + return logical; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + const auto nx = m_dimensions[0]; + const auto nxy = nx * m_dimensions[1]; + logical[0] = index % nx; + logical[1] = (index % nxy) / nx; + logical[2] = index / nxy; + return logical; + } + + /// @} + + /** + * \brief Turn a logical index into a flat index. + * + * \param logical The logical indexto convert to a flat index. + * + * \return The index that corresponds to the \a logical index. + */ + /// @{ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 1, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return logical[0]; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return logical[1] * m_dimensions[0] + logical[0]; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return (logical[2] * m_dimensions[1] * m_dimensions[0]) + (logical[1] * m_dimensions[0]) + logical[0]; + } + + /// @} + + LogicalIndex m_dimensions{1}; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 9928db1c44..e2d1ee0e7f 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ #include "axom/mir/views/Shapes.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" namespace axom { @@ -28,8 +29,9 @@ class StructuredTopologyView { public: using IndexType = IndexT; - using LogicalIndexType = StackArray; - constexpr static int Dimensions = NDIMS; + using LogicalIndexType = axom::StackArray; //typename StructuredIndexing::LogicalIndex; + + constexpr static int dimensions() { return NDIMS; } /** * \brief Constructor @@ -37,7 +39,7 @@ class StructuredTopologyView * \param conn The mesh connectivity. */ AXOM_HOST_DEVICE - StructuredTopologyView(const LogicalIndexType &dims) : m_dimensions(dims) + StructuredTopologyView(const LogicalIndexType &dims) : m_shape(dims) { } @@ -49,10 +51,7 @@ class StructuredTopologyView AXOM_HOST_DEVICE IndexType size() const { - IndexType sz = 1; - for(int i = 0; i < NDIMS; i++) - sz *= m_dimensions[i]; - return sz; + return m_shape.size(); } /** @@ -67,10 +66,10 @@ class StructuredTopologyView * * \return The mesh logical dimensions. */ - const LogicalIndexType &dimensions() const { return m_dimensions; } + const LogicalIndexType &logicalDimensions() const { return m_shape; } /** - * \brief Execute a function for each zone in the mesh. + * \brief Execute a function for each zone in the mesh using axom::for_all. * * \tparam ExecSpace The execution space for the function body. * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. @@ -83,35 +82,135 @@ class StructuredTopologyView { const auto nzones = numberOfZones(); - if constexpr (NDIMS == 2) + if constexpr (NDIMS == 3) + { + const StructuredIndexing zoneShape{m_shape}, + nodeShape{{m_shape.m_dimensions[0] + 1, + m_shape.m_dimensions[1] + 1, + m_shape.m_dimensions[2] + 1}}; + const auto jp = nodeShape.jStride(); + const auto kp = nodeShape.kStride(); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + using ShapeType = HexShape; + + const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + IndexType data[8]; + data[0] = nodeShape.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + data[4] = data[0] + kp; + data[5] = data[1] + kp; + data[6] = data[2] + kp; + data[7] = data[3] + kp; + + const ShapeType shape(axom::ArrayView(data, 8)); + func(zoneIndex, shape); + }); + } + else if constexpr (NDIMS == 2) { // Q: Should we make a for_all() that iterates over multiple ranges? // Q: Should the logical index be passed to the lambda? + const StructuredIndexing zoneShape{m_shape}, + nodeShape{{m_shape.m_dimensions[0] + 1, + m_shape.m_dimensions[1] + 1}}; + const auto jp = nodeShape.jStride(); + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { using ShapeType = QuadShape; + + const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); IndexType data[4]; + data[0] = nodeShape.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + const ShapeType shape(axom::ArrayView(data, 4)); func(zoneIndex, shape); }); } + // TODO: NDIMS == 1 + } + + /** + * \brief Execute a function for each zone in the mesh using axom::for_all. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam ViewType A concrete ArrayView that contains selected zone ids. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + { + const auto nSelectedZones = selectedIdsview.size(); + ViewType idsView(selectedIdsView); + if constexpr (NDIMS == 3) { - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + const StructuredIndexing zoneShape{m_shape}, + nodeShape{{m_shape.m_dimensions[0] + 1, + m_shape.m_dimensions[1] + 1, + m_shape.m_dimensions[2] + 1}}; + const auto jp = nodeShape.jStride(); + const auto kp = nodeShape.kStride(); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) { using ShapeType = HexShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); IndexType data[8]; + data[0] = nodeShape.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + data[4] = data[0] + kp; + data[5] = data[1] + kp; + data[6] = data[2] + kp; + data[7] = data[3] + kp; + const ShapeType shape(axom::ArrayView(data, 8)); func(zoneIndex, shape); }); } + else if constexpr (NDIMS == 2) + { + // Q: Should we make a for_all() that iterates over multiple ranges? + // Q: Should the logical index be passed to the lambda? + + const StructuredIndexing zoneShape{m_shape}, + nodeShape{{m_shape.m_dimensions[0] + 1, + m_shape.m_dimensions[1] + 1}}; + const auto jp = nodeShape.jStride(); + + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + { + using ShapeType = QuadShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + IndexType data[4]; + data[0] = nodeShape.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + + const ShapeType shape(axom::ArrayView(data, 4)); + func(zoneIndex, shape); + }); + } + // TODO: NDIMS == 1 } private: - StructuredTopologyView() = delete; - - LogicalIndexType m_dimensions; + StructuredIndexing m_shape; }; } // end namespace views diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index dde015a57b..fb457821a6 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -9,6 +9,7 @@ #include "axom/core/StackArray.hpp" #include "axom/core/ArrayView.hpp" #include "axom/primal/geometry/Point.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" namespace axom { @@ -28,7 +29,7 @@ template class UniformCoordsetView { public: - using LogicalIndexType = axom::StackArray; + using LogicalIndexType = typename StructuredIndexing::LogicalIndex; using ExtentsType = axom::StackArray; using IndexType = axom::IndexType; using value_type = DataType; @@ -44,7 +45,7 @@ class UniformCoordsetView AXOM_HOST_DEVICE UniformCoordsetView(const LogicalIndexType dims, const ExtentsType origin, - const ExtentsType spacing) : m_dimensions{dims}, m_origin{origin}, m_spacing{spacing} + const ExtentsType spacing) : m_shape{dims}, m_origin{origin}, m_spacing{spacing} { } @@ -56,10 +57,7 @@ class UniformCoordsetView AXOM_HOST_DEVICE IndexType size() const { - IndexType sz = 1; - for(int i = 0; i < NDIMS; i++) - sz *= m_dimensions[i]; - return sz; + return m_shape.size(); } /** @@ -70,7 +68,7 @@ class UniformCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType getPoint(LogicalIndexType vertex_index) const + PointType getPoint(const LogicalIndexType &vertex_index) const { PointType pt; for(int i = 0; i < NDIMS; i++) @@ -87,7 +85,7 @@ class UniformCoordsetView */ AXOM_HOST_DEVICE PointType - operator[](LogicalIndexType vertex_index) const + operator[](const LogicalIndexType &vertex_index) const { return getPoint(vertex_index); } @@ -103,55 +101,12 @@ class UniformCoordsetView PointType operator[](IndexType vertex_index) const { - return getPoint(IndexToLogicalIndex(vertex_index)); + return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); } - -private: - /** - * \brief Turn an index into a logical IJ index. - * - * \param index The index to convert. - * - * \return The logical index that corresponds to the \a index. - */ - template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, LogicalIndexType>::type - IndexToLogicalIndex(IndexType index) const - { - LogicalIndexType logical; - const auto nx = m_dimensions[0]; - assert(index >= 0); - logical[0] = index % nx; - logical[1] = index / nx; - return logical; - } - - /** - * \brief Turn an index into a logical IJK index. - * - * \param index The index to convert. - * - * \return The logical index that corresponds to the \a index. - */ - template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, LogicalIndexType>::type - IndexToLogicalIndex(IndexType index) const - { - LogicalIndexType logical; - const auto nx = m_dimensions[0]; - const auto nxy = nx * m_dimensions[1]; - logical[0] = index % nx; - logical[1] = (index % nxy) / nx; - logical[2] = index / nxy; - return logical; - } - - LogicalIndexType m_dimensions; - ExtentsType m_origin; - ExtentsType m_spacing; + StructuredIndexing m_shape; + ExtentsType m_origin; + ExtentsType m_spacing; }; } // end namespace views diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 0fa6dbe348..336c86fec5 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -143,6 +143,21 @@ class UnstructuredTopologyPolyhedralView }); } + template + void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + { + const auto nSelectedZones = selectedIdsView.size(); + + ViewType idsView(selectedIdsView); + const PolyhedronData sd(m_data); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + { + const auto zoneIndex = idsView[selectIndex]; + const PolyhedronShape shape(sd, zoneIndex); + func(zoneIndex, shape); + }); + } + private: PolyhedronData m_data; }; diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 2bde88c452..9bbedfb101 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -48,6 +48,9 @@ class UnstructuredTopologySingleShapeView const axom::ArrayView &sizes, const axom::ArrayView &offsets) : m_connectivity(conn), m_sizes(sizes), m_offsets(offsets) { + assert(m_sizes.size() != 0); + assert(m_offsets.size() != 0); + assert(m_offsets.size() == m_sizes.size()); } /** @@ -76,10 +79,6 @@ class UnstructuredTopologySingleShapeView axom::ArrayView connectivity(m_connectivity); if constexpr (ShapeType::is_variable_size()) { - assert(m_sizes.size() != 0); - assert(m_offsets.size() != 0); - assert(m_offsets.size() == m_sizes.size()); - axom::ArrayView sizes(m_sizes); axom::ArrayView offsets(m_offsets); axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) @@ -100,6 +99,46 @@ class UnstructuredTopologySingleShapeView } } + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + { + const auto nSelectedZones = selectedIdsView.size(); + + ViewType idsView(selectedIdsView); + axom::ArrayView connectivity(m_connectivity); + + if constexpr (ShapeType::is_variable_size()) + { + axom::ArrayView sizes(m_sizes); + axom::ArrayView offsets(m_offsets); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + { + const auto zoneIndex = idsView[selectIndex]; + const axom::ArrayView shapeData(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex]); + const ShapeType shape(shapeData); + func(zoneIndex, shape); + }); + } + else + { + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + { + const auto zoneIndex = idsView[selectIndex]; + const axom::ArrayView shapeData(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const ShapeType shape(shapeData); + func(zoneIndex, shape); + }); + } + } + private: axom::ArrayView m_connectivity; axom::ArrayView m_sizes; diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 8d60c32c63..77a30faad2 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -29,7 +29,8 @@ constexpr int AnyShape = -1; * \tparam ShapeTypes Allows us to limit which shape types get compiled in. * \tparam FuncType The function/lambda type that will be invoked on the view. * - * \ + * \param topo The node that contains the topology. + * \param func The function/lambda to call with the topology view. */ template void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) From 7bd693a7a6cdefc5d928c6fb5f9383a3e11496ce Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 27 Jun 2024 18:53:16 -0700 Subject: [PATCH 058/290] Working on some node to zone relation stuff --- src/axom/mir/NodeToZoneRelation.hpp | 319 ++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) create mode 100644 src/axom/mir/NodeToZoneRelation.hpp diff --git a/src/axom/mir/NodeToZoneRelation.hpp b/src/axom/mir/NodeToZoneRelation.hpp new file mode 100644 index 0000000000..5b004c9891 --- /dev/null +++ b/src/axom/mir/NodeToZoneRelation.hpp @@ -0,0 +1,319 @@ + +/** + * \brief Build an o2m relation that lets us look up the zones for a node. + */ +template +class NodeToZoneRelationBuilder +{ +public: + + /** + * \brief This + */ + void execute(const conduit::Node &topo, conduit::Node &relation); + +private: + template + void buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const; + +}; + +/** + * \brief This function makes a unique array of values from an input list of keys. + * + * \tparam ExecSpace The execution space. + * \tparam KeyType The data type for the keys. + * + * \param keys_orig_view The input view that contains the input keys to be made unique. + * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. + * \param[out] sindices An array of indices that indicate where in the original view the keys came from. + * + * \note This code is adapted from Ascent/DevilRay. + * + */ +template +void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) +{ + using DataType = typename KeyViewType::value_type; + using for_policy = axom::execution_space::for_policy; + using reduce_policy = axom::execution_space::reduce_policy; + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a copy of the keys and make original indices. + const auto n = keys_orig_view.size(); + axom::Array keys(n, n, allocatorID); + axom::Array indices(n, n, allocatorID); + auto keys_view = keys.view(); + auto indices_view = indices.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + keys_view[i] = keys_orig_view[i]; + indices_view[i] = i; + }); + + // Sort the keys, indices in place. + RAJA::sort_pairs(RAJA::make_span(keys_view, n), + RAJA::make_span(indices_view, n)); + + // Make a mask array for where differences occur. + axom::Array mask(n, n, allocatorID); + auto mask_view = mask.view(); + RAJA::ReduceSum mask_sum(0); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; + const axom::IndexType m = (i >= 1) ? different : 1; + mask_view[i] = m; + mask_sum += m; + }); + + // Do a scan on the mask array to build an offset array. + axom::Array offsets(n, n, allocatorID); + auto offsets_view = offsets.view(); + RAJA::exclusive_scan(RAJA::make_span(mask_view, n), + RAJA::make_span(offsets_view, n), + RAJA::operators::plus{}); + + // Allocate the output arrays. + const axom::IndexType newsize = mask_sum.get(); + skeys = axom::Array(newsize, newsize, allocatorID); + sindices = axom::Array(newsize, newsize, allocatorID); + + // Iterate over the mask/offsets to store values at the right + // offset in the new array. + auto skeys_view = skeys.view(); + auto sindices_view = sindices.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + if(mask_view[i]) + { + skeys_view[offsets_view[i]] = keys_view[i]; + sindices_view[offsets_view[i]] = indices_view[i]; + } + }); +} + +/// SHIT! What does this relation mean when some nodes are not used in a zone as in the strided structured case? + + +/** + * \brief Make an unstructured representation of a structured topology. + */ +template +void +to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const std::string &topoName, conduit::Node &mesh) +{ + const std::string type = topo.fetch_existing("type").as_string(); + const auto allocatorID = axom::execution_space::allocatorID(); + + mesh["coordsets"][coordset.name()].set_external(coordset); + conduit::Node &newtopo = mesh["topologies"][topoName]; + + if(type == "unstructured") + { + newtopo.set_external(topo); + } + else + { + newtopo["type"] = "unstructured"; + conduit::Node &n_newconn = newtopo["elements/connectivity"]; + n_newconn.set_allocator(allocatorID); + + // Fill in the connectivity. + views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) + { + const auto nzones = topoView.numberOfZones(); + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + newtopo["elements/shape"] = shape; + + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + axom::ArrayView conn(reinterpret_cast(n_newconn.data_ptr()), connSize); + auto conn_view = conn.view(); + topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < 4; i++) + conn_view[start + i] = static_cast(zone.getIds()[i]); + }); + }); + } +} + + +/** + * \brief Given views that contain the nodes and zones, sort the zones using the + * node numbers to produce a list of zones for each node and an offsets array + * that points to the start of each list of zones. + * + * \param[in] nodes_view A view that contains the set of all of the nodes in the topology (the connectivity) + * \param[inout[ zones_view A view (same size as \a nodes_view) that contains the zone number of each node. + * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. + */ +template +void +NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const +{ + assert(nodes_view.size() == zones_view.size()); + + using for_policy = axom::execution_space::for_policy; + using reduce_policy = axom::execution_space::reduce_policy; + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a copy of the nodes that we'll use as keys. + const auto n = nodes_view.size(); + axom::Array keys(n, n, allocatorID); + auto keys_view = keys.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + keys_view[i] = nodes_view[i]; + }); + + // Sort the keys, zones in place. This sorts the zones_view which we want for output. + RAJA::sort_pairs(RAJA::make_span(keys_view, n), + RAJA::make_span(zones_view, n)); + + // Make a mask array for where differences occur. + axom::Array mask(n, n, allocatorID); + auto mask_view = mask.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; + const axom::IndexType m = (i >= 1) ? different : 1; + mask_view[i] = m; + }); + + // Do a scan on the mask array to build an offset array. + axom::Array dest_offsets(n, n, allocatorID); + auto dest_offsets_view = dest_offsets.view(); + RAJA::exclusive_scan(RAJA::make_span(mask_view, n), + RAJA::make_span(dest_offsets_view, n), + RAJA::operators::plus{}); + + // Build the offsets to each node's zone ids. + axom::for_all(offsets_view.size(), AXOM_LAMBDA(axom::IndexType i) + { + offsets_view[i] = 0; + }); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + if(mask_view[i]) + { + offsets_view[dest_offsets_view[i]] = i; + } + }); +} + +template +void +NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) +{ + using loop_policy = axom::execution_space::loop_policy; + const std::string type = topo.fetch_existing("type").as_string(); + const auto allocatorID = axom::execution_space::allocatorID(); + + conduit::Node &n_zones = relation["zones"]; + conduit::Node &n_sizes = relation["sizes"]; + conduit::Node &n_offsets = relation["offsets"]; + n_zones.set_allocator(allocatorID); + n_sizes.set_allocator(allocatorID); + n_offsets.set_allocator(allocatorID); + + if(type == "unstructured") + { + conduit::blueprint::mesh::utils::Shape shape(topo); + const conduit::Node &n_connectivity = topo["elements/connectivity"]; + const auto intTypeId = n_connectivity.dtype().id() + const auto connSize = n_connectivity.dtype().number_of_elements() + + // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const auto nnodes = conduit::blueprint::mesh::utils::coordset::length(*coordset); + + if(shape.is_polyhedral()) + { + const conduit::Node &n_topo_sizes = topo["elements/sizes"]; + const conduit::Node &n_topo_offsets = topo["elements/offsets"]; + // TODO: I could iterate over zones and store node ids into a big buffer but it would overcount zones in the end. + // Do something better. + } + else if(shape.is_polygonal()) + { + const conduit::Node &n_topo_sizes = topo["elements/sizes"]; + const conduit::Node &n_topo_offsets = topo["elements/offsets"]; + + const auto nzones = n_topo_sizes.dtype().number_of_elements(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + // Make zones for each node + views::IndexNode_to_ArrayView_same(n_zones, n_topo_sizes, n_topo_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) + { + using DataType = typename decltype(zonesView)::value_type; + axom::for_all(0, nzones, AXOM_LAMBDA(axom::IndexType zoneIndex) + { + for(DataType i = 0; i < sizesView[zoneIndex]; i++) + zonesView[offsetsView[zoneIndex] + i] = zoneIndex; + }); + }); + + views::IndexNode_to_ArrayView_same(n_connectivity, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) + { + // Make the relation, outputting into the zonesView and offsetsView. + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + { + sizes_view[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + }); + } + } + else + { + // Shapes are all the same size. + const auto nodesPerShape = shape.indices; + const auto nzones = connSize / nodesPerShape; + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same(n_connectivity, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) + { + // Make zones for each node + axom::for_all(0, connSize, AXOM_LAMBDA(axom::IndexType index) + { + zonesView[index] = index / nodesPerShape; + }); + // Make the relation, outputting into the zonesView and offsetsView. + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + { + sizes_view[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + }); + } + } + } + else + { + // These are all structured topos of some sort. Make an unstructured representation and recurse. + + conduit::Node mesh; + to_unstructured(topo, *coordset, "newtopo", mesh); + + // Recurse using the unstructured mesh. + execute(mesh["newtopo"], relation); + } +} From a5d2d3add99282ab531b668edf5a459772e06152 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 28 Jun 2024 12:44:46 -0700 Subject: [PATCH 059/290] Separated out some dispatch functions --- src/axom/mir/EquiZAlgorithm.cpp | 236 ++---------------- src/axom/mir/NodeToZoneRelation.hpp | 183 ++++---------- src/axom/mir/blueprint_utilities.hpp | 91 +++++++ src/axom/mir/utilities.hpp | 108 ++++++++ .../views/dispatch_rectilinear_topology.hpp | 65 +++++ .../views/dispatch_structured_topology.hpp | 92 +++++++ src/axom/mir/views/dispatch_topology.hpp | 50 ++++ .../mir/views/dispatch_uniform_topology.hpp | 65 +++++ 8 files changed, 533 insertions(+), 357 deletions(-) create mode 100644 src/axom/mir/blueprint_utilities.hpp create mode 100644 src/axom/mir/utilities.hpp create mode 100644 src/axom/mir/views/dispatch_rectilinear_topology.hpp create mode 100644 src/axom/mir/views/dispatch_structured_topology.hpp create mode 100644 src/axom/mir/views/dispatch_topology.hpp create mode 100644 src/axom/mir/views/dispatch_uniform_topology.hpp diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 7d084cf5f0..31a22f83db 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -7,7 +7,7 @@ #include "axom/core/ArrayView.hpp" #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_coordset.hpp" -#include "axom/mir/views/dispatch_unstructured_topology.hpp" +#include "axom/mir/views/dispatch_topology.hpp" #include @@ -42,96 +42,6 @@ #endif // clang-format on -namespace axom -{ -namespace mir -{ - -#if 0 -template -void for_all_zones(const conduit::Node &topo, FuncType &&func) -{ - conduit::index_t dims[3] = {1, 1, 1}; - conduit::blueprint::mesh::utils::topology::logical_dims(topo, dims, 3); - const conduit::index_t dimension = conduit::blueprint::mesh::topology::dims(topo); - const conduit::index_t nzones = conduit::blueprint::mesh::topology::length(topo); - - if(dimension == 1) - { - // Line elements - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - conduit::index_t ids[2]; - ids[0] = zoneIndex; - ids[1] = zoneIndex + 1; - func(zoneIndex, ids, 2); - }); - } - else if(dimension == 2) - { - // Quad elements - const conduit::index_t nx = dims[0] + 1; - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // zoneIndex to i,j - const conduit::index_t i = zoneIndex % dims[0]; - const conduit::index_t j = zoneIndex / dims[0]; - // Make node ids - const conduit::index_t jnx = j * nx; - const conduit::index_t j1nx = jnx + nx; - conduit::index_t ids[4]; - ids[0] = jnx + i; - ids[1] = jnx + i + 1; - ids[2] = j1nx + i + 1; - ids[3] = j1nx + i; - func(zoneIndex, ids, 4); - }); - } - else if(dimension == 3) - { - // Hex elements - const conduit::index_t nx = dims[0] + 1; - const conduit::index_t ny = dims[1] + 1; - const conduit::index_t nz = dims[2] + 1; - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // zoneIndex to i,j,k - const conduit::index_t i = zoneIndex % dims[0]; - const conduit::index_t j = (zoneIndex % (dims[0] * dims[1])) / dims[0]; - const conduit::index_t k = zoneIndex / (dims[0] * dims[1]); - - // Make node ids - const conduit::index_t knxny = k * nx * ny; - const conduit::index_t k1nxny = (k + 1) * nx * ny; - const conduit::index_t jnx = j * nx; - const conduit::index_t j1nx = jnx + nx; - conduit::index_t ids[8]; - ids[0] = knxny + jnx + i; - ids[1] = knxny + jnx + i + 1; - ids[2] = knxny + j1nx + i + 1; - ids[3] = knxny + j1nx + i; - ids[4] = k1nxny + jnx + i; - ids[5] = k1nxny + jnx + i + 1; - ids[6] = k1nxny + j1nx + i + 1; - ids[7] = k1nxny + j1nx + i; - - func(zoneIndex, ids, 4); - }); - } - else - { -// CONDUIT_ERROR("Unsupported dimension given to traverse_structured " -// << dimension << "."); - } -} -#endif - void EquiZAlgorithm::execute(const conduit::Node &topo, const conduit::Node &coordset, const conduit::Node &options, @@ -165,120 +75,6 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, #endif } -template -void dispatch_uniform(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) -{ - const conduit::Node &n_dims = coordset["dims"]; - const conduit::index_t ndims = n_dims.dtype().number_of_elements(); - if(ndims == 3) - { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - dims[2] = n_dims.as_int_accessor()[2]; - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(ndims == 2) - { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else if(ndims == 1) - { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); - } -} - -template -void dispatch_rectilinear(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) -{ - const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); - if(axes.size() == 3) - { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(axes.size() == 2) - { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else if(axes.size() == 1) - { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); - } -} - -template -void dispatch_structured(const conduit::Node &topo, FuncType &&func) -{ - if(topo.has_path("elements/dims/k")) - { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - dims[2] = topo.fetch_existing("elements/dims/k").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(topo.has_path("elements/dims/j")) - { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else - { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); - } -} - -template -void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) -{ - const auto type = topo.fetch_existing("type").as_string(); - - if(type == "uniform") - dispatch_uniform(topo, coordset, func); - else if(type == "rectilinear") - dispatch_rectilinear(topo, coordset, func); - else if(type == "structured") - dispatch_structured(topo, func); - else if(type == "unstructured") - views::dispatch_unstructured_topology(topo, func); -} - template void EquiZAlgorithm::executeImpl(const conduit::Node &topo, const conduit::Node &coordset, @@ -290,28 +86,30 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, { const conduit::Node &zones = options.fetch_existing("zones"); const auto nzones = zones.dtype().number_of_elements(); -#if 0 - detail::IndexNodeToArrayView(options["zones"], [&](auto zonesView) - { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType i) { - // Do something with zonesView[i]. - // Get number of materials for zone. - // for each material, make a plane eq for its interface. +/// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? - // for each material, intersect zone with the plane. Make PH fragment and volume, add fragment to new_topo - - }); + // Operate on a list of zones. + views::IndexNode_To_ArrayView(options["zones"], [&](auto zonesView) + { + views::dispatch_coordset(coordset, [&](auto &coordsetView) + { + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + { + topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + + }); + }); + }); }); -#endif } else { + // Operate on all zones. views::dispatch_coordset(coordset, [&](auto &coordsetView) { - dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { diff --git a/src/axom/mir/NodeToZoneRelation.hpp b/src/axom/mir/NodeToZoneRelation.hpp index 5b004c9891..d4d041b014 100644 --- a/src/axom/mir/NodeToZoneRelation.hpp +++ b/src/axom/mir/NodeToZoneRelation.hpp @@ -1,3 +1,29 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_NODE_TO_ZONE_RELATION_BUILDER_HPP_ +#define AXOM_MIR_NODE_TO_ZONE_RELATION_BUILDER_HPP_ + +#include "axom/core/memory_management.hpp" +#include "axom/core/execution/execution_space.hpp" +#include "axom/core/execution/for_all.hpp" +#include "axom/core/Array.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/mir/utilities.hpp" + +#include +#include + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ /** * \brief Build an o2m relation that lets us look up the zones for a node. @@ -8,153 +34,28 @@ class NodeToZoneRelationBuilder public: /** - * \brief This + * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. + * + * \param topo The topology for which we're building the O2M relation. + * \param[out] The node that will contain the O2M relation. */ void execute(const conduit::Node &topo, conduit::Node &relation); private: + /** + * \brief Given views that contain the nodes and zones, sort the zones using the + * node numbers to produce a list of zones for each node and an offsets array + * that points to the start of each list of zones. + * + * \param[in] nodes_view A view that contains the set of all of the nodes in the topology (the connectivity) + * \param[inout[ zones_view A view (same size as \a nodes_view) that contains the zone number of each node. + * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. + */ template void buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const; - }; -/** - * \brief This function makes a unique array of values from an input list of keys. - * - * \tparam ExecSpace The execution space. - * \tparam KeyType The data type for the keys. - * - * \param keys_orig_view The input view that contains the input keys to be made unique. - * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. - * \param[out] sindices An array of indices that indicate where in the original view the keys came from. - * - * \note This code is adapted from Ascent/DevilRay. - * - */ -template -void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) -{ - using DataType = typename KeyViewType::value_type; - using for_policy = axom::execution_space::for_policy; - using reduce_policy = axom::execution_space::reduce_policy; - const int allocatorID = axom::execution_space::allocatorID(); - - // Make a copy of the keys and make original indices. - const auto n = keys_orig_view.size(); - axom::Array keys(n, n, allocatorID); - axom::Array indices(n, n, allocatorID); - auto keys_view = keys.view(); - auto indices_view = indices.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - keys_view[i] = keys_orig_view[i]; - indices_view[i] = i; - }); - - // Sort the keys, indices in place. - RAJA::sort_pairs(RAJA::make_span(keys_view, n), - RAJA::make_span(indices_view, n)); - - // Make a mask array for where differences occur. - axom::Array mask(n, n, allocatorID); - auto mask_view = mask.view(); - RAJA::ReduceSum mask_sum(0); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; - const axom::IndexType m = (i >= 1) ? different : 1; - mask_view[i] = m; - mask_sum += m; - }); - - // Do a scan on the mask array to build an offset array. - axom::Array offsets(n, n, allocatorID); - auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view, n), - RAJA::make_span(offsets_view, n), - RAJA::operators::plus{}); - - // Allocate the output arrays. - const axom::IndexType newsize = mask_sum.get(); - skeys = axom::Array(newsize, newsize, allocatorID); - sindices = axom::Array(newsize, newsize, allocatorID); - // Iterate over the mask/offsets to store values at the right - // offset in the new array. - auto skeys_view = skeys.view(); - auto sindices_view = sindices.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - if(mask_view[i]) - { - skeys_view[offsets_view[i]] = keys_view[i]; - sindices_view[offsets_view[i]] = indices_view[i]; - } - }); -} - -/// SHIT! What does this relation mean when some nodes are not used in a zone as in the strided structured case? - - -/** - * \brief Make an unstructured representation of a structured topology. - */ -template -void -to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const std::string &topoName, conduit::Node &mesh) -{ - const std::string type = topo.fetch_existing("type").as_string(); - const auto allocatorID = axom::execution_space::allocatorID(); - - mesh["coordsets"][coordset.name()].set_external(coordset); - conduit::Node &newtopo = mesh["topologies"][topoName]; - - if(type == "unstructured") - { - newtopo.set_external(topo); - } - else - { - newtopo["type"] = "unstructured"; - conduit::Node &n_newconn = newtopo["elements/connectivity"]; - n_newconn.set_allocator(allocatorID); - - // Fill in the connectivity. - views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) - { - const auto nzones = topoView.numberOfZones(); - int ptsPerZone = 2; - if(shape == "quad") - ptsPerZone = 4; - else if(shape == "hex") - ptsPerZone = 8; - - newtopo["elements/shape"] = shape; - - const auto connSize = nzones * ptsPerZone; - n_newconn.set(conduit::DataType::index_t(connSize)); - axom::ArrayView conn(reinterpret_cast(n_newconn.data_ptr()), connSize); - auto conn_view = conn.view(); - topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < 4; i++) - conn_view[start + i] = static_cast(zone.getIds()[i]); - }); - }); - } -} - - -/** - * \brief Given views that contain the nodes and zones, sort the zones using the - * node numbers to produce a list of zones for each node and an offsets array - * that points to the start of each list of zones. - * - * \param[in] nodes_view A view that contains the set of all of the nodes in the topology (the connectivity) - * \param[inout[ zones_view A view (same size as \a nodes_view) that contains the zone number of each node. - * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. - */ template void NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const @@ -317,3 +218,9 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit execute(mesh["newtopo"], relation); } } + +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp new file mode 100644 index 0000000000..a0471473e6 --- /dev/null +++ b/src/axom/mir/blueprint_utilities.hpp @@ -0,0 +1,91 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_BLUEPRINT_UTILITIES_HPP_ +#define AXOM_MIR_BLUEPRINT_UTILITIES_HPP_ + +#include "axom/core/execution/execution_space.hpp" +#include "axom/core/Array.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/core/memory_management.hpp" +#include "axom/mir/views/dispatch_structured_topologies.hpp" + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +// TODO: Add in a routine to migrate a Conduit node to a new memory space. +// copy(const conduit::Node &src, conduit::Node &dest); + +/** + * \accelerated + * \brief Make an unstructured representation of a structured topology. + * + * \tparam ExecSpace The execution space where the work will be done. + * + * \param + * + * \note There are blueprint methods for this sort of thing but this one is accelerated. + */ +template +void +to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const std::string &topoName, conduit::Node &mesh) +{ + const std::string type = topo.fetch_existing("type").as_string(); + const auto allocatorID = axom::execution_space::allocatorID(); + + mesh["coordsets"][coordset.name()].set_external(coordset); + conduit::Node &newtopo = mesh["topologies"][topoName]; + + if(type == "unstructured") + { + newtopo.set_external(topo); + } + else + { + newtopo["type"] = "unstructured"; + conduit::Node &n_newconn = newtopo["elements/connectivity"]; + n_newconn.set_allocator(allocatorID); + + // Fill in the connectivity. + views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) + { + const auto nzones = topoView.numberOfZones(); + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + newtopo["elements/shape"] = shape; + + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + axom::ArrayView conn(reinterpret_cast(n_newconn.data_ptr()), connSize); + auto conn_view = conn.view(); + topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < 4; i++) + conn_view[start + i] = static_cast(zone.getIds()[i]); + }); + }); + } +} + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp new file mode 100644 index 0000000000..13edc8ae63 --- /dev/null +++ b/src/axom/mir/utilities.hpp @@ -0,0 +1,108 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_UTILITIES_HPP_ +#define AXOM_MIR_UTILITIES_HPP_ + +#include "axom/core/Array.hpp" +#include "axom/core/ArrayView.hpp" +#include "axom/core/memory_management.hpp" + +#include +#include + +#include + +/// This header is for device utility functions for MIR. + +// Q: does this belong in core with a better name? + +namespace axom +{ +namespace mir +{ +namespace utilities +{ + +/** + * \brief This function makes a unique array of values from an input list of keys. + * + * \tparam ExecSpace The execution space. + * \tparam KeyType The data type for the keys. + * + * \param keys_orig_view The input view that contains the input keys to be made unique. + * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. + * \param[out] sindices An array of indices that indicate where in the original view the keys came from. + * + * \note This code is adapted from Ascent/DevilRay. + * + */ +template +void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) +{ + using DataType = typename KeyViewType::value_type; + using for_policy = axom::execution_space::for_policy; + using reduce_policy = axom::execution_space::reduce_policy; + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a copy of the keys and make original indices. + const auto n = keys_orig_view.size(); + axom::Array keys(n, n, allocatorID); + axom::Array indices(n, n, allocatorID); + auto keys_view = keys.view(); + auto indices_view = indices.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + keys_view[i] = keys_orig_view[i]; + indices_view[i] = i; + }); + + // Sort the keys, indices in place. + RAJA::sort_pairs(RAJA::make_span(keys_view, n), + RAJA::make_span(indices_view, n)); + + // Make a mask array for where differences occur. + axom::Array mask(n, n, allocatorID); + auto mask_view = mask.view(); + RAJA::ReduceSum mask_sum(0); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; + const axom::IndexType m = (i >= 1) ? different : 1; + mask_view[i] = m; + mask_sum += m; + }); + + // Do a scan on the mask array to build an offset array. + axom::Array offsets(n, n, allocatorID); + auto offsets_view = offsets.view(); + RAJA::exclusive_scan(RAJA::make_span(mask_view, n), + RAJA::make_span(offsets_view, n), + RAJA::operators::plus{}); + + // Allocate the output arrays. + const axom::IndexType newsize = mask_sum.get(); + skeys = axom::Array(newsize, newsize, allocatorID); + sindices = axom::Array(newsize, newsize, allocatorID); + + // Iterate over the mask/offsets to store values at the right + // offset in the new array. + auto skeys_view = skeys.view(); + auto sindices_view = sindices.view(); + axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) + { + if(mask_view[i]) + { + skeys_view[offsets_view[i]] = keys_view[i]; + sindices_view[offsets_view[i]] = indices_view[i]; + } + }); +} + +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp new file mode 100644 index 0000000000..137cb8f854 --- /dev/null +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -0,0 +1,65 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_RECTILINEAR_TOPOLOGY_HPP_ +#define AXOM_MIR_DISPATCH_RECTILINEAR_TOPOLOGY_HPP_ + +#include "axom/mir/views/StructuredTopologyView.hpp" +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. + * + * \tparam FuncType The function/lambda type to invoke on the view. + * + * \param topo The node that contains the rectilinear topology. + * \param coordset The coordset node that contains the topology dimensions. + * \param func The function to invoke using the view. + */ +template +void dispatch_rectilinear_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); + if(axes.size() == 3) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + else if(axes.size() == 2) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + else if(axes.size() == 1) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp new file mode 100644 index 0000000000..c8a9acf1fc --- /dev/null +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -0,0 +1,92 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ +#define AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ + +#include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_uniform_topology.hpp" +#include "axom/mir/views/dispatch_rectilinear_topology.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. + * + * \tparam FuncType The function/lambda type to invoke on the view. + * + * \param topo The node that contains the rectilinear topology. + * \param coordset The coordset node that contains the topology dimensions. + * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. + */ +template +void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) +{ + // TODO: add strided structured variant + //StructuredTopologyView> topoView(StructuredIndexing(dims)); + //StructuredTopologyView> topoView(StridedStructuredIndexing(dims, offset, stride)); + + + if(topo.has_path("elements/dims/k")) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + dims[2] = topo.fetch_existing("elements/dims/k").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + else if(topo.has_path("elements/dims/j")) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + else + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } +} + +/** + * \brief Creates a topology view compatible with various logically "structured" topologies (uniform, rectilinear, structured) and passes that view to the supplied function. + * + * \param topo The node that contains the topology. + * \param coordset The coordset node that contains the topology dimensions. + * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. + + */ +template +void dispatch_structured_topologies(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const std::string type = topo["type"].as_string(); + if(type == "uniform") + dispatch_uniform_topology(topo, coordset, func); + else if(type == "rectilinear") + dispatch_rectilinear_topology(topo, coordset, func); + else if(type == "structured") + dispatch_structured_topology(topo, coordset, func); +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp new file mode 100644 index 0000000000..9075bd0d74 --- /dev/null +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -0,0 +1,50 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_TOPOLOGY_HPP_ +#define AXOM_MIR_DISPATCH_TOPOLOGY_HPP_ + +#include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_uniform_topology.hpp" +#include "axom/mir/views/dispatch_rectilinear_topology.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Creates a topology view and passes that view to the supplied function. + * + * \tparam FuncType The function/lambda type to invoke on the view. + * + * \param topo The node that contains the rectilinear topology. + * \param coordset The coordset node that contains the topology dimensions. + * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. + */ +template +void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +{ + const auto type = topo.fetch_existing("type").as_string(); + + if(type == "uniform") + dispatch_uniform(topo, coordset, func); + else if(type == "rectilinear") + dispatch_rectilinear(topo, coordset, func); + else if(type == "structured") + dispatch_structured(topo, func); + else if(type == "unstructured") + dispatch_unstructured_topology(topo, func); +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp new file mode 100644 index 0000000000..6a9737a8d5 --- /dev/null +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -0,0 +1,65 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_UNIFORM_TOPOLOGY_HPP_ +#define AXOM_MIR_DISPATCH_UNIFORM_TOPOLOGY_HPP_ + +#include "axom/mir/views/StructuredTopologyView.hpp" +#include + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. + * + * \tparam FuncType The function/lambda type to invoke on the view. + * + * \param coordset The coordset node that contains the topology dimensions. + * \param func The function to invoke using the view. + */ +template +void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) +{ + const conduit::Node &n_dims = coordset["dims"]; + const conduit::index_t ndims = n_dims.dtype().number_of_elements(); + if(ndims == 3) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + dims[2] = n_dims.as_int_accessor()[2]; + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + else if(ndims == 2) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + else if(ndims == 1) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From 4b025ee74218f093ad2ea8e54f033701cd172510 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 28 Jun 2024 15:12:19 -0700 Subject: [PATCH 060/290] Exclude dimensions in topology dispatch. --- src/axom/mir/EquiZAlgorithm.cpp | 15 ++-- src/axom/mir/views/StructuredTopologyView.hpp | 2 +- .../views/dispatch_rectilinear_topology.hpp | 66 +++++++++------- .../views/dispatch_structured_topology.hpp | 78 +++++++++++-------- src/axom/mir/views/dispatch_topology.hpp | 11 ++- .../mir/views/dispatch_uniform_topology.hpp | 66 +++++++++------- src/axom/mir/views/dispatch_utilities.hpp | 37 +++++++++ 7 files changed, 180 insertions(+), 95 deletions(-) create mode 100644 src/axom/mir/views/dispatch_utilities.hpp diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 31a22f83db..594138d969 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -8,6 +8,7 @@ #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/dispatch_topology.hpp" +#include "axom/mir/views/dispatch_utilities.hpp" #include @@ -42,6 +43,11 @@ #endif // clang-format on +namespace axom +{ +namespace mir +{ + void EquiZAlgorithm::execute(const conduit::Node &topo, const conduit::Node &coordset, const conduit::Node &options, @@ -84,17 +90,16 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, { if(options.has_path("zones")) { - const conduit::Node &zones = options.fetch_existing("zones"); - const auto nzones = zones.dtype().number_of_elements(); + const conduit::Node &n_zones = options.fetch_existing("zones"); /// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? // Operate on a list of zones. - views::IndexNode_To_ArrayView(options["zones"], [&](auto zonesView) + views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) { views::dispatch_coordset(coordset, [&](auto &coordsetView) { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) { @@ -109,7 +114,7 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, // Operate on all zones. views::dispatch_coordset(coordset, [&](auto &coordsetView) { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index e2d1ee0e7f..e54b89f9a5 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -149,7 +149,7 @@ class StructuredTopologyView template void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const { - const auto nSelectedZones = selectedIdsview.size(); + const auto nSelectedZones = selectedIdsView.size(); ViewType idsView(selectedIdsView); if constexpr (NDIMS == 3) diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index 137cb8f854..eb05cc57a2 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -7,7 +7,9 @@ #define AXOM_MIR_DISPATCH_RECTILINEAR_TOPOLOGY_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_utilities.hpp" #include +#include namespace axom { @@ -20,41 +22,53 @@ namespace views * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. * * \param topo The node that contains the rectilinear topology. * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. */ -template +template void dispatch_rectilinear_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) { const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); - if(axes.size() == 3) + switch(axes.size()) { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(axes.size() == 2) - { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else if(axes.size() == 1) - { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); + case 3: + if constexpr (dimension_selected(SelectedDimensions, 3)) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + break; + case 2: + if constexpr (dimension_selected(SelectedDimensions, 2)) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + break; + case 1: + if constexpr (dimension_selected(SelectedDimensions, 1)) + { + axom::StackArray dims; + dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } + break; + default: + break; } } diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index c8a9acf1fc..c2af5f4262 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/views/dispatch_uniform_topology.hpp" #include "axom/mir/views/dispatch_rectilinear_topology.hpp" @@ -23,66 +24,79 @@ namespace views * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension * * \param topo The node that contains the rectilinear topology. * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) { // TODO: add strided structured variant //StructuredTopologyView> topoView(StructuredIndexing(dims)); //StructuredTopologyView> topoView(StridedStructuredIndexing(dims, offset, stride)); - - - if(topo.has_path("elements/dims/k")) - { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - dims[2] = topo.fetch_existing("elements/dims/k").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(topo.has_path("elements/dims/j")) + int ndims = 1; + ndims += topo.has_path("elements/dims/j") ? 1 : 0; + ndims += topo.has_path("elements/dims/k") ? 1 : 0; + switch(ndims) { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else - { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); + case 3: + if constexpr (dimension_selected(SelectedDimensions, 3)) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + dims[2] = topo.fetch_existing("elements/dims/k").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + break; + case 2: + if constexpr (dimension_selected(SelectedDimensions, 2)) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + dims[1] = topo.fetch_existing("elements/dims/j").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + break; + case 1: + if constexpr (dimension_selected(SelectedDimensions, 1)) + { + axom::StackArray dims; + dims[0] = topo.fetch_existing("elements/dims/i").as_int(); + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } } } /** * \brief Creates a topology view compatible with various logically "structured" topologies (uniform, rectilinear, structured) and passes that view to the supplied function. * + * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension + * * \param topo The node that contains the topology. * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_structured_topologies(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) { const std::string type = topo["type"].as_string(); if(type == "uniform") - dispatch_uniform_topology(topo, coordset, func); + dispatch_uniform_topology(topo, coordset, func); else if(type == "rectilinear") - dispatch_rectilinear_topology(topo, coordset, func); + dispatch_rectilinear_topology(topo, coordset, func); else if(type == "structured") - dispatch_structured_topology(topo, coordset, func); + dispatch_structured_topology(topo, func); } } // end namespace views diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index 9075bd0d74..7b0b99cbdc 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -9,6 +9,8 @@ #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_uniform_topology.hpp" #include "axom/mir/views/dispatch_rectilinear_topology.hpp" +#include "axom/mir/views/dispatch_structured_topology.hpp" +#include "axom/mir/views/dispatch_unstructured_topology.hpp" #include @@ -23,22 +25,23 @@ namespace views * \brief Creates a topology view and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension * * \param topo The node that contains the rectilinear topology. * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) { const auto type = topo.fetch_existing("type").as_string(); if(type == "uniform") - dispatch_uniform(topo, coordset, func); + dispatch_uniform_topology(topo, coordset, func); else if(type == "rectilinear") - dispatch_rectilinear(topo, coordset, func); + dispatch_rectilinear_topology(topo, coordset, func); else if(type == "structured") - dispatch_structured(topo, func); + dispatch_structured_topology(topo, func); else if(type == "unstructured") dispatch_unstructured_topology(topo, func); } diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 6a9737a8d5..c17012cdbc 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_DISPATCH_UNIFORM_TOPOLOGY_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/dispatch_utilities.hpp" #include namespace axom @@ -19,42 +20,53 @@ namespace views /** * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. * - * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam FuncType The function/lambda type to invoke on the view. + * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. * * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. */ -template +template void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) { const conduit::Node &n_dims = coordset["dims"]; const conduit::index_t ndims = n_dims.dtype().number_of_elements(); - if(ndims == 3) + switch(ndims) { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - dims[2] = n_dims.as_int_accessor()[2]; - views::StructuredTopologyView topoView(dims); - const std::string shape("hex"); - func(shape, topoView); - } - else if(ndims == 2) - { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - views::StructuredTopologyView topoView(dims); - const std::string shape("quad"); - func(shape, topoView); - } - else if(ndims == 1) - { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - views::StructuredTopologyView topoView(dims); - const std::string shape("line"); - func(shape, topoView); + default: + case 3: + if constexpr (dimension_selected(SelectedDimensions, 3)) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + dims[2] = n_dims.as_int_accessor()[2]; + views::StructuredTopologyView topoView(dims); + const std::string shape("hex"); + func(shape, topoView); + } + break; + case 2: + if constexpr (dimension_selected(SelectedDimensions, 2)) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + dims[1] = n_dims.as_int_accessor()[1]; + views::StructuredTopologyView topoView(dims); + const std::string shape("quad"); + func(shape, topoView); + } + break; + case 1: + if constexpr (dimension_selected(SelectedDimensions, 3)) + { + axom::StackArray dims; + dims[0] = n_dims.as_int_accessor()[0]; + views::StructuredTopologyView topoView(dims); + const std::string shape("line"); + func(shape, topoView); + } + break; } } diff --git a/src/axom/mir/views/dispatch_utilities.hpp b/src/axom/mir/views/dispatch_utilities.hpp new file mode 100644 index 0000000000..e5d0498b7c --- /dev/null +++ b/src/axom/mir/views/dispatch_utilities.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_DISPATCH_UTILITIES_HPP_ +#define AXOM_MIR_DISPATCH_UTILITIES_HPP_ + +namespace axom +{ +namespace mir +{ +namespace views +{ + +template +constexpr int encode_dimensions(Dimensions... dims) +{ + return (... | dims); +} + +template +constexpr int select_dimensions(Dimensions... dims) +{ + return encode_dimensions((1 << dims)...); +} + +constexpr bool dimension_selected(int encoded_dims, int dim) +{ + return encoded_dims & (1 << dim); +} + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From 4a9984c20b0a510e5b0b387f4a8c2c235436903a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 28 Jun 2024 16:25:49 -0700 Subject: [PATCH 061/290] Added nodal relation code for PH zones --- src/axom/mir/NodeToZoneRelation.hpp | 63 +++++++++++++++++-- src/axom/mir/views/Shapes.hpp | 7 +++ .../UnstructuredTopologyPolyhedralView.hpp | 37 +++++++++++ .../views/dispatch_rectilinear_topology.hpp | 2 +- .../views/dispatch_unstructured_topology.hpp | 36 ++++++++--- 5 files changed, 130 insertions(+), 15 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelation.hpp b/src/axom/mir/NodeToZoneRelation.hpp index d4d041b014..aff19b8ed6 100644 --- a/src/axom/mir/NodeToZoneRelation.hpp +++ b/src/axom/mir/NodeToZoneRelation.hpp @@ -138,10 +138,63 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit if(shape.is_polyhedral()) { - const conduit::Node &n_topo_sizes = topo["elements/sizes"]; - const conduit::Node &n_topo_offsets = topo["elements/offsets"]; - // TODO: I could iterate over zones and store node ids into a big buffer but it would overcount zones in the end. - // Do something better. + dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) + { + const auto nzones = topoView.numberOfZones(); + axom::Array sizes(nzones, nzones, allocatorID); + auto sizes_view = sizes.view(); + + // Run through the topology once to do a count of each zone's unique node ids. + RAJA::ReduceSum count(0); + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); + const auto connSize = count.get(); + + // Do a scan on the size array to build an offset array. + axom::Array offsets(nzones, nzones, allocatorID); + auto offsets_view = offsets.view(); + RAJA::exclusive_scan(RAJA::make_span(sizes_view, nzones), + RAJA::make_span(offsets_view, nzones), + RAJA::operators::plus{}); + sizes.clear(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + conduit::Node n_conn; + n_conn.set_allocator(allocatorID); + n_conn.set(conduit::DataType(intTypeId, connSize)); + + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same(n_conn, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) + { + // Run through the data one more time to build the nodes and zones arrays. + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); + + // Make the relation, outputting into the zonesView and offsetsView. + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + { + sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + }); + }); + }); } else if(shape.is_polygonal()) { @@ -202,7 +255,7 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit // Compute sizes from offsets. axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) { - sizes_view[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); }); } } diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 4ebd627146..e23b26f219 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -336,6 +336,13 @@ struct Shape : public ShapeTraits */ AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + /** + * \brief Get the unique ids that make up this shape. For basic shapes, assume they are unique. + * + * \return A view containing the ids that make up this shape. + */ + AXOM_HOST_DEVICE axom::ArrayView getUniqueIds() const { return m_ids; } + /** * \brief Get the ids for the requested face. * diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 336c86fec5..fd656c0d84 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -92,6 +92,35 @@ class UnstructuredTopologyPolyhedralView { if(nnodes < MaximumNumberOfIds) m_ids[nnodes++] = faceIds[i]; + else + { + SLIC_ERROR("m_ids is not large enough to hold all node ids."); + break; + } + } + } + return axom::ArrayView(m_ids.m_data, nnodes); + } + + AXOM_HOST_DEVICE axom::ArrayView getUniqueIds() const + { + axom::IndexType nnodes = 0; + const auto nFaces = numberOfFaces(); + for(axom::IndexType f = 0; f < nFaces; f++) + { + const auto faceIds = getFace(f); + for(axom::IndexType i = 0; i < faceIds.size(); i++) + { + if(!find(m_ids, nnodes)) + { + if(nnodes < MaximumNumberOfIds) + m_ids[nnodes++] = faceIds[i]; + else + { + SLIC_ERROR("m_ids is not large enough to hold all node ids."); + break; + } + } } } return axom::ArrayView(m_ids.m_data, nnodes); @@ -106,6 +135,14 @@ class UnstructuredTopologyPolyhedralView } private: + AXOM_HOST_DEVICE bool find(const IndexType *arr, axom::IndexType n, IndexType value) const + { + bool found = false; + for(axom::IndexType i = 0; i < n && !found; i++) + found = arr[i] == value; + return found; + } + PolyhedronData m_data; IndexType m_zoneIndex {0}; mutable axom::StackArray m_ids; diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index eb05cc57a2..40545e453d 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -29,7 +29,7 @@ namespace views * \param func The function to invoke using the view. */ template -void dispatch_rectilinear_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) { const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); switch(axes.size()) diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 77a30faad2..a3cd3f4b36 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -22,6 +22,32 @@ namespace views constexpr int AnyShape = -1; +/** + * \brief This function dispatches a Conduit polyhedral unstructured topology. + * + * \tparam FuncType The function/lambda type that will be invoked on the view. + * + * \param topo The node that contains the topology. + * \param func The function/lambda to call with the topology view. + */ +template +void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncType &&func) +{ + const std::string shape = topo["elements/shape"].as_string(); + if(shape == "polyhedral") + { + IndexNode_to_ArrayView_same( + topo["subelements/connectivity"], topo["subelements/sizes"], topo["subelements/offsets"], + topo["elements/connectivity"], topo["elements/sizes"], topo["elements/offsets"], + [&](auto seConnView, auto seSizesView, auto seOffsetsView, auto connView, auto sizesView, auto offsetsView) + { + using IndexType = typename decltype(seConnView)::value_type; + UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); + func(shape, ugView); + }); + } +} + /** * \brief This function dispatches a Conduit topology to the right view type * and passes that view to the supplied function/lambda. @@ -46,15 +72,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { if(shape == "polyhedral") { - IndexNode_to_ArrayView_same( - topo["subelements/connectivity"], topo["subelements/sizes"], topo["subelements/offsets"], - topo["elements/connectivity"], topo["elements/sizes"], topo["elements/offsets"], - [&](auto seConnView, auto seSizesView, auto seOffsetsView, auto connView, auto sizesView, auto offsetsView) - { - using IndexType = typename decltype(seConnView)::value_type; - UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); - func(shape, ugView); - }); + dispatch_unstructured_polyhedral_topology(topo, func); eligible = false; } } From 1dc7fec65b8468d85334a8d5501c18fd532d0dde Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 28 Jun 2024 16:34:17 -0700 Subject: [PATCH 062/290] rename --- .../mir/{NodeToZoneRelation.hpp => NodeToZoneRelationBuilder.hpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/axom/mir/{NodeToZoneRelation.hpp => NodeToZoneRelationBuilder.hpp} (100%) diff --git a/src/axom/mir/NodeToZoneRelation.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp similarity index 100% rename from src/axom/mir/NodeToZoneRelation.hpp rename to src/axom/mir/NodeToZoneRelationBuilder.hpp From cfed8a3d288eca513bd015d67245e42660b848c1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 28 Jun 2024 18:19:29 -0700 Subject: [PATCH 063/290] strided structure indexing --- .../mir/views/StridedStructuredIndexing.hpp | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 src/axom/mir/views/StridedStructuredIndexing.hpp diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp new file mode 100644 index 0000000000..e3fc9d4a4e --- /dev/null +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -0,0 +1,184 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_STRIDED_STRUCTURED_INDEXING_HPP_ +#define AXOM_MIR_STRIDED_STRUCTURED_INDEXING_HPP_ + +#include "axom/core/StackArray.hpp" +#include "axom/core/ArrayView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief This class encapsulates a structured mesh size and contains methods to + * help with indexing into it. + * + * \tparam NDIMS The number of dimensions. + */ +template +struct StridedStructuredIndexing +{ + using IndexType = IndexT; + using LogicalIndex = axom::StackArray; + + AXOM_HOST_DEVICE constexpr static int dimensions() { return NDIMS; } + + /** + * \brief constructor + * + * \param dims The dimensions we're indexing. + */ + AXOM_HOST_DEVICE + StridedStructuredIndexing() : m_dimensions(), m_offset(), m_stride() + { + for(int i = 0; i < NDIMS; i++) + { + m_dimensions[i] = 1; + m_offset[i] = 0; + m_stride[i] = 1; + } + } + + AXOM_HOST_DEVICE + StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offset, const LogicalIndex &stride) : m_dimensions(dims), m_offset(offset), m_stride(stride) + { + } + + /** + * \brief Return the number of values in the index space. + * + * \return The number of values in the index space. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) + sz *= m_dimensions[i]; + return sz; + } + + /** + * \brief Return the j stride. + * + * \return The j stride to move up a row. + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims >= 2, IndexType>::type + jStride() const + { + return m_stride[1]; + } + + /** + * \brief Return the k stride. + * + * \return The k stride to move forward a "page". + */ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, IndexType>::type + kStride() const + { + return m_stride[2]; + } + + /** + * \brief Turn an index into a logical index. + * + * \param index The index to convert. + * + * \return The logical index that corresponds to the \a index. + */ + /// @{ + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 1, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + logical[0] = index - m_origin[0]; + return logical; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + logical[0] = index % m_stride[1] - m_origin[0]; + logical[1] = index / m_stride[1] - m_origin[1]; + return logical; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, LogicalIndex>::type + IndexToLogicalIndex(IndexType index) const + { + LogicalIndex logical; + logical[0] = (index % m_stride[1]) - m_offset[0]; + logical[1] = ((index % m_stride[2]) / m_stride[1]) - m_offset[1]; + logical[2] = (index / m_stride[2]) - m_offset[2]; + return logical; + } + + /// @} + + /** + * \brief Turn a logical index into a flat index. + * + * \param logical The logical indexto convert to a flat index. + * + * \return The index that corresponds to the \a logical index. + */ + /// @{ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 1, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return ((m_offset[0] + logical[0]) * m_stride[0]); + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return ((m_offset[0] + logical[0]) * m_stride[0]) + + ((m_offset[1] + logical[1]) * m_stride[1]); + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, IndexType>::type + LogicalIndexToIndex(const LogicalIndex &logical) const + { + return ((m_offset[0] + logical[0]) * m_stride[0]) + + ((m_offset[1] + logical[1]) * m_stride[1]) + + ((m_offset[2] + logical[2]) * m_stride[2]); + } + + /// @} + + LogicalIndex m_dimensions{}; + LogicalIndex m_offsets{}; + LogicalIndex m_stride{}; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From 845685efc342de2147e9f49d9c29471aac356de9 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 1 Jul 2024 16:48:19 -0700 Subject: [PATCH 064/290] Started adding test --- src/axom/mir/CMakeLists.txt | 13 +++++- src/axom/mir/tests/CMakeLists.txt | 3 +- src/axom/mir/tests/mir_views_indexing.cpp | 53 +++++++++++++++++++++++ 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 src/axom/mir/tests/mir_views_indexing.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 51caa54521..14f94b6335 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -29,16 +29,27 @@ set(mir_headers MIRAlgorithm.hpp EquiZAlgorithm.hpp + views/dispatch_coordset.hpp + views/dispatch_material.hpp + views/dispatch_rectilinear_topology.hpp + views/dispatch_structured_topology.hpp + views/dispatch_topology.hpp + views/dispatch_uniform_topology.hpp views/dispatch_unstructured_topology.hpp - views/ExplitCoordsetView.hpp + views/dispatch_utilities.hpp + views/ExplicitCoordsetView.hpp + views/MaterialView.hpp views/NodeArrayView.hpp views/RectilinearCoordsetView.hpp views/Shapes.hpp + views/StridedStructuredIndexing.hpp + views/StructuredIndexing.hpp views/StructuredTopologyView.hpp views/UniformCoordsetView.hpp views/UnstructuredTopologyPolyhedralView.hpp views/UnstructuredTopologySingleShapeView.hpp + clipping/ClipCases.h clipping/ClipTableManager.hpp ) diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 236c87c9e3..88eb91959e 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and # other Axom Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (BSD-3-Clause) @@ -15,6 +15,7 @@ set(gtest_mir_tests mir_mesh.cpp mir_cell_clipper.cpp mir_utilities.cpp + mir_views_indexing.cpp mir_cell_generator.cpp ) diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp new file mode 100644 index 0000000000..801c7b4c1c --- /dev/null +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +//---------------------------------------------------------------------- + +TEST(mir_views_indexing, strided_structured_indexing_2d) +{ + using Indexing2D = views::StridedStructuredIndexing; + using LogicalIndex = typename views::StridedStructuredIndexing::LogicalIndex; + LogicalIndex dims{6, 5}; + LogicalIndex origin{2, 2}; + LogicalIndex stride{1, 6}; + Indexing2D indexing(dims, origin, stride); + + EXPECT_EQ(indexing.dimensions(), 2); + EXPECT_EQ(indexing.size(), dims[0] * dims[1]); + + const auto index2_2 = indexing.LogicalIndexToIndex(LogicalIndex{2, 2}); + EXPECT_EQ(index2_2, 14); + LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2); + EXPECT_EQ(logical, LogicalIndex{2,2}); + + for(int j = 0; j < 3; j++) + { + for(int i = 0; i < 4; i++) + { + LogicalIndex logical{i, j}; + const auto flat = indexing.LogicalIndexToIndex(logical); + const auto logical2 = indexing.IndexToLogicalIndex(flat); + EXPECT_EQ(logical, logical2); + } + } +} + +//---------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} From c6955837f6a5af2d7f3e085340fc12496a699be2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 1 Jul 2024 17:07:46 -0700 Subject: [PATCH 065/290] Moved StaticArray --- src/axom/core/CMakeLists.txt | 1 + src/axom/core/StaticArray.hpp | 72 +++++++++++++++++++++++++++++ src/axom/mir/views/MaterialView.hpp | 72 +++++------------------------ 3 files changed, 84 insertions(+), 61 deletions(-) create mode 100644 src/axom/core/StaticArray.hpp diff --git a/src/axom/core/CMakeLists.txt b/src/axom/core/CMakeLists.txt index 796bbd2f52..5d0ba6defe 100644 --- a/src/axom/core/CMakeLists.txt +++ b/src/axom/core/CMakeLists.txt @@ -62,6 +62,7 @@ set(core_headers Path.hpp RangeAdapter.hpp StackArray.hpp + StaticArray.hpp Types.hpp memory_management.hpp diff --git a/src/axom/core/StaticArray.hpp b/src/axom/core/StaticArray.hpp new file mode 100644 index 0000000000..07d49c6043 --- /dev/null +++ b/src/axom/core/StaticArray.hpp @@ -0,0 +1,72 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_STATICARRAY_HPP_ +#define AXOM_STATICARRAY_HPP_ + +#include "axom/config.hpp" // for compile-time defines +#include "axom/core/Macros.hpp" +#include "axom/core/StackArray.hpp" + +namespace axom +{ + +/** + * \brief This class extends StackArray with some std::vector-like convenience methods. + * + * \tparam T the type of the values to hold. + * \tparam N the number of values in the array. + * + */ +template +class StaticArray : public StackArray +{ +public: + AXOM_HOST_DEVICE + constexpr size_t capacity() const + { + return static_cast(N); + } + + AXOM_HOST_DEVICE + size_t size() const + { + return m_size; + } + + AXOM_HOST_DEVICE + void push_back(const T &e) + { + if(m_size + 1 < capacity()) + StackArray::m_data[m_size++] = e; + } + + AXOM_HOST_DEVICE + void pop_back() + { + m_size = (m_size > 0) ? (m_size - 1) : 0; + } + + AXOM_HOST_DEVICE + void clear() + { + m_size = 0; + } + + AXOM_HOST_DEVICE + void fill(const T &e) + { + for(size_t i = 0; i < capacity(); i++) + StackArray::m_data[i] = e; + } + +private: + size_t m_size{0}; +}; + + +} /* namespace axom */ + +#endif /* AXOM_STATICARRAY_HPP_ */ diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 3aa1ed36cb..1c0c00139b 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -6,8 +6,11 @@ #ifndef AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ #define AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ +#include "axom/core.hpp" #include "axom/mir/views/Shapes.hpp" +#include + namespace axom { namespace mir @@ -15,59 +18,6 @@ namespace mir namespace views { -template -class StaticArray -{ -public: - AXOM_HOST_DEVICE - constexpr size_t capacity() const - { - return MAXELEM; - } - - AXOM_HOST_DEVICE - size_t size() const - { - return m_size; - } - - AXOM_HOST_DEVICE - void push_back(const ElementType &e) - { - if(m_size + 1 < capacity()) - m_data[m_size++] = e; - } - - AXOM_HOST_DEVICE - void clear() - { - m_size = 0; - } - - AXOM_HOST_DEVICE - ElementType &operator[](size_t index) - { - return m_data[index]; - } - - AXOM_HOST_DEVICE - const ElementType &operator[](size_t index) const - { - return m_data[index]; - } - - AXOM_HOST_DEVICE - void fill(const ElementType &e) - { - for(size_t i = 0; i < capacity(); i++) - m_data[i] = e; - } - -private: - axom::StackArray m_data{}; - size_t m_size{0}; -}; - /** * \brief This object contains information about the materials as provided by a Conduit node. * @@ -97,16 +47,16 @@ class MaterialInformation } } - const std::map &getNamesToIds() const { return m_namesToIds; } - const std::map &getIdsToNames() const { return m_idsToNames; } - const std::vector &getIds() const { return m_ids; } - const std::vector &getNames() const { return m_names; } + const axom::Map &getNamesToIds() const { return m_namesToIds; } + const axom::Map &getIdsToNames() const { return m_idsToNames; } + const axom::Array &getIds() const { return m_ids; } + const axom::Array &getNames() const { return m_names; } private: - std::map m_namesToIds{}; - std::map m_idsToNames{}; - std::vector m_ids{}; - std::vector m_names{}; + axom::Map m_namesToIds{}; + axom::Map m_idsToNames{}; + axom::Array m_ids{}; + axom::Array m_names{}; }; //--------------------------------------------------------------------------- From cfe5255bfe7256c9386371fae0cf9fe3e3e2674b Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 1 Jul 2024 17:44:14 -0700 Subject: [PATCH 066/290] Start of ElementFieldToVertexField.hpp --- src/axom/mir/ElementFieldToVertexField.hpp | 117 +++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 src/axom/mir/ElementFieldToVertexField.hpp diff --git a/src/axom/mir/ElementFieldToVertexField.hpp b/src/axom/mir/ElementFieldToVertexField.hpp new file mode 100644 index 0000000000..1ac2491aac --- /dev/null +++ b/src/axom/mir/ElementFieldToVertexField.hpp @@ -0,0 +1,117 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_ELEMENT_FIELD_TO_VERTEX_FIELD_HPP_ +#define AXOM_MIR_ELEMENT_FIELD_TO_VERTEX_FIELD_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/utilities.hpp" + +#include + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ + +/** + * \brief Convert an element field to a vertex field. + */ +template +class ElementFieldToVertexField +{ +public: + + /** + * \brief Convert an element field to a vertex field, storing the new field in a new Conduit node. + * + * \param field The input field. + * \param relation The node that contains an o2m relation with nodes to zones. + * \param outField[out] The node that will contain the new field. + */ + void execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField); +}; + +template +void +ElementFieldToVertexField::execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField) +{ + using loop_policy = axom::execution_space::loop_policy; + const std::string association = field.fetch_existing("association").as_string(); + const auto allocatorID = axom::execution_space::allocatorID(); + + outField["association"] = "vertex"; + outField["topology"] = field["topology"]; + + auto handleComponent = [](const conduit::Node &relation, const conduit::Node &n_comp, conduit::Node &n_out, int allocatorID) + { + n_out.set_allocator(allocatorID); + n_out.set(n_comp.dtype().id(), n_comp.dtype().number_of_elements()); + + const conduit::Node &n_zones = relation["zones"]; + const conduit::Node &n_sizes = relation["sizes"]; + const conduit::Node &n_offsets = relation["offsets"]; + views::IndexNode_to_ArrayView_same(n_zones, n_sizes, n_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) + { + views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) + { + using Precision = typename compView::value_type; + const auto nnodes = sizesView.size(); + axom::for_all(nnodes, AXOM_LAMBDA(int nodeIndex) + { + const auto n = sizesView[nodeIndex]; + const auto offset = offsetsView[nodeIndex]; + + double sum = 0.; + for(int i = 0; i < n; i++) + { + const auto zid = zonesView[offset + i]; + sum += compView[zid]; + } + + outView[nodeIndex] = static_cast(sum / n); + }); + }); + }); + }; + + if(association == "element") + { + const conduit::Node &n_zones = relation["zones"]; + const conduit::Node &n_sizes = relation["sizes"]; + const conduit::Node &n_offsets = relation["offsets"]; + views::IndexNode_to_ArrayView_same(n_zones, n_sizes, n_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) + { + const auto nnodes = sizesView.size(); + const conduit::Node &n_values = field["values"]; + if(n_values.number_of_children() > 0) + { + for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) + { + const conduit::Node &n_comp = n_values[c]; + handleComponent(relation, n_comp, outField["values"][n_comp.name()], allocatorID); + } + } + else + { + handleComponent(relation, n_values, outField["values"], allocatorID); + } + }); + } + else if(association == "vertex") + { + outField["values"].set_external(field["values"]); + } +} + +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif From e0164e7482270b0ea728e3a2ef94957a969b69b4 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 11:48:11 -0700 Subject: [PATCH 067/290] Made more general --- src/axom/mir/ElementFieldToVertexField.hpp | 115 +++++++++++---------- 1 file changed, 60 insertions(+), 55 deletions(-) diff --git a/src/axom/mir/ElementFieldToVertexField.hpp b/src/axom/mir/ElementFieldToVertexField.hpp index 1ac2491aac..c0b7328f08 100644 --- a/src/axom/mir/ElementFieldToVertexField.hpp +++ b/src/axom/mir/ElementFieldToVertexField.hpp @@ -3,114 +3,119 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef AXOM_MIR_ELEMENT_FIELD_TO_VERTEX_FIELD_HPP_ -#define AXOM_MIR_ELEMENT_FIELD_TO_VERTEX_FIELD_HPP_ +#ifndef AXOM_MIR_RECENTER_FIELD_HPP_ +#define AXOM_MIR_RECENTER_FIELD_HPP_ #include "axom/core.hpp" -#include "axom/mir/utilities.hpp" +#include "axom/mir/views/NodeToArrayView.hpp" #include - -#include +#include namespace axom { namespace mir { -namespace utilities + +/** + * \brief This struct contains the type that should be used to accumulate values of type T. + */ +template +struct accumulate_traits { + using value_type = double; +}; + +template <> +struct accumulate_traits +{ + using value_type = float; +}; /** - * \brief Convert an element field to a vertex field. + * \brief Convert a field with one association type to a field of another association type using an o2mrelation. */ template -class ElementFieldToVertexField +class RecenterField { public: /** - * \brief Convert an element field to a vertex field, storing the new field in a new Conduit node. + * \brief Convert the input field to a different association type using the o2mrelation and store the new field in the output field. * * \param field The input field. - * \param relation The node that contains an o2m relation with nodes to zones. + * \param relation The node that contains an o2mrelation with nodes to zones. * \param outField[out] The node that will contain the new field. */ - void execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField); + static void execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField); }; template void -ElementFieldToVertexField::execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField) +RecenterField::execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField) { - using loop_policy = axom::execution_space::loop_policy; - const std::string association = field.fetch_existing("association").as_string(); - const auto allocatorID = axom::execution_space::allocatorID(); - - outField["association"] = "vertex"; - outField["topology"] = field["topology"]; - auto handleComponent = [](const conduit::Node &relation, const conduit::Node &n_comp, conduit::Node &n_out, int allocatorID) { - n_out.set_allocator(allocatorID); - n_out.set(n_comp.dtype().id(), n_comp.dtype().number_of_elements()); + // Get the data field for the o2m relation. + const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); - const conduit::Node &n_zones = relation["zones"]; + // Use the o2mrelation to average data from n_comp to the n_out. + const conduit::Node &n_relvalues = relation[data_paths[0]]; const conduit::Node &n_sizes = relation["sizes"]; const conduit::Node &n_offsets = relation["offsets"]; - views::IndexNode_to_ArrayView_same(n_zones, n_sizes, n_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) + views::IndexNode_to_ArrayView_same(n_relvalues, n_sizes, n_offsets, [&](auto relView, auto sizesView, auto offsetsView) { + const auto relSize = sizesView.size(); + + // Allocate data for n_out (same type as n_comp). + n_out.set_allocator(allocatorID); + n_out.set(n_comp.dtype().id(), relSize); + views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) { - using Precision = typename compView::value_type; - const auto nnodes = sizesView.size(); - axom::for_all(nnodes, AXOM_LAMBDA(int nodeIndex) + using Precision = typename decltype(compView)::value_type; + using AccumType = accumulate_traits::value_type; + axom::for_all(relSize, AXOM_LAMBDA(int relIndex) { - const auto n = sizesView[nodeIndex]; - const auto offset = offsetsView[nodeIndex]; + const auto n = sizesView[relIndex]; + const auto offset = offsetsView[relIndex]; - double sum = 0.; + AccumType sum = 0; for(int i = 0; i < n; i++) { - const auto zid = zonesView[offset + i]; - sum += compView[zid]; + const auto id = relView[offset + i]; + sum += static_cast(compView[id]); } - outView[nodeIndex] = static_cast(sum / n); + outView[relIndex] = static_cast(sum / n); }); }); }); }; - if(association == "element") + const std::string association = field.fetch_existing("association").as_string(); + const auto allocatorID = axom::execution_space::allocatorID(); + + // Assume that we're flipping the association. + outField["association"] = (association == "element") ? "vertex" : "element"; + outField["topology"] = field["topology"]; + + // Make output values. + const conduit::Node &n_values = field["values"]; + if(n_values.number_of_children() > 0) { - const conduit::Node &n_zones = relation["zones"]; - const conduit::Node &n_sizes = relation["sizes"]; - const conduit::Node &n_offsets = relation["offsets"]; - views::IndexNode_to_ArrayView_same(n_zones, n_sizes, n_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) + for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { - const auto nnodes = sizesView.size(); - const conduit::Node &n_values = field["values"]; - if(n_values.number_of_children() > 0) - { - for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) - { - const conduit::Node &n_comp = n_values[c]; - handleComponent(relation, n_comp, outField["values"][n_comp.name()], allocatorID); - } - } - else - { - handleComponent(relation, n_values, outField["values"], allocatorID); - } - }); + const conduit::Node &n_comp = n_values[c]; + handleComponent(relation, n_comp, outField["values"][n_comp.name()], allocatorID); + } } - else if(association == "vertex") + else { - outField["values"].set_external(field["values"]); + handleComponent(relation, n_values, outField["values"], allocatorID); } } -} // end namespace utilities } // end namespace mir } // end namespace axom From c628a6f4765a8ad7347eaa3de8b993d373dee982 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 11:48:28 -0700 Subject: [PATCH 068/290] Renamed --- src/axom/mir/{ElementFieldToVertexField.hpp => RecenterField.hpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/axom/mir/{ElementFieldToVertexField.hpp => RecenterField.hpp} (100%) diff --git a/src/axom/mir/ElementFieldToVertexField.hpp b/src/axom/mir/RecenterField.hpp similarity index 100% rename from src/axom/mir/ElementFieldToVertexField.hpp rename to src/axom/mir/RecenterField.hpp From fe229f209cd75d9881782f3bb58be26677dd5246 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 12:23:16 -0700 Subject: [PATCH 069/290] undef --- src/axom/mir/clipping/ClipCases.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/axom/mir/clipping/ClipCases.h b/src/axom/mir/clipping/ClipCases.h index 33fb487484..de6c2a00de 100644 --- a/src/axom/mir/clipping/ClipCases.h +++ b/src/axom/mir/clipping/ClipCases.h @@ -159,6 +159,7 @@ extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly8[]; // Axom modifications #define ST_MIN ST_TET #define ST_MAX (ST_PNT + 1) +#undef VISIT_VTK_LIGHT_API } // namespace visit } // namespace clipping } // namespace mir From b4662ca8f0b9016809a3f99e29d6065a5fe83b42 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 12:23:39 -0700 Subject: [PATCH 070/290] rename --- src/axom/mir/clipping/{ClipCases.h => ClipCases.hpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/axom/mir/clipping/{ClipCases.h => ClipCases.hpp} (100%) diff --git a/src/axom/mir/clipping/ClipCases.h b/src/axom/mir/clipping/ClipCases.hpp similarity index 100% rename from src/axom/mir/clipping/ClipCases.h rename to src/axom/mir/clipping/ClipCases.hpp From 2f401a70bb9ac106b93db4dc86605a3d69de6e26 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 13:45:02 -0700 Subject: [PATCH 071/290] compilation fixes --- src/axom/core/StaticArray.hpp | 6 ++ src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/RecenterField.hpp | 4 +- .../clipping/{ClipCases.hpp => ClipCases.h} | 7 ++ src/axom/mir/clipping/ClipCasesHex.cpp | 1 + src/axom/mir/clipping/ClipCasesPyr.cpp | 1 + src/axom/mir/clipping/ClipCasesQuad.cpp | 1 + src/axom/mir/clipping/ClipCasesTet.cpp | 1 + src/axom/mir/clipping/ClipCasesTri.cpp | 1 + src/axom/mir/clipping/ClipCasesWdg.cpp | 1 + src/axom/mir/clipping/ClipTableManager.hpp | 65 +++++++++-------- src/axom/mir/tests/mir_views_indexing.cpp | 17 +++-- src/axom/mir/views/MaterialView.hpp | 72 +++++++++---------- .../mir/views/StridedStructuredIndexing.hpp | 38 +++++----- src/axom/mir/views/dispatch_material.hpp | 16 +++-- 15 files changed, 130 insertions(+), 102 deletions(-) rename src/axom/mir/clipping/{ClipCases.hpp => ClipCases.h} (95%) diff --git a/src/axom/core/StaticArray.hpp b/src/axom/core/StaticArray.hpp index 07d49c6043..f80db5fb63 100644 --- a/src/axom/core/StaticArray.hpp +++ b/src/axom/core/StaticArray.hpp @@ -55,6 +55,12 @@ class StaticArray : public StackArray m_size = 0; } + AXOM_HOST_DEVICE + bool empty() const + { + return m_size == 0; + } + AXOM_HOST_DEVICE void fill(const T &e) { diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 14f94b6335..0097cc4499 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -29,6 +29,7 @@ set(mir_headers MIRAlgorithm.hpp EquiZAlgorithm.hpp + RecenterField.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index c0b7328f08..393042eca6 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -7,7 +7,7 @@ #define AXOM_MIR_RECENTER_FIELD_HPP_ #include "axom/core.hpp" -#include "axom/mir/views/NodeToArrayView.hpp" +#include "axom/mir/views/NodeArrayView.hpp" #include #include @@ -74,7 +74,7 @@ RecenterField::execute(const conduit::Node &field, const conduit::Nod views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) { using Precision = typename decltype(compView)::value_type; - using AccumType = accumulate_traits::value_type; + using AccumType = typename accumulate_traits::value_type; axom::for_all(relSize, AXOM_LAMBDA(int relIndex) { const auto n = sizesView[relIndex]; diff --git a/src/axom/mir/clipping/ClipCases.hpp b/src/axom/mir/clipping/ClipCases.h similarity index 95% rename from src/axom/mir/clipping/ClipCases.hpp rename to src/axom/mir/clipping/ClipCases.h index de6c2a00de..8db2e39d3f 100644 --- a/src/axom/mir/clipping/ClipCases.hpp +++ b/src/axom/mir/clipping/ClipCases.h @@ -8,6 +8,7 @@ // Axom modifications //#include #define VISIT_VTK_LIGHT_API +#include namespace axom { namespace mir { namespace clipping { @@ -160,6 +161,12 @@ extern VISIT_VTK_LIGHT_API unsigned char clipShapesPoly8[]; #define ST_MIN ST_TET #define ST_MAX (ST_PNT + 1) #undef VISIT_VTK_LIGHT_API +extern const size_t clipShapesTriSize; +extern const size_t clipShapesQuaSize; +extern const size_t clipShapesTetSize; +extern const size_t clipShapesPyrSize; +extern const size_t clipShapesWdgSize; +extern const size_t clipShapesHexSize; } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesHex.cpp b/src/axom/mir/clipping/ClipCasesHex.cpp index 0bec543099..7efe45e1d5 100644 --- a/src/axom/mir/clipping/ClipCasesHex.cpp +++ b/src/axom/mir/clipping/ClipCasesHex.cpp @@ -3498,6 +3498,7 @@ unsigned char clipShapesHex[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesHexSize = sizeof(clipShapesHex) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesPyr.cpp b/src/axom/mir/clipping/ClipCasesPyr.cpp index 06bde5764b..983624a8d7 100644 --- a/src/axom/mir/clipping/ClipCasesPyr.cpp +++ b/src/axom/mir/clipping/ClipCasesPyr.cpp @@ -219,6 +219,7 @@ unsigned char clipShapesPyr[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesPyrSize = sizeof(clipShapesPyr) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesQuad.cpp b/src/axom/mir/clipping/ClipCasesQuad.cpp index d105f5dd93..7794a036c8 100644 --- a/src/axom/mir/clipping/ClipCasesQuad.cpp +++ b/src/axom/mir/clipping/ClipCasesQuad.cpp @@ -95,6 +95,7 @@ unsigned char clipShapesQua[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesQuaSize = sizeof(clipShapesQua) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesTet.cpp b/src/axom/mir/clipping/ClipCasesTet.cpp index 9b810cd5b6..7c26ba8d8c 100644 --- a/src/axom/mir/clipping/ClipCasesTet.cpp +++ b/src/axom/mir/clipping/ClipCasesTet.cpp @@ -87,6 +87,7 @@ unsigned char clipShapesTet[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesTetSize = sizeof(clipShapesTet) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesTri.cpp b/src/axom/mir/clipping/ClipCasesTri.cpp index f23a59717b..b5e0f5f405 100644 --- a/src/axom/mir/clipping/ClipCasesTri.cpp +++ b/src/axom/mir/clipping/ClipCasesTri.cpp @@ -57,6 +57,7 @@ unsigned char clipShapesTri[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesTriSize = sizeof(clipShapesTri) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipCasesWdg.cpp b/src/axom/mir/clipping/ClipCasesWdg.cpp index 7356138f07..031fbde43c 100644 --- a/src/axom/mir/clipping/ClipCasesWdg.cpp +++ b/src/axom/mir/clipping/ClipCasesWdg.cpp @@ -615,6 +615,7 @@ unsigned char clipShapesWdg[] = { //--------------------------------------------------------------------------- // Axom modifications +const size_t clipShapesWdgSize = sizeof(clipShapesWdg) / sizeof(unsigned char); } // namespace visit } // namespace clipping } // namespace mir diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index d01b281f0a..b9dc60ed2f 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -8,7 +8,7 @@ #include "axom/core/Array.hpp" #include "axom/core/ArrayView.hpp" -#include "axom/mir/clipping/ClipCases.hpp" +#include "axom/mir/clipping/ClipCases.h" namespace axom { @@ -20,14 +20,13 @@ namespace clipping /** * \brief This struct contains data for a clipping table. * - * \tparam ContainerType The container for the clipping data. - * \tparam SPACE The memory space for the clipping data. + * \tparam ClipIndexContainerType The container for int clipping data. + * \tparam ClipDataContainerType The container for uint8 clipping data. */ -template +template struct ClipTableBase { - using IntContainerType = ContainerType; - using UInt8ContainerType = ContainerType; + using ClipDataType = typename ClipDataContainerType::value_type; // Q: Do I need to explicitly provide a constructor to get it marked as AXOM_HOST_DEVICE? @@ -61,20 +60,20 @@ struct ClipTableBase * \return A container that holds the shape data for the case, probably a view. */ AXOM_HOST_DEVICE - UInt8ContainerType getShape(size_t caseId, size_t shapeId) const + ClipDataContainerType getShape(size_t caseId, size_t shapeId) const { assert(caseId < m_shapes.size()); assert(shapeId < shapesForCase(caseId)); - const uint8 *shapeStart = m_table.data() + m_offsets[caseId]; + const auto *shapeStart = m_table.data() + m_offsets[caseId]; size_t shapeLen = 0; - for(int i = 0; i < shapeId; i++) + for(size_t i = 0; i < shapeId; i++) { shapeLen = advance(*shapeStart); shapeStart += shapeLen; } shapeLen = advance(*shapeStart); - return UInt8ContainerType(shapeStart, shapeLen); + return ClipDataContainerType(shapeStart, shapeLen); } /** @@ -85,11 +84,12 @@ struct ClipTableBase * \return The number of values to advance. */ AXOM_HOST_DEVICE - size_t advance(uint8 shape) const + size_t advance(ClipDataType shape) const { + size_t retval = 0; if(shape == ST_TRI) retval = 2 + 3; - else if(shape == ST_QUAD) + else if(shape == ST_QUA) retval = 2 + 4; else if(shape == ST_TET) retval = 2 + 4; @@ -102,18 +102,21 @@ struct ClipTableBase return retval; } - IntContainerType m_shapes; - IntContainerType m_offsets; - UInt8ContainerType m_table; + ClipIndexContainerType m_shapes; + ClipIndexContainerType m_offsets; + ClipDataContainerType m_table; }; /** * \brief This class contains data arrays for the clipping table and can produce a view for the data. */ template -struct ClipTable : public ClipTableBase +struct ClipTable : public ClipTableBase, axom::Array> { - using ClipTableView = ClipTableBase + using SuperClass = ClipTableBase, axom::Array>; + using IntContainerType = axom::Array; + using Uint8ContainerType = axom::Array; + using ClipTableView = ClipTableBase, axom::ArrayView>; /** * \brief Load clipping data into the arrays, moving data as needed. @@ -124,11 +127,11 @@ struct ClipTable : public ClipTableBase * \param table The clipping table data. * \param tableLen The size of the clipping table data. */ - void load(size_t n, const int *shapes, const int *offsets, const uint8 *table, size_t tableLen) + void load(size_t n, const int *shapes, const int *offsets, const unsigned char *table, size_t tableLen) { - m_shapes = IntContainerType(shapes, n); - m_offsets = IntContainerType(offsets, n); - m_table = UInt8ContainerType(table, tableLen); + SuperClass::m_shapes = IntContainerType(shapes, n); + SuperClass::m_offsets = IntContainerType(offsets, n); + SuperClass::m_table = Uint8ContainerType(table, tableLen); } /** @@ -139,9 +142,9 @@ struct ClipTable : public ClipTableBase ClipTableView view() const { ClipTableView v; - v.m_shapes = m_shapes.view(); - v.m_offsets = m_offsets.view(); - v.m_table = m_table.view(); + v.m_shapes = SuperClass::m_shapes.view(); + v.m_offsets = SuperClass::m_offsets.view(); + v.m_table = SuperClass::m_table.view(); return v; } @@ -208,15 +211,15 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesTri, axom::mir::clipping::visit::startClipShapesTri, axom::mir::clipping::visit::clipShapesTri, - sizeof(axom::mir::clipping::visit::clipShapesTri) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesTriSize); } - else if(shape == ST_QUAD) + else if(shape == ST_QUA) { m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesQua, axom::mir::clipping::visit::numClipShapesQua, axom::mir::clipping::visit::startClipShapesQua, axom::mir::clipping::visit::clipShapesQua, - sizeof(axom::mir::clipping::visit::clipShapesQua) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesQuaSize); } else if(shape == ST_TET) { @@ -224,7 +227,7 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesTet, axom::mir::clipping::visit::startClipShapesTet, axom::mir::clipping::visit::clipShapesTet, - sizeof(axom::mir::clipping::visit::clipShapesTet) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesTetSize); } else if(shape == ST_PYR) { @@ -232,7 +235,7 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesPyr, axom::mir::clipping::visit::startClipShapesPyr, axom::mir::clipping::visit::clipShapesPyr, - sizeof(axom::mir::clipping::visit::clipShapesTet) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesTetSize); } else if(shape == ST_WDG) { @@ -240,7 +243,7 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesWdg, axom::mir::clipping::visit::startClipShapesWdg, axom::mir::clipping::visit::clipShapesWdg, - sizeof(axom::mir::clipping::visit::clipShapesWdg) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesWdgSize); } else if(shape == ST_HEX) { @@ -248,7 +251,7 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesHex, axom::mir::clipping::visit::startClipShapesHex, axom::mir::clipping::visit::clipShapesHex, - sizeof(axom::mir::clipping::visit::clipShapesHex) / sizeof(unsigned char)); + axom::mir::clipping::visit::clipShapesHexSize); } } diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index 801c7b4c1c..5db190602a 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -7,13 +7,14 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" +#include "axom/mir/views/StridedStructuredIndexing.hpp" //---------------------------------------------------------------------- TEST(mir_views_indexing, strided_structured_indexing_2d) { - using Indexing2D = views::StridedStructuredIndexing; - using LogicalIndex = typename views::StridedStructuredIndexing::LogicalIndex; + using Indexing2D = axom::mir::views::StridedStructuredIndexing; + using LogicalIndex = typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; LogicalIndex dims{6, 5}; LogicalIndex origin{2, 2}; LogicalIndex stride{1, 6}; @@ -22,10 +23,16 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) EXPECT_EQ(indexing.dimensions(), 2); EXPECT_EQ(indexing.size(), dims[0] * dims[1]); - const auto index2_2 = indexing.LogicalIndexToIndex(LogicalIndex{2, 2}); - EXPECT_EQ(index2_2, 14); + const LogicalIndex logical0_0{0, 0}; + const auto index0_0 = indexing.LogicalIndexToIndex(logical0_0); + EXPECT_EQ(index0_0, 14); + + const LogicalIndex logical2_2{2, 2}; + const auto index2_2 = indexing.LogicalIndexToIndex(logical2_2); + EXPECT_EQ(index2_2, 28); + LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2); - EXPECT_EQ(logical, LogicalIndex{2,2}); + EXPECT_TRUE(logical == logical2_2); for(int j = 0; j < 3; j++) { diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 1c0c00139b..b502f711f0 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -9,7 +9,11 @@ #include "axom/core.hpp" #include "axom/mir/views/Shapes.hpp" +#include + #include +#include +#include namespace axom { @@ -26,6 +30,11 @@ namespace views class MaterialInformation { public: + using StringIntMap = std::map; + using IntStringMap = std::map; + using IntVector = std::vector; + using StringVector = std::vector; + void set(const conduit::Node &matset) { if(matset.has_child("material_map")) @@ -47,16 +56,16 @@ class MaterialInformation } } - const axom::Map &getNamesToIds() const { return m_namesToIds; } - const axom::Map &getIdsToNames() const { return m_idsToNames; } - const axom::Array &getIds() const { return m_ids; } - const axom::Array &getNames() const { return m_names; } + const StringIntMap &getNamesToIds() const { return m_namesToIds; } + const IntStringMap &getIdsToNames() const { return m_idsToNames; } + const IntVector &getIds() const { return m_ids; } + const StringVector &getNames() const { return m_names; } private: - axom::Map m_namesToIds{}; - axom::Map m_idsToNames{}; - axom::Array m_ids{}; - axom::Array m_names{}; + StringIntMap m_namesToIds{}; + IntStringMap m_idsToNames{}; + IntVector m_ids{}; + StringVector m_names{}; }; //--------------------------------------------------------------------------- @@ -94,10 +103,10 @@ class UnibufferMaterialView constexpr static size_t MaxMaterials = MAXMATERIALS; - void set(const axom::ArrayView &material_ids; - const axom::ArrayView &volume_fractions; - const axom::ArrayView &sizes; - const axom::ArrayView &offsets; + void set(const axom::ArrayView &material_ids, + const axom::ArrayView &volume_fractions, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets, const axom::ArrayView &indices) { m_material_ids = material_ids; @@ -235,8 +244,6 @@ class MultiBufferMaterialView AXOM_HOST_DEVICE size_t getNumberOfMaterials(ZoneIndex zi) const { - assert(zi < m_sizes.size()); - size_t nmats = 0; for(size_t i = 0; i < m_size; i++) { @@ -254,8 +261,6 @@ class MultiBufferMaterialView AXOM_HOST_DEVICE void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { - assert(zi < m_sizes.size()); - ids.clear(); vfs.clear(); @@ -279,7 +284,6 @@ class MultiBufferMaterialView assert(mat < m_size); assert(zi < m_indices[mat].size()); - bool contains = false; const auto idx = m_indices[mat][zi]; return m_values[mat][zi] > 0.; } @@ -290,7 +294,6 @@ class MultiBufferMaterialView assert(mat < m_size); assert(zi < m_indices[mat].size()); - bool contains = false; const auto idx = m_indices[mat][zi]; vf = m_values[mat][zi]; return vf > 0.; @@ -327,27 +330,23 @@ class ElementDominantMaterialView void add(const axom::ArrayView &vfs) { - assert(m_size + 1 < MaxMaterials); - - m_indices[m_size] = ids; - m_values[m_size] = vfs; - m_size++; + m_volume_fractions.push_back(vfs); } AXOM_HOST_DEVICE size_t getNumberOfZones() const { - return m_size > 0 ? m_volume_fractions[0].size() : 0; + return (m_volume_fractions.size() > 0) ? m_volume_fractions[0].size() : 0; } AXOM_HOST_DEVICE size_t getNumberOfMaterials(ZoneIndex zi) const { size_t nmats = 0; - if(m_size > 0) + if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); - for(size_t i = 0; i < m_size; i++) + for(size_t i = 0; i < m_volume_fractions.size(); i++) nmats += m_volume_fractions[i][zi] > 0. ? 1 : 0; } return nmats; @@ -359,10 +358,10 @@ class ElementDominantMaterialView ids.clear(); vfs.clear(); - if(m_size > 0) + if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); - for(size_t i = 0; i < m_size; i++) + for(size_t i = 0; i < m_volume_fractions.size(); i++) { if(m_volume_fractions[i][zi] > 0) { @@ -377,7 +376,7 @@ class ElementDominantMaterialView bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { bool contains = false; - if(m_size > 0) + if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); contains = m_volume_fractions[mat][zi] > 0; @@ -390,7 +389,7 @@ class ElementDominantMaterialView { bool contains = false; vf = 0; - if(m_size > 0) + if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); vf = m_volume_fractions[mat][zi] > 0; @@ -399,8 +398,7 @@ class ElementDominantMaterialView return contains; } private: - axom::StackArray> m_volume_fractions{}; - size_t m_size{0}; + axom::StaticArray, MAXMATERIALS> m_volume_fractions{}; }; /** @@ -451,7 +449,7 @@ class MaterialDominantMaterialView { const auto sz = m_element_ids[mi].size(); for(size_t i = 0; i < sz; i++) - m_nzones = axom::utilties::max(m_nzones, m_element_ids[mi][i]); + m_nzones = axom::utilities::max(m_nzones, m_element_ids[mi][i]); #if 0 // host-only // Eh, do this. @@ -511,7 +509,7 @@ class MaterialDominantMaterialView if(m_element_ids[mi][i] == zi) { ids.push_back(mi); - vfs.push_back(m_volume_fractions[mi][i]; + vfs.push_back(m_volume_fractions[mi][i]); break; } } @@ -534,13 +532,12 @@ class MaterialDominantMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { - assert(zi < m_sizes.size()); assert(mat < m_element_ids.size()); bool found = false; #if 1 const auto element_ids = m_element_ids[mat]; - for(size_t i = 0; i < selectedIds.size(); i++) + for(size_t i = 0; i < element_ids.size(); i++) { if(element_ids[i] == zi) { @@ -555,13 +552,12 @@ class MaterialDominantMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const { - assert(zi < m_sizes.size()); assert(mat < m_element_ids.size()); bool found = false; #if 1 const auto element_ids = m_element_ids[mat]; - for(size_t i = 0; i < selectedIds.size(); i++) + for(size_t i = 0; i < element_ids.size(); i++) { if(element_ids[i] == zi) { diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index e3fc9d4a4e..aab55f198d 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -36,18 +36,18 @@ struct StridedStructuredIndexing * \param dims The dimensions we're indexing. */ AXOM_HOST_DEVICE - StridedStructuredIndexing() : m_dimensions(), m_offset(), m_stride() + StridedStructuredIndexing() : m_dimensions(), m_offsets(), m_strides() { for(int i = 0; i < NDIMS; i++) { m_dimensions[i] = 1; - m_offset[i] = 0; - m_stride[i] = 1; + m_offsets[i] = 0; + m_strides[i] = 1; } } AXOM_HOST_DEVICE - StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offset, const LogicalIndex &stride) : m_dimensions(dims), m_offset(offset), m_stride(stride) + StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offset, const LogicalIndex &stride) : m_dimensions(dims), m_offsets(offset), m_strides(stride) { } @@ -75,7 +75,7 @@ struct StridedStructuredIndexing typename std::enable_if<_ndims >= 2, IndexType>::type jStride() const { - return m_stride[1]; + return m_strides[1]; } /** @@ -88,7 +88,7 @@ struct StridedStructuredIndexing typename std::enable_if<_ndims == 3, IndexType>::type kStride() const { - return m_stride[2]; + return m_strides[2]; } /** @@ -106,7 +106,7 @@ struct StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = index - m_origin[0]; + logical[0] = index - m_offsets[0]; return logical; } @@ -116,8 +116,8 @@ struct StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = index % m_stride[1] - m_origin[0]; - logical[1] = index / m_stride[1] - m_origin[1]; + logical[0] = index % m_strides[1] - m_offsets[0]; + logical[1] = index / m_strides[1] - m_offsets[1]; return logical; } @@ -127,9 +127,9 @@ struct StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = (index % m_stride[1]) - m_offset[0]; - logical[1] = ((index % m_stride[2]) / m_stride[1]) - m_offset[1]; - logical[2] = (index / m_stride[2]) - m_offset[2]; + logical[0] = (index % m_strides[1]) - m_offsets[0]; + logical[1] = ((index % m_strides[2]) / m_strides[1]) - m_offsets[1]; + logical[2] = (index / m_strides[2]) - m_offsets[2]; return logical; } @@ -148,7 +148,7 @@ struct StridedStructuredIndexing typename std::enable_if<_ndims == 1, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { - return ((m_offset[0] + logical[0]) * m_stride[0]); + return ((m_offsets[0] + logical[0]) * m_strides[0]); } template @@ -156,8 +156,8 @@ struct StridedStructuredIndexing typename std::enable_if<_ndims == 2, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { - return ((m_offset[0] + logical[0]) * m_stride[0]) + - ((m_offset[1] + logical[1]) * m_stride[1]); + return ((m_offsets[0] + logical[0]) * m_strides[0]) + + ((m_offsets[1] + logical[1]) * m_strides[1]); } template @@ -165,16 +165,16 @@ struct StridedStructuredIndexing typename std::enable_if<_ndims == 3, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { - return ((m_offset[0] + logical[0]) * m_stride[0]) + - ((m_offset[1] + logical[1]) * m_stride[1]) + - ((m_offset[2] + logical[2]) * m_stride[2]); + return ((m_offsets[0] + logical[0]) * m_strides[0]) + + ((m_offsets[1] + logical[1]) * m_strides[1]) + + ((m_offsets[2] + logical[2]) * m_strides[2]); } /// @} LogicalIndex m_dimensions{}; LogicalIndex m_offsets{}; - LogicalIndex m_stride{}; + LogicalIndex m_strides{}; }; } // end namespace views diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp index 99f8fb8725..c65beb6e62 100644 --- a/src/axom/mir/views/dispatch_material.hpp +++ b/src/axom/mir/views/dispatch_material.hpp @@ -9,6 +9,8 @@ #include "axom/mir/views/MaterialView.hpp" #include "axom/mir/views/NodeArrayView.hpp" +#include + namespace axom { namespace mir @@ -30,7 +32,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) { constexpr static size_t MaxMaterials = 20; - if(conduit::mesh::matset::is_uni_buffer(matset)) + if(conduit::blueprint::mesh::matset::is_uni_buffer(matset)) { IndexNode_to_ArrayView_same(matset["material_ids"],matset["sizes"],matset["offsets"],matset["indices"], [&](auto material_ids, auto sizes, auto offsets, auto indices) @@ -46,7 +48,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) }); }); } - else if(conduit::mesh::matset::is_multi_buffer(matset)) + else if(conduit::blueprint::mesh::matset::is_multi_buffer(matset)) { const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); const conduit::Node &n_firstValues = volume_fractions[0].fetch_existing("values"); @@ -58,7 +60,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) using IntView = decltype(firstIndices); using IntElement = typename IntView::value_type; using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; + using FloatElement = typename FloatView::value_type; MultiBufferMaterialView matsetView; @@ -79,14 +81,14 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) }); }); } - else if(conduit::mesh::matset::is_element_dominant(matset)) + else if(conduit::blueprint::mesh::matset::is_element_dominant(matset)) { const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); const conduit::Node &n_firstValues = volume_fractions[0]; FloatNode_To_ArrayView(n_firstValues, [&](auto firstValues) { using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; + using FloatElement = typename FloatView::value_type; ElementDominantMaterialView matsetView; @@ -101,7 +103,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) func(matsetView); }); } - else if(conduit::mesh::matset::is_material_dominant(matset)) + else if(conduit::blueprint::mesh::matset::is_material_dominant(matset)) { const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); const conduit::Node &element_ids = matset.fetch_existing("element_ids"); @@ -115,7 +117,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) using IntView = decltype(firstIndices); using IntElement = typename IntView::value_type; using FloatView = decltype(firstValues); - using FloatElement = typename Floatview::value_type; + using FloatElement = typename FloatView::value_type; MaterialDominantMaterialView matsetView; From 58340e4ece1d34acfea2a4304a381a0ad364bc9a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 16:18:54 -0700 Subject: [PATCH 072/290] rename --- src/axom/mir/CMakeLists.txt | 2 +- src/axom/mir/clipping/{ClipCasesQuad.cpp => ClipCasesQua.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/axom/mir/clipping/{ClipCasesQuad.cpp => ClipCasesQua.cpp} (100%) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 0097cc4499..3f65f478d0 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -68,7 +68,7 @@ set(mir_sources EquiZAlgorithm.cpp clipping/ClipCasesHex.cpp clipping/ClipCasesPyr.cpp - clipping/ClipCasesQuad.cpp + clipping/ClipCasesQua.cpp clipping/ClipCasesTet.cpp clipping/ClipCasesTri.cpp clipping/ClipCasesWdg.cpp diff --git a/src/axom/mir/clipping/ClipCasesQuad.cpp b/src/axom/mir/clipping/ClipCasesQua.cpp similarity index 100% rename from src/axom/mir/clipping/ClipCasesQuad.cpp rename to src/axom/mir/clipping/ClipCasesQua.cpp From 63a0b9a0044c690036aeb32020d7a42a3906de82 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 16:19:15 -0700 Subject: [PATCH 073/290] StridedStructuredIndexing test --- src/axom/mir/tests/mir_views_indexing.cpp | 58 +++++++++++++++++-- .../mir/views/StridedStructuredIndexing.hpp | 29 ++++++++++ 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index 5db190602a..6955e12ab9 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -10,12 +10,11 @@ #include "axom/mir/views/StridedStructuredIndexing.hpp" //---------------------------------------------------------------------- - TEST(mir_views_indexing, strided_structured_indexing_2d) { using Indexing2D = axom::mir::views::StridedStructuredIndexing; using LogicalIndex = typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; - LogicalIndex dims{6, 5}; + LogicalIndex dims{4, 3}; // window size in 4*3 elements in 6*5 overall LogicalIndex origin{2, 2}; LogicalIndex stride{1, 6}; Indexing2D indexing(dims, origin, stride); @@ -34,16 +33,65 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2); EXPECT_TRUE(logical == logical2_2); - for(int j = 0; j < 3; j++) + for(int j = 0; j < dims[1]; j++) { - for(int i = 0; i < 4; i++) + for(int i = 0; i < dims[0]; i++) { LogicalIndex logical{i, j}; const auto flat = indexing.LogicalIndexToIndex(logical); const auto logical2 = indexing.IndexToLogicalIndex(flat); EXPECT_EQ(logical, logical2); } - } + } + + EXPECT_TRUE(indexing.contains(logical0_0)); + EXPECT_TRUE(indexing.contains(LogicalIndex{dims[0] - 1, dims[1] - 1})); + EXPECT_FALSE(indexing.contains(LogicalIndex{4, 0})); + EXPECT_FALSE(indexing.contains(LogicalIndex{4, 3})); +} + +//---------------------------------------------------------------------- +TEST(mir_views_indexing, strided_structured_indexing_3d) +{ + using Indexing3D = axom::mir::views::StridedStructuredIndexing; + using LogicalIndex = typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; + LogicalIndex dims{4, 3, 3}; // window size in 4*3*3 elements in 6*5*5 overall + LogicalIndex origin{2, 2, 2}; + LogicalIndex stride{1, 6, 30}; + Indexing3D indexing(dims, origin, stride); + + EXPECT_EQ(indexing.dimensions(), 3); + EXPECT_EQ(indexing.size(), dims[0] * dims[1] * dims[2]); + + const LogicalIndex logical0_0_0{0, 0, 0}; + const auto index0_0_0 = indexing.LogicalIndexToIndex(logical0_0_0); + EXPECT_EQ(index0_0_0, 2 * 30 + 14); + + const LogicalIndex logical2_2_2{2, 2, 2}; + const auto index2_2_2 = indexing.LogicalIndexToIndex(logical2_2_2); + EXPECT_EQ(index2_2_2, (2 + 2) * 30 + 28); + + LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2_2); + EXPECT_TRUE(logical == logical2_2_2); + + for(int k = 0; k < dims[2]; k++) + { + for(int j = 0; j < dims[1]; j++) + { + for(int i = 0; i < dims[0]; i++) + { + LogicalIndex logical{i, j, k}; + const auto flat = indexing.LogicalIndexToIndex(logical); + const auto logical2 = indexing.IndexToLogicalIndex(flat); + EXPECT_EQ(logical, logical2); + } + } + } + + EXPECT_TRUE(indexing.contains(logical0_0_0)); + EXPECT_TRUE(indexing.contains(LogicalIndex{dims[0] - 1, dims[1] - 1, dims[2] - 1})); + EXPECT_FALSE(indexing.contains(LogicalIndex{4, 0, 0})); + EXPECT_FALSE(indexing.contains(LogicalIndex{4, 3, 0})); } //---------------------------------------------------------------------- diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index aab55f198d..0da5c0f02a 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -172,6 +172,35 @@ struct StridedStructuredIndexing /// @} + /** + * \brief Determines whether the indexing contains the supplied logical index. + * + * \param logical The logical index being tested. + * + * \return True if the logical index is within the index, false otherwise. + */ + bool contains(const LogicalIndex &logical) const + { + bool retval = true; + for(int i = 0; i < dimensions(); i++) + { + retval &= (logical[i] >= 0 && logical[i] < m_dimensions[i]); + } + return retval; + } + + /** + * \brief Determines whether the indexing contains the supplied index. + * + * \param index The index being tested. + * + * \return True if the index is within the index, false otherwise. + */ + bool contains(const IndexType index) const + { + return contains(IndexToLogicalIndex(index)); + } + LogicalIndex m_dimensions{}; LogicalIndex m_offsets{}; LogicalIndex m_strides{}; From e7b3e25b8d36db941aad5668528523ec982087b7 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 17:19:34 -0700 Subject: [PATCH 074/290] Create clip table in mir algorithm. --- src/axom/mir/EquiZAlgorithm.cpp | 5 + src/axom/mir/clipping/ClipTableManager.hpp | 167 +++++++++++------- src/axom/mir/views/StructuredTopologyView.hpp | 2 +- .../UnstructuredTopologyPolyhedralView.hpp | 7 + .../UnstructuredTopologySingleShapeView.hpp | 7 + 5 files changed, 122 insertions(+), 66 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 594138d969..f399a1e9ad 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -9,6 +9,7 @@ #include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/dispatch_topology.hpp" #include "axom/mir/views/dispatch_utilities.hpp" +#include "axom/mir/clipping/ClipTableManager.hpp" #include @@ -101,6 +102,10 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, { views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { + // Create the clipping tables for the topo dimension. + axom::mir::clipping::ClipTableManager clipManager; + clipManager.load(topoView.dimension()); + topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) { diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index b9dc60ed2f..28d1f7ff77 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -110,13 +110,16 @@ struct ClipTableBase /** * \brief This class contains data arrays for the clipping table and can produce a view for the data. */ -template -struct ClipTable : public ClipTableBase, axom::Array> +template +struct ClipTable : public ClipTableBase, axom::Array> { - using SuperClass = ClipTableBase, axom::Array>; - using IntContainerType = axom::Array; - using Uint8ContainerType = axom::Array; - using ClipTableView = ClipTableBase, axom::ArrayView>; + using IntContainerType = axom::Array; + using Uint8ContainerType = axom::Array; + using IntViewType = axom::ArrayView; + using Uint8ViewType = axom::ArrayView; + + using SuperClass = ClipTableBase; + using ClipTableView = ClipTableBase; /** * \brief Load clipping data into the arrays, moving data as needed. @@ -129,9 +132,17 @@ struct ClipTable : public ClipTableBase, axom::Array< */ void load(size_t n, const int *shapes, const int *offsets, const unsigned char *table, size_t tableLen) { - SuperClass::m_shapes = IntContainerType(shapes, n); - SuperClass::m_offsets = IntContainerType(offsets, n); - SuperClass::m_table = Uint8ContainerType(table, tableLen); + const int allocatorID = execution_space::allocatorID(); + + // Allocate space. + SuperClass::m_shapes = IntContainerType(n, n, allocatorID); + SuperClass::m_offsets = IntContainerType(n, n, allocatorID); + SuperClass::m_table = Uint8ContainerType(tableLen, tableLen, allocatorID); + + // Copy data to the arrays. + axom::copy(SuperClass::m_shapes.data(), shapes, n * sizeof(int)); + axom::copy(SuperClass::m_offsets.data(), offsets, n * sizeof(int)); + axom::copy(SuperClass::m_table.data(), table, tableLen * sizeof(unsigned char)); } /** @@ -153,7 +164,7 @@ struct ClipTable : public ClipTableBase, axom::Array< /** * \brief Manage several clipping tables. */ -template +template class ClipTableManager { public: @@ -163,7 +174,7 @@ class ClipTableManager ClipTableManager() { for(size_t shape = ST_MIN; shape < ST_MAX; shape++) - m_clipTables[shapeToIndex(shape)] = ClipTable(); + m_clipTables[shapeToIndex(shape)] = ClipTable(); } /** @@ -173,17 +184,40 @@ class ClipTableManager * * \return A reference to the clipping table. */ - const ClipTable &operator[](size_t shape) + const ClipTable &operator[](size_t shape) { - assert(shape < ST_MAX); const auto index = shapeToIndex(shape); - if(m_clipTables[index].size() == 0) - { - load(shape); - } + assert(shape < ST_MAX); + assert(index >= 0); + load(shape, 0); return m_clipTables[index]; } + /** + * \brief Load tables based on dimension. + */ + void load(int dim) + { + for(const auto shape : shapes(dim)) + load(shape, 0); + } + + /** + * \brief Return a vector of clipping shape ids for the given dimension. + * + * \param The spatial dimension. + * + * \return A vector of clipping shape ids. + */ + std::vector shapes(int dim) const + { + std::vector s; + if(dim == 2) + s = std::vector{ST_TRI, ST_QUA}; + else if(dim == 3) + s = std::vector{ST_TET, ST_PYR, ST_WDG, ST_HEX}; + return s; + } private: /** * \brief Turn a shape into an table index. @@ -202,60 +236,63 @@ class ClipTableManager * * \param shape The shape whose table will be loaded. */ - void load(size_t shape) + void load(size_t shape, int) { const auto index = shapeToIndex(shape); - if(shape == ST_TRI) - { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTri, - axom::mir::clipping::visit::numClipShapesTri, - axom::mir::clipping::visit::startClipShapesTri, - axom::mir::clipping::visit::clipShapesTri, - axom::mir::clipping::visit::clipShapesTriSize); - } - else if(shape == ST_QUA) - { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesQua, - axom::mir::clipping::visit::numClipShapesQua, - axom::mir::clipping::visit::startClipShapesQua, - axom::mir::clipping::visit::clipShapesQua, - axom::mir::clipping::visit::clipShapesQuaSize); - } - else if(shape == ST_TET) - { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTet, - axom::mir::clipping::visit::numClipShapesTet, - axom::mir::clipping::visit::startClipShapesTet, - axom::mir::clipping::visit::clipShapesTet, - axom::mir::clipping::visit::clipShapesTetSize); - } - else if(shape == ST_PYR) - { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesPyr, - axom::mir::clipping::visit::numClipShapesPyr, - axom::mir::clipping::visit::startClipShapesPyr, - axom::mir::clipping::visit::clipShapesPyr, - axom::mir::clipping::visit::clipShapesTetSize); - } - else if(shape == ST_WDG) - { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesWdg, - axom::mir::clipping::visit::numClipShapesWdg, - axom::mir::clipping::visit::startClipShapesWdg, - axom::mir::clipping::visit::clipShapesWdg, - axom::mir::clipping::visit::clipShapesWdgSize); - } - else if(shape == ST_HEX) + if(m_clipTables[index].size() == 0) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesHex, - axom::mir::clipping::visit::numClipShapesHex, - axom::mir::clipping::visit::startClipShapesHex, - axom::mir::clipping::visit::clipShapesHex, - axom::mir::clipping::visit::clipShapesHexSize); + if(shape == ST_TRI) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTri, + axom::mir::clipping::visit::numClipShapesTri, + axom::mir::clipping::visit::startClipShapesTri, + axom::mir::clipping::visit::clipShapesTri, + axom::mir::clipping::visit::clipShapesTriSize); + } + else if(shape == ST_QUA) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesQua, + axom::mir::clipping::visit::numClipShapesQua, + axom::mir::clipping::visit::startClipShapesQua, + axom::mir::clipping::visit::clipShapesQua, + axom::mir::clipping::visit::clipShapesQuaSize); + } + else if(shape == ST_TET) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTet, + axom::mir::clipping::visit::numClipShapesTet, + axom::mir::clipping::visit::startClipShapesTet, + axom::mir::clipping::visit::clipShapesTet, + axom::mir::clipping::visit::clipShapesTetSize); + } + else if(shape == ST_PYR) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesPyr, + axom::mir::clipping::visit::numClipShapesPyr, + axom::mir::clipping::visit::startClipShapesPyr, + axom::mir::clipping::visit::clipShapesPyr, + axom::mir::clipping::visit::clipShapesTetSize); + } + else if(shape == ST_WDG) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesWdg, + axom::mir::clipping::visit::numClipShapesWdg, + axom::mir::clipping::visit::startClipShapesWdg, + axom::mir::clipping::visit::clipShapesWdg, + axom::mir::clipping::visit::clipShapesWdgSize); + } + else if(shape == ST_HEX) + { + m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesHex, + axom::mir::clipping::visit::numClipShapesHex, + axom::mir::clipping::visit::startClipShapesHex, + axom::mir::clipping::visit::clipShapesHex, + axom::mir::clipping::visit::clipShapesHexSize); + } } } - ClipTable m_clipTables[ST_MAX - ST_MIN]; + ClipTable m_clipTables[ST_MAX - ST_MIN]; }; } // end namespace clipping diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index e54b89f9a5..232f328103 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -31,7 +31,7 @@ class StructuredTopologyView using IndexType = IndexT; using LogicalIndexType = axom::StackArray; //typename StructuredIndexing::LogicalIndex; - constexpr static int dimensions() { return NDIMS; } + constexpr static int dimension() { return NDIMS; } /** * \brief Constructor diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index fd656c0d84..4e1228b9e1 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -167,6 +167,13 @@ class UnstructuredTopologyPolyhedralView return m_data.m_element_sizes.size(); } + /** + * \brief Return the dimension of the shape. + * + * \return The dimension of the shape. + */ + static constexpr int dimension() { return 3; } + template void for_all_zones(FuncType &&func) const { diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 9bbedfb101..6661bd6299 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -53,6 +53,13 @@ class UnstructuredTopologySingleShapeView assert(m_offsets.size() == m_sizes.size()); } + /** + * \brief Return the dimension of the shape. + * + * \return The dimension of the shape. + */ + static constexpr int dimension() { return ShapeT::dimension(); } + /** * \brief Return the number of zones. * From c553f308f7095e8d7aefc1743ad2fb7375231000 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 2 Jul 2024 18:27:41 -0700 Subject: [PATCH 075/290] starting to add some matset view code to mir algorithm --- src/axom/mir/EquiZAlgorithm.cpp | 49 +++--- src/axom/mir/EquiZAlgorithm.hpp | 8 +- src/axom/mir/MIRAlgorithm.cpp | 42 +++++- src/axom/mir/MIRAlgorithm.hpp | 18 ++- src/axom/mir/blueprint_utilities.hpp | 183 +++++++++++++++++++++++ src/axom/mir/views/dispatch_material.hpp | 2 + 6 files changed, 274 insertions(+), 28 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index f399a1e9ad..4f02c2f818 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -8,6 +8,7 @@ #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/dispatch_topology.hpp" +#include "axom/mir/views/dispatch_material.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/clipping/ClipTableManager.hpp" @@ -50,33 +51,35 @@ namespace mir { void EquiZAlgorithm::execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset) + const conduit::Node &coordset, + const conduit::Node &matset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset, + conduit::Node &new_matset) { #if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) switch(m_execPolicy) { #if defined(AXOM_USE_OPENMP) case RuntimePolicy::omp: - executeImpl(topo, coordset, options, new_topo, new_coordset); + executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; #endif #if defined(AXOM_USE_CUDA) case RuntimePolicy::cuda: - executeImpl(topo, coordset, options, new_topo, new_coordset); + executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; #endif #if defined(AXOM_USE_HIP) case RuntimePolicy::hip: - executeImpl(topo, coordset, options, new_topo, new_coordset); + executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; #endif default: // Falls through case RuntimePolicy::seq: - executeImpl(topo, coordset, options, new_topo, new_coordset); + executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; } #endif @@ -85,10 +88,14 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, template void EquiZAlgorithm::executeImpl(const conduit::Node &topo, const conduit::Node &coordset, + const conduit::Node &matset, const conduit::Node &options, conduit::Node &new_topo, - conduit::Node &new_coordset) + conduit::Node &new_coordset, + conduit::Node &new_matset) { + // TODO: migrate data for topo, coordset to appropriate memory space if needed. + if(options.has_path("zones")) { const conduit::Node &n_zones = options.fetch_existing("zones"); @@ -102,13 +109,16 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, { views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { - // Create the clipping tables for the topo dimension. - axom::mir::clipping::ClipTableManager clipManager; - clipManager.load(topoView.dimension()); - - topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - + views::dispatch_material(matset, [&](auto &matsetView) + { + // Create the clipping tables for the topo dimension. + axom::mir::clipping::ClipTableManager clipManager; + clipManager.load(topoView.dimension()); + + topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + + }); }); }); }); @@ -121,9 +131,12 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, { views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { - topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { + views::dispatch_material(matset, [&](auto &matsetView) + { + topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + }); }); }); }); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 8c5d2637d6..f1f2ab9955 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -43,18 +43,22 @@ class EquiZAlgorithm : public MIRAlgorithm /// Implement the EquiZ algorithm on a single domain. virtual void execute(const conduit::Node &topo, const conduit::Node &coordset, + const conduit::Node &matset, const conduit::Node &options, conduit::Node &new_topo, - conduit::Node &new_coordset) override; + conduit::Node &new_coordset, + conduit::Node &new_matset) override; /// Implement the EquiZ algorithm on a single domain for a given ExecSpace. template void executeImpl(const conduit::Node &topo, const conduit::Node &coordset, + const conduit::Node &matset, const conduit::Node &options, conduit::Node &new_topo, - conduit::Node &new_coordset); + conduit::Node &new_coordset, + conduit::Node &new_matset); RuntimePolicy m_execPolicy{RuntimePolicy::seq}; }; diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index 1e9c51f2d2..7612d63ad7 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -30,16 +30,19 @@ void MIRAlgorithm::execute(const conduit::Node &root, const std::string topoName = topologyName(dom, options); const std::string newTopoName = newTopologyName(dom, options); const std::string newCSName = newCoordsetName(dom, options); + const std::string newMatName = newMatsetName(dom, options); conduit::Node &newDomain = output.append(); const conduit::Node &topologies = dom.fetch_existing("topologies"); const conduit::Node &topo = topologies.fetch_existing(topoName); const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node &mset = matset(dom, options); conduit::Node &newTopo = newDomain["topologies/" + newTopoName]; conduit::Node &newCoordset = newDomain["coordsets/" + newCSName]; + conduit::Node &newMatset = newDomain["matsets/" + newMatName]; copyState(dom, newDomain); - execute(topo, *cset, options, newTopo, newCoordset); + execute(topo, *cset, mset, options, newTopo, newCoordset, newMatset); } } else if(domains.size() > 0) @@ -50,15 +53,18 @@ void MIRAlgorithm::execute(const conduit::Node &root, const std::string topoName = topologyName(dom, options); const std::string newTopoName = newTopologyName(dom, options); const std::string newCSName = newCoordsetName(dom, options); + const std::string newMatName = newMatsetName(dom, options); const conduit::Node &topologies = dom.fetch_existing("topologies"); const conduit::Node &topo = topologies.fetch_existing(topoName); const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node &mset = matset(root, options); conduit::Node &newTopo = output["topologies/" + newTopoName]; conduit::Node &newCoordset = output["coordsets/" + newCSName]; + conduit::Node &newMatset = output["matsets/" + newMatName]; copyState(dom, output); - execute(topo, *cset, options, newTopo, newCoordset); + execute(topo, *cset, mset, options, newTopo, newCoordset, newMatset); } } @@ -111,6 +117,31 @@ MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, const conduit::Node &op return csetName; } +std::string +MIRAlgorithm::matsetName(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::string matName; + if(options.has_path("matset")) + matName = options.fetch_existing("matset").as_string(); + else if(mesh.has_path("matsets")) + { + const conduit::Node &matsets = mesh.fetch_existing("matsets"); + matName = matsets[0].name(); + } + return matName; +} + +std::string +MIRAlgorithm::newMatsetName(const conduit::Node &mesh, const conduit::Node &options) const +{ + std::string matName; + if(options.has_path("new_matset")) + matName = options.fetch_existing("new_matset").as_string(); + else + matName = matsetName(mesh, options); + return matName; +} + const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, const conduit::Node &options) const { const std::string topoName = topologyName(mesh, options); @@ -120,7 +151,8 @@ const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, const con const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, const conduit::Node &options) const { - const std::string topoName = topologyName(mesh, options); + const std::string matName = matsetName(mesh, options); +#if 0 const conduit::Node &matsets = mesh.fetch_existing("matsets"); for(conduit::index_t i = 0; i < matsets.number_of_children(); i++) { @@ -131,6 +163,10 @@ const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, const condu // We did not find one. TODO: throw exception. // return first to eliminate compiler warning. return matsets[0]; +#else + const conduit::Node &matsets = mesh.fetch_existing("matsets"); + return matsets.fetch_existing(matName); +#endif } std::vector diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index 7bf3d0923d..b8c7d033c7 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -42,8 +42,10 @@ class MIRAlgorithm options: topology: main + matset: matset new_topology: mirtopo new_coordset: mircoords + new_matset: cleanmat fields: - temperature - pressure @@ -53,6 +55,7 @@ class MIRAlgorithm The "topology" option specifies which topology we'll reconstruct. It must have an associated matset. "new_topology" is the name of the topology that will be created in the output node. "new_coordset" is the name of the new coordset that will be created in the output node. If it is not provided then the name of the topology's coordset will be used. + "new_matset" is the name of the new matset that will be created in the output node. If it is not provided then the name of the topology's matset will be used. "fields" is the name of the fields to map to the new topology. If fields is specified but empty, no fields will be mapped. If fields is not present then all fields will be mapped. "zones" is a list of zone indices from the topology that need to be reconstructed. If not present then all zones will be considered. "mapping" indicates whether we should include an original_element_numbers field on the new topology to indicate where each new zone came from in the original topology. @@ -78,15 +81,22 @@ class MIRAlgorithm */ virtual void execute(const conduit::Node &topo, const conduit::Node &coordset, + const conduit::Node &matset, const conduit::Node &options, conduit::Node &new_topo, - conduit::Node &new_coordset) = 0; + conduit::Node &new_coordset, + conduit::Node &new_matset) = 0; // Utility methods for derived types. void copyState(const conduit::Node &mesh, conduit::Node &destMesh) const; std::string topologyName(const conduit::Node &mesh, const conduit::Node &options) const; std::string newTopologyName(const conduit::Node &mesh, const conduit::Node &options) const; + std::string newCoordsetName(const conduit::Node &mesh, const conduit::Node &options) const; + + std::string matsetName(const conduit::Node &mesh, const conduit::Node &options) const; + std::string newMatsetName(const conduit::Node &mesh, const conduit::Node &options) const; + std::vector fieldNames(const conduit::Node &mesh, const conduit::Node &options) const; const conduit::Node &topology(const conduit::Node &input, const conduit::Node &options) const; @@ -282,7 +292,7 @@ void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_alloca } } #endif - +#if 0 class ElviraMIRAlgorithm : public MIRAlgorithm { public: @@ -312,9 +322,7 @@ class ElviraMIRAlgorithm : public MIRAlgorithm RuntimePolicy m_execPolicy{RuntimePolicy::seq}; }; - -// class EquiZMIRAlgorithm : public MIRAlgorithml - +#endif } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index a0471473e6..8b47856645 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -83,6 +83,189 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const } } +#if 0 +/** + \brief This method slices a Conduit field using a node that contains index value + and stores the data in another node. The data in the nodes are assumed to + be in the memory space suitable for the ExecSpace. + + \tparam ExecSpace The execution space. + + \param[in] field The Blueprint field to be sliced. + \param[in] indices A Conduit node that contains an array of index values into the field. + \param[out] output A node that contains the new sliced field. + + */ +template +void +sliceField(const conduit::Node &field, const conduit::Node &indices, conduit::Node &output) +{ + // Start making an output field. + output["topology"] = field["topology"].as_string(); + output["association"] = field["association"].as_string(); + conduit::Node &output_values = output["values"]; + + const conduit::Node &field_values = field["values"]; + const int allocatorID = axom::execution_space::allocatorID(); + const axom::IndexType = indices.dtype().number_of_elements(); + + // Slice the field using the indices and store values in the output field. + if(field_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) + { + const conduit::Node &componentNode = field_values[i]; + const std::string componentName(componentNode.name()); + + // Allocate memory for the output view using the allocator for the ExecSpace. + conduit::Node &destNode = output_values[componentName]; + destNode.set_allocator(allocatorID); + destNode.set(conduit::DataType(componentNode.dtype().id(), numIndices)); + + views::IndexNode_to_ArrayView(indices, [&](auto indicesView) + { + views::Node_to_ArrayView(componentNode, destNode, [&](auto fieldValuesView, auto destView) + { + axom::for_all( + numIndices, + AXOM_LAMBDA(axom::IndexType i) { + destView[i] = fieldValuesView[indicesView[i]]; + }); + }); + }); + } + } + else + { + // Allocate memory for the output view using the allocator for the ExecSpace. + output_values.set_allocator(allocatorID); + output_values.set(conduit::DataType(field_values.dtype().id(), numIndices)); + + IndexNodeToArrayView(indices, [&](auto indicesView) + { + NodeToArrayView(field_values, output_values, [&](auto fieldValuesView, auto destView) + { + axom::for_all( + numIndices, + AXOM_LAMBDA(axom::IndexType i) { + destView[i] = fieldValuesView[indicesView[i]]; + }); + }); + }); + } +} + +template +void +blendField(const conduit::Node &field, const conduit::Node &indices, const conduit::Node &weights, const conduit::Node &sizes, const conduit::Node &offsets, conduit::Node &output) +{ + // Start making an output field. + output["topology"] = field["topology"].as_string(); + output["association"] = "element"; + conduit::Node &output_values = output["values"]; + + const conduit::Node &field_values = field["values"]; + const auto n_sizes = static_cast(sizes.dtype().number_of_elements()); + int execSpaceAllocatorID = axom::execution_space::allocatorID(); + + // Slice the field using the indices and store values in the output field. + if(field_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) + { + const conduit::Node &componentNode = field_values[i]; + const std::string componentName(componentNode.name()); + + // Allocate memory for the output view using the allocator for the ExecSpace. + conduit::Node &destNode = output_values[componentName]; + destNode.set_allocator(execSpaceAllocatorID); + destNode.set(conduit::DataType(componentNode.dtype().id(), n_sizes)); + + IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) + { + NodeToArrayView(componentNode, weights, destNode, [&](auto fieldValuesView, auto weightsView, auto destView) + { + axom::for_all( + n_sizes, + AXOM_LAMBDA(axom::IndexType i) { + const auto offset = offsetsView[i]; + const auto size = sizesView[i]; + destView[i] = 0; + for(int j = 0; j < size; j++) + { + const auto idx = indicesView[offset + j]; + destView[i] += fieldValuesView[idx] * weightsView[idx]; + } + }); + }); + }); + } + } + else + { + // Allocate memory for the output view using the allocator for the ExecSpace. + output_values.set_allocator(execSpaceAllocatorID); + output_values.set(conduit::DataType(field_values.dtype().id(), n_sizes)); + + IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) + { + NodeToArrayView(field_values, weights, output_values, [&](auto fieldValuesView, auto weightsView, auto destView) + { + axom::for_all( + n_sizes, + AXOM_LAMBDA(axom::IndexType i) { + const auto offset = offsetsView[i]; + const auto size = sizesView[i]; + destView[i] = 0; + for(int j = 0; j < size; j++) + { + const auto idx = indicesView[offset + j]; + destView[i] += fieldValuesView[idx] * weightsView[idx]; + } + }); + }); + }); + } +} + +void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_allocator) +{ + if(src.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < src.number_of_children() + { + conduit_move(src[i], dest[src[i].name()], allocator); + } + } + else + { + if(src.dtype().number_of_elements() > 1) + { + // Allocate the node's memory in the right place. + dest.reset(); + dest.set_allocator(allocator); + dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); + + // Copy the data to the destination node. Axom uses Umpire to manage that. + if(src.is_compact()) + axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); + else + { + // NOTE: this assumes that src is on the host. Why would we have strided data on device? + conduit::Node tmp; + src.compact_to(tmp); + axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); + } + } + else + { + // The node data fits in the node. It's on the host. + dest.set(src); + } + } +} +#endif + } // end namespace blueprint } // end namespace utilities } // end namespace mir diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp index c65beb6e62..f445ee4bc0 100644 --- a/src/axom/mir/views/dispatch_material.hpp +++ b/src/axom/mir/views/dispatch_material.hpp @@ -48,6 +48,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) }); }); } +#if 0 else if(conduit::blueprint::mesh::matset::is_multi_buffer(matset)) { const conduit::Node &volume_fractions = matset.fetch_existing("volume_fractions"); @@ -138,6 +139,7 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) }); }); } +#endif } } // end namespace views From 32486b7961113b93c08cb44b3edab9c938b35404 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 3 Jul 2024 18:46:14 -0700 Subject: [PATCH 076/290] refactoring --- src/axom/core/StackArray.hpp | 8 + src/axom/mir/EquiZAlgorithm.cpp | 145 +++++++++++++- src/axom/mir/MIRAlgorithm.hpp | 185 ------------------ src/axom/mir/NodeToZoneRelationBuilder.hpp | 62 +++--- src/axom/mir/blueprint_utilities.hpp | 29 ++- src/axom/mir/clipping/ClipTableManager.hpp | 20 +- src/axom/mir/utilities.hpp | 7 +- src/axom/mir/views/MaterialView.hpp | 49 ++--- .../mir/views/RectilinearCoordsetView.hpp | 50 ++--- src/axom/mir/views/Shapes.hpp | 49 ++++- .../mir/views/StridedStructuredIndexing.hpp | 92 ++++++++- src/axom/mir/views/StructuredIndexing.hpp | 60 +++++- src/axom/mir/views/StructuredTopologyView.hpp | 149 ++++++++------ src/axom/mir/views/UniformCoordsetView.hpp | 28 ++- .../views/dispatch_rectilinear_topology.hpp | 25 +-- .../views/dispatch_structured_topology.hpp | 116 +++++++++-- .../mir/views/dispatch_uniform_topology.hpp | 27 ++- 17 files changed, 675 insertions(+), 426 deletions(-) diff --git a/src/axom/core/StackArray.hpp b/src/axom/core/StackArray.hpp index e638e9ca69..cae02871f1 100644 --- a/src/axom/core/StackArray.hpp +++ b/src/axom/core/StackArray.hpp @@ -27,6 +27,14 @@ namespace axom template struct StackArray { + using value_type = T; + + /*! + * \brief Return size of the array. + */ + AXOM_HOST_DEVICE + constexpr static int size() { return N; } + /*! * \brief Accessor, returns a reference to the value at the given index. * diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 4f02c2f818..d0733cbb70 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -11,6 +11,7 @@ #include "axom/mir/views/dispatch_material.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/clipping/ClipTableManager.hpp" +#include "axom/mir/NodeToZoneRelationBuilder.hpp" #include @@ -85,6 +86,96 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, #endif } +#if 0 +// I might want to do the real work here in a more targeted class. This way, a host code could choose to instantiate a single +template +class EquiZAlgorithm +{ +public: + void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &matset, + const conduit::Node &options, + const TopologyView &topoView, // I'd rather just pass the views to the method. + const CoordsetView &coordsetView, + const MatsetView &matsetView); +}; + +/// Provide overloads of initialize_topology_view for every possible topology type. +template +void initialize_topology_view(const std::conduit::Node &topo, StructuredTopologyView> &topoView) +{ + // Initialize the view from the Conduit node. +} + +template +void +EquiZAlgorithm::execute( + const TopologyView &topoView, + const CoordsetView &coordsetView, + const MatsetView &matsetView, + const conduit::Node &options) +{ + if(options.has_path("zones")) + { + const conduit::Node &n_zones = options.fetch_existing("zones"); + +/// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? + + // Operate on a list of zones. + views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) + { + MaterialInformation matinfo = materials(matset); + for(const auto &mat : matinfo) + { + const auto matID = mat.number; + + // Going this way, the relation builder should take in the topoView to do its work. + axom::mir::utilities::NodeToZoneRelationBuilder nz; + nz.execute(topoView); + + const auto relZonesView = nz.zones().view(); + const auto relSizesView = nz.sizes().view(); + const auto relOffsetsView = nz.offsets().view(); + + // Create the clipping tables for the topo dimension. + axom::mir::clipping::ClipTableManager clipManager; + clipManager.load(topoView.dimension()); + + // We need to get views for the various shape types. + axom::StackArray + + topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + + }); + } + }); + } + else + { + // Operate on all zones. + + topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + + }); + } +} + + +// I'm starting to want to just run EquiZ on a certain execution space with a certain input mesh rather than all of them... +// +// EquiZAlgorithm>> mir; +// mir.execute(topo, coordset, matset, options, new_topo, new_coordset, new_matset); +// +// But what about fields?? +// +// mir.execute(mesh, options, output); +// mir.execute(mesh, options, output["topologies/topo"], output["coordset/newcoords"], output["matsets/newmatset"]); +// mir.execute(mesh, options, output["topologies/topo"], output["coordset/newcoords"], output["matsets/newmatset"], output["fields"]); +#endif + template void EquiZAlgorithm::executeImpl(const conduit::Node &topo, const conduit::Node &coordset, @@ -95,7 +186,19 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, conduit::Node &new_matset) { // TODO: migrate data for topo, coordset to appropriate memory space if needed. - +#if 0 + views::dispatch_coordset(coordset, [&](auto &coordsetView) + { + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + { + views::dispatch_material(matset, [&](auto &matsetView) + { + EquiZAlgorithm mir; + mir.execute(topoView, coordsetView, matsetView, options); + }); + }); + }); +#else if(options.has_path("zones")) { const conduit::Node &n_zones = options.fetch_existing("zones"); @@ -105,9 +208,9 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, // Operate on a list of zones. views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) { - views::dispatch_coordset(coordset, [&](auto &coordsetView) + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + views::dispatch_coordset(coordset, [&](auto &coordsetView) { views::dispatch_material(matset, [&](auto &matsetView) { @@ -115,13 +218,34 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, axom::mir::clipping::ClipTableManager clipManager; clipManager.load(topoView.dimension()); - topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - - }); - }); - }); - }); + const auto matinfo = views::materials(matset); + for(const auto &mat : matinfo) + { + const auto matID = mat.number; + + axom::mir::utilities::NodeToZoneRelationBuilder nz; + conduit::Node rel; + nz.execute(topo, rel); + + views::IndexNode_to_ArrayView_same(rel["zones"], rel["sizes"], rel["offsets"], [&](auto relZonesView, auto relSizesView, auto relOffsetsView) + { +#if 1 + // We need to get views for the various shape types. + using TableView = typename axom::mir::clipping::ClipTableManager::Table::View; + axom::StackArray tables; + tables[ST_TET] = clipManager[ST_TET].view(); +#endif + + topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + + }); + }); + } + + }); // dispatch_matset + }); // dispatch_coordset + }); // dispatch_topology }); } else @@ -142,6 +266,7 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, }); } +#endif } } // namespace mir diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index b8c7d033c7..600ed7e54a 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -107,191 +107,6 @@ class MIRAlgorithm // TODO: method for mapping vertex field to new topo }; -#if 0 -// Find a home for this stuff. - - -/** - \brief This method slices a Conduit field using a node that contains index value - and stores the data in another node. The data in the nodes are assumed to - be in the memory space suitable for the ExecSpace. - - \tparam ExecSpace The execution space. - - \param[in] field The Blueprint field to be sliced. - \param[in] indices A Conduit node that contains an array of index values into the field. - \param[out] output A node that contains the new sliced field. - - */ -template -void -sliceField(const conduit::Node &field, const conduit::Node &indices, conduit::Node &output) -{ - // Start making an output field. - output["topology"] = field["topology"].as_string(); - output["association"] = "element"; - conduit::Node &output_values = output["values"]; - - const conduit::Node &field_values = field["values"]; - const auto n_indices = static_cast(indices.dtype().number_of_elements()); - int execSpaceAllocatorID = axom::execution_space::allocatorID(); - - // Slice the field using the indices and store values in the output field. - if(field_values.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) - { - const conduit::Node &componentNode = field_values[i]; - const std::string componentName(componentNode.name()); - - // Allocate memory for the output view using the allocator for the ExecSpace. - conduit::Node &destNode = output_values[componentName]; - destNode.set_allocator(execSpaceAllocatorID); - destNode.set(conduit::DataType(componentNode.dtype().id(), n_indices)); - - IndexNodeToArrayView(indices, [&](auto indicesView) - { - NodeToArrayView(componentNode, destNode, [&](auto fieldValuesView, auto destView) - { - axom::for_all( - n_indices, - AXOM_LAMBDA(axom::IndexType i) { - destView[i] = fieldValuesView[indicesView[i]]; - }); - }); - }); - } - } - else - { - // Allocate memory for the output view using the allocator for the ExecSpace. - output_values.set_allocator(execSpaceAllocatorID); - output_values.set(conduit::DataType(field_values.dtype().id(), n_indices)); - - IndexNodeToArrayView(indices, [&](auto indicesView) - { - NodeToArrayView(field_values, output_values, [&](auto fieldValuesView, auto destView) - { - axom::for_all( - n_indices, - AXOM_LAMBDA(axom::IndexType i) { - destView[i] = fieldValuesView[indicesView[i]]; - }); - }); - }); - } -} - -template -void -blendField(const conduit::Node &field, const conduit::Node &indices, const conduit::Node &weights, const conduit::Node &sizes, const conduit::Node &offsets, conduit::Node &output) -{ - // Start making an output field. - output["topology"] = field["topology"].as_string(); - output["association"] = "element"; - conduit::Node &output_values = output["values"]; - - const conduit::Node &field_values = field["values"]; - const auto n_sizes = static_cast(sizes.dtype().number_of_elements()); - int execSpaceAllocatorID = axom::execution_space::allocatorID(); - - // Slice the field using the indices and store values in the output field. - if(field_values.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) - { - const conduit::Node &componentNode = field_values[i]; - const std::string componentName(componentNode.name()); - - // Allocate memory for the output view using the allocator for the ExecSpace. - conduit::Node &destNode = output_values[componentName]; - destNode.set_allocator(execSpaceAllocatorID); - destNode.set(conduit::DataType(componentNode.dtype().id(), n_sizes)); - - IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) - { - NodeToArrayView(componentNode, weights, destNode, [&](auto fieldValuesView, auto weightsView, auto destView) - { - axom::for_all( - n_sizes, - AXOM_LAMBDA(axom::IndexType i) { - const auto offset = offsetsView[i]; - const auto size = sizesView[i]; - destView[i] = 0; - for(int j = 0; j < size; j++) - { - const auto idx = indicesView[offset + j]; - destView[i] += fieldValuesView[idx] * weightsView[idx]; - } - }); - }); - }); - } - } - else - { - // Allocate memory for the output view using the allocator for the ExecSpace. - output_values.set_allocator(execSpaceAllocatorID); - output_values.set(conduit::DataType(field_values.dtype().id(), n_sizes)); - - IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) - { - NodeToArrayView(field_values, weights, output_values, [&](auto fieldValuesView, auto weightsView, auto destView) - { - axom::for_all( - n_sizes, - AXOM_LAMBDA(axom::IndexType i) { - const auto offset = offsetsView[i]; - const auto size = sizesView[i]; - destView[i] = 0; - for(int j = 0; j < size; j++) - { - const auto idx = indicesView[offset + j]; - destView[i] += fieldValuesView[idx] * weightsView[idx]; - } - }); - }); - }); - } -} - -void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_allocator) -{ - if(src.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < src.number_of_children() - { - conduit_move(src[i], dest[src[i].name()], allocator); - } - } - else - { - if(src.dtype().number_of_elements() > 1) - { - // Allocate the node's memory in the right place. - dest.reset(); - dest.set_allocator(allocator); - dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); - - // Copy the data to the destination node. Axom uses Umpire to manage that. - if(src.is_compact()) - axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); - else - { - // NOTE: this assumes that src is on the host. Why would we have strided data on device? - conduit::Node tmp; - src.compact_to(tmp); - axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); - } - } - else - { - // The node data fits in the node. It's on the host. - dest.set(src); - } - } -} -#endif #if 0 class ElviraMIRAlgorithm : public MIRAlgorithm { diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index aff19b8ed6..4d786c80d7 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -12,9 +12,12 @@ #include "axom/core/Array.hpp" #include "axom/core/ArrayView.hpp" #include "axom/mir/utilities.hpp" +#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/views/dispatch_unstructured_topology.hpp" #include #include +#include #include @@ -56,14 +59,14 @@ class NodeToZoneRelationBuilder }; +template template void NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const { assert(nodes_view.size() == zones_view.size()); - using for_policy = axom::execution_space::for_policy; - using reduce_policy = axom::execution_space::reduce_policy; + using loop_policy = typename axom::execution_space::loop_policy; const int allocatorID = axom::execution_space::allocatorID(); // Make a copy of the nodes that we'll use as keys. @@ -76,8 +79,8 @@ NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, }); // Sort the keys, zones in place. This sorts the zones_view which we want for output. - RAJA::sort_pairs(RAJA::make_span(keys_view, n), - RAJA::make_span(zones_view, n)); + RAJA::sort_pairs(RAJA::make_span(keys_view, n), + RAJA::make_span(zones_view, n)); // Make a mask array for where differences occur. axom::Array mask(n, n, allocatorID); @@ -92,9 +95,9 @@ NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, // Do a scan on the mask array to build an offset array. axom::Array dest_offsets(n, n, allocatorID); auto dest_offsets_view = dest_offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view, n), - RAJA::make_span(dest_offsets_view, n), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(mask_view, n), + RAJA::make_span(dest_offsets_view, n), + RAJA::operators::plus{}); // Build the offsets to each node's zone ids. axom::for_all(offsets_view.size(), AXOM_LAMBDA(axom::IndexType i) @@ -114,7 +117,8 @@ template void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) { - using loop_policy = axom::execution_space::loop_policy; + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; const std::string type = topo.fetch_existing("type").as_string(); const auto allocatorID = axom::execution_space::allocatorID(); @@ -125,20 +129,21 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit n_sizes.set_allocator(allocatorID); n_offsets.set_allocator(allocatorID); + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + if(type == "unstructured") { - conduit::blueprint::mesh::utils::Shape shape(topo); + conduit::blueprint::mesh::utils::ShapeType shape(topo); const conduit::Node &n_connectivity = topo["elements/connectivity"]; - const auto intTypeId = n_connectivity.dtype().id() - const auto connSize = n_connectivity.dtype().number_of_elements() + const auto intTypeId = n_connectivity.dtype().id(); + const auto connSize = n_connectivity.dtype().number_of_elements(); // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); const auto nnodes = conduit::blueprint::mesh::utils::coordset::length(*coordset); if(shape.is_polyhedral()) { - dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) + views::dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) { const auto nzones = topoView.numberOfZones(); axom::Array sizes(nzones, nzones, allocatorID); @@ -157,9 +162,9 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit // Do a scan on the size array to build an offset array. axom::Array offsets(nzones, nzones, allocatorID); auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(sizes_view, nzones), - RAJA::make_span(offsets_view, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(sizes_view, nzones), + RAJA::make_span(offsets_view, nzones), + RAJA::operators::plus{}); sizes.clear(); // Allocate Conduit arrays on the device in a data type that matches the connectivity. @@ -186,12 +191,13 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit }); // Make the relation, outputting into the zonesView and offsetsView. - buildRelation(connectivityView, zonesView, offsetsView); + using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); }); }); }); @@ -222,14 +228,15 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit views::IndexNode_to_ArrayView_same(n_connectivity, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make the relation, outputting into the zonesView and offsetsView. - buildRelation(connectivityView, zonesView, offsetsView); + using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) { - sizes_view[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsets_view[i]); + sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); }); - } + }); } else { @@ -250,14 +257,15 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit zonesView[index] = index / nodesPerShape; }); // Make the relation, outputting into the zonesView and offsetsView. - buildRelation(connectivityView, zonesView, offsetsView); +// using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(axom::IndexType index) + axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) { sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); }); - } + }); } } else @@ -265,7 +273,7 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit // These are all structured topos of some sort. Make an unstructured representation and recurse. conduit::Node mesh; - to_unstructured(topo, *coordset, "newtopo", mesh); + axom::mir::utilities::blueprint::to_unstructured(topo, *coordset, "newtopo", mesh); // Recurse using the unstructured mesh. execute(mesh["newtopo"], relation); diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 8b47856645..c714c1fc9d 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -10,7 +10,7 @@ #include "axom/core/Array.hpp" #include "axom/core/ArrayView.hpp" #include "axom/core/memory_management.hpp" -#include "axom/mir/views/dispatch_structured_topologies.hpp" +#include "axom/mir/views/dispatch_structured_topology.hpp" #include #include @@ -46,6 +46,7 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const mesh["coordsets"][coordset.name()].set_external(coordset); conduit::Node &newtopo = mesh["topologies"][topoName]; + newtopo["coordset"] = coordset.name(); if(type == "unstructured") { @@ -55,12 +56,14 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const { newtopo["type"] = "unstructured"; conduit::Node &n_newconn = newtopo["elements/connectivity"]; + conduit::Node &n_newsizes = newtopo["elements/sizes"]; + conduit::Node &n_newoffsets = newtopo["elements/offsets"]; n_newconn.set_allocator(allocatorID); + n_newsizes.set_allocator(allocatorID); + n_newoffsets.set_allocator(allocatorID); - // Fill in the connectivity. views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) { - const auto nzones = topoView.numberOfZones(); int ptsPerZone = 2; if(shape == "quad") ptsPerZone = 4; @@ -69,15 +72,27 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const newtopo["elements/shape"] = shape; + // Allocate new mesh data. + const auto nzones = topoView.numberOfZones(); const auto connSize = nzones * ptsPerZone; n_newconn.set(conduit::DataType::index_t(connSize)); - axom::ArrayView conn(reinterpret_cast(n_newconn.data_ptr()), connSize); - auto conn_view = conn.view(); + n_newsizes.set(conduit::DataType::index_t(nzones)); + n_newoffsets.set(conduit::DataType::index_t(nzones)); + + // Make views for the mesh data. + axom::ArrayView connView(reinterpret_cast(n_newconn.data_ptr()), connSize); + axom::ArrayView sizesView(reinterpret_cast(n_newsizes.data_ptr()), nzones); + axom::ArrayView offsetsView(reinterpret_cast(n_newoffsets.data_ptr()), nzones); + + // Fill in the new connectivity. topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < 4; i++) - conn_view[start + i] = static_cast(zone.getIds()[i]); + for(int i = 0; i < ptsPerZone; i++) + connView[start + i] = static_cast(zone.getIds()[i]); + + sizesView[zoneIndex] = ptsPerZone; + offsetsView[zoneIndex] = start; }); }); } diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 28d1f7ff77..b521c0bf7d 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -115,11 +115,11 @@ struct ClipTable : public ClipTableBase, axom::Array; using Uint8ContainerType = axom::Array; - using IntViewType = axom::ArrayView; - using Uint8ViewType = axom::ArrayView; + using IntViewType = axom::ArrayView; + using Uint8ViewType = axom::ArrayView; using SuperClass = ClipTableBase; - using ClipTableView = ClipTableBase; + using View = ClipTableBase; /** * \brief Load clipping data into the arrays, moving data as needed. @@ -150,12 +150,12 @@ struct ClipTable : public ClipTableBase, axom::Array class ClipTableManager { public: + using Table = ClipTable; + /** * \brief Constructor */ @@ -184,7 +186,7 @@ class ClipTableManager * * \return A reference to the clipping table. */ - const ClipTable &operator[](size_t shape) + const Table &operator[](size_t shape) { const auto index = shapeToIndex(shape); assert(shape < ST_MAX); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 13edc8ae63..b559e2411e 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -42,14 +42,13 @@ namespace utilities template void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) { - using DataType = typename KeyViewType::value_type; - using for_policy = axom::execution_space::for_policy; - using reduce_policy = axom::execution_space::reduce_policy; + using for_policy = typename axom::execution_space::for_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; const int allocatorID = axom::execution_space::allocatorID(); // Make a copy of the keys and make original indices. const auto n = keys_orig_view.size(); - axom::Array keys(n, n, allocatorID); + axom::Array keys(n, n, allocatorID); axom::Array indices(n, n, allocatorID); auto keys_view = keys.view(); auto indices_view = indices.view(); diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index b502f711f0..95475ff0cc 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -27,46 +27,27 @@ namespace views * * \note This would only be used on the host. */ -class MaterialInformation +struct Material { -public: - using StringIntMap = std::map; - using IntStringMap = std::map; - using IntVector = std::vector; - using StringVector = std::vector; + int number; + std::string name; +}; + +using MaterialInformation = std::vector; - void set(const conduit::Node &matset) +MaterialInformation materials(const conduit::Node &matset) +{ + MaterialInformation info; + if(matset.has_child("material_map")) { - if(matset.has_child("material_map")) + const conduit::Node &mm = matset["material_map"]; + for(conduit::index_t i = 0; i < mm.number_of_children(); i++) { - m_namesToIds.clear(); - m_idsToNames.clear(); - m_ids.clear(); - m_names.clear(); - - const conduit::Node &mm = matset["material_map"]; - for(conduit::index_t i = 0; i < mm.number_of_children(); i++) - { - const auto id = mm[i].to_int32(); - m_namesToIds[mm[i].name()] = id; - m_idsToNames[id] = mm[i].name(); - m_ids.push_back(id); - m_names.push_back(mm[i].name()); - } + info.push_back(Material{static_cast(i), mm[i].name()}); } } - - const StringIntMap &getNamesToIds() const { return m_namesToIds; } - const IntStringMap &getIdsToNames() const { return m_idsToNames; } - const IntVector &getIds() const { return m_ids; } - const StringVector &getNames() const { return m_names; } - -private: - StringIntMap m_namesToIds{}; - IntStringMap m_idsToNames{}; - IntVector m_ids{}; - StringVector m_names{}; -}; + return info; +} //--------------------------------------------------------------------------- // Material views - These objects are meant to wrap Blueprint Matsets behind diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index 7916a95e87..341f8fc8e1 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -28,7 +28,7 @@ template class RectilinearCoordsetView2 { public: - using LogicalIndexType = axom::StackArray; + using LogicalIndex = axom::StackArray; using IndexType = axom::IndexType; using value_type = DataType; using PointType = axom::primal::Point; @@ -41,10 +41,8 @@ class RectilinearCoordsetView2 */ AXOM_HOST_DEVICE RectilinearCoordsetView2(const axom::ArrayView &x, - const axom::ArrayView &y) : m_coordinates{x, y} + const axom::ArrayView &y) : m_coordinates{x, y}, m_indexing(LogicalIndex{{x.size(), y.size()}}) { - m_shape.m_dimensions[0] = m_coordinates[0].size(); - m_shape.m_dimensions[1] = m_coordinates[1].size(); } /** @@ -55,7 +53,7 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE IndexType size() const { - return m_shape.size(); + return m_indexing.size(); } /** @@ -66,7 +64,7 @@ class RectilinearCoordsetView2 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType getPoint(LogicalIndexType vertex_index) const + PointType getPoint(LogicalIndex vertex_index) const { return PointType(m_coordinates[0][vertex_index[0]], m_coordinates[1][vertex_index[1]]); @@ -82,7 +80,7 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); + return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } /** @@ -94,7 +92,7 @@ class RectilinearCoordsetView2 */ AXOM_HOST_DEVICE PointType - operator[](LogicalIndexType vertex_index) const + operator[](LogicalIndex vertex_index) const { return getPoint(vertex_index); } @@ -110,12 +108,12 @@ class RectilinearCoordsetView2 PointType operator[](IndexType vertex_index) const { - return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); + return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } private: axom::ArrayView m_coordinates[2]; - StructuredIndexing m_shape; + StructuredIndexing m_indexing; }; /** @@ -125,7 +123,7 @@ template class RectilinearCoordsetView3 { public: - using LogicalIndexType = axom::StackArray; + using LogicalIndex = axom::StackArray; using IndexType = axom::IndexType; using value_type = DataType; using PointType = axom::primal::Point; @@ -140,23 +138,29 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE RectilinearCoordsetView3(const axom::ArrayView &x, const axom::ArrayView &y, - const axom::ArrayView &z) : m_coordinates{x, y, z}, m_shape() + const axom::ArrayView &z) : m_coordinates{x, y, z}, m_indexing(LogicalIndex{{x.size(), y.size(), z.size()}}) { - m_shape.m_dimensions[0] = m_coordinates[0].size(); - m_shape.m_dimensions[1] = m_coordinates[1].size(); - m_shape.m_dimensions[2] = m_coordinates[2].size(); } /** - * \brief Return the number of points in the coordset. + * \brief Return the number of nodes in the coordset. * - * \return The number of points in the coordset. + * \return The number of nodes in the coordset. */ + /// @{ + AXOM_HOST_DEVICE IndexType size() const { - return m_shape.size(); + return m_indexing.size(); + } + + AXOM_HOST_DEVICE + IndexType numberOfNodes() const + { + return m_indexing.size(); } + /// @} /** * \brief Return the requested point from the coordset. @@ -166,7 +170,7 @@ class RectilinearCoordsetView3 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType getPoint(LogicalIndexType vertex_index) const + PointType getPoint(LogicalIndex vertex_index) const { return PointType(m_coordinates[0][vertex_index[0]], m_coordinates[1][vertex_index[1]], @@ -183,7 +187,7 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); + return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } /** @@ -195,7 +199,7 @@ class RectilinearCoordsetView3 */ AXOM_HOST_DEVICE PointType - operator[](LogicalIndexType vertex_index) const + operator[](LogicalIndex vertex_index) const { return getPoint(vertex_index); } @@ -211,12 +215,12 @@ class RectilinearCoordsetView3 PointType operator[](IndexType vertex_index) const { - return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); + return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } private: axom::ArrayView m_coordinates[3]; - StructuredIndexing m_shape; + StructuredIndexing m_indexing; }; } // end namespace views diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index e23b26f219..5d68d0ab12 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -30,19 +30,46 @@ namespace views { // TODO: PointTraits -// TODO: LineTraits /* - 3*-----------* 2 - | | - | | - | | - | | - | | + 0*-----------* 1 */ template +struct LineTraits +{ + using IndexType = IndexT; + + AXOM_HOST_DEVICE constexpr static int id() { return 1 << 2; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 1; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 2; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 2; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 2; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + + AXOM_HOST_DEVICE constexpr static IndexType faces[][2] = {{0, 1}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}}; +}; + +/* + 2* + |\ + | \ + | \ + | \ + | \ + 0*-----* 1 + + */ +template struct TriTraits { using IndexType = IndexT; @@ -61,8 +88,8 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}}; + AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0, 1, 2}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,0}}; }; /* @@ -370,6 +397,9 @@ struct Shape : public ShapeTraits }; // Make some concrete shape classes based on the shape traits. +template +using LineShape = Shape>; + template using TriShape = Shape>; @@ -387,7 +417,6 @@ using WedgeShape = Shape>; template using HexShape = Shape>; -//struct HexShape : public Shape> {}; } // end namespace views } // end namespace mir diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index 0da5c0f02a..103c3284e4 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -17,14 +17,17 @@ namespace views { /** - * \brief This class encapsulates a structured mesh size and contains methods to - * help with indexing into it. + * \accelerated + * \class StridedStructuredIndexing + * + * \brief This class encapsulates data for strided structured indexing and provides methods for creating/manipulating indices. * * \tparam NDIMS The number of dimensions. */ template -struct StridedStructuredIndexing +class StridedStructuredIndexing { +public: using IndexType = IndexT; using LogicalIndex = axom::StackArray; @@ -32,8 +35,6 @@ struct StridedStructuredIndexing /** * \brief constructor - * - * \param dims The dimensions we're indexing. */ AXOM_HOST_DEVICE StridedStructuredIndexing() : m_dimensions(), m_offsets(), m_strides() @@ -46,8 +47,15 @@ struct StridedStructuredIndexing } } + /** + * \brief Constructor + * + * \param dims The number of zones in each logical dimension. + * \param offsets The offset of the first zone from the mesh origin in each logical dimension. + * \param strides The amount to stride when moving to the next element for each logical dimension. + */ AXOM_HOST_DEVICE - StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offset, const LogicalIndex &stride) : m_dimensions(dims), m_offsets(offset), m_strides(stride) + StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offsets, const LogicalIndex &strides) : m_dimensions(dims), m_offsets(offsets), m_strides(strides) { } @@ -65,6 +73,14 @@ struct StridedStructuredIndexing return sz; } + /** + * \brief Return the logical dimensions. + * + * \return The logical dimensions. + */ + AXOM_HOST_DEVICE + const LogicalIndex &logicalDimensions() const { return m_dimensions; } + /** * \brief Return the j stride. * @@ -179,6 +195,7 @@ struct StridedStructuredIndexing * * \return True if the logical index is within the index, false otherwise. */ + AXOM_HOST_DEVICE bool contains(const LogicalIndex &logical) const { bool retval = true; @@ -196,11 +213,74 @@ struct StridedStructuredIndexing * * \return True if the index is within the index, false otherwise. */ + AXOM_HOST_DEVICE bool contains(const IndexType index) const { return contains(IndexToLogicalIndex(index)); } + /** + * \brief Expand the current StridedStructuredIndexing by one in each dimension. + * + * \return An expanded StridedStructuredIndexing. + */ + AXOM_HOST_DEVICE + StridedStructuredIndexing expand() const + { + StridedStructuredIndexing retval(*this); + for(int i = 0; i < dimensions(); i++) + retval.m_dimensions[i]++; + + return retval; + } + + /** + * \brief Expand the current StridedStructuredIndexing by one in each dimension. + * + * \return An expanded StridedStructuredIndexing. + */ + /// @{ + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 1, StridedStructuredIndexing>::type + expand() const + { + StridedStructuredIndexing retval(*this); + retval.m_dimensions[0]++; + return retval; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, StridedStructuredIndexing>::type + expand() const + { + StridedStructuredIndexing retval(*this); + retval.m_dimensions[0]++; + retval.m_dimensions[1]++; + retval.m_strides[1]++; + return retval; + } + + template + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 3, StridedStructuredIndexing>::type + expand() const + { + StridedStructuredIndexing retval(*this); + retval.m_dimensions[0]++; + retval.m_dimensions[1]++; + retval.m_dimensions[2]++; + const auto nx = retval.m_strides[1]; + const auto ny = retval.m_strides[2] / nx; + retval.m_strides[1] = nx + 1; + retval.m_strides[2] = (ny + 1) * (nx + 1); + return retval; + } + + /// @} + +private: LogicalIndex m_dimensions{}; LogicalIndex m_offsets{}; LogicalIndex m_strides{}; diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index 79979147f8..a6bc0b6e24 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -24,10 +24,11 @@ namespace views * \tparam NDIMS The number of dimensions. */ template -struct StructuredIndexing +class StructuredIndexing { +public: using IndexType = IndexT; - using LogicalIndex = axom::StackArray; + using LogicalIndex = axom::StackArray; AXOM_HOST_DEVICE constexpr static int dimensions() { return NDIMS; } @@ -60,6 +61,14 @@ struct StructuredIndexing return sz; } + /** + * \brief Return the logical dimensions. + * + * \return The logical dimensions. + */ + AXOM_HOST_DEVICE + const LogicalIndex &logicalDimensions() const { return m_dimensions; } + /** * \brief Return the j stride. * @@ -167,7 +176,52 @@ struct StructuredIndexing /// @} - LogicalIndex m_dimensions{1}; + /** + * \brief Determines whether the indexing contains the supplied logical index. + * + * \param logical The logical index being tested. + * + * \return True if the logical index is within the index, false otherwise. + */ + AXOM_HOST_DEVICE + bool contains(const LogicalIndex &logical) const + { + bool retval = true; + for(int i = 0; i < dimensions(); i++) + { + retval &= (logical[i] >= 0 && logical[i] < m_dimensions[i]); + } + return retval; + } + + /** + * \brief Determines whether the indexing contains the supplied index. + * + * \param index The index being tested. + * + * \return True if the index is within the index, false otherwise. + */ + AXOM_HOST_DEVICE + bool contains(const IndexType index) const + { + return contains(IndexToLogicalIndex(index)); + } + + /** + * \brief Expand the current StructuredIndexing by one in each dimension. + * + * \return An expanded StructuredIndexing. + */ + AXOM_HOST_DEVICE + StructuredIndexing expand() const + { + StructuredIndexing retval(*this); + for(int i = 0; i < dimensions(); i++) + retval.m_dimensions[i]++; + return retval; + } +private: + LogicalIndex m_dimensions{}; }; } // end namespace views diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 232f328103..076a48e4bf 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -7,7 +7,6 @@ #define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ #include "axom/mir/views/Shapes.hpp" -#include "axom/mir/views/StructuredIndexing.hpp" namespace axom { @@ -16,30 +15,34 @@ namespace mir namespace views { -// NOTE: we could subclass this one and make a strided structured view that lets one define zones where the zones do not span all of the nodes. - /** - * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. + * \brief This class provides a view for Conduit/Blueprint structured grid types. * - * \tparam IndexT The index type that will be used for connectivity, etc. - * \tparam ShapeT The shape type. + * \tparam IndexPolicy The policy for making/using indices. */ -template +template class StructuredTopologyView { public: - using IndexType = IndexT; - using LogicalIndexType = axom::StackArray; //typename StructuredIndexing::LogicalIndex; + using IndexingPolicy = IndexPolicy; + using IndexType = typename IndexingPolicy::IndexType; + using LogicalIndex = typename IndexingPolicy::LogicalIndex; - constexpr static int dimension() { return NDIMS; } + /** + * \brief Return the number of dimensions. + * + * \return The number of dimensions. + */ + AXOM_HOST_DEVICE + constexpr static int dimension() { return IndexingPolicy::dimensions(); } /** * \brief Constructor * - * \param conn The mesh connectivity. + * \param indexing The indexing policy for the topology (num zones in each dimension). */ AXOM_HOST_DEVICE - StructuredTopologyView(const LogicalIndexType &dims) : m_shape(dims) + StructuredTopologyView(const IndexingPolicy &indexing) : m_indexing(indexing) { } @@ -51,7 +54,7 @@ class StructuredTopologyView AXOM_HOST_DEVICE IndexType size() const { - return m_shape.size(); + return m_indexing.size(); } /** @@ -66,7 +69,7 @@ class StructuredTopologyView * * \return The mesh logical dimensions. */ - const LogicalIndexType &logicalDimensions() const { return m_shape; } + const LogicalIndex &logicalDimensions() const { return m_indexing.logicalDimensions(); } /** * \brief Execute a function for each zone in the mesh using axom::for_all. @@ -82,21 +85,23 @@ class StructuredTopologyView { const auto nzones = numberOfZones(); - if constexpr (NDIMS == 3) + // Q: Should we make a for_all() that iterates over multiple ranges? + // Q: Should the logical index be passed to the lambda? + + if constexpr (IndexingPolicy::dimensions() == 3) { - const StructuredIndexing zoneShape{m_shape}, - nodeShape{{m_shape.m_dimensions[0] + 1, - m_shape.m_dimensions[1] + 1, - m_shape.m_dimensions[2] + 1}}; - const auto jp = nodeShape.jStride(); - const auto kp = nodeShape.kStride(); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); + + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { using ShapeType = HexShape; - const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[8]; - data[0] = nodeShape.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -109,23 +114,20 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr (NDIMS == 2) + else if constexpr (IndexingPolicy::dimensions() == 2) { - // Q: Should we make a for_all() that iterates over multiple ranges? - // Q: Should the logical index be passed to the lambda? + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); - const StructuredIndexing zoneShape{m_shape}, - nodeShape{{m_shape.m_dimensions[0] + 1, - m_shape.m_dimensions[1] + 1}}; - const auto jp = nodeShape.jStride(); + const auto jp = nodeIndexing.jStride(); axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { using ShapeType = QuadShape; - const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[4]; - data[0] = nodeShape.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -134,7 +136,24 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - // TODO: NDIMS == 1 + else if constexpr (IndexingPolicy::dimensions() == 1) + { + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); + + axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + { + using ShapeType = LineShape; + + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + IndexType data[2]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + + const ShapeType shape(axom::ArrayView(data, 2)); + func(zoneIndex, shape); + }); + } } /** @@ -152,22 +171,24 @@ class StructuredTopologyView const auto nSelectedZones = selectedIdsView.size(); ViewType idsView(selectedIdsView); - if constexpr (NDIMS == 3) + // Q: Should we make a for_all() that iterates over multiple ranges? + // Q: Should the logical index be passed to the lambda? + + if constexpr (IndexingPolicy::dimensions() == 3) { - const StructuredIndexing zoneShape{m_shape}, - nodeShape{{m_shape.m_dimensions[0] + 1, - m_shape.m_dimensions[1] + 1, - m_shape.m_dimensions[2] + 1}}; - const auto jp = nodeShape.jStride(); - const auto kp = nodeShape.kStride(); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); + + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { using ShapeType = HexShape; const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[8]; - data[0] = nodeShape.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -180,24 +201,20 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr (NDIMS == 2) + else if constexpr (IndexingPolicy::dimensions() == 2) { - // Q: Should we make a for_all() that iterates over multiple ranges? - // Q: Should the logical index be passed to the lambda? + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); - const StructuredIndexing zoneShape{m_shape}, - nodeShape{{m_shape.m_dimensions[0] + 1, - m_shape.m_dimensions[1] + 1}}; - const auto jp = nodeShape.jStride(); - - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + const auto jp = nodeIndexing.jStride(); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { using ShapeType = QuadShape; const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneShape.IndexToLogicalIndex(zoneIndex); + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[4]; - data[0] = nodeShape.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -206,11 +223,29 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - // TODO: NDIMS == 1 + else if constexpr (IndexingPolicy::dimensions() == 1) + { + const IndexingPolicy zoneIndexing = m_indexing; + const IndexingPolicy nodeIndexing = m_indexing.expand(); + + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) + { + using ShapeType = LineShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + IndexType data[2]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + + const ShapeType shape(axom::ArrayView(data, 2)); + func(zoneIndex, shape); + }); + } } private: - StructuredIndexing m_shape; + IndexingPolicy m_indexing; }; } // end namespace views diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index fb457821a6..c31061c72a 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -29,8 +29,8 @@ template class UniformCoordsetView { public: - using LogicalIndexType = typename StructuredIndexing::LogicalIndex; - using ExtentsType = axom::StackArray; + using LogicalIndex = axom::StackArray; + using ExtentsType = axom::StackArray;; using IndexType = axom::IndexType; using value_type = DataType; using PointType = axom::primal::Point; @@ -43,9 +43,9 @@ class UniformCoordsetView * \param spacing The spacing inbetween points. */ AXOM_HOST_DEVICE - UniformCoordsetView(const LogicalIndexType dims, - const ExtentsType origin, - const ExtentsType spacing) : m_shape{dims}, m_origin{origin}, m_spacing{spacing} + UniformCoordsetView(const LogicalIndex &dims, + const ExtentsType &origin, + const ExtentsType &spacing) : m_indexing(dims), m_origin(origin), m_spacing(spacing) { } @@ -54,12 +54,20 @@ class UniformCoordsetView * * \return The number of points in the coordset. */ + /// @{ AXOM_HOST_DEVICE IndexType size() const { - return m_shape.size(); + return m_indexing.size(); } + AXOM_HOST_DEVICE + IndexType numberOfNodes() const + { + return m_indexing.size(); + } + /// @} + /** * \brief Return the requested point from the coordset. * @@ -68,7 +76,7 @@ class UniformCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType getPoint(const LogicalIndexType &vertex_index) const + PointType getPoint(const LogicalIndex &vertex_index) const { PointType pt; for(int i = 0; i < NDIMS; i++) @@ -85,7 +93,7 @@ class UniformCoordsetView */ AXOM_HOST_DEVICE PointType - operator[](const LogicalIndexType &vertex_index) const + operator[](const LogicalIndex &vertex_index) const { return getPoint(vertex_index); } @@ -101,10 +109,10 @@ class UniformCoordsetView PointType operator[](IndexType vertex_index) const { - return getPoint(m_shape.IndexToLogicalIndex(vertex_index)); + return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } - StructuredIndexing m_shape; + StructuredIndexing m_indexing; ExtentsType m_origin; ExtentsType m_spacing; }; diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index 40545e453d..d3deea1f0b 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_DISPATCH_RECTILINEAR_TOPOLOGY_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include #include @@ -37,11 +38,11 @@ void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo) case 3: if constexpr (dimension_selected(SelectedDimensions, 3)) { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - dims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + zoneDims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("hex"); func(shape, topoView); } @@ -49,10 +50,10 @@ void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo) case 2: if constexpr (dimension_selected(SelectedDimensions, 2)) { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - dims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("quad"); func(shape, topoView); } @@ -60,9 +61,9 @@ void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo) case 1: if constexpr (dimension_selected(SelectedDimensions, 1)) { - axom::StackArray dims; - dims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements(); - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("line"); func(shape, topoView); } diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index c2af5f4262..fc1adc844f 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -7,6 +7,8 @@ #define AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" +#include "axom/mir/views/StridedStructuredIndexing.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/views/dispatch_uniform_topology.hpp" #include "axom/mir/views/dispatch_rectilinear_topology.hpp" @@ -20,6 +22,35 @@ namespace mir namespace views { +/** + * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. + * + * \tparam ArrayType The array type to use. + * + * \param n The conduit node that contains the named array, if it exists. + * \param key The name of the node. + * \param[out] The array to be filled. + * \param fillValue the value to use if the array is not found. + */ +template +bool +fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr, int fillValue) +{ + bool found = false; + if((found = n.has_path(key)) == true) + { + const auto acc = n.fetch_existing(key).as_int_accessor(); + for(int i = 0; i < arr.size(); i++) + arr[i] = acc[i]; + } + else + { + for(int i = 0; i < arr.size(); i++) + arr[i] = fillValue; + } + return found; +} + /** * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. * @@ -39,39 +70,94 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) int ndims = 1; ndims += topo.has_path("elements/dims/j") ? 1 : 0; ndims += topo.has_path("elements/dims/k") ? 1 : 0; + static const std::string offsetsKey("elements/offsets"); + static const std::string stridesKey("elements/strides"); + switch(ndims) { case 3: if constexpr (dimension_selected(SelectedDimensions, 3)) { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - dims[2] = topo.fetch_existing("elements/dims/k").as_int(); - views::StructuredTopologyView topoView(dims); const std::string shape("hex"); - func(shape, topoView); + axom::StackArray zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); + + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + axom::StackArray offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + strides[2] = zoneDims[0] * zoneDims[1]; + } + + views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } + else + { + views::StructuredIndexing zoneIndexing(zoneDims); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } } break; case 2: if constexpr (dimension_selected(SelectedDimensions, 2)) { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - dims[1] = topo.fetch_existing("elements/dims/j").as_int(); - views::StructuredTopologyView topoView(dims); const std::string shape("quad"); - func(shape, topoView); + axom::StackArray zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + axom::StackArray offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + } + + views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } + else + { + views::StructuredIndexing zoneIndexing(zoneDims); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } } break; case 1: if constexpr (dimension_selected(SelectedDimensions, 1)) { - axom::StackArray dims; - dims[0] = topo.fetch_existing("elements/dims/i").as_int(); - views::StructuredTopologyView topoView(dims); const std::string shape("line"); - func(shape, topoView); + axom::StackArray zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + axom::StackArray offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + fillFromNode(topo, stridesKey, strides, 1); + + views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } + else + { + views::StructuredIndexing zoneIndexing(zoneDims); + views::StructuredTopologyView> topoView(zoneIndexing); + func(shape, topoView); + } } } } diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index c17012cdbc..c34eea2302 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -30,18 +30,17 @@ template void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) { const conduit::Node &n_dims = coordset["dims"]; - const conduit::index_t ndims = n_dims.dtype().number_of_elements(); - switch(ndims) + switch(n_dims.dtype().number_of_elements()) { default: case 3: if constexpr (dimension_selected(SelectedDimensions, 3)) { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - dims[2] = n_dims.as_int_accessor()[2]; - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + zoneDims[1] = n_dims.as_int_accessor()[1] - 1; + zoneDims[2] = n_dims.as_int_accessor()[2] - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("hex"); func(shape, topoView); } @@ -49,10 +48,10 @@ void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), co case 2: if constexpr (dimension_selected(SelectedDimensions, 2)) { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - dims[1] = n_dims.as_int_accessor()[1]; - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + zoneDims[1] = n_dims.as_int_accessor()[1] - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("quad"); func(shape, topoView); } @@ -60,9 +59,9 @@ void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), co case 1: if constexpr (dimension_selected(SelectedDimensions, 3)) { - axom::StackArray dims; - dims[0] = n_dims.as_int_accessor()[0]; - views::StructuredTopologyView topoView(dims); + axom::StackArray zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + views::StructuredTopologyView> topoView(zoneDims); const std::string shape("line"); func(shape, topoView); } From 911aa6c916a3c9ef19d47f5442909fc57caa8379 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 10 Jul 2024 18:02:22 -0700 Subject: [PATCH 077/290] progress adapting clipfield --- src/axom/mir/EquiZAlgorithm.cpp | 994 +++++++++++++++++++++++++++++++- 1 file changed, 982 insertions(+), 12 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index d0733cbb70..cc8862f850 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -87,20 +87,990 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, } #if 0 +AXOM_HOST_DEVICE +template +size_t clip_case(const ZoneType &zone, const ArrayViewType &view) +{ + size_t clipcase = 0; + for(size_t i = 0; i < zone.numberOfNodes(); i++) + { + const auto id = zone.getId(i); + if(view[id] > 0) + { + clipcase |= (1 << j); + } + } + return clipcase; +} + +//------------------------------------------------------------------------------ +template +struct zone2index { static constexpr size_t st_index = 0; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_TRI; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_QUA; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_TET; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_PYR; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_WDG; }; + +template <> +struct zone2index { static constexpr size_t st_index = ST_HEX; }; + +//------------------------------------------------------------------------------ +template +constexpr size_t indexOfZoneType() { return 0; } + +template <> +constexpr size_t indexOfZoneType() { return 0; } + +template <> +constexpr size_t indexOfZoneType() { return 1; } + +template <> +constexpr size_t indexOfZoneType() { return 2; } + +template <> +constexpr size_t indexOfZoneType() { return 3; } + +template <> +constexpr size_t indexOfZoneType() { return 4; } + +template <> +constexpr size_t indexOfZoneType() { return 5; } + +//------------------------------------------------------------------------------ +template +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT8_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT16_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT32_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT64_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT8_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT16_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT32_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT64_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::FLOAT32_ID; }; + +template <> +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::FLOAT64_ID; }; + + +//------------------------------------------------------------------------------ +AXOM_HOST_DEVICE +constexpr int getClipTableIndex(int dimension, int nnodes) +{ + return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); +} + +template +AXOM_HOST_DEVICE +constexpr bool bitIsSet(FlagType flags, BitType bit) +{ + return (flags & (1 << bit)) > 0; +} + +template +AXOM_HOST_DEVICE +constexpr void setBit(FlagType &flags, BitType bit) +{ + flags |= (1 << bit); +} + +template +AXOM_HOST_DEVICE +int countBits(IntType value) +{ + constexpr int n = sizeof(IntType) * 8; + int count = 0, mask = 1; + for(int i = 0; i < n; i++) + count += ((value & mask) > 0) ? 1 : 0; + return count; +} + +AXOM_HOST_DEVICE +constexpr bool color0Selected(int selection) +{ + return bitIsSet(selection, 0); +} + +AXOM_HOST_DEVICE +constexpr bool color1Selected(int selection) +{ + return bitIsSet(selection, 1); +} + +AXOM_HOST_DEVICE +constexpr bool generatedPointIsSelected(unsigned char color, int selection) +{ + return color == NOCOLOR || + (color0Selected(selection) && color == COLOR0) || + (color1Selected(selection) && color == COLOR1); +} + +AXOM_HOST_DEVICE +constexpr bool shapeIsSelected(unsigned char color, int selection) +{ + return (color0Selected(selection) && color == COLOR0) || + (color1Selected(selection) && color == COLOR1); +} + +AXOM_HOST_DEVICE +float computeT(float d0, float d1) +{ + const float delta = d1 - d0; + const float abs_delta = (delta < 0) ? -delta : delta; + const float t = (abs_delta != 0.) ? (-d0 / delta) : 0.; + return t; +} + +//------------------------------------------------------------------------- +template +AXOM_HOST_DEVICE +int32 bsearch(T value, const axom::ArrayView &view) +{ + int32 index = -1; + int32 left = 0; + int32 right = view.size() - 1; + while(left <= right) + { + int32 m = (left + right) / 2; + if(view[m] < value) + left = m + 1; + else if(view[m] > value) + right = m - 1; + else + { + index = m; + break; + } + } + + return index; +} + +//------------------------------------------------------------------------------ +/// Based on a Jenkins hash, modified to include length and hash forwards and +/// backwards to make int64 rather than int32. +AXOM_HOST_DEVICE +uint64 hash_bytes(const uint8 *data, uint32 length) +{ + uint32 hash = 0; + + // Build the length into the hash. + const auto ldata = reinterpret_cast(&length); + for(int e = 0; e < 4; e++) + { + hash += ldata[e]; + hash += hash << 10; + hash ^= hash >> 6; + } + + uint32 hashr = hash; + for(uint32 i = 0; i < length; i++) + { + hash += data[i]; + hash += hash << 10; + hash ^= hash >> 6; + + hashr += data[length - 1 - i]; + hashr += hashr << 10; + hashr ^= hashr >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + + hashr += hashr << 3; + hashr ^= hashr >> 11; + hashr += hashr << 15; + + return (static_cast(hash) << 32) | hashr; +} + +//------------------------------------------------------------------------------ +template +uint64 +AXOM_HOST_DEVICE +make_name_1(ValueType id) +{ + return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); +}; + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE +uint64 +make_name_2(ValueType id0, ValueType id1) +{ + ValueType data[2] = {id0, id1}; + if(id1 < id0) + { + data[0] = id1; + data[1] = id0; + } + return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); +}; + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE +uint32 +sort_values(ValueType *v, IndexType n) +{ + for(IndexType i = 0; i < n-1; i++) + { + const IndexType m = n - i - 1; + for(IndexType j = 0; j < m; j++) + { + if(v[j] > v[j+1]) + { + axom::utilities::swap(v[j], v[j+1]); + } + } + } +} + +//------------------------------------------------------------------------- +template +uint64 +AXOM_HOST_DEVICE +make_name_n(const ViewType &view, int32 start, int32 n) +{ + using value_type = typename ViewType::value_type; + + if(n == 2) + return make_name_2(view[start], view[start + 1]); + + value_type v[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // pick largest number of blends. + for(int32 i = 0; i < n; i++) + { + v[i] = view[start + i]; + } + sort_values(v, n); + + return hash_bytes(reinterpret_cast(v), n * sizeof(value_type)); +} + +//------------------------------------------------------------------------------ // I might want to do the real work here in a more targeted class. This way, a host code could choose to instantiate a single -template -class EquiZAlgorithm +template +class ClipField { public: - void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - const TopologyView &topoView, // I'd rather just pass the views to the method. + void execute(const TopologyView &topoView, // I'd rather just pass the views to the method. const CoordsetView &coordsetView, - const MatsetView &matsetView); + const conduit::Node &clipField, + conduit::Node &outputMesh) + { + using int32 = conduit::int32; + using uint32 = conduit::uint32; + using int64 = conduit::int64; + using uint64 = conduit::uint64; + + using KeyType = uint64; + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + const auto allocatorID = axom::execution_space::allocatorID(); + + // Load clip table data and make views. + ClipTableManager clipTables; + clipTables.load(topoView.dimension()); + axom::StackArray::View, 6> clipTableViews; + if(topoView.dimension() == 2) + { + clipTableViews[indexOfZoneType()] = clipTables[ST_TRI].view(); + clipTableViews[indexOfZoneType()] = clipTables[ST_QUA].view(); + } + if(topoView.dimension() == 2) + { + clipTableViews[indexOfZoneType()] = clipTables[ST_TET].view(); + clipTableViews[indexOfZoneType()] = clipTables[ST_PYR].view(); + clipTableViews[indexOfZoneType()] = clipTables[ST_WDG].view(); + clipTableViews[indexOfZoneType()] = clipTables[ST_HEX].view(); + } + + // ---------------------------------------------------------------------- + // + // Stage 1: Iterate over elements and their respective clip cases to + // determine sizes of outputs. + // + // ---------------------------------------------------------------------- + RAJA::ReduceSum fragment_sum(0); + RAJA::ReduceSum fragment_nids_sum(0); + RAJA::ReduceSum blendGroups_sum(0); + RAJA::ReduceSum blendGroupLen_sum(0); + + // Allocate some memory + const auto nzones = topoView.numberOfZones(); + axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. + axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. + axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. + axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. + axom::Array fragmentsSize(nzones, nzones, allocatorID); // The total number of points produced for all fragments in a zone. + auto clipCasesView = clipCases.view(); + auto blendGroupsview = blendGroups.view(); + auto blendGroupsLenView = blendGroupsLen.view(); + auto fragmentsView = fragments.view(); + auto fragmentsSizeView = fragmentsSize.view(); + + views::Node_to_ArrayView(clipField, [&](auto clipFieldView) + { + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // Get the clip case for the current zone. + const auto clipcase = clip_case(zone, clipFieldView); + clipCasesView[zoneIndex] = clipcase; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto &ctView = clipTableViews[clipTableIndex]; + const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); + + int thisBlendGroups = 0; // The number of blend groups produced in this case. + int thisBlendGroupLen = 0; // The total length of the blend groups. + int thisFragments = 0; // The number of zone fragments produced in this case. + int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. + int64 ptused = 0; // A bitset indicating which ST_XX nodes are used. + + for(size_t si = 0; si < nShapes; si++) + { + // Get the si'th shape in the clip case. + const auto caseData = ctView.getShape(clipcase, si); + + if(caseData[0] == ST_PNT) + { + if(generatedPointIsSelected(caseData[2], selection)) + { + const size_t nIds = caseData[3]; + for(size_t ni = 4; ni < nIds; ni++) + { + const auto pid = caseData[ni]; + + // Increase the blend size to include this center point. + if(pid <= P7) + { + // corner point. + thisBlendGroupLen++; + } + else if(pid >= EA && pid <= EL) + { + // edge points are derived from 2 corner points. If + // those appear here then we're probably creating a + // face point. We can store the 2 corner points in place + // of the edge point (along with some blending coeff). + thisBlendGroupLen += 2; + } + } + + // This center or face point counts as a blend group. + thisBlendGroups++; + } + } + else + { + if(shapeShapeSelected(caseData[1], selection)) + { + thisFragments++; + const int nIdsThisShape = caseData.size() - 2; + thisFragmentNumIds += nIdsThisShape; + + // Mark which points were used in this cell. + for(size_t i = 2; i < caseData.size(); i++) + { + setBit(ptused, caseData[i]); + } + } + } + } + + // Count which points in the original cell are used. + for(unsigned char pid = P0; pid <= P7; pid++) + { + const int incr = bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += incr; // {p0} + thisBlendGroups += incr; + } + + // Count edges that are used. + for(unsigned char pid = EA; pid <= EL; pid++) + { + const int incr = bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += 2 * incr; // {p0 p1} + thisBlendGroups += incr; + } + + // Save the results. + blendGroupsView[zoneIndex] = thisBlendGroups; + blendGroupLenView[zoneIndex] = thisBlendGroupLen; + fragmentsView[zoneIndex] = thisFragments; + fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; + + // Sum up the sizes overall. + fragment_sum += thisFragments; + fragment_nids_sum += thisFragmentNumIds; + blendGroups_sum += thisBlendGroups; + blendGroupLen_sum += thisBlendGroupLen; + }); + }); + + +// TODO: change int to topoView::IndexType + + // ---------------------------------------------------------------------- + // + // Stage 2: Do some scans to fill out blendOffset and blendGroupOffsets, + // which is where we fill in the real data. + // + // blendOffset : Starting offset for blending data like blendIds, blendCoeff. + // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. + // fragmentOffsets : Where an element's fragments begin in the output. + // ---------------------------------------------------------------------- + axom::Array blendOffset(nzones, nzones, allocatorID); + axom::Array blendGroupOffsets(nzones, nzones, allocatorID); + axom::Array fragmentOffsets(nzones, nzones, allocatorID); + axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); + + auto blendOffsetView = blendOffset.view(); + auto blendGroupOffsetsView = blendGroupOffsets.view(); + auto fragmentOffsetsView = fragmentOffsets.view(); + + // Make offsets via scan. + RAJA::exclusive_scan(RAJA::make_span(blendGroupLenView, nzones), + RAJA::make_span(blendOffsetView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), + RAJA::make_span(blendGroupOffsetsView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), + RAJA::make_span(fragmentOffsetsView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), + RAJA::make_span(fragmentSizeOffsetsView, nzones), + RAJA::operators::plus{}); + + // ---------------------------------------------------------------------- + // + // Stage 3: Iterate over the elements/cases again and fill in the blend + // groups that get produced: blendNames, blendGroupSizes, + // blendCoeff, blendIds. These are used to produce the new points. + // + // NOTE: blendGroupStart is a scan of blendGroupSizes. + // + // ---------------------------------------------------------------------- + const auto blendGroupsSize = blendGroups_sum.get(); + const auto blendGroupLenSize = blendGroupLen_sum.get(); + + axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); + axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); + + auto blendNamesView = blendNames.view(); + auto blendGroupSizesView = blendGroupSizes.view(); + auto blendGroupStartView = blendGroupStart.view(); + auto blendIdsView = blendIds.view(); + auto blendCoeffView = blendCoeff.view(); + + views::Node_to_ArrayView(clipField, [&](auto clipFieldView) + { + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // Get the clip case for the current zone. + const auto clipcase = clipCasesView[zoneIndex]; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto &ctView = clipTableViews[clipTableIndex]; + const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); + + int64 ptused = 0; + + // Starting offset of where we store this element's blend groups. + int32 bgStart = blendOffsetView[zoneIndex]; + int32 bgOffset = blendGroupOffsetsView[zoneIndex]; + + for(size_t si = 0; si < nShapes; si++) + { + // Get the si'th shape in the clip case. + const auto caseData = ctView.getShape(clipcase, si); + + if(caseData[0] == ST_PNT) + { + if(generatedPointIsSelected(caseData[2], selection)) + { +// TODO: put the ability to select on color back in... 0, 1, or both + const int nIds = static_cast(caseData[3]); + const auto one_over_n = 1.f / static_cast(nIds); + const auto start = bgStart; + + for(int ni = 0; ni < nIds; ni++) + { + const auto ptid = caseData[4 + ni]; + + // Add the point to the blend group. + if(ptid <= P7) + { + // corner point. + blendIdsView[bgStart] = zone.getId(ptid); + blendCoeffView[bgStart] = one_over_n; + + bgStart++; + } + else if(ptid >= EA && ptid <= EL) + { + // edge points are derived from 2 corner points. If + // those appear here then we're probably creating a + // face point. We can store the 2 corner points in place + // of the edge point (along with some blending coeff). + const auto edgeIndex = ptid - EA; + const auto edge = ZoneType::edges[edgeIndex]; + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = computeT(clipFieldView[id0], clipFieldView[id1]); + + blendIdsView[bgStart] = id0; + blendIdsView[bgStart+1] = id1; + blendCoeffView[bgStart] = one_over_n * (1. - t); + blendCoeffView[bgStart+1] = one_over_n * t; + + bgStart += 2; + } + } + + // Store how many points make up this blend group. Note that the + // size will not necessarily be equal to npts if edges were involved. + int32 nblended = bgStart - blendGroupStartView[bgOffset]; + blendGroupSizesView[bgOffset] = nblended; + + // Store "name" of blend group. + const auto blendName = make_name_n(blendIdsView, start, nblended); + blendNamesView[bgOffset++] = blendName; + } + } + else + { + if(shapeShapeSelected(caseData[1], selection)) + { + // Mark which points were used in this zone. + for(size_t i = 2; i < caseData.size(); i++) + { + setBit(ptused, caseData[i]); + } + } + } + } + + // Add blend group for each original point that was used. + for(unsigned char pid = P0; pid <= P7; pid++) + { + if(bitIsSet(ptused, pid)) + { + // Store blend group info + blendIdsView[bgStart] = zone.getId(pid); + blendCoeffView[bgStart] = 1.; + + // Store how many points make up this blend group. + blendGroupSizesView[bgOffset] = 1; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + blendGroupStartView[bgOffset] = bgStart; + + // Store "name" of blend group. + blendNamesView[bgOffset++] = make_name_1(zone.getId(pid)); + + bgStart++; + } + } + + // Add blend group for each edge point that was used. + for(unsigned char pid = EA; pid <= EL; pid++) + { + if(bitIsSet(ptused, pid)) + { + const auto edgeIndex = pid - EA; + const auto edge = ZoneTypeEdges::edges[edgeIndex]; + const auto id0 = zone.getId(c[0]); + const auto id1 = zone.getId(c[1]); + + // Figure out the blend for edge. + const float t = computeT(clipFieldView[id0], clipfieldView[id1]); + + // Store blend group info + blendIdsView[bgStart] = id0; + blendIdsView[bgStart+1] = id1; + blendCoeffView[bgStart] = (1. - t); + blendCoeffView[bgStart+1] = t; + + // Store how many points make up this blend group. + blendGroupSizesView[bgOffset] = 2; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + blendGroupStartView[bgOffset] = bgStart; + + // Store "name" of blend group. + blendNamesView[bgOffset++] = make_name_2(id0, id1); + + bgStart += 2; + } + } + }); + }); + + // ---------------------------------------------------------------------- + // + // Stage 4 - Make the blend groups unique based on their blendName. + // + // ---------------------------------------------------------------------- + // At this point, we have created the blend group data. We can now use the + // blendNames to make unique blend groups. uNames contains a sorted list of + // the unique blend group names while uIndices is their original index in + // blendNames/blendGroupOffsets/blendGroupSizes. + axom::Array uNames; + axom::Array uIndices; + unique(blendNames, uNames, uIndices); + + auto uNamesView = uNames.view(); + auto uIndicesView = uIndices.view(); + +// I just had this thought like it would be nice to output the volumes... and a mapping of old2new zones +// I suppose with the mapping and the old/new meshes, I could compute the volume fractions. +// Should I pass in a outputTopo, outputCoordset, outputFields nodes? +// Then I don't have to care about the names - it's done outside this code. + + // ---------------------------------------------------------------------- + // + // Stage 5 - Make new connectivity. + // + // ---------------------------------------------------------------------- + conduit::Node &n_topologies = outputMesh["topologies"]; + conduit::Node &n_topo = n_topologies[topoView.name()]; + + const auto finalNumZones = fragment_sum.get(); + const auto finalConnSize = fragment_nids_sum.get(); + + using ConnectivityType = typename topoView::IndexType; + const auto connTypeID = cpp2conduit::type; + + // Allocate connectivity. + conduit::Node &n_conn = n_topo["elements/connectivity"]; + n_conn.set_allocator(allocatorID); + n_conn.set(conduit::DataType(connTypeID, finalConnSize)); + auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); + + // Allocate shapes. + conduit::Node &n_shapes = n_topo["elements/shapes"]; + n_shapes.set_allocator(allocatorID); + n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); + auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + + // Allocate sizes. + conduit::Node &n_sizes = n_topo["elements/sizes"]; + n_sizes.set_allocator(allocatorID); + n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); + auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + + // Allocate offsets. + conduit::Node &n_offsets = n_topo["elements/offsets"]; + n_offsets.set_allocator(allocatorID); + n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); + auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); + + + const int32 uNames_len = uNames.size(); + + RAJA::ReduceBitOr shapesUsed_reduce(0); + + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // If there are no fragments, return from lambda. + if(fragmentsView[zoneIndex] == 0) + return; + + const auto clipcase = clip_case(zone, clipFieldView); + + const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto ctView = clipTableViews[clipTableIndex]; + const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); + + int64 ptused = 0; + + // Iterate over the tet fragments to see which points they use. + // The points correspond to blend groups that we have made in + // the final output. + for(size_t si = 0; si < nShapes; si++) + { + // Get the si'th shape in the clip case. + const auto caseData = ctView.getShape(clipcase, si); + + if(caseData[0] == ST_PNT) + { + // Count the point as used since a blend group would have + // been emitted for it under these conditions. This makes + // sure that we get the ordering for point_2_newdof right + // when a set of shapes happens to not use the blended point. + // That, of course, means that the lut needs to be fixed a bit. + if(generatedPointIsSelected(caseData[2], selection)) + { + setBit(ptused, N0 + caseData[1]); + } + } + else + { + if(shapeShapeSelected(caseData[1], selection)) + { + for(int i = 2; i < caseData.size(); i++) + setBit(ptused, i); + } + } + } + + // Seek to the start of the blend groups for this zone. + const int32 bgStart = blendGroupOffsetsView[zoneIndex]; + + // Go through the points in the order they would have been added as blend + // groups, get their blendName, and then overall index of that blendName + // in uNames, the unique list of new dof names. That will be their index + // in the final points. + ConnectivityType point_2_new[N3 + 1]; + for(unsigned char pid = N0; pid <= N3; pid++) + { + if(bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + for(unsigned char pid = P0; pid <= P7; pid++) + { + if(bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + for(unsigned char pid = EA; pid <= EL; pid++) + { + if(bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + + // This is where the output fragment connectivity start for this zone + int outputIndex = fragmentSizeOffsetsView[zoneIndex]; + // This is where the output fragment sizes/shapes start for this zone. + int sizeIndex = fragmentOffsetsView[zoneIndex]; + uint64 shapesUsed = 0; + for(size_t si = 0; si < nShapes; si++) + { + // Get the si'th shape in the clip case. + const auto caseData = ctView.getShape(clipcase, si); + + if(caseData[0] != ST_PNT) + { + if(shapeIsSelected(caseData[1], selection)) + { + // Output the nodes used in this zone. + for(int i = 2; i < caseData.size(); i++) + connView[outputIndex++] = point_2_new[caseData[i]]; + + const auto nIdsInZone = caseData.size() - 2; + sizesView[sizeIndex] = nIdsInZone; + shapeView[sizeIndex] = zone.id(); + setBit(shapesUsed, zone.id()); + sizeIndex++; + } + } + } + + shapesUsed_reduce |= shapesUsed; + }); + + // Make offsets + RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), + RAJA::make_span(offsetsView, nzones), + RAJA::operators::plus{}); + + // Add shape information + const auto shapesUsed = shapesUsed_reduce.get(); + const auto shapeMap = shapeMap_ValueName(shapesUsed); + if(countBits(shapesUsed) > 1) + { + n_topo["elements/shape"] = "mixed"; + conduit::Node &n_shape_map = n_topo["elements/shape_map"]; + for(const auto &[key, value] : shapeMap) + n_shape_map[key] = value; + } + else + { + n_shapes.reset(); + n_topo["elements"].remove("shapes"); + + n_topo["elements/shape"] = shapeMap.begin()->first; + } + + //----------------------------------------------------------------------- + // STAGE 6 - make originalElements (this will later be optional) + //----------------------------------------------------------------------- + conduit::Node &n_fields = outputMesh["fields"]; + if(n_fields.has_child("originalElements")) + { + // originalElements already exists. We need to map it forward. + conduit::Node &n_orig = n_fields["originalElements"]; + conduit::Node &n_orig_values = n_orig["values"]; + views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) + { + using value_type = typename origValuesView::value_type; + conduit::Node n_values; + n_values.set_allocator(allocatorID); + n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); + + n_orig_values.move(n_values); + }); + } + else + { + // Make a new node and populate originalElement. + conduit::Node &n_orig = n_fields["originalElements"]; + n_orig["association"] = "element"; + n_orig["topology"] = n_topo.name(); + conduit::Node &n_values = n_orig["values"]; + n_values.set_allocator(allocatorID); + n_values.set(conduit::DataType(connTypeID, finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = zoneIndex; + }); + } + + //----------------------------------------------------------------------- + // STAGE 7 - Make new fields. + //----------------------------------------------------------------------- + +//----------------------------------------------------------------------------------- + } // end of execute }; +template +std::map reverse_map(const std::map &m) +{ + std::map output; + for(const auto& [key, value] : m) + { + output[value] = key; + } + return output; +} + +std::map +shapeMap_NameValue(const conduit::Node &n_shape_map) +{ + std::map sm; + for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) + { + sm[n_shape_map[i].name()] = n_shape_map[i].to_int(); + } + return sm; +} + +std::map +shapeMap_ValueName(const conduit::Node &n_shape_map) +{ + std::map sm; + for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) + { + sm[n_shape_map[i].to_int()] = n_shape_map[i].name(); + } + return sm; +} + +std::map +shapeMap_FromFlags(uint64 shapes) +{ + std::map sm; + + if((shapes & LineShape::id()) > 0) + sm["line"] = LineShape::id(); + + if((shapes & TriShape::id()) > 0) + sm["tri"] = TriShape::id(); + + if((shapes & QuadShape::id()) > 0) + sm["quad"] = QuadShape::id(); + + if((shapes & TetShape::id()) > 0) + sm["tet"] = TetShape::id(); + + if((shapes & PyramidShape::id()) > 0) + sm["pyramid"] = PyramidShape::id(); + + if((shapes & WedgeShape::id()) > 0) + sm["wedge"] = WedgeShape::id(); + + if((shapes & HexShape::id()) > 0) + sm["hex"] = HexShape::id(); + + return sm; +} + +template +void +EquiZAlgorithm::execute( + const TopologyView &topoView, + const CoordsetView &coordsetView, + const conduit::Node &options) +{ +} +//---------------------------------------------------------------------------------------- +#endif + +#if 0 /// Provide overloads of initialize_topology_view for every possible topology type. template void initialize_topology_view(const std::conduit::Node &topo, StructuredTopologyView> &topoView) @@ -206,13 +1176,13 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, /// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? // Operate on a list of zones. - views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) + views::dispatch_material(matset, [&](auto &matsetView) { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) + views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) { - views::dispatch_coordset(coordset, [&](auto &coordsetView) + views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) { - views::dispatch_material(matset, [&](auto &matsetView) + views::dispatch_coordset(coordset, [&](auto &coordsetView) { // Create the clipping tables for the topo dimension. axom::mir::clipping::ClipTableManager clipManager; From 74bc4ee737186d8cfcac4f31655b750f8be66751 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:51:49 -0700 Subject: [PATCH 078/290] Moved functions to BitUtilities --- src/axom/core/utilities/BitUtilities.hpp | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index e75b5f23c2..546d367ccc 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -197,6 +197,71 @@ AXOM_HOST_DEVICE inline std::int32_t countl_zero(std::int32_t word) noexcept } // gpu_macros_example_end +/*! + * \brief Determine whether the \a bit is set in \a flags. + * \accelerated + * + * \tparam FlagType The integer type that contains the flags. + * \tparam BitType The index type that stores the bit index. + * + * \param[in] flags The flags whose bit we're testing. + * \param[in] bit The bit we're testing. + * + * \return True if the bit is set; false otherwise. + */ +template +AXOM_HOST_DEVICE +constexpr bool bitIsSet(FlagType flags, BitType bit) +{ + static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3))); + return (flags & (1 << bit)) > 0; +} + +/*! + * \brief Set the bit in flags. + * \accelerated + * + * \tparam FlagType The integer type that contains the flags. + * \tparam BitType The index type that stores the bit index. + * + * \param[inout] flags The flags whose bit we're setting. + * \param[in] bit The bit we're setting. + * \param[in] value The value we're setting into the bit. + */ +template +AXOM_HOST_DEVICE +constexpr void setBit(FlagType &flags, BitType bit, bool value = true) +{ + static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3))); + constexpr auto mask = 1 << bit; + flags = value ? (flags | mask) : (flags & ~mask); +} + +/*! + * \brief Count the number if bits that are on in the value. + * \accelerated + * + * \tparam IntType The integer type being tested. + * + * \param[in] value The value whose bits are counted. + * + * \return + */ +template +AXOM_HOST_DEVICE +int countBits(IntType value) +{ + constexpr size_t n = sizeof(IntType) << 3; + int count = 0; + IntType mask = 1; + for(size_t i = 0; i < n; i++) + { + count += ((value & mask) > 0) ? 1 : 0; + mask <<= 1; + } + return count; +} + } // namespace utilities } // namespace axom From 5b827313a82f779a84d34a57d17fe3a462d1b53e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:52:18 -0700 Subject: [PATCH 079/290] Added some operators to Point. --- src/axom/primal/geometry/Point.hpp | 126 ++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/axom/primal/geometry/Point.hpp b/src/axom/primal/geometry/Point.hpp index 6c33efec5b..7af5749dce 100644 --- a/src/axom/primal/geometry/Point.hpp +++ b/src/axom/primal/geometry/Point.hpp @@ -113,7 +113,7 @@ class Point * \return d the dimension of the point. * \post d >= 1. */ - static int dimension() { return NDIMS; }; + constexpr static int dimension() { return NDIMS; }; /// \name Overloaded [] operator methods ///@{ @@ -179,6 +179,54 @@ class Point return !(lhs == rhs); } + /*! + * \brief Add a point to the current point and return the result. + * \param[in] obj The point that will be added to the current point. + * \return The sum of the points. + */ + AXOM_HOST_DEVICE + Point operator + (const Point &obj) const; + + /*! + * \brief Subtract a point from the current point and return the result. + * \param[in] obj The point that will be subtracted from the current point. + * \return The sum of the points. + */ + AXOM_HOST_DEVICE + Point operator - (const Point &obj) const; + + /*! + * \brief Scale the current point by a scale factor and return the result. + * \param[in] scale The scale factor. + * \return The scaled point. + */ + AXOM_HOST_DEVICE + Point operator * (const T scale) const; + + /*! + * \brief Scale the current point by dividing by a scale factor and return the result. + * \param[in] divisor The divisor. + * \return The scaled point. + */ + AXOM_HOST_DEVICE + Point operator / (const T divisor) const; + + /*! + * \brief Add a point to the current point, changing its value. + * \param[in] obj The point that will be added to the current point. + * \return The sum of the points. + */ + AXOM_HOST_DEVICE + Point operator += (const Point &obj); + + /*! + * \brief Add a point to the current point, changing its value. + * \param[in] obj The point that will be added to the current point. + * \return The sum of the points. + */ + AXOM_HOST_DEVICE + Point operator += (const Point &obj); + /*! * \brief Simple formatted print of a point instance * \param os The output stream to write to @@ -320,6 +368,82 @@ std::ostream& Point::print(std::ostream& os) const return os; } +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator + (const Point& obj) const +{ + PointType res; + for(int i = 0; i < NDIMS; ++i) + { + res[i] = m_components[i] + obj[i]; + } + return res; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator - (const Point& obj) const +{ + PointType res; + for(int i = 0; i < NDIMS; ++i) + { + res[i] = m_components[i] - obj[i]; + } + return res; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator * (const T scale) const +{ + PointType res; + for(int i = 0; i < NDIMS; ++i) + { + res[i] = m_components[i] * scale; + } + return res; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator / (const T divisor) const +{ + PointType res; + for(int i = 0; i < NDIMS; ++i) + { + res[i] = m_components[i] / divisor; + } + return res; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator += (const Point& obj) +{ + for(int i = 0; i < NDIMS; ++i) + { + m_components[i] += obj[i]; + } + return *this; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE inline Point +Point::operator -= (const Point& obj) +{ + for(int i = 0; i < NDIMS; ++i) + { + m_components[i] -= obj[i]; + } + return *this; +} + //------------------------------------------------------------------------------ /// Free functions implementing Point's operators //------------------------------------------------------------------------------ From 9f3fe77deaf837f49e5a8c08b07465d624d8b37c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:52:49 -0700 Subject: [PATCH 080/290] Changed some edges --- src/axom/mir/views/Shapes.hpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 5d68d0ab12..67e7f8405c 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -184,11 +184,11 @@ struct PolygonShape / | \ face 2: 1,2,3 / | \ face 3: 2,0,3 0*----|----* 2 - \ | / edge 0: 0,2 - \ | / edge 1: 2,1 - \ | / edge 2: 1,0 - \|/ edge 3: 1,3 - * edge 4: 3,0 + \ | / edge 0: 0,1 + \ | / edge 1: 1,2 + \ | / edge 2: 2,0 + \|/ edge 3: 0,3 + * edge 4: 1,3 1 edge 5: 2,3 */ @@ -212,7 +212,7 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,2}, {2,1}, {1,0}, {1,3}, {3,0}, {2,3}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0, 1}, {1, 2}, {2, 0},{0, 3}, {1, 3}, {2, 3}}; }; /* @@ -337,7 +337,7 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = { {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,3}, {3,2}, {2,1}, {1,0}, {1,5}, {5,4}, {4,0}, {2,6}, {6,5}, {3,7}, {7,6}, {7,4}}; + AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0, 1}, {1, 2}, {3, 2}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; }; /** @@ -356,6 +356,13 @@ struct Shape : public ShapeTraits assert(m_ids.size() == ShapeTraits::numberOfNodes()); } + /** + * \brief Get a specific id that makes up this shape. + * + * \return The i'th id that makes up this shape. + */ + AXOM_HOST_DEVICE IndexType getId(size_t index) const { return m_ids[index]; } + /** * \brief Get the ids that make up this shape. * From 1959dd7b147056a404c2f734395349a66a93544f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:53:14 -0700 Subject: [PATCH 081/290] accumulation_traits --- src/axom/mir/utilities.hpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index b559e2411e..d91886fb03 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -26,6 +26,27 @@ namespace mir namespace utilities { +//------------------------------------------------------------------------------ +/** + * \brief This class and its specializations provide a type trait that lets us + * determine the type that should be used to accumulate values when we + * do floating point math. + * + * \note this belongs in algorithm utilities, maybe core. + */ +template +struct accumulation_traits { using type = float; }; + +template <> +struct accumulation_traits { using type = double; }; + +template <> +struct accumulation_traits { using type = double; }; + +template <> +struct accumulation_traits { using type = double; }; + +//------------------------------------------------------------------------------ /** * \brief This function makes a unique array of values from an input list of keys. * From bdc5833cc22179a8c8360ce866c28fe6c52db669 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:54:03 -0700 Subject: [PATCH 082/290] cpp2conduit --- src/axom/mir/blueprint_utilities.hpp | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index c714c1fc9d..bf90724cef 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -24,6 +24,44 @@ namespace utilities namespace blueprint { +//------------------------------------------------------------------------------ +/** + * \brief This class provides a couple of type traits that let us map C++ types + * to types / values useful in Conduit. + */ +template +struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; }; + +template <> +struct cpp2conduit { using type = conduit::int8; static constexpr conduit::index_t id = conduit::DataType::INT8_ID; }; + +template <> +struct cpp2conduit { using type = conduit::int16; static constexpr conduit::index_t id = conduit::DataType::INT16_ID; }; + +template <> +struct cpp2conduit { using type = conduit::int32; static constexpr conduit::index_t id = conduit::DataType::INT32_ID; }; + +template <> +struct cpp2conduit { using type = conduit::int64; static constexpr conduit::index_t id = conduit::DataType::INT64_ID; }; + +template <> +struct cpp2conduit { using type = conduit::uint8; static constexpr conduit::index_t id = conduit::DataType::UINT8_ID; }; + +template <> +struct cpp2conduit { using type = conduit::uint16; static constexpr conduit::index_t id = conduit::DataType::UINT16_ID; }; + +template <> +struct cpp2conduit { using type = conduit::uint32; static constexpr conduit::index_t id = conduit::DataType::UINT32_ID; }; + +template <> +struct cpp2conduit { using type = conduit::uint64; static constexpr conduit::index_t id = conduit::DataType::UINT64_ID; }; + +template <> +struct cpp2conduit { using type = conduit::float32; static constexpr conduit::index_t id = conduit::DataType::FLOAT32_ID; }; + +template <> +struct cpp2conduit { using type = conduit::float64; static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; }; + // TODO: Add in a routine to migrate a Conduit node to a new memory space. // copy(const conduit::Node &src, conduit::Node &dest); From 426d97c2b18cfca8b3f432f89e01cbe764372ef8 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 11 Jul 2024 17:54:25 -0700 Subject: [PATCH 083/290] blender/slicer classes --- src/axom/mir/CoordsetBlender.hpp | 109 ++++++++++++++++++++++++++++ src/axom/mir/FieldBlender.hpp | 121 +++++++++++++++++++++++++++++++ src/axom/mir/FieldSlicer.hpp | 101 ++++++++++++++++++++++++++ 3 files changed, 331 insertions(+) create mode 100644 src/axom/mir/CoordsetBlender.hpp create mode 100644 src/axom/mir/FieldBlender.hpp create mode 100644 src/axom/mir/FieldSlicer.hpp diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp new file mode 100644 index 0000000000..e14740580f --- /dev/null +++ b/src/axom/mir/CoordsetBlender.hpp @@ -0,0 +1,109 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_COORDSET_BLENDER_HPP_ +#define AXOM_MIR_COORDSET_BLENDER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/FieldBlender.hpp" // for BlendData +#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit +#include "axom/primal/Point.hpp" + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \accelerated + * \class FieldBlender + * + * \tparam ExecSpace The execution space for the algorithm. + * \tparam CSVType The coordset view type. + * + * \brief This class uses BlendData to generate a new blended field from an input coordset. + * The output coordset will be explicit. + */ +template +class CoordsetBlender +{ +public: + using CoordsetViewType = CSVType; + + /** + * \brief Create a new blended field from the \a n_input field and place it in \a n_output. + * + * \param blend The BlendData that will be used to make the new coordset. + * \param n_input The input coordset that we're blending. + * \param n_output The output node that will contain the new coordset. + * + * \note The coordset view must agree with the coordset in n_input. We pass both + * a view and the coordset node since the view may not be able to contain + * come coordset metadata and remain trivially copyable. + */ + void execute(const BlendData &blend, const CoordsetViewType &view, const conduit::Node &n_input, conduit::Node &n_output) const + { + using value_type = typename decltype(view)::value_type; + using PointType = typename decltype(view)::PointType; + + const auto allocatorID = axom::execution_space::allocatorID(); + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); + const auto nComponents = axes.size(); + assert(PointType::NDIMS == nComponents); + + n_output.reset(); + n_output["type"] = "explicit"; + conduit::Node &n_values = n_output["values"]; + + // Make output nodes using axis names from the input coordset. Make array views too. + const auto outputSize = blend.m_uniqueIndices.size(); + axom::StackArray, PointType::NDIMS> compViews; + for(size_t i = 0; i < nComponents; i++) + { + // Use the view's value_type + n_output[axisName[i]].set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); + auto *comp_data = static_cast(n_output.fetch_existing(axisName[i]).data_ptr()); + compViews[i] = axom::ArrayView(comp_data, outputSize); + } + + // Iterate over each blend group. + axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) + { + // Original blendGroup index. + const auto origBGIdx = blend.m_uniqueIndicesView[bgid]; + const auto start = blend.m_blendGroupStartView[origBGIdx]; + const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; + + // Blend points for this blend group. + PointType blend{}; + for(int32 i = start; i < end; i++) + { + const auto index = blend.m_blendIdsView[i]; + const auto weight = blend.m_blendCoeffView[i]; + + blended += (view[index] * weight); + } + + // Store the point into the Conduit component arrays. + for(int comp = 0; comp < PointType::NDIMS; comp++) + { + compViews[comp][bgid] = blended[comp]; + } + }); + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp new file mode 100644 index 0000000000..67c2d4c919 --- /dev/null +++ b/src/axom/mir/FieldBlender.hpp @@ -0,0 +1,121 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_FIELD_BLENDER_HPP_ +#define AXOM_MIR_FIELD_BLENDER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/views/NodeArrayView.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief This class contains views of blend data. + */ +struct BlendData +{ + axom::ArrayView m_blendNamesView; // Contains unique hash ids for the sorted ids in each blend group. + axom::ArrayView m_uniqueIndicesView; // Contains the index into the original blend group data for a unique blend group. + + axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. + axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. +}; + +/** + * \accelerated + * \class FieldBlender + * + * \brief This class uses BlendData to generate a new blended field from an input field. + */ +template +class FieldBlender +{ +public: + /** + * \brief Create a new blended field from the \a n_input field and place it in \a n_output. + * + * \param blend The BlendData that will be used to make the new field. + * \param n_input The input field that we're blending. + * \param n_output The output node that will contain the new field. + */ + void execute(const BlendData &blend, const conduit::Node &n_input, conduit::Node &n_output) const + { + n_output.reset(); + n_output["association"] = n_input["association"]; + n_output["topology"] = n_input["topology"]; + + const conduit::Node &n_input_values = n_input["values"]; + conduit::Node &n_output_values = n_output["values"]; + if(n_input_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < n_input_values.number_of_children(); i++) + { + const conduit::Node &n_comp = n_input_values[i]; + conduit::Node &n_out_comp = n_output_values[n_comp.name()]; + blendSingleComponent(blend, n_comp, n_out_comp); + } + } + else + { + blendSingleComponent(blend, n_input_values, n_output_values); + } + } + +private: + /** + * \brief Blend data for a single field component. + * + * \param blend The BlendData that will be used to make the new field. + * \param n_values The input values that we're blending. + * \param n_output_values The output node that will contain the new field. + */ + void blendSingleComponent(const BlendData &blend, const conduit::Node &n_values, conduit::Node &n_output_values) const + { + const auto allocatorID = axom::execution_space::allocatorID(); + const auto outputSize = blend.m_uniqueIndices.size(); + n_output_values.set_allocator(allocatorID); + n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize); + + views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto compView, auto outView) + { + using value_type = typename decltype(compView)::value_type; + using accum_type = axom::mir::utilities::accumulation_traits::type; + + axom::for_all(nbg, AXOM_LAMBDA(auto bgid) + { + // Original blendGroup index. + const auto origBGIdx = blend.m_uniqueIndicesView[bgid]; + const auto start = blend.m_blendGroupStartView[origBGIdx]; + const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; + + accum_type blended = 0; + for(int32 i = start; i < end; i++) + { + const auto index = blend.m_blendIdsView[i]; + const auto weight = blend.m_blendCoeffView[i]; + blended += static_cast(compView[index]) * weight; + } + outView[bgid] = static_cast(blended); + }); + }); + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp new file mode 100644 index 0000000000..789e1a77a7 --- /dev/null +++ b/src/axom/mir/FieldSlicer.hpp @@ -0,0 +1,101 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_FIELD_SLICER_HPP_ +#define AXOM_MIR_FIELD_SLICER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/views/NodeArrayView.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief Contains the indices to be sliced out of a Blueprint field. + */ +struct SliceData +{ + axom::ArrayView m_indices; +}; + +/** + * \accelerated + * \class FieldSlicer + * + * \brief This class uses SliceData to generate a new sliced field from an input field. + */ +template +class FieldSlicer +{ +public: + /** + * \brief Execute the slice on the \a n_input field and store the new sliced field in \a n_output. + * + * \param slice The slice data that indicates how the field will be sliced. + * \param n_input A Conduit node containing the field to be sliced. + * \param n_output A node that will contain the sliced field. + * + * \note We assume for now that n_input != n_output. + */ + void execute(const SliceData &slice, const conduit::Node &n_input, conduit::Node &n_output) + { + n_output.reset(); + n_output["association"] = n_input["association"]; + n_output["topology"] = n_input["topology"]; + + const conduit::Node &n_input_values = n_input["values"]; + conduit::Node &n_output_values = n_output["values"]; + if(n_input_values.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < n_input_values.number_of_children(); i++) + { + const conduit::Node &n_comp = n_input_values[i]; + conduit::Node &n_out_comp = n_output_values[n_comp.name()]; + sliceSingleComponent(slice, n_comp, n_out_comp); + } + } + else + { + sliceSingleComponent(slice, n_input_values, n_output_values); + } + } +private: + /** + * \brief Slice data for a single field component. + * + * \param slice The SliceData that will be used to make the new field. + * \param n_values The input values that we're slicing. + * \param n_output_values The output node that will contain the new field. + */ + void sliceSingleComponent(const SliceData &slice, const conduit::Node &n_values, conduit::Node &n_output_values) const + { + const auto allocatorID = axom::execution_space::allocatorID(); + const auto outputSize = slice.m_indices.size(); + n_output_values.set_allocator(allocatorID); + n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); + + views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto valuesView, auto outputView) + { + axom::for_all(outputSize, AXOM_LAMBDA(auto index) + { + outputView[index] = valuesView[slice.m_indicesView[index]]; + }); + }); + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif From d947251f085d4dc12f720f90732ac1a3a7df5279 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 12 Jul 2024 15:02:35 -0700 Subject: [PATCH 084/290] Refactor clip table. Move CLipField out to its own. --- src/axom/mir/CMakeLists.txt | 8 +- src/axom/mir/ClipField.hpp | 852 ++++++++++++++++++ src/axom/mir/EquiZAlgorithm.cpp | 972 +-------------------- src/axom/mir/clipping/ClipTableManager.hpp | 292 +++++-- src/axom/mir/utilities.hpp | 128 +++ src/axom/mir/views/Shapes.hpp | 14 +- 6 files changed, 1203 insertions(+), 1063 deletions(-) create mode 100644 src/axom/mir/ClipField.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 3f65f478d0..b9ac39a7c1 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -27,9 +27,15 @@ set(mir_headers MIRUtilities.hpp CellGenerator.hpp - MIRAlgorithm.hpp + blueprint_utilities.hpp + ClipField.hpp + CoordsetBlender.hpp EquiZAlgorithm.hpp + FieldBlender.hpp + FieldSlicer.hpp + MIRAlgorithm.hpp RecenterField.hpp + utilities.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp new file mode 100644 index 0000000000..fce2bf49ec --- /dev/null +++ b/src/axom/mir/ClipField.hpp @@ -0,0 +1,852 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_CLIP_FIELD_HPP_ +#define AXOM_MIR_CLIP_FIELD_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/FieldBlender.hpp" +#include "axom/mir/CoordsetBlender.hpp" +#include "axom/mir/FieldSlicer.hpp" +#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit +#include "axom/mir/utilities.hpp" // for cpp2conduit +#include "axom/primal/Point.hpp" + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace clipping +{ +namespace details +{ +template +std::map reverse_map(const std::map &m) +{ + std::map output; + for(const auto& [key, value] : m) + { + output[value] = key; + } + return output; +} + +std::map +shapeMap_NameValue(const conduit::Node &n_shape_map) +{ + std::map sm; + for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) + { + sm[n_shape_map[i].name()] = n_shape_map[i].to_int(); + } + return sm; +} + +std::map +shapeMap_ValueName(const conduit::Node &n_shape_map) +{ + std::map sm; + for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) + { + sm[n_shape_map[i].to_int()] = n_shape_map[i].name(); + } + return sm; +} + +std::map +shapeMap_FromFlags(uint64 shapes) +{ + std::map sm; + + if((shapes & LineShape::id()) > 0) + sm["line"] = LineShape::id(); + + if((shapes & TriShape::id()) > 0) + sm["tri"] = TriShape::id(); + + if((shapes & QuadShape::id()) > 0) + sm["quad"] = QuadShape::id(); + + if((shapes & TetShape::id()) > 0) + sm["tet"] = TetShape::id(); + + if((shapes & PyramidShape::id()) > 0) + sm["pyramid"] = PyramidShape::id(); + + if((shapes & WedgeShape::id()) > 0) + sm["wedge"] = WedgeShape::id(); + + if((shapes & HexShape::id()) > 0) + sm["hex"] = HexShape::id(); + + return sm; +} + +AXOM_HOST_DEVICE +constexpr int getClipTableIndex(int dimension, int nnodes) +{ + return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); +} +AXOM_HOST_DEVICE +constexpr bool color0Selected(int selection) +{ + return axom::utilities::bitIsSet(selection, 0); +} + +AXOM_HOST_DEVICE +constexpr bool color1Selected(int selection) +{ + return axom::utilities::bitIsSet(selection, 1); +} + +AXOM_HOST_DEVICE +constexpr bool generatedPointIsSelected(unsigned char color, int selection) +{ + return color == NOCOLOR || + (color0Selected(selection) && color == COLOR0) || + (color1Selected(selection) && color == COLOR1); +} + +AXOM_HOST_DEVICE +constexpr bool shapeIsSelected(unsigned char color, int selection) +{ + return (color0Selected(selection) && color == COLOR0) || + (color1Selected(selection) && color == COLOR1); +} + +AXOM_HOST_DEVICE +float computeT(float d0, float d1) +{ + const float delta = d1 - d0; + const float abs_delta = (delta < 0) ? -delta : delta; + const float t = (abs_delta != 0.) ? (-d0 / delta) : 0.; + return t; +} + +AXOM_HOST_DEVICE +template +size_t clip_case(const ZoneType &zone, const ArrayViewType &view) +{ + size_t clipcase = 0; + for(size_t i = 0; i < zone.numberOfNodes(); i++) + { + const auto id = zone.getId(i); + if(view[id] > 0) + { + clipcase |= (1 << j); + } + } + return clipcase; +} + +//------------------------------------------------------------------------------ +template +constexpr size_t indexOfZoneType() { return 0; } + +template <> +constexpr size_t indexOfZoneType() { return 0; } + +template <> +constexpr size_t indexOfZoneType() { return 1; } + +template <> +constexpr size_t indexOfZoneType() { return 2; } + +template <> +constexpr size_t indexOfZoneType() { return 3; } + +template <> +constexpr size_t indexOfZoneType() { return 4; } + +template <> +constexpr size_t indexOfZoneType() { return 5; } + +} // end namespace details + +/** + * \accelerated + * \brief This class clips a topology using a field and puts the new topology into a new Conduit node. + */ +template +class ClipField +{ +public: +/* + void execute(const conduit::Node &inputMesh, const std::string &clipField, conduit::Node &outputMesh) + { + + I was going to say that we could create the views here but there's a lot of dispatch code devoted to making typed array views, etc that would need to be done before we even know the types. + + I could include the dispatch_coordset method for example but then this class would have all the various instantiations of the algorithm rather than just the one we want. + + CoordsetView csview; + } + */ + + void execute(const TopologyView &topoView, // I'd rather just pass the views to the method. + const CoordsetView &coordsetView, + const conduit::Node &n_inputMesh, + const std::string &clipField, + conduit::Node &n_outputMesh) + { + using KeyType = uint64; + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + const auto allocatorID = axom::execution_space::allocatorID(); + + // Get the clip field. + conduit::Node &n_clip_field = n_inputMesh["fields"].fetch_existing(clipField); + + // TODO: process options (or add some class members to hold attributes) + int selection = -1; // All bits set. + + + // Load clip table data and make views. + ClipTableManager clipTables; + clipTables.load(topoView.dimension()); + axom::StackArray::View, 6> clipTableViews; + if(topoView.dimension() == 2) + { + clipTableViews[details::indexOfZoneType()] = clipTables[ST_TRI].view(); + clipTableViews[details::indexOfZoneType()] = clipTables[ST_QUA].view(); + } + if(topoView.dimension() == 2) + { + clipTableViews[details::indexOfZoneType()] = clipTables[ST_TET].view(); + clipTableViews[details::indexOfZoneType()] = clipTables[ST_PYR].view(); + clipTableViews[details::indexOfZoneType()] = clipTables[ST_WDG].view(); + clipTableViews[details::indexOfZoneType()] = clipTables[ST_HEX].view(); + } + + // ---------------------------------------------------------------------- + // + // Stage 1: Iterate over elements and their respective clip cases to + // determine sizes of outputs. + // + // ---------------------------------------------------------------------- + RAJA::ReduceSum fragment_sum(0); + RAJA::ReduceSum fragment_nids_sum(0); + RAJA::ReduceSum blendGroups_sum(0); + RAJA::ReduceSum blendGroupLen_sum(0); + + // Allocate some memory + const auto nzones = topoView.numberOfZones(); + axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. + axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone + axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. + axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. + axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. + axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. + + auto clipCasesView = clipCases.view(); + auto pointsUsedView = pointsUsed.view(); + auto blendGroupsview = blendGroups.view(); + auto blendGroupsLenView = blendGroupsLen.view(); + auto fragmentsView = fragments.view(); + auto fragmentsSizeView = fragmentsSize.view(); + + views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) + { + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // Get the clip case for the current zone. + const auto clipcase = details::clip_case(zone, clipFieldView); + clipCasesView[zoneIndex] = clipcase; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto &ctView = clipTableViews[clipTableIndex]; + + int thisBlendGroups = 0; // The number of blend groups produced in this case. + int thisBlendGroupLen = 0; // The total length of the blend groups. + int thisFragments = 0; // The number of zone fragments produced in this case. + int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. + int64 ptused = 0; // A bitset indicating which ST_XX nodes are used. + + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) + { + // Get the current shape in the clip case. + const auto caseData = *it; + + if(caseData[0] == ST_PNT) + { + if(details::generatedPointIsSelected(caseData[2], selection)) + { + const size_t nIds = caseData[3]; + for(size_t ni = 4; ni < nIds; ni++) + { + const auto pid = caseData[ni]; + + // Increase the blend size to include this center point. + if(pid <= P7) + { + // corner point. + thisBlendGroupLen++; + } + else if(pid >= EA && pid <= EL) + { + // edge points are derived from 2 corner points. If + // those appear here then we're probably creating a + // face point. We can store the 2 corner points in place + // of the edge point (along with some blending coeff). + thisBlendGroupLen += 2; + } + } + + // This center or face point counts as a blend group. + thisBlendGroups++; + + // Mark the point used. + axom::utilities::setBit(ptused, N0 + caseData[1]); + } + } + else + { + if(details::shapeIsSelected(caseData[1], selection)) + { + thisFragments++; + const int nIdsThisShape = caseData.size() - 2; + thisFragmentsNumIds += nIdsThisShape; + + // Mark the points in this fragment used. + for(size_t i = 2; i < caseData.size(); i++) + { + axom::utilities::setBit(ptused, caseData[i]); + } + } + } + } + + // Save the flags for the points that were used in this zone + pointsUsedView[zoneIndex] = ptused; + + // Count which points in the original cell are used. + for(unsigned char pid = P0; pid <= P7; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += incr; // {p0} + thisBlendGroups += incr; + } + + // Count edges that are used. + for(unsigned char pid = EA; pid <= EL; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += 2 * incr; // {p0 p1} + thisBlendGroups += incr; + } + + // Save the results. + blendGroupsView[zoneIndex] = thisBlendGroups; + blendGroupLenView[zoneIndex] = thisBlendGroupLen; + fragmentsView[zoneIndex] = thisFragments; + fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; + + // Sum up the sizes overall. + fragment_sum += thisFragments; + fragment_nids_sum += thisFragmentNumIds; + blendGroups_sum += thisBlendGroups; + blendGroupLen_sum += thisBlendGroupLen; + }); + }); + + +// TODO: change int to topoView::IndexType + + // ---------------------------------------------------------------------- + // + // Stage 2: Do some scans to fill out blendOffset and blendGroupOffsets, + // which is where we fill in the real data. + // + // blendOffset : Starting offset for blending data like blendIds, blendCoeff. + // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. + // fragmentOffsets : Where an element's fragments begin in the output. + // ---------------------------------------------------------------------- + axom::Array blendOffset(nzones, nzones, allocatorID); + axom::Array blendGroupOffsets(nzones, nzones, allocatorID); + axom::Array fragmentOffsets(nzones, nzones, allocatorID); + axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); + + auto blendOffsetView = blendOffset.view(); + auto blendGroupOffsetsView = blendGroupOffsets.view(); + auto fragmentOffsetsView = fragmentOffsets.view(); + + // Make offsets via scan. + RAJA::exclusive_scan(RAJA::make_span(blendGroupLenView, nzones), + RAJA::make_span(blendOffsetView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), + RAJA::make_span(blendGroupOffsetsView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), + RAJA::make_span(fragmentOffsetsView, nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), + RAJA::make_span(fragmentSizeOffsetsView, nzones), + RAJA::operators::plus{}); + + // ---------------------------------------------------------------------- + // + // Stage 3: Iterate over the elements/cases again and fill in the blend + // groups that get produced: blendNames, blendGroupSizes, + // blendCoeff, blendIds. These are used to produce the new points. + // + // NOTE: blendGroupStart is a scan of blendGroupSizes. + // + // ---------------------------------------------------------------------- + const auto blendGroupsSize = blendGroups_sum.get(); + const auto blendGroupLenSize = blendGroupLen_sum.get(); + + axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); + axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); + + auto blendNamesView = blendNames.view(); + auto blendGroupSizesView = blendGroupSizes.view(); + auto blendGroupStartView = blendGroupStart.view(); + auto blendIdsView = blendIds.view(); + auto blendCoeffView = blendCoeff.view(); + + views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) + { + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // Get the clip case for the current zone. + const auto clipcase = clipCasesView[zoneIndex]; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto &ctView = clipTableViews[clipTableIndex]; + + const int64 ptused = pointsUsedView[zoneIndex]; + + // Starting offset of where we store this element's blend groups. + int32 bgStart = blendOffsetView[zoneIndex]; + int32 bgOffset = blendGroupOffsetsView[zoneIndex]; + + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) + { + // Get the current shape in the clip case. + const auto caseData = *it; + + if(caseData[0] == ST_PNT) + { + if(details::generatedPointIsSelected(caseData[2], selection)) + { +// TODO: put the ability to select on color back in... 0, 1, or both + const int nIds = static_cast(caseData[3]); + const auto one_over_n = 1.f / static_cast(nIds); + const auto start = bgStart; + + for(int ni = 0; ni < nIds; ni++) + { + const auto ptid = caseData[4 + ni]; + + // Add the point to the blend group. + if(ptid <= P7) + { + // corner point. + blendIdsView[bgStart] = zone.getId(ptid); + blendCoeffView[bgStart] = one_over_n; + + bgStart++; + } + else if(ptid >= EA && ptid <= EL) + { + // edge points are derived from 2 corner points. If + // those appear here then we're probably creating a + // face point. We can store the 2 corner points in place + // of the edge point (along with some blending coeff). + const auto edgeIndex = ptid - EA; + const auto edge = ZoneType::edges[edgeIndex]; + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = details::computeT(clipFieldView[id0], clipFieldView[id1]); + + blendIdsView[bgStart] = id0; + blendIdsView[bgStart+1] = id1; + blendCoeffView[bgStart] = one_over_n * (1. - t); + blendCoeffView[bgStart+1] = one_over_n * t; + + bgStart += 2; + } + } + + // Store how many points make up this blend group. Note that the + // size will not necessarily be equal to npts if edges were involved. + int32 nblended = bgStart - blendGroupStartView[bgOffset]; + blendGroupSizesView[bgOffset] = nblended; + + // Store "name" of blend group. + const auto blendName = make_name_n(blendIdsView, start, nblended); + blendNamesView[bgOffset++] = blendName; + } + } + } + + // Add blend group for each original point that was used. + for(unsigned char pid = P0; pid <= P7; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + // Store blend group info + blendIdsView[bgStart] = zone.getId(pid); + blendCoeffView[bgStart] = 1.; + + // Store how many points make up this blend group. + blendGroupSizesView[bgOffset] = 1; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + blendGroupStartView[bgOffset] = bgStart; + + // Store "name" of blend group. + blendNamesView[bgOffset++] = make_name_1(zone.getId(pid)); + + bgStart++; + } + } + + // Add blend group for each edge point that was used. + for(unsigned char pid = EA; pid <= EL; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto edgeIndex = pid - EA; + const auto edge = ZoneTypeEdges::edges[edgeIndex]; + const auto id0 = zone.getId(c[0]); + const auto id1 = zone.getId(c[1]); + + // Figure out the blend for edge. + const float t = details::computeT(clipFieldView[id0], clipfieldView[id1]); + + // Store blend group info + blendIdsView[bgStart] = id0; + blendIdsView[bgStart+1] = id1; + blendCoeffView[bgStart] = (1. - t); + blendCoeffView[bgStart+1] = t; + + // Store how many points make up this blend group. + blendGroupSizesView[bgOffset] = 2; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + blendGroupStartView[bgOffset] = bgStart; + + // Store "name" of blend group. + blendNamesView[bgOffset++] = make_name_2(id0, id1); + + bgStart += 2; + } + } + }); + }); + + // ---------------------------------------------------------------------- + // + // Stage 4 - Make the blend groups unique based on their blendName. + // + // ---------------------------------------------------------------------- + // At this point, we have created the blend group data. We can now use the + // blendNames to make unique blend groups. uNames contains a sorted list of + // the unique blend group names while uIndices is their original index in + // blendNames/blendGroupOffsets/blendGroupSizes. + axom::Array uNames; + axom::Array uIndices; + unique(blendNames, uNames, uIndices); + + auto uNamesView = uNames.view(); + auto uIndicesView = uIndices.view(); + + // Bundle up blend data. + axom::mir::utilities::blueprint::BlendData blend; + blend.m_blendNamesView = uNames.view(); + blend.m_uniqueIdsView = uIndices.view(); + blend.m_blendGroupSizesView = blendGroupSizes.view(); + blend.m_blendGroupStartView = blendGroupStart.view(); + blend.m_blendIdsView = blendIds.view(); + blend.m_blendCoeffView = blendCoeff.view(); + + +// I just had this thought like it would be nice to output the volumes... and a mapping of old2new zones +// I suppose with the mapping and the old/new meshes, I could compute the volume fractions. +// Should I pass in a outputTopo, outputCoordset, outputFields nodes? +// Then I don't have to care about the names - it's done outside this code. + + // ---------------------------------------------------------------------- + // + // Stage 5 - Make new connectivity. + // + // ---------------------------------------------------------------------- + conduit::Node &n_topologies = outputMesh["topologies"]; + conduit::Node &n_topo = n_topologies[topoView.name()]; + + const auto finalNumZones = fragment_sum.get(); + const auto finalConnSize = fragment_nids_sum.get(); + + using ConnectivityType = typename topoView::IndexType; + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + + // Allocate connectivity. + conduit::Node &n_conn = n_topo["elements/connectivity"]; + n_conn.set_allocator(allocatorID); + n_conn.set(conduit::DataType(connTypeID, finalConnSize)); + auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); + + // Allocate shapes. + conduit::Node &n_shapes = n_topo["elements/shapes"]; + n_shapes.set_allocator(allocatorID); + n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); + auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + + // Allocate sizes. + conduit::Node &n_sizes = n_topo["elements/sizes"]; + n_sizes.set_allocator(allocatorID); + n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); + auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + + // Allocate offsets. + conduit::Node &n_offsets = n_topo["elements/offsets"]; + n_offsets.set_allocator(allocatorID); + n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); + auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); + + + const int32 uNames_len = uNames.size(); + + RAJA::ReduceBitOr shapesUsed_reduce(0); + + // Here we get the node ids from the unique blend names so we might be able to skip the zone iteration since that is more expensive than a normal axom::for_all. + // I'd have to store the clipTableIndex though. +#if 1 + AXOM_HOST_DEVICE + constexpr int32 encode_case(int32 clipcase, int32 clipTableIndex, int32 nFragments) + { + return (clipcase & 0xff) | + ((clipTableIndex & 0xff) << 8) | + ((nFragments & 0xff) << 16); + } + AXOM_HOST_DEVICE + constexpr void decode_case(int32 value, int32 &clipcase, int32 &clipTableIndex, int32 &nFragments) + { + clipcase = (value & 0xff); + clipTableIndex = (value >> 8) & 0xff; + nFragments = (value >> 16) & 0xff; + } + // Of course, I can't do an exclusive scan on the fragmentsView if it contains other stuff. + +#endif + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + // If there are no fragments, return from lambda. + if(fragmentsView[zoneIndex] == 0) + return; + + // Seek to the start of the blend groups for this zone. + const int32 bgStart = blendGroupOffsetsView[zoneIndex]; + + // Go through the points in the order they would have been added as blend + // groups, get their blendName, and then overall index of that blendName + // in uNames, the unique list of new dof names. That will be their index + // in the final points. + const int64 ptused = pointsUsedView[zoneIndex]; + ConnectivityType point_2_new[N3 + 1]; + for(unsigned char pid = N0; pid <= N3; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + for(unsigned char pid = P0; pid <= P7; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + for(unsigned char pid = EA; pid <= EL; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto name = blendNamesView[bgStart++]; + point_2_new[pid] = bsearch(name, uNamesView); + } + } + + // This is where the output fragment connectivity start for this zone + int outputIndex = fragmentSizeOffsetsView[zoneIndex]; + // This is where the output fragment sizes/shapes start for this zone. + int sizeIndex = fragmentOffsetsView[zoneIndex]; + uint64 shapesUsed = 0; + + // Iterate over the selected fragments and emit connectivity for them. + const auto clipcase = clipCasesView[zoneIndex]; + const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto ctView = clipTableViews[clipTableIndex]; + it = ctView.begin(clipcase); + for(; it != end; it++) + { + // Get the current shape in the clip case. + const auto caseData = *it; + + if(caseData[0] != ST_PNT) + { + if(details::shapeIsSelected(caseData[1], selection)) + { + // Output the nodes used in this zone. + for(int i = 2; i < caseData.size(); i++) + connView[outputIndex++] = point_2_new[caseData[i]]; + + const auto nIdsInZone = caseData.size() - 2; + sizesView[sizeIndex] = nIdsInZone; + shapeView[sizeIndex] = zone.id(); + sizeIndex++; + + // Record which shape type was used. (zone.id() are already powers of 2) + shapesUsed |= zone.id(); + } + } + } + + shapesUsed_reduce |= shapesUsed; + }); + + // Make offsets + RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), + RAJA::make_span(offsetsView, nzones), + RAJA::operators::plus{}); + + // Add shape information to the connectivity. + const auto shapesUsed = shapesUsed_reduce.get(); + const auto shapeMap = shapeMap_ValueName(shapesUsed); + if(axom::utilities::countBits(shapesUsed) > 1) + { + n_topo["elements/shape"] = "mixed"; + conduit::Node &n_shape_map = n_topo["elements/shape_map"]; + for(const auto &[key, value] : shapeMap) + n_shape_map[key] = value; + } + else + { + n_shapes.reset(); + n_topo["elements"].remove("shapes"); + + n_topo["elements/shape"] = shapeMap.begin()->first; + } + + //----------------------------------------------------------------------- + // STAGE 6 - Make new coordset. + //----------------------------------------------------------------------- + + make_fields(blend, inputMesh["fields"], outputMesh["fields"]); + + //----------------------------------------------------------------------- + // STAGE 6 - Make new fields. + //----------------------------------------------------------------------- + if(inputMesh.has_path("fields")) + { + make_fields(blend, slice, topoName, inputMesh["fields"], outputMesh["fields"]); + } + + //----------------------------------------------------------------------- + // STAGE 6 - make originalElements (this will later be optional) + //----------------------------------------------------------------------- + conduit::Node &n_fields = outputMesh["fields"]; + if(n_fields.has_child("originalElements")) + { + // originalElements already exists. We need to map it forward. + conduit::Node &n_orig = n_fields["originalElements"]; + conduit::Node &n_orig_values = n_orig["values"]; + views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) + { + using value_type = typename origValuesView::value_type; + conduit::Node n_values; + n_values.set_allocator(allocatorID); + n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); + + n_orig_values.move(n_values); + }); + } + else + { + // Make a new node and populate originalElement. + conduit::Node &n_orig = n_fields["originalElements"]; + n_orig["association"] = "element"; + n_orig["topology"] = n_topo.name(); + conduit::Node &n_values = n_orig["values"]; + n_values.set_allocator(allocatorID); + n_values.set(conduit::DataType(connTypeID, finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = zoneIndex; + }); + } + +//----------------------------------------------------------------------------------- + } // end of execute + +private: + void make_coordset(const BlendData &blend, const CoordsetView &csview, conduit::Node &n_new_coordset) const + { + axom::mir::utilities::blueprint::CoordsetBlender cb; + b.execute(blend, csview, n_coordset, n_new_coordset); + } + + void make_fields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const + { + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + const conduit::Node &n_field = n_fields[i]; + if(n_field["topology"].as_string() == topoName) + { + const std::string association = n_field["association"].as_string(); + if(association == "element") + { + axom::mir::utilities::blueprint::FieldSlicer s; + s.execute(blend, n_field, n_out_fields[n_field.name()]); + } + else if(association == "vertex") + { + axom::mir::utilities::blueprint::FieldBlender b; + b.execute(blend, n_field, n_out_fields[n_field.name()]); + } + } + } + } + + // NOTE: I probably want to make the clip tables be a class member so I can reuse the object without having to reload the clip tables each time. +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index cc8862f850..0edada9380 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -86,978 +86,34 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, #endif } -#if 0 -AXOM_HOST_DEVICE -template -size_t clip_case(const ZoneType &zone, const ArrayViewType &view) -{ - size_t clipcase = 0; - for(size_t i = 0; i < zone.numberOfNodes(); i++) - { - const auto id = zone.getId(i); - if(view[id] > 0) - { - clipcase |= (1 << j); - } - } - return clipcase; -} - -//------------------------------------------------------------------------------ -template -struct zone2index { static constexpr size_t st_index = 0; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_TRI; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_QUA; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_TET; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_PYR; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_WDG; }; - -template <> -struct zone2index { static constexpr size_t st_index = ST_HEX; }; - -//------------------------------------------------------------------------------ -template -constexpr size_t indexOfZoneType() { return 0; } - -template <> -constexpr size_t indexOfZoneType() { return 0; } - -template <> -constexpr size_t indexOfZoneType() { return 1; } - -template <> -constexpr size_t indexOfZoneType() { return 2; } - -template <> -constexpr size_t indexOfZoneType() { return 3; } - -template <> -constexpr size_t indexOfZoneType() { return 4; } - -template <> -constexpr size_t indexOfZoneType() { return 5; } - -//------------------------------------------------------------------------------ -template -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT8_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT16_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT32_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::INT64_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT8_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT16_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT32_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::UINT64_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::FLOAT32_ID; }; - -template <> -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::FLOAT64_ID; }; - - -//------------------------------------------------------------------------------ -AXOM_HOST_DEVICE -constexpr int getClipTableIndex(int dimension, int nnodes) -{ - return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); -} - -template -AXOM_HOST_DEVICE -constexpr bool bitIsSet(FlagType flags, BitType bit) -{ - return (flags & (1 << bit)) > 0; -} - -template -AXOM_HOST_DEVICE -constexpr void setBit(FlagType &flags, BitType bit) -{ - flags |= (1 << bit); -} - -template -AXOM_HOST_DEVICE -int countBits(IntType value) -{ - constexpr int n = sizeof(IntType) * 8; - int count = 0, mask = 1; - for(int i = 0; i < n; i++) - count += ((value & mask) > 0) ? 1 : 0; - return count; -} - -AXOM_HOST_DEVICE -constexpr bool color0Selected(int selection) -{ - return bitIsSet(selection, 0); -} - -AXOM_HOST_DEVICE -constexpr bool color1Selected(int selection) -{ - return bitIsSet(selection, 1); -} - -AXOM_HOST_DEVICE -constexpr bool generatedPointIsSelected(unsigned char color, int selection) -{ - return color == NOCOLOR || - (color0Selected(selection) && color == COLOR0) || - (color1Selected(selection) && color == COLOR1); -} - -AXOM_HOST_DEVICE -constexpr bool shapeIsSelected(unsigned char color, int selection) -{ - return (color0Selected(selection) && color == COLOR0) || - (color1Selected(selection) && color == COLOR1); -} - -AXOM_HOST_DEVICE -float computeT(float d0, float d1) -{ - const float delta = d1 - d0; - const float abs_delta = (delta < 0) ? -delta : delta; - const float t = (abs_delta != 0.) ? (-d0 / delta) : 0.; - return t; -} - -//------------------------------------------------------------------------- -template -AXOM_HOST_DEVICE -int32 bsearch(T value, const axom::ArrayView &view) -{ - int32 index = -1; - int32 left = 0; - int32 right = view.size() - 1; - while(left <= right) - { - int32 m = (left + right) / 2; - if(view[m] < value) - left = m + 1; - else if(view[m] > value) - right = m - 1; - else - { - index = m; - break; - } - } - - return index; -} - //------------------------------------------------------------------------------ -/// Based on a Jenkins hash, modified to include length and hash forwards and -/// backwards to make int64 rather than int32. -AXOM_HOST_DEVICE -uint64 hash_bytes(const uint8 *data, uint32 length) -{ - uint32 hash = 0; - - // Build the length into the hash. - const auto ldata = reinterpret_cast(&length); - for(int e = 0; e < 4; e++) - { - hash += ldata[e]; - hash += hash << 10; - hash ^= hash >> 6; - } - - uint32 hashr = hash; - for(uint32 i = 0; i < length; i++) - { - hash += data[i]; - hash += hash << 10; - hash ^= hash >> 6; - - hashr += data[length - 1 - i]; - hashr += hashr << 10; - hashr ^= hashr >> 6; - } - hash += hash << 3; - hash ^= hash >> 11; - hash += hash << 15; - - hashr += hashr << 3; - hashr ^= hashr >> 11; - hashr += hashr << 15; - - return (static_cast(hash) << 32) | hashr; -} - -//------------------------------------------------------------------------------ -template -uint64 -AXOM_HOST_DEVICE -make_name_1(ValueType id) +#if 0 +// NOTE: it might be useful to combine some things for simpler templating. +template +struct DomainView { - return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); + TopologyView topoView; + CoordsetView coordsetView; }; -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE -uint64 -make_name_2(ValueType id0, ValueType id1) -{ - ValueType data[2] = {id0, id1}; - if(id1 < id0) - { - data[0] = id1; - data[1] = id0; - } - return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); -}; +// Q: if we could make a FieldView, what would it look like? -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE -uint32 -sort_values(ValueType *v, IndexType n) +struct FieldView { - for(IndexType i = 0; i < n-1; i++) + template + void for_each_field(conduit::Node &n_fields, FuncType &&func) { - const IndexType m = n - i - 1; - for(IndexType j = 0; j < m; j++) + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { - if(v[j] > v[j+1]) + views::Node_to_ArrayView(n_fields[i], [&](auto arrayView) { - axom::utilities::swap(v[j], v[j+1]); + func(n_fields[i], arrayView); } } } } -//------------------------------------------------------------------------- -template -uint64 -AXOM_HOST_DEVICE -make_name_n(const ViewType &view, int32 start, int32 n) -{ - using value_type = typename ViewType::value_type; - - if(n == 2) - return make_name_2(view[start], view[start + 1]); - - value_type v[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // pick largest number of blends. - for(int32 i = 0; i < n; i++) - { - v[i] = view[start + i]; - } - sort_values(v, n); - - return hash_bytes(reinterpret_cast(v), n * sizeof(value_type)); -} - -//------------------------------------------------------------------------------ -// I might want to do the real work here in a more targeted class. This way, a host code could choose to instantiate a single -template -class ClipField -{ -public: - void execute(const TopologyView &topoView, // I'd rather just pass the views to the method. - const CoordsetView &coordsetView, - const conduit::Node &clipField, - conduit::Node &outputMesh) - { - using int32 = conduit::int32; - using uint32 = conduit::uint32; - using int64 = conduit::int64; - using uint64 = conduit::uint64; - - using KeyType = uint64; - using loop_policy = typename axom::execution_space::loop_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; - const auto allocatorID = axom::execution_space::allocatorID(); - - // Load clip table data and make views. - ClipTableManager clipTables; - clipTables.load(topoView.dimension()); - axom::StackArray::View, 6> clipTableViews; - if(topoView.dimension() == 2) - { - clipTableViews[indexOfZoneType()] = clipTables[ST_TRI].view(); - clipTableViews[indexOfZoneType()] = clipTables[ST_QUA].view(); - } - if(topoView.dimension() == 2) - { - clipTableViews[indexOfZoneType()] = clipTables[ST_TET].view(); - clipTableViews[indexOfZoneType()] = clipTables[ST_PYR].view(); - clipTableViews[indexOfZoneType()] = clipTables[ST_WDG].view(); - clipTableViews[indexOfZoneType()] = clipTables[ST_HEX].view(); - } - - // ---------------------------------------------------------------------- - // - // Stage 1: Iterate over elements and their respective clip cases to - // determine sizes of outputs. - // - // ---------------------------------------------------------------------- - RAJA::ReduceSum fragment_sum(0); - RAJA::ReduceSum fragment_nids_sum(0); - RAJA::ReduceSum blendGroups_sum(0); - RAJA::ReduceSum blendGroupLen_sum(0); - - // Allocate some memory - const auto nzones = topoView.numberOfZones(); - axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. - axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. - axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. - axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. - axom::Array fragmentsSize(nzones, nzones, allocatorID); // The total number of points produced for all fragments in a zone. - auto clipCasesView = clipCases.view(); - auto blendGroupsview = blendGroups.view(); - auto blendGroupsLenView = blendGroupsLen.view(); - auto fragmentsView = fragments.view(); - auto fragmentsSizeView = fragmentsSize.view(); - - views::Node_to_ArrayView(clipField, [&](auto clipFieldView) - { - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // Get the clip case for the current zone. - const auto clipcase = clip_case(zone, clipFieldView); - clipCasesView[zoneIndex] = clipcase; - - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); - const auto &ctView = clipTableViews[clipTableIndex]; - const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); - - int thisBlendGroups = 0; // The number of blend groups produced in this case. - int thisBlendGroupLen = 0; // The total length of the blend groups. - int thisFragments = 0; // The number of zone fragments produced in this case. - int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. - int64 ptused = 0; // A bitset indicating which ST_XX nodes are used. - - for(size_t si = 0; si < nShapes; si++) - { - // Get the si'th shape in the clip case. - const auto caseData = ctView.getShape(clipcase, si); - - if(caseData[0] == ST_PNT) - { - if(generatedPointIsSelected(caseData[2], selection)) - { - const size_t nIds = caseData[3]; - for(size_t ni = 4; ni < nIds; ni++) - { - const auto pid = caseData[ni]; - - // Increase the blend size to include this center point. - if(pid <= P7) - { - // corner point. - thisBlendGroupLen++; - } - else if(pid >= EA && pid <= EL) - { - // edge points are derived from 2 corner points. If - // those appear here then we're probably creating a - // face point. We can store the 2 corner points in place - // of the edge point (along with some blending coeff). - thisBlendGroupLen += 2; - } - } - - // This center or face point counts as a blend group. - thisBlendGroups++; - } - } - else - { - if(shapeShapeSelected(caseData[1], selection)) - { - thisFragments++; - const int nIdsThisShape = caseData.size() - 2; - thisFragmentNumIds += nIdsThisShape; - - // Mark which points were used in this cell. - for(size_t i = 2; i < caseData.size(); i++) - { - setBit(ptused, caseData[i]); - } - } - } - } - - // Count which points in the original cell are used. - for(unsigned char pid = P0; pid <= P7; pid++) - { - const int incr = bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += incr; // {p0} - thisBlendGroups += incr; - } - - // Count edges that are used. - for(unsigned char pid = EA; pid <= EL; pid++) - { - const int incr = bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += 2 * incr; // {p0 p1} - thisBlendGroups += incr; - } - - // Save the results. - blendGroupsView[zoneIndex] = thisBlendGroups; - blendGroupLenView[zoneIndex] = thisBlendGroupLen; - fragmentsView[zoneIndex] = thisFragments; - fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; - - // Sum up the sizes overall. - fragment_sum += thisFragments; - fragment_nids_sum += thisFragmentNumIds; - blendGroups_sum += thisBlendGroups; - blendGroupLen_sum += thisBlendGroupLen; - }); - }); - - -// TODO: change int to topoView::IndexType - - // ---------------------------------------------------------------------- - // - // Stage 2: Do some scans to fill out blendOffset and blendGroupOffsets, - // which is where we fill in the real data. - // - // blendOffset : Starting offset for blending data like blendIds, blendCoeff. - // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. - // fragmentOffsets : Where an element's fragments begin in the output. - // ---------------------------------------------------------------------- - axom::Array blendOffset(nzones, nzones, allocatorID); - axom::Array blendGroupOffsets(nzones, nzones, allocatorID); - axom::Array fragmentOffsets(nzones, nzones, allocatorID); - axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); - - auto blendOffsetView = blendOffset.view(); - auto blendGroupOffsetsView = blendGroupOffsets.view(); - auto fragmentOffsetsView = fragmentOffsets.view(); - - // Make offsets via scan. - RAJA::exclusive_scan(RAJA::make_span(blendGroupLenView, nzones), - RAJA::make_span(blendOffsetView, nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), - RAJA::make_span(blendGroupOffsetsView, nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), - RAJA::make_span(fragmentOffsetsView, nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), - RAJA::make_span(fragmentSizeOffsetsView, nzones), - RAJA::operators::plus{}); - - // ---------------------------------------------------------------------- - // - // Stage 3: Iterate over the elements/cases again and fill in the blend - // groups that get produced: blendNames, blendGroupSizes, - // blendCoeff, blendIds. These are used to produce the new points. - // - // NOTE: blendGroupStart is a scan of blendGroupSizes. - // - // ---------------------------------------------------------------------- - const auto blendGroupsSize = blendGroups_sum.get(); - const auto blendGroupLenSize = blendGroupLen_sum.get(); - - axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); - axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); - - auto blendNamesView = blendNames.view(); - auto blendGroupSizesView = blendGroupSizes.view(); - auto blendGroupStartView = blendGroupStart.view(); - auto blendIdsView = blendIds.view(); - auto blendCoeffView = blendCoeff.view(); - - views::Node_to_ArrayView(clipField, [&](auto clipFieldView) - { - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // Get the clip case for the current zone. - const auto clipcase = clipCasesView[zoneIndex]; - - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); - const auto &ctView = clipTableViews[clipTableIndex]; - const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); - - int64 ptused = 0; - - // Starting offset of where we store this element's blend groups. - int32 bgStart = blendOffsetView[zoneIndex]; - int32 bgOffset = blendGroupOffsetsView[zoneIndex]; - - for(size_t si = 0; si < nShapes; si++) - { - // Get the si'th shape in the clip case. - const auto caseData = ctView.getShape(clipcase, si); - - if(caseData[0] == ST_PNT) - { - if(generatedPointIsSelected(caseData[2], selection)) - { -// TODO: put the ability to select on color back in... 0, 1, or both - const int nIds = static_cast(caseData[3]); - const auto one_over_n = 1.f / static_cast(nIds); - const auto start = bgStart; - - for(int ni = 0; ni < nIds; ni++) - { - const auto ptid = caseData[4 + ni]; - - // Add the point to the blend group. - if(ptid <= P7) - { - // corner point. - blendIdsView[bgStart] = zone.getId(ptid); - blendCoeffView[bgStart] = one_over_n; - - bgStart++; - } - else if(ptid >= EA && ptid <= EL) - { - // edge points are derived from 2 corner points. If - // those appear here then we're probably creating a - // face point. We can store the 2 corner points in place - // of the edge point (along with some blending coeff). - const auto edgeIndex = ptid - EA; - const auto edge = ZoneType::edges[edgeIndex]; - const auto id0 = zone.getId(edge[0]); - const auto id1 = zone.getId(edge[1]); - - // Figure out the blend for edge. - const float t = computeT(clipFieldView[id0], clipFieldView[id1]); - - blendIdsView[bgStart] = id0; - blendIdsView[bgStart+1] = id1; - blendCoeffView[bgStart] = one_over_n * (1. - t); - blendCoeffView[bgStart+1] = one_over_n * t; - - bgStart += 2; - } - } - - // Store how many points make up this blend group. Note that the - // size will not necessarily be equal to npts if edges were involved. - int32 nblended = bgStart - blendGroupStartView[bgOffset]; - blendGroupSizesView[bgOffset] = nblended; - - // Store "name" of blend group. - const auto blendName = make_name_n(blendIdsView, start, nblended); - blendNamesView[bgOffset++] = blendName; - } - } - else - { - if(shapeShapeSelected(caseData[1], selection)) - { - // Mark which points were used in this zone. - for(size_t i = 2; i < caseData.size(); i++) - { - setBit(ptused, caseData[i]); - } - } - } - } - - // Add blend group for each original point that was used. - for(unsigned char pid = P0; pid <= P7; pid++) - { - if(bitIsSet(ptused, pid)) - { - // Store blend group info - blendIdsView[bgStart] = zone.getId(pid); - blendCoeffView[bgStart] = 1.; - - // Store how many points make up this blend group. - blendGroupSizesView[bgOffset] = 1; - - // Store where this blendGroup starts in the blendIds,blendCoeff. - blendGroupStartView[bgOffset] = bgStart; - - // Store "name" of blend group. - blendNamesView[bgOffset++] = make_name_1(zone.getId(pid)); - - bgStart++; - } - } - - // Add blend group for each edge point that was used. - for(unsigned char pid = EA; pid <= EL; pid++) - { - if(bitIsSet(ptused, pid)) - { - const auto edgeIndex = pid - EA; - const auto edge = ZoneTypeEdges::edges[edgeIndex]; - const auto id0 = zone.getId(c[0]); - const auto id1 = zone.getId(c[1]); - - // Figure out the blend for edge. - const float t = computeT(clipFieldView[id0], clipfieldView[id1]); - - // Store blend group info - blendIdsView[bgStart] = id0; - blendIdsView[bgStart+1] = id1; - blendCoeffView[bgStart] = (1. - t); - blendCoeffView[bgStart+1] = t; - - // Store how many points make up this blend group. - blendGroupSizesView[bgOffset] = 2; - - // Store where this blendGroup starts in the blendIds,blendCoeff. - blendGroupStartView[bgOffset] = bgStart; - - // Store "name" of blend group. - blendNamesView[bgOffset++] = make_name_2(id0, id1); - - bgStart += 2; - } - } - }); - }); - - // ---------------------------------------------------------------------- - // - // Stage 4 - Make the blend groups unique based on their blendName. - // - // ---------------------------------------------------------------------- - // At this point, we have created the blend group data. We can now use the - // blendNames to make unique blend groups. uNames contains a sorted list of - // the unique blend group names while uIndices is their original index in - // blendNames/blendGroupOffsets/blendGroupSizes. - axom::Array uNames; - axom::Array uIndices; - unique(blendNames, uNames, uIndices); - - auto uNamesView = uNames.view(); - auto uIndicesView = uIndices.view(); - -// I just had this thought like it would be nice to output the volumes... and a mapping of old2new zones -// I suppose with the mapping and the old/new meshes, I could compute the volume fractions. -// Should I pass in a outputTopo, outputCoordset, outputFields nodes? -// Then I don't have to care about the names - it's done outside this code. - - // ---------------------------------------------------------------------- - // - // Stage 5 - Make new connectivity. - // - // ---------------------------------------------------------------------- - conduit::Node &n_topologies = outputMesh["topologies"]; - conduit::Node &n_topo = n_topologies[topoView.name()]; - - const auto finalNumZones = fragment_sum.get(); - const auto finalConnSize = fragment_nids_sum.get(); - - using ConnectivityType = typename topoView::IndexType; - const auto connTypeID = cpp2conduit::type; - - // Allocate connectivity. - conduit::Node &n_conn = n_topo["elements/connectivity"]; - n_conn.set_allocator(allocatorID); - n_conn.set(conduit::DataType(connTypeID, finalConnSize)); - auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); - - // Allocate shapes. - conduit::Node &n_shapes = n_topo["elements/shapes"]; - n_shapes.set_allocator(allocatorID); - n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); - auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); - - // Allocate sizes. - conduit::Node &n_sizes = n_topo["elements/sizes"]; - n_sizes.set_allocator(allocatorID); - n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); - auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); - - // Allocate offsets. - conduit::Node &n_offsets = n_topo["elements/offsets"]; - n_offsets.set_allocator(allocatorID); - n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); - auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); - - - const int32 uNames_len = uNames.size(); - - RAJA::ReduceBitOr shapesUsed_reduce(0); - - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // If there are no fragments, return from lambda. - if(fragmentsView[zoneIndex] == 0) - return; - - const auto clipcase = clip_case(zone, clipFieldView); - - const auto clipTableIndex = getClipTableIndex(zone.dimension(), zone.numberOfNodes()); - const auto ctView = clipTableViews[clipTableIndex]; - const size_t nShapes = ctView.shapesForCase(clipCases[zoneIndex]); - - int64 ptused = 0; - - // Iterate over the tet fragments to see which points they use. - // The points correspond to blend groups that we have made in - // the final output. - for(size_t si = 0; si < nShapes; si++) - { - // Get the si'th shape in the clip case. - const auto caseData = ctView.getShape(clipcase, si); - - if(caseData[0] == ST_PNT) - { - // Count the point as used since a blend group would have - // been emitted for it under these conditions. This makes - // sure that we get the ordering for point_2_newdof right - // when a set of shapes happens to not use the blended point. - // That, of course, means that the lut needs to be fixed a bit. - if(generatedPointIsSelected(caseData[2], selection)) - { - setBit(ptused, N0 + caseData[1]); - } - } - else - { - if(shapeShapeSelected(caseData[1], selection)) - { - for(int i = 2; i < caseData.size(); i++) - setBit(ptused, i); - } - } - } - - // Seek to the start of the blend groups for this zone. - const int32 bgStart = blendGroupOffsetsView[zoneIndex]; - - // Go through the points in the order they would have been added as blend - // groups, get their blendName, and then overall index of that blendName - // in uNames, the unique list of new dof names. That will be their index - // in the final points. - ConnectivityType point_2_new[N3 + 1]; - for(unsigned char pid = N0; pid <= N3; pid++) - { - if(bitIsSet(ptused, pid)) - { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); - } - } - for(unsigned char pid = P0; pid <= P7; pid++) - { - if(bitIsSet(ptused, pid)) - { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); - } - } - for(unsigned char pid = EA; pid <= EL; pid++) - { - if(bitIsSet(ptused, pid)) - { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); - } - } - - // This is where the output fragment connectivity start for this zone - int outputIndex = fragmentSizeOffsetsView[zoneIndex]; - // This is where the output fragment sizes/shapes start for this zone. - int sizeIndex = fragmentOffsetsView[zoneIndex]; - uint64 shapesUsed = 0; - for(size_t si = 0; si < nShapes; si++) - { - // Get the si'th shape in the clip case. - const auto caseData = ctView.getShape(clipcase, si); - - if(caseData[0] != ST_PNT) - { - if(shapeIsSelected(caseData[1], selection)) - { - // Output the nodes used in this zone. - for(int i = 2; i < caseData.size(); i++) - connView[outputIndex++] = point_2_new[caseData[i]]; - - const auto nIdsInZone = caseData.size() - 2; - sizesView[sizeIndex] = nIdsInZone; - shapeView[sizeIndex] = zone.id(); - setBit(shapesUsed, zone.id()); - sizeIndex++; - } - } - } - - shapesUsed_reduce |= shapesUsed; - }); - - // Make offsets - RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), - RAJA::make_span(offsetsView, nzones), - RAJA::operators::plus{}); - - // Add shape information - const auto shapesUsed = shapesUsed_reduce.get(); - const auto shapeMap = shapeMap_ValueName(shapesUsed); - if(countBits(shapesUsed) > 1) - { - n_topo["elements/shape"] = "mixed"; - conduit::Node &n_shape_map = n_topo["elements/shape_map"]; - for(const auto &[key, value] : shapeMap) - n_shape_map[key] = value; - } - else - { - n_shapes.reset(); - n_topo["elements"].remove("shapes"); - - n_topo["elements/shape"] = shapeMap.begin()->first; - } - - //----------------------------------------------------------------------- - // STAGE 6 - make originalElements (this will later be optional) - //----------------------------------------------------------------------- - conduit::Node &n_fields = outputMesh["fields"]; - if(n_fields.has_child("originalElements")) - { - // originalElements already exists. We need to map it forward. - conduit::Node &n_orig = n_fields["originalElements"]; - conduit::Node &n_orig_values = n_orig["values"]; - views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) - { - using value_type = typename origValuesView::value_type; - conduit::Node n_values; - n_values.set_allocator(allocatorID); - n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = origValuesView[zoneIndex]; - }); - - n_orig_values.move(n_values); - }); - } - else - { - // Make a new node and populate originalElement. - conduit::Node &n_orig = n_fields["originalElements"]; - n_orig["association"] = "element"; - n_orig["topology"] = n_topo.name(); - conduit::Node &n_values = n_orig["values"]; - n_values.set_allocator(allocatorID); - n_values.set(conduit::DataType(connTypeID, finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = zoneIndex; - }); - } - - //----------------------------------------------------------------------- - // STAGE 7 - Make new fields. - //----------------------------------------------------------------------- - -//----------------------------------------------------------------------------------- - } // end of execute -}; - -template -std::map reverse_map(const std::map &m) -{ - std::map output; - for(const auto& [key, value] : m) - { - output[value] = key; - } - return output; -} - -std::map -shapeMap_NameValue(const conduit::Node &n_shape_map) -{ - std::map sm; - for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) - { - sm[n_shape_map[i].name()] = n_shape_map[i].to_int(); - } - return sm; -} - -std::map -shapeMap_ValueName(const conduit::Node &n_shape_map) -{ - std::map sm; - for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) - { - sm[n_shape_map[i].to_int()] = n_shape_map[i].name(); - } - return sm; -} - -std::map -shapeMap_FromFlags(uint64 shapes) -{ - std::map sm; - - if((shapes & LineShape::id()) > 0) - sm["line"] = LineShape::id(); - - if((shapes & TriShape::id()) > 0) - sm["tri"] = TriShape::id(); - - if((shapes & QuadShape::id()) > 0) - sm["quad"] = QuadShape::id(); - - if((shapes & TetShape::id()) > 0) - sm["tet"] = TetShape::id(); - - if((shapes & PyramidShape::id()) > 0) - sm["pyramid"] = PyramidShape::id(); - - if((shapes & WedgeShape::id()) > 0) - sm["wedge"] = WedgeShape::id(); - - if((shapes & HexShape::id()) > 0) - sm["hex"] = HexShape::id(); - - return sm; -} +#endif template void diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index b521c0bf7d..42394763d9 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -18,111 +18,211 @@ namespace clipping { /** - * \brief This struct contains data for a clipping table. - * - * \tparam ClipIndexContainerType The container for int clipping data. - * \tparam ClipDataContainerType The container for uint8 clipping data. + * \accelerated + * \brief This class contains a view of table data and it provides an + * iterator for traversing shapes in a case. */ -template -struct ClipTableBase +class TableView { - using ClipDataType = typename ClipDataContainerType::value_type; + using IndexData = int; + using TableData = unsigned char; + using IndexView = axom::ArrayView; + using TableDataView = axom::ArrayView; + + /** + * \brief An iterator for shapes within a table case. + */ + class iterator + { + public: + /** + * \brief Return the index of the iterator's current shape. + * \return The index of the iterator's current shape. + */ + AXOM_HOST_DEVICE + inline int index() const { return m_currentShape; } + + /** + * \brief Return the number of shapes in the iterator's case. + * \return The number of shapes in the iterator's case. + */ + AXOM_HOST_DEVICE + inline int size() const { return m_numShapes; } + + /** + * \brief Increment the iterator, moving it to the next shape. + */ + AXOM_HOST_DEVICE + inline void operator++() + { + if(m_currentShape < m_numShapes) + { + const TableData *ptr = m_shapeStart + m_offset; + m_offset += shapeLength(ptr); + m_currentShape++; + } + } + + /** + * \brief Increment the iterator, moving it to the next shape. + */ + AXOM_HOST_DEVICE + inline void operator++(int) + { + if(m_currentShape < m_numShapes) + { + const TableData *ptr = m_shapeStart + m_offset; + m_offset += shapeLength(ptr); + m_currentShape++; + } + } + + /** + * \brief Compare 2 iterators for equality. + * \param it The iterator to be compared to this. + * \return true if the iterators are equal; false otherwise. + */ + AXOM_HOST_DEVICE + inline bool operator == (const iterator &it) const + { + // Do not worry about m_offset + return m_shapeStart == it.m_shapeStart && + m_currentShape == it.m_currentShape && + m_numShapes == it.m_numShapes; + } + + /** + * \brief Compare 2 iterators to see if not equal. + * \param it The iterator to be compared to this. + * \return true if the iterators are different; false otherwise. + */ + AXOM_HOST_DEVICE + inline bool operator != (const iterator &it) const + { + // Do not worry about m_offset + return m_shapeStart != it.m_shapeStart || + m_currentShape != it.m_currentShape || + m_numShapes != it.m_numShapes; + } + + /** + * \brief Dereference operator that wraps the current shape data in an array + * view so the caller can use the shape data. + */ + AXOM_HOST_DEVICE + inline ShapeDataView operator*() const + { + TableData *ptr = m_shapeStart + m_offset; + const auto len = shapeLength(ptr); + return TableDataView(ptr, len); + } + private: + friend class TableView; + + /** + * \brief Given the input shape, return how many values to advance to get to the next shape. + * + * \param shape The shape type. + * + * \return The number of values to advance. + */ + AXOM_HOST_DEVICE + size_t shapeLength(const TableData *caseData) const + { + size_t retval = 0; + const auto shape = caseData[0]; + switch(shape) + { + case ST_PNT: retval = 4 + caseData[3]; break; + case ST_TRI: retval = 2 + 3; break; + case ST_QUA: retval = 2 + 4; break; + case ST_TET: retval = 2 + 4; break; + case ST_PYR: retval = 2 + 5; break; + case ST_WDG: retval = 2 + 6; break; + case ST_HEX: retval = 2 + 8; break; + } + return retval; + } - // Q: Do I need to explicitly provide a constructor to get it marked as AXOM_HOST_DEVICE? + TableData *m_shapeStart{nullptr}; + int m_offset{0}; + int m_currentShape{0}; + int m_numShapes{0}; + }; /** - * \brief Return the number of cases for the clipping table. + * \brief Constructor * - * \return The number of cases for the clipping table. + * \param shapes The number of shapes in each table case. + * \param offsets The offsets to each shape case in the \a table. + * \param table The table data that contains all cases. */ AXOM_HOST_DEVICE - size_t size() const { return m_shapes.size(); } + TableView(const IndexView &shapes, const IndexView &offsets, const TableDataView &table) : m_shapes(shapes), m_offsets(offsets), m_table(table) + { + } /** - * \brief Return the number of shapes for a given clipping case. - * - * \param caseId The index of the clipping case. + * \brief Return the number of cases for the clipping table. * - * \return The number of shapes in the clipping case. + * \return The number of cases for the clipping table. */ AXOM_HOST_DEVICE - size_t shapesForCase(size_t caseId) const - { - assert(caseId < m_shapes.size()); - return m_shapes[caseId]; - } + size_t size() const { return m_shapes.size(); } /** - * \brief Return data for the requested shape in the clipping case. - * - * \param index The index of the clipping case. + * \brief Return the iterator for the beginning of a case. * - * \return A container that holds the shape data for the case, probably a view. + * \param caseId The case whose begin iterator we want. + * \return The iterator at the begin of the case. */ AXOM_HOST_DEVICE - ClipDataContainerType getShape(size_t caseId, size_t shapeId) const + iterator begin(size_t caseId) const { assert(caseId < m_shapes.size()); - assert(shapeId < shapesForCase(caseId)); - - const auto *shapeStart = m_table.data() + m_offsets[caseId]; - size_t shapeLen = 0; - for(size_t i = 0; i < shapeId; i++) - { - shapeLen = advance(*shapeStart); - shapeStart += shapeLen; - } - shapeLen = advance(*shapeStart); - return ClipDataContainerType(shapeStart, shapeLen); + iterator it; + it.m_shapeStart = const_cast(m_table.data() + m_offsets[caseId]); + it.m_offset = 0; + it.m_currentShape = 0; + it.m_numShapes = m_shapes[caseId]; } /** - * \brief Given the input shape, return how many values to advance to get to the next shape. - * - * \param shape The shape type. + * \brief Return the iterator for the end of a case. * - * \return The number of values to advance. + * \param caseId The case whose end iterator we want. + * \return The iterator at the end of the case. */ AXOM_HOST_DEVICE - size_t advance(ClipDataType shape) const + iterator end(size_t caseId) const { - size_t retval = 0; - if(shape == ST_TRI) - retval = 2 + 3; - else if(shape == ST_QUA) - retval = 2 + 4; - else if(shape == ST_TET) - retval = 2 + 4; - else if(shape == ST_PYR) - retval = 2 + 5; - else if(shape == ST_WDG) - retval = 2 + 6; - else if(shape == ST_HEX) - retval = 2 + 8; - return retval; + assert(caseId < m_shapes.size()); + iterator it; + it.m_shapeStart = const_cast(m_table.data() + m_offsets[caseId]); + it.m_offset = 0; // not checked in iterator::operator== + it.m_currentShape = m_shapes[caseId]; + it.m_numShapes = m_shapes[caseId]; } - - ClipIndexContainerType m_shapes; - ClipIndexContainerType m_offsets; - ClipDataContainerType m_table; + +private: + IndexView m_shapes; // The number of shapes in each case. + IndexView m_offsets; // The offset to the case in the table. + TableDataView m_table; // The table data that contains the shapes. }; /** - * \brief This class contains data arrays for the clipping table and can produce a view for the data. + * \brief This class manages data table arrays and can produce a view for the data. */ template -struct ClipTable : public ClipTableBase, axom::Array> +class Table { - using IntContainerType = axom::Array; - using Uint8ContainerType = axom::Array; - using IntViewType = axom::ArrayView; - using Uint8ViewType = axom::ArrayView; - - using SuperClass = ClipTableBase; - using View = ClipTableBase; + using IndexData = int; + using TableData = unsigned char; + using IndexDataArray = axom::Array; + using TableDataArray = axom::Array; /** - * \brief Load clipping data into the arrays, moving data as needed. + * \brief Load table data into the arrays, moving data as needed. * * \param n The number of cases in the clip table. * \param shapes The number of shapes produced by clipping cases. @@ -130,19 +230,19 @@ struct ClipTable : public ClipTableBase, axom::Array::allocatorID(); // Allocate space. - SuperClass::m_shapes = IntContainerType(n, n, allocatorID); - SuperClass::m_offsets = IntContainerType(n, n, allocatorID); - SuperClass::m_table = Uint8ContainerType(tableLen, tableLen, allocatorID); + m_shapes = IndexDataArray(n, n, allocatorID); + m_offsets = IndexDataArray(n, n, allocatorID); + m_table = TableDataArray(tableLen, tableLen, allocatorID); // Copy data to the arrays. - axom::copy(SuperClass::m_shapes.data(), shapes, n * sizeof(int)); - axom::copy(SuperClass::m_offsets.data(), offsets, n * sizeof(int)); - axom::copy(SuperClass::m_table.data(), table, tableLen * sizeof(unsigned char)); + axom::copy(m_shapes.data(), shapes, n * sizeof(int)); + axom::copy(m_offsets.data(), offsets, n * sizeof(int)); + axom::copy(m_table.data(), table, tableLen * sizeof(unsigned char)); } /** @@ -150,15 +250,15 @@ struct ClipTable : public ClipTableBase, axom::Array class ClipTableManager { public: - using Table = ClipTable; - /** * \brief Constructor */ ClipTableManager() { for(size_t shape = ST_MIN; shape < ST_MAX; shape++) - m_clipTables[shapeToIndex(shape)] = ClipTable(); + m_tables[shapeToIndex(shape)] = ClipTable(); } /** @@ -186,13 +284,13 @@ class ClipTableManager * * \return A reference to the clipping table. */ - const Table &operator[](size_t shape) + const Table &operator[](size_t shape) { const auto index = shapeToIndex(shape); assert(shape < ST_MAX); assert(index >= 0); load(shape, 0); - return m_clipTables[index]; + return m_tables[index]; } /** @@ -226,7 +324,7 @@ class ClipTableManager * * \param shape The shape type ST_XXX. * - * \return An index into the m_clipTables array. + * \return An index into the m_tables array. */ size_t shapeToIndex(size_t shape) const { @@ -238,14 +336,14 @@ class ClipTableManager * * \param shape The shape whose table will be loaded. */ - void load(size_t shape, int) + void load(size_t shape) { const auto index = shapeToIndex(shape); - if(m_clipTables[index].size() == 0) + if(m_tables[index].size() == 0) { if(shape == ST_TRI) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTri, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesTri, axom::mir::clipping::visit::numClipShapesTri, axom::mir::clipping::visit::startClipShapesTri, axom::mir::clipping::visit::clipShapesTri, @@ -253,7 +351,7 @@ class ClipTableManager } else if(shape == ST_QUA) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesQua, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesQua, axom::mir::clipping::visit::numClipShapesQua, axom::mir::clipping::visit::startClipShapesQua, axom::mir::clipping::visit::clipShapesQua, @@ -261,7 +359,7 @@ class ClipTableManager } else if(shape == ST_TET) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesTet, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesTet, axom::mir::clipping::visit::numClipShapesTet, axom::mir::clipping::visit::startClipShapesTet, axom::mir::clipping::visit::clipShapesTet, @@ -269,7 +367,7 @@ class ClipTableManager } else if(shape == ST_PYR) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesPyr, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesPyr, axom::mir::clipping::visit::numClipShapesPyr, axom::mir::clipping::visit::startClipShapesPyr, axom::mir::clipping::visit::clipShapesPyr, @@ -277,7 +375,7 @@ class ClipTableManager } else if(shape == ST_WDG) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesWdg, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesWdg, axom::mir::clipping::visit::numClipShapesWdg, axom::mir::clipping::visit::startClipShapesWdg, axom::mir::clipping::visit::clipShapesWdg, @@ -285,7 +383,7 @@ class ClipTableManager } else if(shape == ST_HEX) { - m_clipTables[index].load(axom::mir::clipping::visit::numClipCasesHex, + m_tables[index].load(axom::mir::clipping::visit::numClipCasesHex, axom::mir::clipping::visit::numClipShapesHex, axom::mir::clipping::visit::startClipShapesHex, axom::mir::clipping::visit::clipShapesHex, @@ -294,7 +392,7 @@ class ClipTableManager } } - ClipTable m_clipTables[ST_MAX - ST_MIN]; + Table m_tables[ST_MAX - ST_MIN]; }; } // end namespace clipping diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index d91886fb03..2e33260cb5 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -46,6 +46,134 @@ struct accumulation_traits { using type = double; }; template <> struct accumulation_traits { using type = double; }; +//------------------------------------------------------------------------- +template +AXOM_HOST_DEVICE +int32 bsearch(T value, const axom::ArrayView &view) +{ + int32 index = -1; + int32 left = 0; + int32 right = view.size() - 1; + while(left <= right) + { + int32 m = (left + right) / 2; + if(view[m] < value) + left = m + 1; + else if(view[m] > value) + right = m - 1; + else + { + index = m; + break; + } + } + + return index; +} + +//------------------------------------------------------------------------------ +/// Based on a Jenkins hash, modified to include length and hash forwards and +/// backwards to make int64 rather than int32. +AXOM_HOST_DEVICE +uint64 hash_bytes(const uint8 *data, uint32 length) +{ + uint32 hash = 0; + + // Build the length into the hash. + const auto ldata = reinterpret_cast(&length); + for(int e = 0; e < 4; e++) + { + hash += ldata[e]; + hash += hash << 10; + hash ^= hash >> 6; + } + + uint32 hashr = hash; + for(uint32 i = 0; i < length; i++) + { + hash += data[i]; + hash += hash << 10; + hash ^= hash >> 6; + + hashr += data[length - 1 - i]; + hashr += hashr << 10; + hashr ^= hashr >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + + hashr += hashr << 3; + hashr ^= hashr >> 11; + hashr += hashr << 15; + + return (static_cast(hash) << 32) | hashr; +} + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE +uint32 +sort_values(ValueType *v, IndexType n) +{ + for(IndexType i = 0; i < n-1; i++) + { + const IndexType m = n - i - 1; + for(IndexType j = 0; j < m; j++) + { + if(v[j] > v[j+1]) + { + axom::utilities::swap(v[j], v[j+1]); + } + } + } +} + +//------------------------------------------------------------------------------ +template +uint64 +AXOM_HOST_DEVICE +make_name_1(ValueType id) +{ + return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); +}; + +//------------------------------------------------------------------------------ +template +AXOM_HOST_DEVICE +uint64 +make_name_2(ValueType id0, ValueType id1) +{ + ValueType data[2] = {id0, id1}; + if(id1 < id0) + { + data[0] = id1; + data[1] = id0; + } + return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); +}; + +//------------------------------------------------------------------------- +template +uint64 +AXOM_HOST_DEVICE +make_name_n(const ViewType &view, int32 start, int32 n) +{ + using value_type = typename ViewType::value_type; + + if(n == 2) + return make_name_2(view[start], view[start + 1]); + + value_type v[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // pick largest number of blends. + for(int32 i = 0; i < n; i++) + { + v[i] = view[start + i]; + } + sort_values(v, n); + + return hash_bytes(reinterpret_cast(v), n * sizeof(value_type)); +} + //------------------------------------------------------------------------------ /** * \brief This function makes a unique array of values from an input list of keys. diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 67e7f8405c..52aff8f62a 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -404,25 +404,25 @@ struct Shape : public ShapeTraits }; // Make some concrete shape classes based on the shape traits. -template +template using LineShape = Shape>; -template +template using TriShape = Shape>; -template +template using QuadShape = Shape>; -template +template using TetShape = Shape>; -template +template using PyramidShape = Shape>; -template +template using WedgeShape = Shape>; -template +template using HexShape = Shape>; } // end namespace views From fe3fc5664cf6ff2da2e2ca82a9a4c46c55d41dd3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 12 Jul 2024 15:38:51 -0700 Subject: [PATCH 085/290] Fix static_assert --- src/axom/core/utilities/BitUtilities.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index 546d367ccc..cc2e6f57da 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -213,7 +213,7 @@ template AXOM_HOST_DEVICE constexpr bool bitIsSet(FlagType flags, BitType bit) { - static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3))); + static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3)), "bit must be in range"); return (flags & (1 << bit)) > 0; } @@ -232,7 +232,7 @@ template AXOM_HOST_DEVICE constexpr void setBit(FlagType &flags, BitType bit, bool value = true) { - static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3))); + static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3)), "bit must be in range"); constexpr auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } From 52d03ef043c23357564cf667c124f81dccbc1071 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 12 Jul 2024 15:39:00 -0700 Subject: [PATCH 086/290] Fix compilation --- src/axom/mir/clipping/ClipTableManager.hpp | 84 +++++++++++----------- 1 file changed, 40 insertions(+), 44 deletions(-) diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 42394763d9..5f372899a0 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -6,8 +6,7 @@ #ifndef AXOM_MIR_CLIPPING_CLIP_TABLE_MANAGER_HPP_ #define AXOM_MIR_CLIPPING_CLIP_TABLE_MANAGER_HPP_ -#include "axom/core/Array.hpp" -#include "axom/core/ArrayView.hpp" +#include "axom/core.hpp" #include "axom/mir/clipping/ClipCases.h" namespace axom @@ -24,10 +23,11 @@ namespace clipping */ class TableView { +public: using IndexData = int; using TableData = unsigned char; using IndexView = axom::ArrayView; - using TableDataView = axom::ArrayView; + using TableDataView = axom::ArrayView; /** * \brief An iterator for shapes within a table case. @@ -110,7 +110,7 @@ class TableView * view so the caller can use the shape data. */ AXOM_HOST_DEVICE - inline ShapeDataView operator*() const + inline TableDataView operator*() const { TableData *ptr = m_shapeStart + m_offset; const auto len = shapeLength(ptr); @@ -179,12 +179,13 @@ class TableView AXOM_HOST_DEVICE iterator begin(size_t caseId) const { - assert(caseId < m_shapes.size()); + assert(static_cast(caseId) < m_shapes.size()); iterator it; it.m_shapeStart = const_cast(m_table.data() + m_offsets[caseId]); it.m_offset = 0; it.m_currentShape = 0; it.m_numShapes = m_shapes[caseId]; + return it; } /** @@ -196,12 +197,13 @@ class TableView AXOM_HOST_DEVICE iterator end(size_t caseId) const { - assert(caseId < m_shapes.size()); + assert(static_cast(caseId) < m_shapes.size()); iterator it; it.m_shapeStart = const_cast(m_table.data() + m_offsets[caseId]); it.m_offset = 0; // not checked in iterator::operator== it.m_currentShape = m_shapes[caseId]; it.m_numShapes = m_shapes[caseId]; + return it; } private: @@ -250,7 +252,7 @@ class Table * * \return A view of the table data. */ - TableView view() const + TableView view() { return TableView(m_shapes.view(), m_offsets.view(), m_table.view()); } @@ -268,14 +270,7 @@ template class ClipTableManager { public: - /** - * \brief Constructor - */ - ClipTableManager() - { - for(size_t shape = ST_MIN; shape < ST_MAX; shape++) - m_tables[shapeToIndex(shape)] = ClipTable(); - } + static constexpr int NumberOfTables = ST_MAX - ST_MIN; /** * \brief Return a reference to the clipping table, which is loaded on demand. @@ -289,17 +284,18 @@ class ClipTableManager const auto index = shapeToIndex(shape); assert(shape < ST_MAX); assert(index >= 0); - load(shape, 0); + loadShape(shape); return m_tables[index]; } /** * \brief Load tables based on dimension. + * \param dim The dimension of shapes to load. */ void load(int dim) { for(const auto shape : shapes(dim)) - load(shape, 0); + loadShape(shape); } /** @@ -326,7 +322,7 @@ class ClipTableManager * * \return An index into the m_tables array. */ - size_t shapeToIndex(size_t shape) const + constexpr static size_t shapeToIndex(size_t shape) { return shape - ST_MIN; } @@ -336,7 +332,7 @@ class ClipTableManager * * \param shape The shape whose table will be loaded. */ - void load(size_t shape) + void loadShape(size_t shape) { const auto index = shapeToIndex(shape); if(m_tables[index].size() == 0) @@ -344,55 +340,55 @@ class ClipTableManager if(shape == ST_TRI) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesTri, - axom::mir::clipping::visit::numClipShapesTri, - axom::mir::clipping::visit::startClipShapesTri, - axom::mir::clipping::visit::clipShapesTri, - axom::mir::clipping::visit::clipShapesTriSize); + axom::mir::clipping::visit::numClipShapesTri, + axom::mir::clipping::visit::startClipShapesTri, + axom::mir::clipping::visit::clipShapesTri, + axom::mir::clipping::visit::clipShapesTriSize); } else if(shape == ST_QUA) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesQua, - axom::mir::clipping::visit::numClipShapesQua, - axom::mir::clipping::visit::startClipShapesQua, - axom::mir::clipping::visit::clipShapesQua, - axom::mir::clipping::visit::clipShapesQuaSize); + axom::mir::clipping::visit::numClipShapesQua, + axom::mir::clipping::visit::startClipShapesQua, + axom::mir::clipping::visit::clipShapesQua, + axom::mir::clipping::visit::clipShapesQuaSize); } else if(shape == ST_TET) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesTet, - axom::mir::clipping::visit::numClipShapesTet, - axom::mir::clipping::visit::startClipShapesTet, - axom::mir::clipping::visit::clipShapesTet, - axom::mir::clipping::visit::clipShapesTetSize); + axom::mir::clipping::visit::numClipShapesTet, + axom::mir::clipping::visit::startClipShapesTet, + axom::mir::clipping::visit::clipShapesTet, + axom::mir::clipping::visit::clipShapesTetSize); } else if(shape == ST_PYR) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesPyr, - axom::mir::clipping::visit::numClipShapesPyr, - axom::mir::clipping::visit::startClipShapesPyr, - axom::mir::clipping::visit::clipShapesPyr, - axom::mir::clipping::visit::clipShapesTetSize); + axom::mir::clipping::visit::numClipShapesPyr, + axom::mir::clipping::visit::startClipShapesPyr, + axom::mir::clipping::visit::clipShapesPyr, + axom::mir::clipping::visit::clipShapesTetSize); } else if(shape == ST_WDG) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesWdg, - axom::mir::clipping::visit::numClipShapesWdg, - axom::mir::clipping::visit::startClipShapesWdg, - axom::mir::clipping::visit::clipShapesWdg, - axom::mir::clipping::visit::clipShapesWdgSize); + axom::mir::clipping::visit::numClipShapesWdg, + axom::mir::clipping::visit::startClipShapesWdg, + axom::mir::clipping::visit::clipShapesWdg, + axom::mir::clipping::visit::clipShapesWdgSize); } else if(shape == ST_HEX) { m_tables[index].load(axom::mir::clipping::visit::numClipCasesHex, - axom::mir::clipping::visit::numClipShapesHex, - axom::mir::clipping::visit::startClipShapesHex, - axom::mir::clipping::visit::clipShapesHex, - axom::mir::clipping::visit::clipShapesHexSize); + axom::mir::clipping::visit::numClipShapesHex, + axom::mir::clipping::visit::startClipShapesHex, + axom::mir::clipping::visit::clipShapesHex, + axom::mir::clipping::visit::clipShapesHexSize); } } } - Table m_tables[ST_MAX - ST_MIN]; + axom::StackArray, NumberOfTables> m_tables{}; }; } // end namespace clipping From 9a92055811e2ffdac2a5bb72fd0ed8f484dd3ca9 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 12 Jul 2024 18:34:55 -0700 Subject: [PATCH 087/290] Fixing some compilation errors --- src/axom/core/utilities/BitUtilities.hpp | 4 +- src/axom/mir/ClipField.hpp | 297 +++++++++++---------- src/axom/mir/CoordsetBlender.hpp | 12 +- src/axom/mir/EquiZAlgorithm.cpp | 8 +- src/axom/mir/FieldBlender.hpp | 25 +- src/axom/mir/FieldSlicer.hpp | 4 +- src/axom/mir/clipping/ClipTableManager.hpp | 8 + src/axom/mir/utilities.hpp | 127 ++++++--- src/axom/primal/geometry/Point.hpp | 124 --------- 9 files changed, 278 insertions(+), 331 deletions(-) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index cc2e6f57da..8b3618ef8c 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -213,7 +213,7 @@ template AXOM_HOST_DEVICE constexpr bool bitIsSet(FlagType flags, BitType bit) { - static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3)), "bit must be in range"); + assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); return (flags & (1 << bit)) > 0; } @@ -232,7 +232,7 @@ template AXOM_HOST_DEVICE constexpr void setBit(FlagType &flags, BitType bit, bool value = true) { - static_assert(bit >= 0 && (bit < (sizeof(BitType) << 3)), "bit must be in range"); + assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); constexpr auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index fce2bf49ec..86d3f75549 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -6,12 +6,14 @@ #define AXOM_MIR_CLIP_FIELD_HPP_ #include "axom/core.hpp" +#include "axom/mir/clipping/ClipCases.h" +#include "axom/mir/clipping/ClipTableManager.hpp" +#include "axom/mir/views/Shapes.hpp" #include "axom/mir/FieldBlender.hpp" #include "axom/mir/CoordsetBlender.hpp" #include "axom/mir/FieldSlicer.hpp" #include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit #include "axom/mir/utilities.hpp" // for cpp2conduit -#include "axom/primal/Point.hpp" #include #include @@ -58,30 +60,30 @@ shapeMap_ValueName(const conduit::Node &n_shape_map) } std::map -shapeMap_FromFlags(uint64 shapes) +shapeMap_FromFlags(std::uint64_t shapes) { std::map sm; +#if 0 + if((shapes & views::LineShape::id()) > 0) + sm["line"] = views::LineShape::id(); - if((shapes & LineShape::id()) > 0) - sm["line"] = LineShape::id(); + if((shapes & views::TriShape::id()) > 0) + sm["tri"] = views::TriShape::id(); - if((shapes & TriShape::id()) > 0) - sm["tri"] = TriShape::id(); + if((shapes & views::QuadShape::id()) > 0) + sm["quad"] = views::QuadShape::id(); - if((shapes & QuadShape::id()) > 0) - sm["quad"] = QuadShape::id(); + if((shapes & views::TetShape::id()) > 0) + sm["tet"] = views::TetShape::id(); - if((shapes & TetShape::id()) > 0) - sm["tet"] = TetShape::id(); + if((shapes & views::PyramidShape::id()) > 0) + sm["pyramid"] = views::PyramidShape::id(); - if((shapes & PyramidShape::id()) > 0) - sm["pyramid"] = PyramidShape::id(); - - if((shapes & WedgeShape::id()) > 0) - sm["wedge"] = WedgeShape::id(); - - if((shapes & HexShape::id()) > 0) - sm["hex"] = HexShape::id(); + if((shapes & views::WedgeShape::id()) > 0) + sm["wedge"] = views::WedgeShape::id(); +#endif + if((shapes & views::HexShape::id()) > 0) + sm["hex"] = views::HexShape::id(); return sm; } @@ -137,34 +139,12 @@ size_t clip_case(const ZoneType &zone, const ArrayViewType &view) const auto id = zone.getId(i); if(view[id] > 0) { - clipcase |= (1 << j); + clipcase |= (1 << i); } } return clipcase; } -//------------------------------------------------------------------------------ -template -constexpr size_t indexOfZoneType() { return 0; } - -template <> -constexpr size_t indexOfZoneType() { return 0; } - -template <> -constexpr size_t indexOfZoneType() { return 1; } - -template <> -constexpr size_t indexOfZoneType() { return 2; } - -template <> -constexpr size_t indexOfZoneType() { return 3; } - -template <> -constexpr size_t indexOfZoneType() { return 4; } - -template <> -constexpr size_t indexOfZoneType() { return 5; } - } // end namespace details /** @@ -175,6 +155,8 @@ template class ClipField { public: + using BlendData = axom::mir::utilities::blueprint::BlendData; + using SliceData = axom::mir::utilities::blueprint::SliceData; /* void execute(const conduit::Node &inputMesh, const std::string &clipField, conduit::Node &outputMesh) { @@ -186,41 +168,75 @@ class ClipField CoordsetView csview; } */ +private: + axom::mir::clipping::ClipTableManager m_clipTables{}; + using ClipTableViews = axom::StackArray; + + void createClipTableViews(ClipTableViews &views, int dimension) + { + if(dimension == 2) + { + views[0] = m_clipTables[ST_TRI].view(); + views[1] = m_clipTables[ST_QUA].view(); + } + if(dimension == 2) + { + views[2] = m_clipTables[ST_TET].view(); + views[3] = m_clipTables[ST_PYR].view(); + views[4] = m_clipTables[ST_WDG].view(); + views[5] = m_clipTables[ST_HEX].view(); + } + } +public: + void execute(const TopologyView &topoView, + const CoordsetView &coordsetView, + const conduit::Node &n_input, + const std::string &clipField, + conduit::Node &n_output) + { + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipField); + const std::string &topoName = n_clipField["topology"].as_string(); + const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); + const std::string &coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); + + execute(topoView, coordsetView, + n_topo, n_coordset, n_fields, + clipField, + n_output["topologies/" + topoName], + n_output["coordsets/" + coordsetName], + n_output["fields"]); + } void execute(const TopologyView &topoView, // I'd rather just pass the views to the method. const CoordsetView &coordsetView, - const conduit::Node &n_inputMesh, + const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, const std::string &clipField, - conduit::Node &n_outputMesh) + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) { - using KeyType = uint64; + using KeyType = typename BlendData::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); + using ConnectivityType = typename TopologyView::IndexType; + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + // Get the clip field. - conduit::Node &n_clip_field = n_inputMesh["fields"].fetch_existing(clipField); + const conduit::Node &n_clip_field = n_fields.fetch_existing(clipField); // TODO: process options (or add some class members to hold attributes) int selection = -1; // All bits set. - // Load clip table data and make views. - ClipTableManager clipTables; - clipTables.load(topoView.dimension()); - axom::StackArray::View, 6> clipTableViews; - if(topoView.dimension() == 2) - { - clipTableViews[details::indexOfZoneType()] = clipTables[ST_TRI].view(); - clipTableViews[details::indexOfZoneType()] = clipTables[ST_QUA].view(); - } - if(topoView.dimension() == 2) - { - clipTableViews[details::indexOfZoneType()] = clipTables[ST_TET].view(); - clipTableViews[details::indexOfZoneType()] = clipTables[ST_PYR].view(); - clipTableViews[details::indexOfZoneType()] = clipTables[ST_WDG].view(); - clipTableViews[details::indexOfZoneType()] = clipTables[ST_HEX].view(); - } + m_clipTables.load(topoView.dimension()); + ClipTableViews clipTableViews; + createClipTableViews(clipTableViews, topoView.dimension()); // ---------------------------------------------------------------------- // @@ -236,7 +252,7 @@ class ClipField // Allocate some memory const auto nzones = topoView.numberOfZones(); axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. - axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone + axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. @@ -244,7 +260,7 @@ class ClipField auto clipCasesView = clipCases.view(); auto pointsUsedView = pointsUsed.view(); - auto blendGroupsview = blendGroups.view(); + auto blendGroupsView = blendGroups.view(); auto blendGroupsLenView = blendGroupsLen.view(); auto fragmentsView = fragments.view(); auto fragmentsSizeView = fragmentsSize.view(); @@ -265,7 +281,7 @@ class ClipField int thisBlendGroupLen = 0; // The total length of the blend groups. int thisFragments = 0; // The number of zone fragments produced in this case. int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. - int64 ptused = 0; // A bitset indicating which ST_XX nodes are used. + std::uint64_t ptused = 0; // A bitset indicating which ST_XX nodes are used. auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -344,13 +360,13 @@ class ClipField // Save the results. blendGroupsView[zoneIndex] = thisBlendGroups; - blendGroupLenView[zoneIndex] = thisBlendGroupLen; + blendGroupsLenView[zoneIndex] = thisBlendGroupLen; fragmentsView[zoneIndex] = thisFragments; fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; // Sum up the sizes overall. fragment_sum += thisFragments; - fragment_nids_sum += thisFragmentNumIds; + fragment_nids_sum += thisFragmentsNumIds; blendGroups_sum += thisBlendGroups; blendGroupLen_sum += thisBlendGroupLen; }); @@ -376,23 +392,24 @@ class ClipField auto blendOffsetView = blendOffset.view(); auto blendGroupOffsetsView = blendGroupOffsets.view(); auto fragmentOffsetsView = fragmentOffsets.view(); + auto fragmentSizeOffsetsView = fragmentSizeOffsets.view(); // Make offsets via scan. - RAJA::exclusive_scan(RAJA::make_span(blendGroupLenView, nzones), - RAJA::make_span(blendOffsetView, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(blendGroupsLenView, nzones), + RAJA::make_span(blendOffsetView, nzones), + RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), - RAJA::make_span(blendGroupOffsetsView, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), + RAJA::make_span(blendGroupOffsetsView, nzones), + RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), - RAJA::make_span(fragmentOffsetsView, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), + RAJA::make_span(fragmentOffsetsView, nzones), + RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), - RAJA::make_span(fragmentSizeOffsetsView, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), + RAJA::make_span(fragmentSizeOffsetsView, nzones), + RAJA::operators::plus{}); // ---------------------------------------------------------------------- // @@ -407,9 +424,9 @@ class ClipField const auto blendGroupLenSize = blendGroupLen_sum.get(); axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); + axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); auto blendNamesView = blendNames.view(); @@ -422,6 +439,8 @@ class ClipField { topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + using ZoneType = decltype(zone); + // Get the clip case for the current zone. const auto clipcase = clipCasesView[zoneIndex]; @@ -429,11 +448,11 @@ class ClipField const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); const auto &ctView = clipTableViews[clipTableIndex]; - const int64 ptused = pointsUsedView[zoneIndex]; + const std::uint64_t ptused = pointsUsedView[zoneIndex]; // Starting offset of where we store this element's blend groups. - int32 bgStart = blendOffsetView[zoneIndex]; - int32 bgOffset = blendGroupOffsetsView[zoneIndex]; + std::uint32_t bgStart = blendOffsetView[zoneIndex]; + std::uint32_t bgOffset = blendGroupOffsetsView[zoneIndex]; auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -489,11 +508,11 @@ class ClipField // Store how many points make up this blend group. Note that the // size will not necessarily be equal to npts if edges were involved. - int32 nblended = bgStart - blendGroupStartView[bgOffset]; + std::uint32_t nblended = bgStart - blendGroupStartView[bgOffset]; blendGroupSizesView[bgOffset] = nblended; // Store "name" of blend group. - const auto blendName = make_name_n(blendIdsView, start, nblended); + const auto blendName = axom::mir::utilities::make_name_n(blendIdsView.data() + start, nblended); blendNamesView[bgOffset++] = blendName; } } @@ -504,7 +523,7 @@ class ClipField { if(axom::utilities::bitIsSet(ptused, pid)) { - // Store blend group info + // Store blend group info blendIdsView[bgStart] = zone.getId(pid); blendCoeffView[bgStart] = 1.; @@ -515,7 +534,7 @@ class ClipField blendGroupStartView[bgOffset] = bgStart; // Store "name" of blend group. - blendNamesView[bgOffset++] = make_name_1(zone.getId(pid)); + blendNamesView[bgOffset++] = axom::mir::utilities::make_name_1(zone.getId(pid)); bgStart++; } @@ -527,12 +546,12 @@ class ClipField if(axom::utilities::bitIsSet(ptused, pid)) { const auto edgeIndex = pid - EA; - const auto edge = ZoneTypeEdges::edges[edgeIndex]; - const auto id0 = zone.getId(c[0]); - const auto id1 = zone.getId(c[1]); + const auto edge = ZoneType::edges[edgeIndex]; + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const float t = details::computeT(clipFieldView[id0], clipfieldView[id1]); + const float t = details::computeT(clipFieldView[id0], clipFieldView[id1]); // Store blend group info blendIdsView[bgStart] = id0; @@ -547,7 +566,7 @@ class ClipField blendGroupStartView[bgOffset] = bgStart; // Store "name" of blend group. - blendNamesView[bgOffset++] = make_name_2(id0, id1); + blendNamesView[bgOffset++] = axom::mir::utilities::make_name_2(id0, id1); bgStart += 2; } @@ -566,7 +585,7 @@ class ClipField // blendNames/blendGroupOffsets/blendGroupSizes. axom::Array uNames; axom::Array uIndices; - unique(blendNames, uNames, uIndices); + axom::mir::utilities::unique(blendNames, uNames, uIndices); auto uNamesView = uNames.view(); auto uIndicesView = uIndices.view(); @@ -574,7 +593,7 @@ class ClipField // Bundle up blend data. axom::mir::utilities::blueprint::BlendData blend; blend.m_blendNamesView = uNames.view(); - blend.m_uniqueIdsView = uIndices.view(); + blend.m_uniqueIndicesView = uIndices.view(); blend.m_blendGroupSizesView = blendGroupSizes.view(); blend.m_blendGroupStartView = blendGroupStart.view(); blend.m_blendIdsView = blendIds.view(); @@ -591,56 +610,52 @@ class ClipField // Stage 5 - Make new connectivity. // // ---------------------------------------------------------------------- - conduit::Node &n_topologies = outputMesh["topologies"]; - conduit::Node &n_topo = n_topologies[topoView.name()]; - const auto finalNumZones = fragment_sum.get(); const auto finalConnSize = fragment_nids_sum.get(); - using ConnectivityType = typename topoView::IndexType; - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + n_newTopo.reset(); // Allocate connectivity. - conduit::Node &n_conn = n_topo["elements/connectivity"]; + conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(allocatorID); n_conn.set(conduit::DataType(connTypeID, finalConnSize)); auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); // Allocate shapes. - conduit::Node &n_shapes = n_topo["elements/shapes"]; + conduit::Node &n_shapes = n_newTopo["elements/shapes"]; n_shapes.set_allocator(allocatorID); n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); // Allocate sizes. - conduit::Node &n_sizes = n_topo["elements/sizes"]; + conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(allocatorID); n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); // Allocate offsets. - conduit::Node &n_offsets = n_topo["elements/offsets"]; + conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(allocatorID); n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); - const int32 uNames_len = uNames.size(); + const std::uint32_t uNames_len = uNames.size(); - RAJA::ReduceBitOr shapesUsed_reduce(0); + RAJA::ReduceBitOr shapesUsed_reduce(0); // Here we get the node ids from the unique blend names so we might be able to skip the zone iteration since that is more expensive than a normal axom::for_all. // I'd have to store the clipTableIndex though. -#if 1 +#if 0 AXOM_HOST_DEVICE - constexpr int32 encode_case(int32 clipcase, int32 clipTableIndex, int32 nFragments) + constexpr std::uint32_t encode_case(std::uint32_t clipcase, std::uint32_t clipTableIndex, std::uint32_t nFragments) { return (clipcase & 0xff) | ((clipTableIndex & 0xff) << 8) | ((nFragments & 0xff) << 16); } AXOM_HOST_DEVICE - constexpr void decode_case(int32 value, int32 &clipcase, int32 &clipTableIndex, int32 &nFragments) + constexpr void decode_case(std::uint32_t value, std::uint32_t &clipcase, std::uint32_t &clipTableIndex, std::uint32_t &nFragments) { clipcase = (value & 0xff); clipTableIndex = (value >> 8) & 0xff; @@ -656,20 +671,20 @@ class ClipField return; // Seek to the start of the blend groups for this zone. - const int32 bgStart = blendGroupOffsetsView[zoneIndex]; + std::uint32_t bgStart = blendGroupOffsetsView[zoneIndex]; // Go through the points in the order they would have been added as blend // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index // in the final points. - const int64 ptused = pointsUsedView[zoneIndex]; + const std::uint64_t ptused = pointsUsedView[zoneIndex]; ConnectivityType point_2_new[N3 + 1]; for(unsigned char pid = N0; pid <= N3; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); + point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); } } for(unsigned char pid = P0; pid <= P7; pid++) @@ -677,7 +692,7 @@ class ClipField if(axom::utilities::bitIsSet(ptused, pid)) { const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); + point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); } } for(unsigned char pid = EA; pid <= EL; pid++) @@ -685,7 +700,7 @@ class ClipField if(axom::utilities::bitIsSet(ptused, pid)) { const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = bsearch(name, uNamesView); + point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); } } @@ -693,13 +708,14 @@ class ClipField int outputIndex = fragmentSizeOffsetsView[zoneIndex]; // This is where the output fragment sizes/shapes start for this zone. int sizeIndex = fragmentOffsetsView[zoneIndex]; - uint64 shapesUsed = 0; + std::uint64_t shapesUsed = 0; // Iterate over the selected fragments and emit connectivity for them. const auto clipcase = clipCasesView[zoneIndex]; const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); const auto ctView = clipTableViews[clipTableIndex]; - it = ctView.begin(clipcase); + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); for(; it != end; it++) { // Get the current shape in the clip case. @@ -715,7 +731,7 @@ class ClipField const auto nIdsInZone = caseData.size() - 2; sizesView[sizeIndex] = nIdsInZone; - shapeView[sizeIndex] = zone.id(); + shapesView[sizeIndex] = zone.id(); sizeIndex++; // Record which shape type was used. (zone.id() are already powers of 2) @@ -728,7 +744,7 @@ class ClipField }); // Make offsets - RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), + RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), RAJA::make_span(offsetsView, nzones), RAJA::operators::plus{}); @@ -737,46 +753,46 @@ class ClipField const auto shapeMap = shapeMap_ValueName(shapesUsed); if(axom::utilities::countBits(shapesUsed) > 1) { - n_topo["elements/shape"] = "mixed"; - conduit::Node &n_shape_map = n_topo["elements/shape_map"]; - for(const auto &[key, value] : shapeMap) - n_shape_map[key] = value; + n_newTopo["elements/shape"] = "mixed"; + conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; + for(const auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) + n_shape_map[it->first] = it->second; } else { n_shapes.reset(); - n_topo["elements"].remove("shapes"); + n_newTopo["elements"].remove("shapes"); - n_topo["elements/shape"] = shapeMap.begin()->first; + n_newTopo["elements/shape"] = shapeMap.begin()->first; } //----------------------------------------------------------------------- // STAGE 6 - Make new coordset. //----------------------------------------------------------------------- - make_fields(blend, inputMesh["fields"], outputMesh["fields"]); + make_coordset(blend, coordsetView, n_coordset, n_newCoordset); //----------------------------------------------------------------------- - // STAGE 6 - Make new fields. + // STAGE 7 - Make new fields. //----------------------------------------------------------------------- - if(inputMesh.has_path("fields")) - { - make_fields(blend, slice, topoName, inputMesh["fields"], outputMesh["fields"]); - } + axom::mir::utilities::blueprint::SliceData slice; + make_fields(blend, slice, n_newTopo.name(), n_fields, n_newFields); //----------------------------------------------------------------------- - // STAGE 6 - make originalElements (this will later be optional) + // STAGE 8 - make originalElements (this will later be optional) //----------------------------------------------------------------------- - conduit::Node &n_fields = outputMesh["fields"]; if(n_fields.has_child("originalElements")) { // originalElements already exists. We need to map it forward. - conduit::Node &n_orig = n_fields["originalElements"]; - conduit::Node &n_orig_values = n_orig["values"]; + const conduit::Node &n_orig = n_fields["originalElements"]; + const conduit::Node &n_orig_values = n_orig["values"]; views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) { - using value_type = typename origValuesView::value_type; - conduit::Node n_values; + using value_type = typename decltype(origValuesView)::value_type; + conduit::Node &n_origElem = n_newFields["originalElements"]; + n_origElem["association"] = "element"; + n_origElem["topology"] = n_topo.name(); + conduit::Node &n_values = n_origElem["values"]; n_values.set_allocator(allocatorID); n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); @@ -787,14 +803,12 @@ class ClipField for(int i = 0; i < nFragments; i++) valuesView[sizeIndex + i] = origValuesView[zoneIndex]; }); - - n_orig_values.move(n_values); }); } else { // Make a new node and populate originalElement. - conduit::Node &n_orig = n_fields["originalElements"]; + conduit::Node &n_orig = n_newFields["originalElements"]; n_orig["association"] = "element"; n_orig["topology"] = n_topo.name(); conduit::Node &n_values = n_orig["values"]; @@ -814,10 +828,11 @@ class ClipField } // end of execute private: - void make_coordset(const BlendData &blend, const CoordsetView &csview, conduit::Node &n_new_coordset) const + void make_coordset(const BlendData &blend, const CoordsetView &coordsetView, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { axom::mir::utilities::blueprint::CoordsetBlender cb; - b.execute(blend, csview, n_coordset, n_new_coordset); + n_newCoordset.reset(); + cb.execute(blend, coordsetView, n_coordset, n_newCoordset); } void make_fields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index e14740580f..d54f209c71 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -8,7 +8,7 @@ #include "axom/core.hpp" #include "axom/mir/FieldBlender.hpp" // for BlendData #include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit -#include "axom/primal/Point.hpp" +#include "axom/primal/geometry/Point.hpp" #include #include @@ -64,13 +64,13 @@ class CoordsetBlender conduit::Node &n_values = n_output["values"]; // Make output nodes using axis names from the input coordset. Make array views too. - const auto outputSize = blend.m_uniqueIndices.size(); + const auto outputSize = blend.m_uniqueIndicesView.size(); axom::StackArray, PointType::NDIMS> compViews; for(size_t i = 0; i < nComponents; i++) { // Use the view's value_type - n_output[axisName[i]].set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); - auto *comp_data = static_cast(n_output.fetch_existing(axisName[i]).data_ptr()); + n_output[axes[i]].set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); + auto *comp_data = static_cast(n_output.fetch_existing(axes[i]).data_ptr()); compViews[i] = axom::ArrayView(comp_data, outputSize); } @@ -83,8 +83,8 @@ class CoordsetBlender const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; // Blend points for this blend group. - PointType blend{}; - for(int32 i = start; i < end; i++) + PointType blended{}; + for(std::uint32_t i = start; i < end; i++) { const auto index = blend.m_blendIdsView[i]; const auto weight = blend.m_blendCoeffView[i]; diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 0edada9380..a590b58a65 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -113,8 +113,6 @@ struct FieldView } } -#endif - template void EquiZAlgorithm::execute( @@ -224,7 +222,8 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, }); }); }); -#else +#endif +#if 0 if(options.has_path("zones")) { const conduit::Node &n_zones = options.fetch_existing("zones"); @@ -255,12 +254,10 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, views::IndexNode_to_ArrayView_same(rel["zones"], rel["sizes"], rel["offsets"], [&](auto relZonesView, auto relSizesView, auto relOffsetsView) { -#if 1 // We need to get views for the various shape types. using TableView = typename axom::mir::clipping::ClipTableManager::Table::View; axom::StackArray tables; tables[ST_TET] = clipManager[ST_TET].view(); -#endif topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) { @@ -268,7 +265,6 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, }); }); } - }); // dispatch_matset }); // dispatch_coordset }); // dispatch_topology diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 67c2d4c919..caeb590379 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -7,6 +7,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" +#include "axom/mir/utilities.hpp" #include @@ -24,13 +25,15 @@ namespace blueprint */ struct BlendData { - axom::ArrayView m_blendNamesView; // Contains unique hash ids for the sorted ids in each blend group. - axom::ArrayView m_uniqueIndicesView; // Contains the index into the original blend group data for a unique blend group. + using KeyType = std::uint64_t; - axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. - axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. - axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups - axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. + axom::ArrayView m_blendNamesView; // Contains unique hash ids for the sorted ids in each blend group. + axom::ArrayView m_uniqueIndicesView; // Contains the index into the original blend group data for a unique blend group. + + axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. + axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; /** @@ -84,16 +87,16 @@ class FieldBlender void blendSingleComponent(const BlendData &blend, const conduit::Node &n_values, conduit::Node &n_output_values) const { const auto allocatorID = axom::execution_space::allocatorID(); - const auto outputSize = blend.m_uniqueIndices.size(); + const auto outputSize = blend.m_uniqueIndicesView.size(); n_output_values.set_allocator(allocatorID); - n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize); + n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto compView, auto outView) { using value_type = typename decltype(compView)::value_type; - using accum_type = axom::mir::utilities::accumulation_traits::type; + using accum_type = typename axom::mir::utilities::accumulation_traits::type; - axom::for_all(nbg, AXOM_LAMBDA(auto bgid) + axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) { // Original blendGroup index. const auto origBGIdx = blend.m_uniqueIndicesView[bgid]; @@ -101,7 +104,7 @@ class FieldBlender const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; accum_type blended = 0; - for(int32 i = start; i < end; i++) + for(IndexType i = start; i < end; i++) { const auto index = blend.m_blendIdsView[i]; const auto weight = blend.m_blendCoeffView[i]; diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 789e1a77a7..f8ab51f341 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -24,7 +24,7 @@ namespace blueprint */ struct SliceData { - axom::ArrayView m_indices; + axom::ArrayView m_indicesView; }; /** @@ -79,7 +79,7 @@ class FieldSlicer void sliceSingleComponent(const SliceData &slice, const conduit::Node &n_values, conduit::Node &n_output_values) const { const auto allocatorID = axom::execution_space::allocatorID(); - const auto outputSize = slice.m_indices.size(); + const auto outputSize = slice.m_indicesView.size(); n_output_values.set_allocator(allocatorID); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 5f372899a0..7575730076 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -150,6 +150,14 @@ class TableView int m_numShapes{0}; }; + /** + * \brief Constructor + */ + AXOM_HOST_DEVICE + TableView() : m_shapes(), m_offsets(), m_table() + { + } + /** * \brief Constructor * diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 2e33260cb5..afcc910f58 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -6,15 +6,18 @@ #ifndef AXOM_MIR_UTILITIES_HPP_ #define AXOM_MIR_UTILITIES_HPP_ -#include "axom/core/Array.hpp" -#include "axom/core/ArrayView.hpp" -#include "axom/core/memory_management.hpp" +#include "axom/core.hpp" +//#include "axom/core/Array.hpp" +//#include "axom/core/ArrayView.hpp" +//#include "axom/core/memory_management.hpp" #include #include #include +#include + /// This header is for device utility functions for MIR. // Q: does this belong in core with a better name? @@ -46,17 +49,26 @@ struct accumulation_traits { using type = double; }; template <> struct accumulation_traits { using type = double; }; -//------------------------------------------------------------------------- +//------------------------------------------------------------------------------ +/** + * \brief Use binary search to find the index of the \a value in the supplied + * sorted view. + * + * \param[in] value The search value. + * \param[in] view The view that contains the sorted search data values. + * + * \return The index where value was located in view or -1 if not found. + */ template AXOM_HOST_DEVICE -int32 bsearch(T value, const axom::ArrayView &view) +std::int32_t bsearch(T value, const axom::ArrayView &view) { - int32 index = -1; - int32 left = 0; - int32 right = view.size() - 1; + std::int32_t index = -1; + std::int32_t left = 0; + std::int32_t right = view.size() - 1; while(left <= right) { - int32 m = (left + right) / 2; + std::int32_t m = (left + right) / 2; if(view[m] < value) left = m + 1; else if(view[m] > value) @@ -72,15 +84,27 @@ int32 bsearch(T value, const axom::ArrayView &view) } //------------------------------------------------------------------------------ -/// Based on a Jenkins hash, modified to include length and hash forwards and -/// backwards to make int64 rather than int32. +/** + * \brief Hash a stream of bytes into a uint64_t hash value. + * + * \param[in] data The bytes to be hashed. + * \param[in] length The number of bytes to be hashed. + * + * \return A uint64_t hash for the byte stream. + * + * \note The byte stream is hashed using a Jenkins-hash algorithm forwards and + * backwards and the two results are merged into a uint64_t. The length is + * also part of the hash to guard against a lot of repeated values in the + * byte stream hashing to the same thing. + * \ + */ AXOM_HOST_DEVICE -uint64 hash_bytes(const uint8 *data, uint32 length) +std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) { - uint32 hash = 0; + std::uint32_t hash = 0; // Build the length into the hash. - const auto ldata = reinterpret_cast(&length); + const auto ldata = reinterpret_cast(&length); for(int e = 0; e < 4; e++) { hash += ldata[e]; @@ -88,8 +112,8 @@ uint64 hash_bytes(const uint8 *data, uint32 length) hash ^= hash >> 6; } - uint32 hashr = hash; - for(uint32 i = 0; i < length; i++) + std::uint32_t hashr = hash; + for(std::uint32_t i = 0; i < length; i++) { hash += data[i]; hash += hash << 10; @@ -107,16 +131,20 @@ uint64 hash_bytes(const uint8 *data, uint32 length) hashr ^= hashr >> 11; hashr += hashr << 15; - return (static_cast(hash) << 32) | hashr; + return (static_cast(hash) << 32) | hashr; } //------------------------------------------------------------------------------ +/** + * \brief A very basic sort for small arrays. + * \param[inout] v The values to be sorted in place. + * \param[in] n The number of values to be sorted. + */ template AXOM_HOST_DEVICE -uint32 -sort_values(ValueType *v, IndexType n) +void sort_values(ValueType *v, IndexType n) { - for(IndexType i = 0; i < n-1; i++) + for(IndexType i = 0; i < n - 1; i++) { const IndexType m = n - i - 1; for(IndexType j = 0; j < m; j++) @@ -130,19 +158,30 @@ sort_values(ValueType *v, IndexType n) } //------------------------------------------------------------------------------ +/** + * \brief Make a hashed "name" for one id. + * + * \param[in] id The id we're hashing. + * \return A hashed name for the id. + */ template -uint64 AXOM_HOST_DEVICE -make_name_1(ValueType id) +std::uint64_t make_name_1(ValueType id) { - return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); + return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); }; //------------------------------------------------------------------------------ +/** + * \brief Make a hashed "name" for two ids. + * + * \param[in] id0 The first id we're hashing. + * \param[in] id1 The second id we're hashing. + * \return A hashed name for the ids. + */ template AXOM_HOST_DEVICE -uint64 -make_name_2(ValueType id0, ValueType id1) +std::uint64_t make_name_2(ValueType id0, ValueType id1) { ValueType data[2] = {id0, id1}; if(id1 < id0) @@ -150,28 +189,38 @@ make_name_2(ValueType id0, ValueType id1) data[0] = id1; data[1] = id0; } - return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); + return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); }; -//------------------------------------------------------------------------- -template -uint64 +//------------------------------------------------------------------------------ +/** + * \brief Make a hashed "name" for multiple ids. + * + * \tparam MaxValues The largest expected number of values. + * + * \param[in] values The ids that are being hashed. + * \param[in] start The starting index for ids. + * \param[in] n The number if ids being hashed. + * + * \return A hashed name for the ids. + */ +template AXOM_HOST_DEVICE -make_name_n(const ViewType &view, int32 start, int32 n) +std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) { - using value_type = typename ViewType::value_type; - + assert(n < MaxValues); if(n == 2) - return make_name_2(view[start], view[start + 1]); + return make_name_2(values[0], values[1]); - value_type v[14]={0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // pick largest number of blends. - for(int32 i = 0; i < n; i++) + // Make sure the values are sorted before hashing. + ValueType sorted[MaxValues]; + for(std::uint32_t i = 0; i < n; i++) { - v[i] = view[start + i]; + sorted[i] = values[i]; } - sort_values(v, n); + sort_values(sorted, n); - return hash_bytes(reinterpret_cast(v), n * sizeof(value_type)); + return hash_bytes(reinterpret_cast(sorted), n * sizeof(ValueType)); } //------------------------------------------------------------------------------ @@ -181,7 +230,7 @@ make_name_n(const ViewType &view, int32 start, int32 n) * \tparam ExecSpace The execution space. * \tparam KeyType The data type for the keys. * - * \param keys_orig_view The input view that contains the input keys to be made unique. + * \param[in] keys_orig_view The input view that contains the input keys to be made unique. * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. * \param[out] sindices An array of indices that indicate where in the original view the keys came from. * diff --git a/src/axom/primal/geometry/Point.hpp b/src/axom/primal/geometry/Point.hpp index 7af5749dce..819a5f15e6 100644 --- a/src/axom/primal/geometry/Point.hpp +++ b/src/axom/primal/geometry/Point.hpp @@ -179,54 +179,6 @@ class Point return !(lhs == rhs); } - /*! - * \brief Add a point to the current point and return the result. - * \param[in] obj The point that will be added to the current point. - * \return The sum of the points. - */ - AXOM_HOST_DEVICE - Point operator + (const Point &obj) const; - - /*! - * \brief Subtract a point from the current point and return the result. - * \param[in] obj The point that will be subtracted from the current point. - * \return The sum of the points. - */ - AXOM_HOST_DEVICE - Point operator - (const Point &obj) const; - - /*! - * \brief Scale the current point by a scale factor and return the result. - * \param[in] scale The scale factor. - * \return The scaled point. - */ - AXOM_HOST_DEVICE - Point operator * (const T scale) const; - - /*! - * \brief Scale the current point by dividing by a scale factor and return the result. - * \param[in] divisor The divisor. - * \return The scaled point. - */ - AXOM_HOST_DEVICE - Point operator / (const T divisor) const; - - /*! - * \brief Add a point to the current point, changing its value. - * \param[in] obj The point that will be added to the current point. - * \return The sum of the points. - */ - AXOM_HOST_DEVICE - Point operator += (const Point &obj); - - /*! - * \brief Add a point to the current point, changing its value. - * \param[in] obj The point that will be added to the current point. - * \return The sum of the points. - */ - AXOM_HOST_DEVICE - Point operator += (const Point &obj); - /*! * \brief Simple formatted print of a point instance * \param os The output stream to write to @@ -368,82 +320,6 @@ std::ostream& Point::print(std::ostream& os) const return os; } -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator + (const Point& obj) const -{ - PointType res; - for(int i = 0; i < NDIMS; ++i) - { - res[i] = m_components[i] + obj[i]; - } - return res; -} - -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator - (const Point& obj) const -{ - PointType res; - for(int i = 0; i < NDIMS; ++i) - { - res[i] = m_components[i] - obj[i]; - } - return res; -} - -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator * (const T scale) const -{ - PointType res; - for(int i = 0; i < NDIMS; ++i) - { - res[i] = m_components[i] * scale; - } - return res; -} - -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator / (const T divisor) const -{ - PointType res; - for(int i = 0; i < NDIMS; ++i) - { - res[i] = m_components[i] / divisor; - } - return res; -} - -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator += (const Point& obj) -{ - for(int i = 0; i < NDIMS; ++i) - { - m_components[i] += obj[i]; - } - return *this; -} - -//------------------------------------------------------------------------------ -template -AXOM_HOST_DEVICE inline Point -Point::operator -= (const Point& obj) -{ - for(int i = 0; i < NDIMS; ++i) - { - m_components[i] -= obj[i]; - } - return *this; -} - //------------------------------------------------------------------------------ /// Free functions implementing Point's operators //------------------------------------------------------------------------------ From 6deefe6eba36aeba07f3392cf5f347b0c15c6348 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 15 Jul 2024 12:21:24 -0700 Subject: [PATCH 088/290] Tests for bit utilities --- src/axom/core/tests/core_bit_utilities.hpp | 29 ++++++++++++++++++++++ src/axom/core/utilities/BitUtilities.hpp | 6 ++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/axom/core/tests/core_bit_utilities.hpp b/src/axom/core/tests/core_bit_utilities.hpp index af3cc03934..1c91942de2 100644 --- a/src/axom/core/tests/core_bit_utilities.hpp +++ b/src/axom/core/tests/core_bit_utilities.hpp @@ -180,3 +180,32 @@ TEST(core_bit_utilities, countl_zero) EXPECT_EQ(bit, axom::utilities::countl_zero(rand_val)); } } + +TEST(core_bit_utilities, setbit_bitisset) +{ + const std::uint32_t pattern = 0xaaaaaaaa; + for(size_t bit = 0; bit < axom::utilities::BitTraits::BITS_PER_WORD; bit++) + { + EXPECT_EQ(axom::utilities::bitIsSet(pattern, bit), ((bit & 1) == 1)); + } + + const bool bitvals[] = {false, true, true, false, true, true, false, true}; + std::uint8_t value = 0; + for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; i++) + { + axom::utilities::setBit(value, i, bitvals[i]); + for(size_t b = 0; b <= i; b++) + { + EXPECT_EQ(axom::utilities::bitIsSet(value, b), bitvals[b]); + } + } + + EXPECT_EQ(axom::utilities::countBits(pattern), 16); + EXPECT_EQ(axom::utilities::countBits(value), 5); + + for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; i++) + { + axom::utilities::setBit(value, i, false); + EXPECT_EQ(axom::utilities::bitIsSet(value, i), false); + } +} diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index 8b3618ef8c..b8a45370f9 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -211,7 +211,7 @@ AXOM_HOST_DEVICE inline std::int32_t countl_zero(std::int32_t word) noexcept */ template AXOM_HOST_DEVICE -constexpr bool bitIsSet(FlagType flags, BitType bit) +bool bitIsSet(FlagType flags, BitType bit) { assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); return (flags & (1 << bit)) > 0; @@ -230,10 +230,10 @@ constexpr bool bitIsSet(FlagType flags, BitType bit) */ template AXOM_HOST_DEVICE -constexpr void setBit(FlagType &flags, BitType bit, bool value = true) +void setBit(FlagType &flags, BitType bit, bool value = true) { assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); - constexpr auto mask = 1 << bit; + const auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } From 4d573b9eda399e6c3ff68127a7855ea503dc1da3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 15 Jul 2024 15:50:56 -0700 Subject: [PATCH 089/290] starting to add a test --- src/axom/mir/tests/CMakeLists.txt | 1 + src/axom/mir/tests/mir_clipfield.cpp | 53 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/axom/mir/tests/mir_clipfield.cpp diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 88eb91959e..6ae2f7a970 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -14,6 +14,7 @@ set(gtest_mir_tests mir_mesh.cpp mir_cell_clipper.cpp + mir_clipfield.cpp mir_utilities.cpp mir_views_indexing.cpp mir_cell_generator.cpp diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp new file mode 100644 index 0000000000..19c559792d --- /dev/null +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include + +using seq_exec = axom::SEQ_EXEC; + +TEST(mir_clipfield, uniform2d) +{ + using Indexing = axom::mir::views::StructuredIndexing; + using TopoView = axom::mir::views::StructuredTopologyView; + using CoordsetView = axom::mir::views::UniformCoordsetView; + + axom::StackArray dims{10, 10}; + + // Create the data + conduit::Node mesh; + conduit::blueprint::mesh::examples::braid("uniform", dims[0], dims[1], 0, mesh); + const conduit::Node &topo = mesh["topologies"][0]; + const conduit::Node &coordset = mesh["coordsets"][0]; + + // Create views + axom::StackArray origin{0., 0.}, spacing{1., 1.}; + CoordsetView coordsetView(dims, origin, spacing); + Indexing zoneIndexing(dims); + TopoView topoView(zoneIndexing); + + // Clip the data + conduit::Node clipmesh; + ClipField clipper; + clipper.execute(topoView, coordsetView, mesh, "radial", clipmesh; + conduit::blueprint::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); + + // Load a clipped baseline file & compare. +} + + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} From 154ec49a0eb89c3793ae599f0ae7a08eb31cdcc2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 15 Jul 2024 16:13:35 -0700 Subject: [PATCH 090/290] Add mit to axom-config.cmake.in --- src/cmake/axom-config.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmake/axom-config.cmake.in b/src/cmake/axom-config.cmake.in index 8e5ddf6e74..1cfe8269fe 100644 --- a/src/cmake/axom-config.cmake.in +++ b/src/cmake/axom-config.cmake.in @@ -42,6 +42,7 @@ if(NOT AXOM_FOUND) set(AXOM_ENABLE_KLEE "@AXOM_ENABLE_KLEE@") set(AXOM_ENABLE_LUMBERJACK "@AXOM_ENABLE_LUMBERJACK@") set(AXOM_ENABLE_MINT "@AXOM_ENABLE_MINT@") + set(AXOM_ENABLE_MIR "@AXOM_ENABLE_MIR@") set(AXOM_ENABLE_PRIMAL "@AXOM_ENABLE_PRIMAL@") set(AXOM_ENABLE_QUEST "@AXOM_ENABLE_QUEST@") set(AXOM_ENABLE_SIDRE "@AXOM_ENABLE_SIDRE@") From 9b143d1b3e476ddf3fa0e586194c005108177dfc Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 15 Jul 2024 17:59:49 -0700 Subject: [PATCH 091/290] Compilation fixes --- src/axom/mir/CMakeLists.txt | 2 + src/axom/mir/ClipField.hpp | 110 ++++++++------------ src/axom/mir/CoordsetBlender.hpp | 26 +++-- src/axom/mir/EquiZAlgorithm.cpp | 114 +-------------------- src/axom/mir/clipping/ClipTableManager.hpp | 14 ++- src/axom/mir/tests/mir_clipfield.cpp | 10 +- src/axom/mir/utilities.cpp | 53 ++++++++++ src/axom/mir/utilities.hpp | 47 ++------- src/axom/mir/views/MaterialView.cpp | 31 ++++++ src/axom/mir/views/MaterialView.hpp | 14 +-- src/axom/mir/views/UniformCoordsetView.hpp | 2 +- 11 files changed, 171 insertions(+), 252 deletions(-) create mode 100644 src/axom/mir/utilities.cpp create mode 100644 src/axom/mir/views/MaterialView.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index b9ac39a7c1..b8d3f60e79 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -78,6 +78,8 @@ set(mir_sources clipping/ClipCasesTet.cpp clipping/ClipCasesTri.cpp clipping/ClipCasesWdg.cpp + views/MaterialView.cpp + utilities.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 86d3f75549..aec1c48227 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -26,16 +26,6 @@ namespace clipping { namespace details { -template -std::map reverse_map(const std::map &m) -{ - std::map output; - for(const auto& [key, value] : m) - { - output[value] = key; - } - return output; -} std::map shapeMap_NameValue(const conduit::Node &n_shape_map) @@ -89,24 +79,24 @@ shapeMap_FromFlags(std::uint64_t shapes) } AXOM_HOST_DEVICE -constexpr int getClipTableIndex(int dimension, int nnodes) +int getClipTableIndex(int dimension, int nnodes) { return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); } AXOM_HOST_DEVICE -constexpr bool color0Selected(int selection) +bool color0Selected(int selection) { return axom::utilities::bitIsSet(selection, 0); } AXOM_HOST_DEVICE -constexpr bool color1Selected(int selection) +bool color1Selected(int selection) { return axom::utilities::bitIsSet(selection, 1); } AXOM_HOST_DEVICE -constexpr bool generatedPointIsSelected(unsigned char color, int selection) +bool generatedPointIsSelected(unsigned char color, int selection) { return color == NOCOLOR || (color0Selected(selection) && color == COLOR0) || @@ -114,7 +104,7 @@ constexpr bool generatedPointIsSelected(unsigned char color, int selection) } AXOM_HOST_DEVICE -constexpr bool shapeIsSelected(unsigned char color, int selection) +bool shapeIsSelected(unsigned char color, int selection) { return (color0Selected(selection) && color == COLOR0) || (color1Selected(selection) && color == COLOR1); @@ -188,9 +178,11 @@ class ClipField } } public: - void execute(const TopologyView &topoView, - const CoordsetView &coordsetView, - const conduit::Node &n_input, + ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) : m_topologyView(topoView), m_coordsetView(coordsetView) + { + } + + void execute(const conduit::Node &n_input, const std::string &clipField, conduit::Node &n_output) { @@ -201,17 +193,14 @@ class ClipField const std::string &coordsetName = n_topo["coordset"].as_string(); const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); - execute(topoView, coordsetView, - n_topo, n_coordset, n_fields, + execute(n_topo, n_coordset, n_fields, clipField, n_output["topologies/" + topoName], n_output["coordsets/" + coordsetName], n_output["fields"]); } - void execute(const TopologyView &topoView, // I'd rather just pass the views to the method. - const CoordsetView &coordsetView, - const conduit::Node &n_topo, + void execute(const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, const std::string &clipField, @@ -234,9 +223,9 @@ class ClipField int selection = -1; // All bits set. // Load clip table data and make views. - m_clipTables.load(topoView.dimension()); + m_clipTables.load(m_topologyView.dimension()); ClipTableViews clipTableViews; - createClipTableViews(clipTableViews, topoView.dimension()); + createClipTableViews(clipTableViews, m_topologyView.dimension()); // ---------------------------------------------------------------------- // @@ -250,7 +239,7 @@ class ClipField RAJA::ReduceSum blendGroupLen_sum(0); // Allocate some memory - const auto nzones = topoView.numberOfZones(); + const auto nzones = m_topologyView.numberOfZones(); axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. @@ -267,7 +256,7 @@ class ClipField views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) { - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = details::clip_case(zone, clipFieldView); @@ -331,7 +320,7 @@ class ClipField thisFragmentsNumIds += nIdsThisShape; // Mark the points in this fragment used. - for(size_t i = 2; i < caseData.size(); i++) + for(int i = 2; i < caseData.size(); i++) { axom::utilities::setBit(ptused, caseData[i]); } @@ -395,20 +384,20 @@ class ClipField auto fragmentSizeOffsetsView = fragmentSizeOffsets.view(); // Make offsets via scan. - RAJA::exclusive_scan(RAJA::make_span(blendGroupsLenView, nzones), - RAJA::make_span(blendOffsetView, nzones), + RAJA::exclusive_scan(RAJA::make_span(blendGroupsLenView.data(), nzones), + RAJA::make_span(blendOffsetView.data(), nzones), RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(blendGroupsView, nzones), - RAJA::make_span(blendGroupOffsetsView, nzones), + RAJA::exclusive_scan(RAJA::make_span(blendGroupsView.data(), nzones), + RAJA::make_span(blendGroupOffsetsView.data(), nzones), RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(fragmentsView, nzones), - RAJA::make_span(fragmentOffsetsView, nzones), + RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), + RAJA::make_span(fragmentOffsetsView.data(), nzones), RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView, nzones), - RAJA::make_span(fragmentSizeOffsetsView, nzones), + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), + RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), RAJA::operators::plus{}); // ---------------------------------------------------------------------- @@ -437,9 +426,9 @@ class ClipField views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) { - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - using ZoneType = decltype(zone); + using ZoneType = typename std::remove_reference::type; // Get the clip case for the current zone. const auto clipcase = clipCasesView[zoneIndex]; @@ -585,7 +574,7 @@ class ClipField // blendNames/blendGroupOffsets/blendGroupSizes. axom::Array uNames; axom::Array uIndices; - axom::mir::utilities::unique(blendNames, uNames, uIndices); + axom::mir::utilities::unique(blendNames, uNames, uIndices); auto uNamesView = uNames.view(); auto uIndicesView = uIndices.view(); @@ -644,27 +633,8 @@ class ClipField RAJA::ReduceBitOr shapesUsed_reduce(0); - // Here we get the node ids from the unique blend names so we might be able to skip the zone iteration since that is more expensive than a normal axom::for_all. - // I'd have to store the clipTableIndex though. -#if 0 - AXOM_HOST_DEVICE - constexpr std::uint32_t encode_case(std::uint32_t clipcase, std::uint32_t clipTableIndex, std::uint32_t nFragments) - { - return (clipcase & 0xff) | - ((clipTableIndex & 0xff) << 8) | - ((nFragments & 0xff) << 16); - } - AXOM_HOST_DEVICE - constexpr void decode_case(std::uint32_t value, std::uint32_t &clipcase, std::uint32_t &clipTableIndex, std::uint32_t &nFragments) - { - clipcase = (value & 0xff); - clipTableIndex = (value >> 8) & 0xff; - nFragments = (value >> 16) & 0xff; - } - // Of course, I can't do an exclusive scan on the fragmentsView if it contains other stuff. - -#endif - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + // Here we get the node ids from the unique blend names, de-duplicating points. + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. if(fragmentsView[zoneIndex] == 0) @@ -744,18 +714,18 @@ class ClipField }); // Make offsets - RAJA::exclusive_scan(RAJA::make_span(sizesView, nzones), - RAJA::make_span(offsetsView, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), nzones), + RAJA::make_span(offsetsView.data(), nzones), + RAJA::operators::plus{}); // Add shape information to the connectivity. const auto shapesUsed = shapesUsed_reduce.get(); - const auto shapeMap = shapeMap_ValueName(shapesUsed); + const auto shapeMap = details::shapeMap_FromFlags(shapesUsed); if(axom::utilities::countBits(shapesUsed) > 1) { n_newTopo["elements/shape"] = "mixed"; conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; - for(const auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) + for(auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) n_shape_map[it->first] = it->second; } else @@ -770,7 +740,7 @@ class ClipField // STAGE 6 - Make new coordset. //----------------------------------------------------------------------- - make_coordset(blend, coordsetView, n_coordset, n_newCoordset); + make_coordset(blend, n_coordset, n_newCoordset); //----------------------------------------------------------------------- // STAGE 7 - Make new fields. @@ -828,11 +798,11 @@ class ClipField } // end of execute private: - void make_coordset(const BlendData &blend, const CoordsetView &coordsetView, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const + void make_coordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { axom::mir::utilities::blueprint::CoordsetBlender cb; n_newCoordset.reset(); - cb.execute(blend, coordsetView, n_coordset, n_newCoordset); + cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } void make_fields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const @@ -846,7 +816,7 @@ class ClipField if(association == "element") { axom::mir::utilities::blueprint::FieldSlicer s; - s.execute(blend, n_field, n_out_fields[n_field.name()]); + s.execute(slice, n_field, n_out_fields[n_field.name()]); } else if(association == "vertex") { @@ -858,6 +828,8 @@ class ClipField } // NOTE: I probably want to make the clip tables be a class member so I can reuse the object without having to reload the clip tables each time. + TopologyView m_topologyView; + CoordsetView m_coordsetView; }; } // end namespace clipping diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index d54f209c71..7436a32283 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -9,6 +9,7 @@ #include "axom/mir/FieldBlender.hpp" // for BlendData #include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit #include "axom/primal/geometry/Point.hpp" +#include "axom/primal/geometry/Vector.hpp" #include #include @@ -51,13 +52,14 @@ class CoordsetBlender */ void execute(const BlendData &blend, const CoordsetViewType &view, const conduit::Node &n_input, conduit::Node &n_output) const { - using value_type = typename decltype(view)::value_type; - using PointType = typename decltype(view)::PointType; + using value_type = typename CoordsetViewType::value_type; + using PointType = typename CoordsetViewType::PointType; + using VectorType = axom::primal::Vector; const auto allocatorID = axom::execution_space::allocatorID(); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); const auto nComponents = axes.size(); - assert(PointType::NDIMS == nComponents); + assert(PointType::DIMENSION == nComponents); n_output.reset(); n_output["type"] = "explicit"; @@ -65,12 +67,14 @@ class CoordsetBlender // Make output nodes using axis names from the input coordset. Make array views too. const auto outputSize = blend.m_uniqueIndicesView.size(); - axom::StackArray, PointType::NDIMS> compViews; + axom::StackArray, PointType::DIMENSION> compViews; for(size_t i = 0; i < nComponents; i++) { - // Use the view's value_type - n_output[axes[i]].set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); - auto *comp_data = static_cast(n_output.fetch_existing(axes[i]).data_ptr()); + // Allocate data in the Conduit node and make a view. + conduit::Node &comp = n_output[axes[i]]; + comp.set_allocator(allocatorID); + comp.set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); + auto *comp_data = static_cast(comp.data_ptr()); compViews[i] = axom::ArrayView(comp_data, outputSize); } @@ -83,17 +87,17 @@ class CoordsetBlender const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; // Blend points for this blend group. - PointType blended{}; - for(std::uint32_t i = start; i < end; i++) + VectorType blended{}; + for(IndexType i = start; i < end; i++) { const auto index = blend.m_blendIdsView[i]; const auto weight = blend.m_blendCoeffView[i]; - blended += (view[index] * weight); + blended += (VectorType(view[index]) * static_cast(weight)); } // Store the point into the Conduit component arrays. - for(int comp = 0; comp < PointType::NDIMS; comp++) + for(int comp = 0; comp < PointType::DIMENSION; comp++) { compViews[comp][bgid] = blended[comp]; } diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index a590b58a65..add59a5ec5 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -59,6 +59,7 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, conduit::Node &new_coordset, conduit::Node &new_matset) { +#if 0 #if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) switch(m_execPolicy) { @@ -84,122 +85,9 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, break; } #endif -} - -//------------------------------------------------------------------------------ -#if 0 -// NOTE: it might be useful to combine some things for simpler templating. -template -struct DomainView -{ - TopologyView topoView; - CoordsetView coordsetView; -}; - -// Q: if we could make a FieldView, what would it look like? - -struct FieldView -{ - template - void for_each_field(conduit::Node &n_fields, FuncType &&func) - { - for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) - { - views::Node_to_ArrayView(n_fields[i], [&](auto arrayView) - { - func(n_fields[i], arrayView); - } - } - } -} - -template -void -EquiZAlgorithm::execute( - const TopologyView &topoView, - const CoordsetView &coordsetView, - const conduit::Node &options) -{ -} -//---------------------------------------------------------------------------------------- #endif - -#if 0 -/// Provide overloads of initialize_topology_view for every possible topology type. -template -void initialize_topology_view(const std::conduit::Node &topo, StructuredTopologyView> &topoView) -{ - // Initialize the view from the Conduit node. -} - -template -void -EquiZAlgorithm::execute( - const TopologyView &topoView, - const CoordsetView &coordsetView, - const MatsetView &matsetView, - const conduit::Node &options) -{ - if(options.has_path("zones")) - { - const conduit::Node &n_zones = options.fetch_existing("zones"); - -/// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? - - // Operate on a list of zones. - views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) - { - MaterialInformation matinfo = materials(matset); - for(const auto &mat : matinfo) - { - const auto matID = mat.number; - - // Going this way, the relation builder should take in the topoView to do its work. - axom::mir::utilities::NodeToZoneRelationBuilder nz; - nz.execute(topoView); - - const auto relZonesView = nz.zones().view(); - const auto relSizesView = nz.sizes().view(); - const auto relOffsetsView = nz.offsets().view(); - - // Create the clipping tables for the topo dimension. - axom::mir::clipping::ClipTableManager clipManager; - clipManager.load(topoView.dimension()); - - // We need to get views for the various shape types. - axom::StackArray - - topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - - }); - } - }); - } - else - { - // Operate on all zones. - - topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - - }); - } } - -// I'm starting to want to just run EquiZ on a certain execution space with a certain input mesh rather than all of them... -// -// EquiZAlgorithm>> mir; -// mir.execute(topo, coordset, matset, options, new_topo, new_coordset, new_matset); -// -// But what about fields?? -// -// mir.execute(mesh, options, output); -// mir.execute(mesh, options, output["topologies/topo"], output["coordset/newcoords"], output["matsets/newmatset"]); -// mir.execute(mesh, options, output["topologies/topo"], output["coordset/newcoords"], output["matsets/newmatset"], output["fields"]); -#endif - template void EquiZAlgorithm::executeImpl(const conduit::Node &topo, const conduit::Node &coordset, diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 7575730076..157b652052 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -226,11 +226,21 @@ class TableView template class Table { +public: using IndexData = int; using TableData = unsigned char; using IndexDataArray = axom::Array; using TableDataArray = axom::Array; + /** + * \brief Returns whether the table data have been loaded. + * \return True if the data have been loaded; false otherwise. + */ + bool isLoaded() const + { + return m_shapes.size() > 0; + } + /** * \brief Load table data into the arrays, moving data as needed. * @@ -287,7 +297,7 @@ class ClipTableManager * * \return A reference to the clipping table. */ - const Table &operator[](size_t shape) + Table &operator[](size_t shape) { const auto index = shapeToIndex(shape); assert(shape < ST_MAX); @@ -343,7 +353,7 @@ class ClipTableManager void loadShape(size_t shape) { const auto index = shapeToIndex(shape); - if(m_tables[index].size() == 0) + if(!m_tables[index].isLoaded()) { if(shape == ST_TRI) { diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 19c559792d..d55fde97d8 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -3,10 +3,14 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +#include "gtest/gtest.h" + #include "axom/core.hpp" #include "axom/mir.hpp" + #include + using seq_exec = axom::SEQ_EXEC; TEST(mir_clipfield, uniform2d) @@ -31,9 +35,9 @@ TEST(mir_clipfield, uniform2d) // Clip the data conduit::Node clipmesh; - ClipField clipper; - clipper.execute(topoView, coordsetView, mesh, "radial", clipmesh; - conduit::blueprint::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(mesh, "radial", clipmesh); + conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); // Load a clipped baseline file & compare. } diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp new file mode 100644 index 0000000000..794c1fd0a1 --- /dev/null +++ b/src/axom/mir/utilities.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/mir/utilities.hpp" + +namespace axom +{ +namespace mir +{ +namespace utilities +{ + +AXOM_HOST_DEVICE +std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) +{ + std::uint32_t hash = 0; + + // Build the length into the hash. + const auto ldata = reinterpret_cast(&length); + for(int e = 0; e < 4; e++) + { + hash += ldata[e]; + hash += hash << 10; + hash ^= hash >> 6; + } + + std::uint32_t hashr = hash; + for(std::uint32_t i = 0; i < length; i++) + { + hash += data[i]; + hash += hash << 10; + hash ^= hash >> 6; + + hashr += data[length - 1 - i]; + hashr += hashr << 10; + hashr ^= hashr >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + + hashr += hashr << 3; + hashr ^= hashr >> 11; + hashr += hashr << 15; + + return (static_cast(hash) << 32) | hashr; +} + +} // end namespace utilities +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index afcc910f58..091d442af6 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -99,40 +99,7 @@ std::int32_t bsearch(T value, const axom::ArrayView &view) * \ */ AXOM_HOST_DEVICE -std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) -{ - std::uint32_t hash = 0; - - // Build the length into the hash. - const auto ldata = reinterpret_cast(&length); - for(int e = 0; e < 4; e++) - { - hash += ldata[e]; - hash += hash << 10; - hash ^= hash >> 6; - } - - std::uint32_t hashr = hash; - for(std::uint32_t i = 0; i < length; i++) - { - hash += data[i]; - hash += hash << 10; - hash ^= hash >> 6; - - hashr += data[length - 1 - i]; - hashr += hashr << 10; - hashr ^= hashr >> 6; - } - hash += hash << 3; - hash ^= hash >> 11; - hash += hash << 15; - - hashr += hashr << 3; - hashr ^= hashr >> 11; - hashr += hashr << 15; - - return (static_cast(hash) << 32) | hashr; -} +std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length); //------------------------------------------------------------------------------ /** @@ -240,7 +207,7 @@ std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) template void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) { - using for_policy = typename axom::execution_space::for_policy; + using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; const int allocatorID = axom::execution_space::allocatorID(); @@ -257,8 +224,8 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array }); // Sort the keys, indices in place. - RAJA::sort_pairs(RAJA::make_span(keys_view, n), - RAJA::make_span(indices_view, n)); + RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), + RAJA::make_span(indices_view.data(), n)); // Make a mask array for where differences occur. axom::Array mask(n, n, allocatorID); @@ -275,9 +242,9 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array // Do a scan on the mask array to build an offset array. axom::Array offsets(n, n, allocatorID); auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view, n), - RAJA::make_span(offsets_view, n), - RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(mask_view.data(), n), + RAJA::make_span(offsets_view.data(), n), + RAJA::operators::plus{}); // Allocate the output arrays. const axom::IndexType newsize = mask_sum.get(); diff --git a/src/axom/mir/views/MaterialView.cpp b/src/axom/mir/views/MaterialView.cpp new file mode 100644 index 0000000000..26883f093d --- /dev/null +++ b/src/axom/mir/views/MaterialView.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/mir/views/MaterialView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +MaterialInformation materials(const conduit::Node &matset) +{ + MaterialInformation info; + if(matset.has_child("material_map")) + { + const conduit::Node &mm = matset["material_map"]; + for(conduit::index_t i = 0; i < mm.number_of_children(); i++) + { + info.push_back(Material{static_cast(i), mm[i].name()}); + } + } + return info; +} + +} // end namespace views +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 95475ff0cc..5e8a3ba4f3 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -35,19 +35,7 @@ struct Material using MaterialInformation = std::vector; -MaterialInformation materials(const conduit::Node &matset) -{ - MaterialInformation info; - if(matset.has_child("material_map")) - { - const conduit::Node &mm = matset["material_map"]; - for(conduit::index_t i = 0; i < mm.number_of_children(); i++) - { - info.push_back(Material{static_cast(i), mm[i].name()}); - } - } - return info; -} +MaterialInformation materials(const conduit::Node &matset); //--------------------------------------------------------------------------- // Material views - These objects are meant to wrap Blueprint Matsets behind diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index c31061c72a..4fba388a51 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -80,7 +80,7 @@ class UniformCoordsetView { PointType pt; for(int i = 0; i < NDIMS; i++) - pt.x[i] = m_origin[i] + vertex_index[i] * m_spacing[i]; + pt[i] = m_origin[i] + vertex_index[i] * m_spacing[i]; return pt; } From a2fa467eaca87c68b2013c855f38253de51e0c56 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 15 Jul 2024 18:17:16 -0700 Subject: [PATCH 092/290] Change accessing edges --- src/axom/mir/ClipField.hpp | 4 +-- src/axom/mir/views/Shapes.hpp | 49 ++++++++++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index aec1c48227..07f9ed85ab 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -479,7 +479,7 @@ class ClipField // face point. We can store the 2 corner points in place // of the edge point (along with some blending coeff). const auto edgeIndex = ptid - EA; - const auto edge = ZoneType::edges[edgeIndex]; + const auto edge = ZoneType::getEdge(edgeIndex); const auto id0 = zone.getId(edge[0]); const auto id1 = zone.getId(edge[1]); @@ -535,7 +535,7 @@ class ClipField if(axom::utilities::bitIsSet(ptused, pid)) { const auto edgeIndex = pid - EA; - const auto edge = ZoneType::edges[edgeIndex]; + const auto edge = ZoneType::getEdge(edgeIndex); const auto id0 = zone.getId(edge[0]); const auto id1 = zone.getId(edge[1]); diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 52aff8f62a..4668b519ef 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -89,7 +89,11 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0, 1, 2}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,0}}; + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}}; + return edges[edgeIndex]; + } }; /* @@ -122,7 +126,12 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}}; + return edges[edgeIndex]; + } }; /* @@ -171,6 +180,14 @@ struct PolygonShape { return m_ids; } + + AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) const + { + const auto p0 = edgeIndex % m_ids.size(); + const auto p1 = (edgeIndex + 1) % m_ids.size(); + return axom::StackArray{p0, p1}; + } + private: axom::ArrayView m_ids; }; @@ -212,7 +229,12 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0, 1}, {1, 2}, {2, 0},{0, 3}, {1, 3}, {2, 3}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 0},{0, 3}, {1, 3}, {2, 3}}; + return edges[edgeIndex]; + } }; /* @@ -253,7 +275,12 @@ struct PyramidTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static int faces[][4] = {{3,2,1,0}, {0,1,4,-1}, {1,2,4,-1}, {2,3,4,-1}, {3,0,4,-1}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,3}, {3,0}, {0,4}, {1,4}, {2,4}, {3,4}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}, {0,4}, {1,4}, {2,4}, {3,4}}; + return edges[edgeIndex]; + } }; /* @@ -296,7 +323,12 @@ struct WedgeTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static int faces[][4] = {{0,2,1,-1}, {3,4,5,-1}, {0,1,4,3}, {1,2,5,4}, {2,0,3,5}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,3}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,3}}; + return edges[edgeIndex]; + } }; /* @@ -337,7 +369,12 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = { {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0, 1}, {1, 2}, {3, 2}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + const axom::StackArray edges[] = {{0, 1}, {1, 2}, {3, 2}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; + return edges[edgeIndex]; + } }; /** From 0930c1cf0e5599a9f6afc305c9028900a8c7ecf4 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 16 Jul 2024 15:21:38 -0700 Subject: [PATCH 093/290] Fixed some coordset views. Added distance field --- src/axom/mir/tests/mir_clipfield.cpp | 39 +++++++++++++++++-- src/axom/mir/views/ExplicitCoordsetView.hpp | 12 +++--- .../mir/views/RectilinearCoordsetView.hpp | 12 +++--- src/axom/mir/views/dispatch_coordset.hpp | 17 ++++---- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index d55fde97d8..912f3fd8c0 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -9,10 +9,41 @@ #include "axom/mir.hpp" #include - +#include using seq_exec = axom::SEQ_EXEC; +template +void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) +{ + int d[3] = {0, 0, 0}; + for(int i = 0; i < dims.size(); i++) + d[i] = dims[i]; + conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); + + // Make a new distance field. + const float dist = 6.5f; + const conduit::Node &n_coordset = mesh["coordsets"][0]; + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) + { + mesh["fields/distance/topology"] = "mesh"; + mesh["fields/distance/association"] = "vertex"; + conduit::Node &n_values = mesh["fields/distance/values"]; + const auto nnodes = coordsetView.size(); + n_values.set(conduit::DataType::float32(nnodes)); + float *valuesPtr = static_cast(n_values.data_ptr()); + for(int index = 0; index < nnodes; index++) + { + const auto pt = coordsetView[index]; + float norm2 = 0.f; + for(int i = 0; i < pt.DIMENSION; i++) + norm2 += pt[i] * pt[i]; + valuesPtr[index] = sqrt(norm2) - dist; + } + }); +} + + TEST(mir_clipfield, uniform2d) { using Indexing = axom::mir::views::StructuredIndexing; @@ -23,7 +54,9 @@ TEST(mir_clipfield, uniform2d) // Create the data conduit::Node mesh; - conduit::blueprint::mesh::examples::braid("uniform", dims[0], dims[1], 0, mesh); + braid("uniform", dims, mesh); + conduit::relay::io::blueprint::save_mesh(mesh, "uniform2d_orig", "hdf5"); + mesh.print(); const conduit::Node &topo = mesh["topologies"][0]; const conduit::Node &coordset = mesh["coordsets"][0]; @@ -36,7 +69,7 @@ TEST(mir_clipfield, uniform2d) // Clip the data conduit::Node clipmesh; axom::mir::clipping::ClipField clipper(topoView, coordsetView); - clipper.execute(mesh, "radial", clipmesh); + clipper.execute(mesh, "distance", clipmesh); conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); // Load a clipped baseline file & compare. diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 11e8153889..18c469c4a8 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -9,6 +9,8 @@ #include "axom/core/ArrayView.hpp" #include "axom/primal/geometry/Point.hpp" +#include + namespace axom { namespace mir @@ -60,8 +62,8 @@ class ExplicitCoordsetView2 getPoint(IndexType vertex_index) const { assert(vertex_index < size()); - return PointType(m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index]); + return PointType(std::initializer_list{m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index]}); } /** @@ -127,9 +129,9 @@ class ExplicitCoordsetView3 PointType getPoint(IndexType vertex_index) const { - return PointType(m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index], - m_coordinates[2][vertex_index]); + return PointType(std::initializer_list{m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index], + m_coordinates[2][vertex_index]}); } /** diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index 341f8fc8e1..d106ff03ac 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -11,6 +11,8 @@ #include "axom/primal/geometry/Point.hpp" #include "axom/mir/views/StructuredIndexing.hpp" +#include + namespace axom { namespace mir @@ -66,8 +68,8 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType(m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]]); + return PointType(std::initializer_list{m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]]}); } /** @@ -172,9 +174,9 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType(m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]], - m_coordinates[2][vertex_index[2]]); + return PointType(std::initializer_list{m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]], + m_coordinates[2][vertex_index[2]]}); } /** diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index d2c5d649ed..27b83e50ac 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -33,20 +33,21 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) { const std::string cstype = coordset["type"].as_string(); if(cstype == "uniform") - { + { + const std::string keys[] = {"i", "j", "k"}; const conduit::Node &n_dims = coordset["dims"]; - const conduit::index_t ndims = n_dims.dtype().number_of_elements(); + const conduit::index_t ndims = n_dims.number_of_children(); if(ndims == 2) { axom::StackArray dims; axom::StackArray origin{0., 0.}, spacing{1., 1.}; for(int i = 0; i < ndims; i++) { - dims[i] = n_dims.as_int_accessor()[i]; + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); if(coordset.has_child("origin")) - origin[i] = coordset["origin"].as_double_accessor()[i]; + origin[i] = coordset["origin"][i].to_double(); if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"].as_double_accessor()[i]; + spacing[i] = coordset["spacing"][i].to_double(); } UniformCoordsetView coordView(dims, origin, spacing); @@ -58,11 +59,11 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) axom::StackArray origin{0., 0., 0.}, spacing{1., 1., 1.}; for(int i = 0; i < ndims; i++) { - dims[i] = n_dims.as_int_accessor()[i]; + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); if(coordset.has_child("origin")) - origin[i] = coordset["origin"].as_double_accessor()[i]; + origin[i] = coordset["origin"][i].to_double(); if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"].as_double_accessor()[i]; + spacing[i] = coordset["spacing"][i].to_double(); } UniformCoordsetView coordView(dims, origin, spacing); From fc240cbdc1395023e6acd254ae3745e3f5d04366 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 16 Jul 2024 15:50:43 -0700 Subject: [PATCH 094/290] SLIC errors --- src/axom/mir/views/NodeArrayView.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 2fd7aa5013..6134fa268e 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -415,6 +415,10 @@ void Node_to_ArrayView_single(const conduit::Node &n, FuncType &&func) { Node_to_ArrayView_single_float64(n, func); } + else + { + SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " << n.path()); + } } template @@ -460,6 +464,10 @@ void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) { Node_to_ArrayView_single_float64(n, func); } + else + { + SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " << n.path()); + } } template @@ -642,6 +650,10 @@ void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit:: { Node_to_ArrayView_same_internal_float64(func, first, args...); } + else + { + SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " << first.path()); + } } template @@ -687,6 +699,10 @@ void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node & { Node_to_ArrayView_same_internal_float64(func, first, args...); } + else + { + SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " << first.path()); + } } /// Reorder args From eefee2bd1f786f352cebf163e9bb51a5ef3f77f7 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 16 Jul 2024 17:54:43 -0700 Subject: [PATCH 095/290] Fix bit functions --- src/axom/core/utilities/BitUtilities.hpp | 36 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index b8a45370f9..a018debb8c 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -50,6 +50,38 @@ namespace utilities template struct BitTraits; +template <> +struct BitTraits +{ + constexpr static int NUM_BYTES = 8; + constexpr static int BITS_PER_WORD = NUM_BYTES << 3; + constexpr static int LG_BITS_PER_WORD = 6; +}; + +template <> +struct BitTraits +{ + constexpr static int NUM_BYTES = 4; + constexpr static int BITS_PER_WORD = NUM_BYTES << 3; + constexpr static int LG_BITS_PER_WORD = 5; +}; + +template <> +struct BitTraits +{ + constexpr static int NUM_BYTES = 2; + constexpr static int BITS_PER_WORD = NUM_BYTES << 3; + constexpr static int LG_BITS_PER_WORD = 4; +}; + +template <> +struct BitTraits +{ + constexpr static int NUM_BYTES = 1; + constexpr static int BITS_PER_WORD = NUM_BYTES << 3; + constexpr static int LG_BITS_PER_WORD = 3; +}; + template <> struct BitTraits { @@ -213,7 +245,7 @@ template AXOM_HOST_DEVICE bool bitIsSet(FlagType flags, BitType bit) { - assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); + assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); return (flags & (1 << bit)) > 0; } @@ -232,7 +264,7 @@ template AXOM_HOST_DEVICE void setBit(FlagType &flags, BitType bit, bool value = true) { - assert(bit >= 0 && (static_cast(bit) < (sizeof(BitType) << 3))); + assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); const auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } From 6af4fa2430bf9b03a5a0016b4855c7585e66d5b2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 16 Jul 2024 17:55:15 -0700 Subject: [PATCH 096/290] Added mechanism for Conduit to allocate through Axom. --- src/axom/mir/ClipField.hpp | 127 ++++++++++++++++----------- src/axom/mir/tests/mir_clipfield.cpp | 51 +++++------ src/axom/mir/utilities.cpp | 38 ++++++++ src/axom/mir/utilities.hpp | 28 +++++- 4 files changed, 163 insertions(+), 81 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 07f9ed85ab..86472af139 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -53,25 +53,25 @@ std::map shapeMap_FromFlags(std::uint64_t shapes) { std::map sm; -#if 0 - if((shapes & views::LineShape::id()) > 0) - sm["line"] = views::LineShape::id(); - if((shapes & views::TriShape::id()) > 0) - sm["tri"] = views::TriShape::id(); + if((shapes & views::LineShape::id()) > 0) + sm["line"] = views::LineShape::id(); - if((shapes & views::QuadShape::id()) > 0) - sm["quad"] = views::QuadShape::id(); + if((shapes & views::TriShape::id()) > 0) + sm["tri"] = views::TriShape::id(); - if((shapes & views::TetShape::id()) > 0) - sm["tet"] = views::TetShape::id(); + if((shapes & views::QuadShape::id()) > 0) + sm["quad"] = views::QuadShape::id(); - if((shapes & views::PyramidShape::id()) > 0) - sm["pyramid"] = views::PyramidShape::id(); + if((shapes & views::TetShape::id()) > 0) + sm["tet"] = views::TetShape::id(); + + if((shapes & views::PyramidShape::id()) > 0) + sm["pyramid"] = views::PyramidShape::id(); + + if((shapes & views::WedgeShape::id()) > 0) + sm["wedge"] = views::WedgeShape::id(); - if((shapes & views::WedgeShape::id()) > 0) - sm["wedge"] = views::WedgeShape::id(); -#endif if((shapes & views::HexShape::id()) > 0) sm["hex"] = views::HexShape::id(); @@ -147,41 +147,28 @@ class ClipField public: using BlendData = axom::mir::utilities::blueprint::BlendData; using SliceData = axom::mir::utilities::blueprint::SliceData; -/* - void execute(const conduit::Node &inputMesh, const std::string &clipField, conduit::Node &outputMesh) - { - - I was going to say that we could create the views here but there's a lot of dispatch code devoted to making typed array views, etc that would need to be done before we even know the types. - - I could include the dispatch_coordset method for example but then this class would have all the various instantiations of the algorithm rather than just the one we want. - - CoordsetView csview; - } - */ -private: - axom::mir::clipping::ClipTableManager m_clipTables{}; using ClipTableViews = axom::StackArray; - void createClipTableViews(ClipTableViews &views, int dimension) - { - if(dimension == 2) - { - views[0] = m_clipTables[ST_TRI].view(); - views[1] = m_clipTables[ST_QUA].view(); - } - if(dimension == 2) - { - views[2] = m_clipTables[ST_TET].view(); - views[3] = m_clipTables[ST_PYR].view(); - views[4] = m_clipTables[ST_WDG].view(); - views[5] = m_clipTables[ST_HEX].view(); - } - } -public: - ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) : m_topologyView(topoView), m_coordsetView(coordsetView) + /** + * \brief Constructor + * + * \param topoView A topology view suitable for the supplied topology. + * \param coordsetView A coordset view suitable for the supplied coordset. + * + */ + ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) : m_topologyView(topoView), m_coordsetView(coordsetView), m_clipTables() { } + /** + * \brief Execute the clipping operation using the data stored in the specified \a clipField. + * + * \param[in] n_input The Conduit node that contains the topology, coordsets, and fields. + * \param[in] clipField The name of the field to use for clipping. + * \param[out] n_output A Conduit node that will hold the clipped output mesh. This should be a different node from \a n_input. + * + * \note The clipField field must currently be vertex-associated. + */ void execute(const conduit::Node &n_input, const std::string &clipField, conduit::Node &n_output) @@ -200,6 +187,19 @@ class ClipField n_output["fields"]); } + /** + * \brief Execute the clipping operation using the data stored in the specified \a clipField. + * + * \param[in] n_topo The node that contains the input mesh topology. + * \param[in] n_coordset The node that contains the input mesh coordset. + * \param[in] n_fields The node that contains the input fields. + * \param[in] clipField The name of the field to use for clipping. + * \param[out] n_newTopo A node that will contain the new clipped topology. + * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. + * \param[out] n_newFields A node that will contain the new fields for the clipped topology. + * + * \note The clipField field must currently be vertex-associated. Also, the output topology will be an unstructured topology with mixed shape types. + */ void execute(const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, @@ -218,6 +218,8 @@ class ClipField // Get the clip field. const conduit::Node &n_clip_field = n_fields.fetch_existing(clipField); + const conduit::Node &n_clip_field_values = n_clip_field["values"]; + assert(n_clip_field["association"].as_string() == "vertex"); // TODO: process options (or add some class members to hold attributes) int selection = -1; // All bits set. @@ -254,7 +256,7 @@ class ClipField auto fragmentsView = fragments.view(); auto fragmentsSizeView = fragmentsSize.view(); - views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { @@ -424,7 +426,7 @@ class ClipField auto blendIdsView = blendIds.view(); auto blendCoeffView = blendCoeff.view(); - views::Node_to_ArrayView(n_clip_field, [&](auto clipFieldView) + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { @@ -604,27 +606,35 @@ class ClipField n_newTopo.reset(); +std::cout << "allocatorID: " << allocatorID << std::endl; +std::cout << "Conduit default allocator (probably not set): " << n_newTopo.allocator() << std::endl; +std::cout << conduit::about() << std::endl; + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::ConduitAllocateThroughAxom c2a(allocatorID); + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + // Allocate connectivity. conduit::Node &n_conn = n_newTopo["elements/connectivity"]; - n_conn.set_allocator(allocatorID); + n_conn.set_allocator(conduitAllocatorID); n_conn.set(conduit::DataType(connTypeID, finalConnSize)); auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); // Allocate shapes. conduit::Node &n_shapes = n_newTopo["elements/shapes"]; - n_shapes.set_allocator(allocatorID); + n_shapes.set_allocator(conduitAllocatorID); n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); // Allocate sizes. conduit::Node &n_sizes = n_newTopo["elements/sizes"]; - n_sizes.set_allocator(allocatorID); + n_sizes.set_allocator(conduitAllocatorID); n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); // Allocate offsets. conduit::Node &n_offsets = n_newTopo["elements/offsets"]; - n_offsets.set_allocator(allocatorID); + n_offsets.set_allocator(conduitAllocatorID); n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); @@ -798,6 +808,22 @@ class ClipField } // end of execute private: + void createClipTableViews(ClipTableViews &views, int dimension) + { + if(dimension == 2) + { + views[0] = m_clipTables[ST_TRI].view(); + views[1] = m_clipTables[ST_QUA].view(); + } + if(dimension == 2) + { + views[2] = m_clipTables[ST_TET].view(); + views[3] = m_clipTables[ST_PYR].view(); + views[4] = m_clipTables[ST_WDG].view(); + views[5] = m_clipTables[ST_HEX].view(); + } + } + void make_coordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { axom::mir::utilities::blueprint::CoordsetBlender cb; @@ -830,6 +856,7 @@ class ClipField // NOTE: I probably want to make the clip tables be a class member so I can reuse the object without having to reload the clip tables each time. TopologyView m_topologyView; CoordsetView m_coordsetView; + axom::mir::clipping::ClipTableManager m_clipTables; }; } // end namespace clipping diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 912f3fd8c0..17175885be 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -46,33 +46,30 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) TEST(mir_clipfield, uniform2d) { - using Indexing = axom::mir::views::StructuredIndexing; - using TopoView = axom::mir::views::StructuredTopologyView; - using CoordsetView = axom::mir::views::UniformCoordsetView; - - axom::StackArray dims{10, 10}; - - // Create the data - conduit::Node mesh; - braid("uniform", dims, mesh); - conduit::relay::io::blueprint::save_mesh(mesh, "uniform2d_orig", "hdf5"); - mesh.print(); - const conduit::Node &topo = mesh["topologies"][0]; - const conduit::Node &coordset = mesh["coordsets"][0]; - - // Create views - axom::StackArray origin{0., 0.}, spacing{1., 1.}; - CoordsetView coordsetView(dims, origin, spacing); - Indexing zoneIndexing(dims); - TopoView topoView(zoneIndexing); - - // Clip the data - conduit::Node clipmesh; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); - clipper.execute(mesh, "distance", clipmesh); - conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); - - // Load a clipped baseline file & compare. + using Indexing = axom::mir::views::StructuredIndexing; + using TopoView = axom::mir::views::StructuredTopologyView; + using CoordsetView = axom::mir::views::UniformCoordsetView; + + axom::StackArray dims{10, 10}; + axom::StackArray zoneDims{dims[0] - 1, dims[1] - 1}; + + // Create the data + conduit::Node mesh; + braid("uniform", dims, mesh); + conduit::relay::io::blueprint::save_mesh(mesh, "uniform2d_orig", "hdf5"); + + // Create views + axom::StackArray origin{0., 0.}, spacing{1., 1.}; + CoordsetView coordsetView(dims, origin, spacing); + TopoView topoView(Indexing{zoneDims}); + + // Clip the data + conduit::Node clipmesh; + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(mesh, "distance", clipmesh); + conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); + + // Load a clipped baseline file & compare. } diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp index 794c1fd0a1..1d842d348e 100644 --- a/src/axom/mir/utilities.cpp +++ b/src/axom/mir/utilities.cpp @@ -48,6 +48,44 @@ std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) return (static_cast(hash) << 32) | hashr; } +conduit::index_t ConduitAllocateThroughAxom::conduitAllocatorID = -1; +int ConduitAllocateThroughAxom::axomAllocatorID = 0; + +ConduitAllocateThroughAxom::ConduitAllocateThroughAxom(int _allocatorID) +{ + // Stash the Axom allocatorID so we don't just use the default later. + axomAllocatorID = _allocatorID; + + // Register internal functions here as Conduit allocator functions. This returns + // the conduitAllocatorID, which is the value that we must pass to Node::set_allocator. + if(conduitAllocatorID == -1) + { + conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); + } +} + +ConduitAllocateThroughAxom::~ConduitAllocateThroughAxom() +{ +} + +conduit::index_t ConduitAllocateThroughAxom::getConduitAllocatorID() const +{ + return conduitAllocatorID; +} + +void *ConduitAllocateThroughAxom::internal_allocate(size_t items, size_t item_size) +{ + void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); + std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; + return ptr; +} + +void ConduitAllocateThroughAxom::internal_free(void *ptr) +{ + std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; + axom::deallocate(ptr); +} + } // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 091d442af6..8772ebb6f1 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -201,8 +201,6 @@ std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. * \param[out] sindices An array of indices that indicate where in the original view the keys came from. * - * \note This code is adapted from Ascent/DevilRay. - * */ template void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) @@ -233,8 +231,7 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array RAJA::ReduceSum mask_sum(0); axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) { - const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; - const axom::IndexType m = (i >= 1) ? different : 1; + const axom::IndexType m = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; mask_view[i] = m; mask_sum += m; }); @@ -265,6 +262,29 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array }); } +// TODO: move to blueprint_utilities when it has a .cpp file. + +/** + * \brief This class uses RAII to register internal functions that make Conduit allocate + * through Axom's allocate/deallocate functions using a specific allocator. This + * permits Conduit to allocate through Axom's UMPIRE logic. + */ +class ConduitAllocateThroughAxom +{ +public: + ConduitAllocateThroughAxom(int _allocatorID); + ~ConduitAllocateThroughAxom(); + + conduit::index_t getConduitAllocatorID() const; + +private: + static void *internal_allocate(size_t items, size_t item_size); + static void internal_free(void *ptr); + + static conduit::index_t conduitAllocatorID; + static int axomAllocatorID; +}; + } // end namespace utilities } // end namespace mir } // end namespace axom From 7f0d09535488a60c5a4322654f0309a95285c512 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 16 Jul 2024 18:44:01 -0700 Subject: [PATCH 097/290] Added mechanism for making Conduit allocate through Axom. Fixed a few other bugs. --- src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/ClipField.hpp | 36 ++++++++++++++--- src/axom/mir/CoordsetBlender.hpp | 7 +++- src/axom/mir/FieldBlender.hpp | 6 ++- src/axom/mir/FieldSlicer.hpp | 6 ++- src/axom/mir/blueprint_utilities.cpp | 58 ++++++++++++++++++++++++++++ src/axom/mir/blueprint_utilities.hpp | 23 +++++++++++ 7 files changed, 127 insertions(+), 10 deletions(-) create mode 100644 src/axom/mir/blueprint_utilities.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index b8d3f60e79..a0dc3502ad 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -70,6 +70,7 @@ set(mir_sources CellClipper.cpp CellGenerator.cpp + blueprint_utilities.cpp MIRAlgorithm.cpp EquiZAlgorithm.cpp clipping/ClipCasesHex.cpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 86472af139..d1f52e88bf 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -605,13 +605,15 @@ class ClipField const auto finalConnSize = fragment_nids_sum.get(); n_newTopo.reset(); + n_newTopo["type"] = "unstructured"; + n_newTopo["coordset"] = n_newCoordset.name(); std::cout << "allocatorID: " << allocatorID << std::endl; std::cout << "Conduit default allocator (probably not set): " << n_newTopo.allocator() << std::endl; std::cout << conduit::about() << std::endl; // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); const int conduitAllocatorID = c2a.getConduitAllocatorID(); // Allocate connectivity. @@ -630,7 +632,7 @@ std::cout << conduit::about() << std::endl; conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(conduitAllocatorID); n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); - auto sizesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + auto sizesView = axom::ArrayView(static_cast(n_sizes.data_ptr()), finalNumZones); // Allocate offsets. conduit::Node &n_offsets = n_newTopo["elements/offsets"]; @@ -723,9 +725,26 @@ std::cout << conduit::about() << std::endl; shapesUsed_reduce |= shapesUsed; }); + // We should have some data in there at this point... + n_newTopo.print(); +#if 0 +// This is where I left off. Totalview dies in the scan... + +/* +elements: + connectivity: [106, 97, 73, ..., 51, 13] + shapes: [16, 16, 16, ..., 16, 16] + sizes: [16540704, 0, 0, ..., 538976288, 538968189] + offsets: [4038, 0, 0, ..., 538976288, 1684930685] +*/ + +axom::exclusive_scan(sizesView, offsetsView, axom::operators::plus{}); + +#endif + // Make offsets - RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), nzones), - RAJA::make_span(offsetsView.data(), nzones), + RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), finalNumZones), + RAJA::make_span(offsetsView.data(), finalNumZones), RAJA::operators::plus{}); // Add shape information to the connectivity. @@ -733,6 +752,7 @@ std::cout << conduit::about() << std::endl; const auto shapeMap = details::shapeMap_FromFlags(shapesUsed); if(axom::utilities::countBits(shapesUsed) > 1) { +std::cout << "multiple output types" << std::endl; n_newTopo["elements/shape"] = "mixed"; conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; for(auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) @@ -740,23 +760,27 @@ std::cout << conduit::about() << std::endl; } else { +std::cout << "single output type" << std::endl; n_shapes.reset(); n_newTopo["elements"].remove("shapes"); n_newTopo["elements/shape"] = shapeMap.begin()->first; } + n_newTopo.print(); //----------------------------------------------------------------------- // STAGE 6 - Make new coordset. //----------------------------------------------------------------------- make_coordset(blend, n_coordset, n_newCoordset); + n_newCoordset.print(); //----------------------------------------------------------------------- // STAGE 7 - Make new fields. //----------------------------------------------------------------------- axom::mir::utilities::blueprint::SliceData slice; make_fields(blend, slice, n_newTopo.name(), n_fields, n_newFields); + n_newFields.print(); //----------------------------------------------------------------------- // STAGE 8 - make originalElements (this will later be optional) @@ -773,7 +797,7 @@ std::cout << conduit::about() << std::endl; n_origElem["association"] = "element"; n_origElem["topology"] = n_topo.name(); conduit::Node &n_values = n_origElem["values"]; - n_values.set_allocator(allocatorID); + n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) @@ -792,7 +816,7 @@ std::cout << conduit::about() << std::endl; n_orig["association"] = "element"; n_orig["topology"] = n_topo.name(); conduit::Node &n_values = n_orig["values"]; - n_values.set_allocator(allocatorID); + n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(connTypeID, finalNumZones)); auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 7436a32283..320ebb1572 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -61,6 +61,9 @@ class CoordsetBlender const auto nComponents = axes.size(); assert(PointType::DIMENSION == nComponents); + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + n_output.reset(); n_output["type"] = "explicit"; conduit::Node &n_values = n_output["values"]; @@ -71,8 +74,8 @@ class CoordsetBlender for(size_t i = 0; i < nComponents; i++) { // Allocate data in the Conduit node and make a view. - conduit::Node &comp = n_output[axes[i]]; - comp.set_allocator(allocatorID); + conduit::Node &comp = n_values[axes[i]]; + comp.set_allocator(c2a.getConduitAllocatorID()); comp.set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); auto *comp_data = static_cast(comp.data_ptr()); compViews[i] = axom::ArrayView(comp_data, outputSize); diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index caeb590379..a5a61a0aa5 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -8,6 +8,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" #include "axom/mir/utilities.hpp" +#include "axom/mir/blueprint_utilities.hpp" #include @@ -88,7 +89,10 @@ class FieldBlender { const auto allocatorID = axom::execution_space::allocatorID(); const auto outputSize = blend.m_uniqueIndicesView.size(); - n_output_values.set_allocator(allocatorID); + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto compView, auto outView) diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index f8ab51f341..5f8d1df600 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -7,6 +7,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" +#include "axom/mir/blueprint_utilities.hpp" #include @@ -80,7 +81,10 @@ class FieldSlicer { const auto allocatorID = axom::execution_space::allocatorID(); const auto outputSize = slice.m_indicesView.size(); - n_output_values.set_allocator(allocatorID); + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto valuesView, auto outputView) diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp new file mode 100644 index 0000000000..1486ff070f --- /dev/null +++ b/src/axom/mir/blueprint_utilities.cpp @@ -0,0 +1,58 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/mir/blueprint_utilities.hpp" + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +conduit::index_t ConduitAllocateThroughAxom::conduitAllocatorID = -1; +int ConduitAllocateThroughAxom::axomAllocatorID = 0; + +ConduitAllocateThroughAxom::ConduitAllocateThroughAxom(int _allocatorID) +{ + // Stash the Axom allocatorID so we don't just use the default later. + axomAllocatorID = _allocatorID; + + // Register internal functions here as Conduit allocator functions. This returns + // the conduitAllocatorID, which is the value that we must pass to Node::set_allocator. + if(conduitAllocatorID == -1) + { + conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); + } +} + +ConduitAllocateThroughAxom::~ConduitAllocateThroughAxom() +{ +} + +conduit::index_t ConduitAllocateThroughAxom::getConduitAllocatorID() const +{ + return conduitAllocatorID; +} + +void *ConduitAllocateThroughAxom::internal_allocate(size_t items, size_t item_size) +{ + void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); + std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; + return ptr; +} + +void ConduitAllocateThroughAxom::internal_free(void *ptr) +{ + std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; + axom::deallocate(ptr); +} + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index bf90724cef..2ab15e68e5 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -62,6 +62,29 @@ struct cpp2conduit { using type = conduit::float32; static con template <> struct cpp2conduit { using type = conduit::float64; static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; }; +//------------------------------------------------------------------------------ +/** + * \brief This class registers a Conduit allocator that can make Conduit allocate + * through Axom's allocate/deallocate functions using a specific allocator. This + * permits Conduit to allocate through Axom's UMPIRE logic. + */ +class ConduitAllocateThroughAxom +{ +public: + ConduitAllocateThroughAxom(int _allocatorID); + ~ConduitAllocateThroughAxom(); + + conduit::index_t getConduitAllocatorID() const; + +private: + static void *internal_allocate(size_t items, size_t item_size); + static void internal_free(void *ptr); + + static conduit::index_t conduitAllocatorID; + static int axomAllocatorID; +}; + +//------------------------------------------------------------------------------ // TODO: Add in a routine to migrate a Conduit node to a new memory space. // copy(const conduit::Node &src, conduit::Node &dest); From 719329b4cc03003424d50af8ba424d609d07b6a7 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 17 Jul 2024 17:35:59 -0700 Subject: [PATCH 098/290] Debugging ClipField --- src/axom/mir/ClipField.hpp | 171 ++++++++++++++++++--------- src/axom/mir/tests/mir_clipfield.cpp | 1 + 2 files changed, 113 insertions(+), 59 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index d1f52e88bf..936f46de0a 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -12,7 +12,7 @@ #include "axom/mir/FieldBlender.hpp" #include "axom/mir/CoordsetBlender.hpp" #include "axom/mir/FieldSlicer.hpp" -#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit +#include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/utilities.hpp" // for cpp2conduit #include @@ -27,6 +27,30 @@ namespace clipping namespace details { +#if 0 +// NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers +// so we can write serial versions for when RAJA is not enabled. +namespace axom +{ +namespace operators +{ +template +using plus = RAJA::operators::plus; +} // end namespace operators +} // end namespace axom + +template +void exclusive_scan(ArrayViewType &input, ArrayViewType &output, OperatorType &op) +{ + assert(input.size() == output.size()); + using loop_policy = typename axom::execution_space::loop_policy; + RAJA::exclusive_scan(RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size()), + op); +} + +#endif + std::map shapeMap_NameValue(const conduit::Node &n_shape_map) { @@ -49,6 +73,37 @@ shapeMap_ValueName(const conduit::Node &n_shape_map) return sm; } +/** + * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate Shape::id() value. + * + * \param st_index The value we want to translate into a Shape::id() value. + * + * \return The Shape::id() value that matches the st_index, or 0 if there is no match. + */ +AXOM_HOST_DEVICE +int ST_Index_to_ShapeID(int st_index) +{ + int shapeID = 0; + switch(st_index) + { + case ST_LIN: shapeID = views::LineShape::id(); break; + case ST_TRI: shapeID = views::TriShape::id(); break; + case ST_QUA: shapeID = views::QuadShape::id(); break; + case ST_TET: shapeID = views::TetShape::id(); break; + case ST_PYR: shapeID = views::PyramidShape::id(); break; + case ST_WDG: shapeID = views::WedgeShape::id(); break; + case ST_HEX: shapeID = views::HexShape::id(); break; + } + return shapeID; +} + +/** + * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. + * + * \param shapes This is a bitwise-or of various Shape::id() values. + * + * \return A map of Conduit shape name to Shape::id() value. + */ std::map shapeMap_FromFlags(std::uint64_t shapes) { @@ -83,6 +138,7 @@ int getClipTableIndex(int dimension, int nnodes) { return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); } + AXOM_HOST_DEVICE bool color0Selected(int selection) { @@ -119,6 +175,19 @@ float computeT(float d0, float d1) return t; } + +// TODO: Could we make ZoneType be a concept? +/** + * \brief Use the ids for the provided zone and the values from the array view to determine a clip case. + * + * \tparam ZoneType A class that implements the Shape interface. + * \tparam ArrayViewType The ArrayView type that contains the data. + * + * \param[in] zone The zone being clipped. + * \param[in] view The view that can access the clipping distance field. + * + * \return The index of the clipping case. + */ AXOM_HOST_DEVICE template size_t clip_case(const ZoneType &zone, const ArrayViewType &view) @@ -127,10 +196,7 @@ size_t clip_case(const ZoneType &zone, const ArrayViewType &view) for(size_t i = 0; i < zone.numberOfNodes(); i++) { const auto id = zone.getId(i); - if(view[id] > 0) - { - clipcase |= (1 << i); - } + clipcase |= (view[id] > 0) ? (1 << i) : 0; } return clipcase; } @@ -318,8 +384,8 @@ class ClipField if(details::shapeIsSelected(caseData[1], selection)) { thisFragments++; - const int nIdsThisShape = caseData.size() - 2; - thisFragmentsNumIds += nIdsThisShape; + const int nIdsThisFragment = caseData.size() - 2; + thisFragmentsNumIds += nIdsThisFragment; // Mark the points in this fragment used. for(int i = 2; i < caseData.size(); i++) @@ -590,12 +656,6 @@ class ClipField blend.m_blendIdsView = blendIds.view(); blend.m_blendCoeffView = blendCoeff.view(); - -// I just had this thought like it would be nice to output the volumes... and a mapping of old2new zones -// I suppose with the mapping and the old/new meshes, I could compute the volume fractions. -// Should I pass in a outputTopo, outputCoordset, outputFields nodes? -// Then I don't have to care about the names - it's done outside this code. - // ---------------------------------------------------------------------- // // Stage 5 - Make new connectivity. @@ -608,10 +668,6 @@ class ClipField n_newTopo["type"] = "unstructured"; n_newTopo["coordset"] = n_newCoordset.name(); -std::cout << "allocatorID: " << allocatorID << std::endl; -std::cout << "Conduit default allocator (probably not set): " << n_newTopo.allocator() << std::endl; -std::cout << conduit::about() << std::endl; - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); const int conduitAllocatorID = c2a.getConduitAllocatorID(); @@ -640,12 +696,9 @@ std::cout << conduit::about() << std::endl; n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); - - const std::uint32_t uNames_len = uNames.size(); - + // Here we fill in the new connectivity, sizes, shapes. + // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. RAJA::ReduceBitOr shapesUsed_reduce(0); - - // Here we get the node ids from the unique blend names, de-duplicating points. m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. @@ -702,8 +755,9 @@ std::cout << conduit::about() << std::endl; { // Get the current shape in the clip case. const auto caseData = *it; + const auto fragmentShape = static_cast(caseData[0]); - if(caseData[0] != ST_PNT) + if(fragmentShape != ST_PNT) { if(details::shapeIsSelected(caseData[1], selection)) { @@ -711,13 +765,14 @@ std::cout << conduit::about() << std::endl; for(int i = 2; i < caseData.size(); i++) connView[outputIndex++] = point_2_new[caseData[i]]; - const auto nIdsInZone = caseData.size() - 2; - sizesView[sizeIndex] = nIdsInZone; - shapesView[sizeIndex] = zone.id(); + const auto nIdsInFragment = caseData.size() - 2; + const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); + sizesView[sizeIndex] = nIdsInFragment; + shapesView[sizeIndex] = shapeID; sizeIndex++; - // Record which shape type was used. (zone.id() are already powers of 2) - shapesUsed |= zone.id(); + // Record which shape type was used. (ids are powers of 2) + shapesUsed |= shapeID; } } } @@ -725,23 +780,6 @@ std::cout << conduit::about() << std::endl; shapesUsed_reduce |= shapesUsed; }); - // We should have some data in there at this point... - n_newTopo.print(); -#if 0 -// This is where I left off. Totalview dies in the scan... - -/* -elements: - connectivity: [106, 97, 73, ..., 51, 13] - shapes: [16, 16, 16, ..., 16, 16] - sizes: [16540704, 0, 0, ..., 538976288, 538968189] - offsets: [4038, 0, 0, ..., 538976288, 1684930685] -*/ - -axom::exclusive_scan(sizesView, offsetsView, axom::operators::plus{}); - -#endif - // Make offsets RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), finalNumZones), RAJA::make_span(offsetsView.data(), finalNumZones), @@ -752,7 +790,6 @@ axom::exclusive_scan(sizesView, offsetsView, axom::operators::plus 1) { -std::cout << "multiple output types" << std::endl; n_newTopo["elements/shape"] = "mixed"; conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; for(auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) @@ -760,27 +797,21 @@ std::cout << "multiple output types" << std::endl; } else { -std::cout << "single output type" << std::endl; n_shapes.reset(); n_newTopo["elements"].remove("shapes"); - n_newTopo["elements/shape"] = shapeMap.begin()->first; } - n_newTopo.print(); //----------------------------------------------------------------------- // STAGE 6 - Make new coordset. - //----------------------------------------------------------------------- - - make_coordset(blend, n_coordset, n_newCoordset); - n_newCoordset.print(); + //----------------------------------------------------------------------- + makeCoordset(blend, n_coordset, n_newCoordset); //----------------------------------------------------------------------- // STAGE 7 - Make new fields. //----------------------------------------------------------------------- axom::mir::utilities::blueprint::SliceData slice; - make_fields(blend, slice, n_newTopo.name(), n_fields, n_newFields); - n_newFields.print(); + makeFields(blend, slice, n_newTopo.name(), n_fields, n_newFields); //----------------------------------------------------------------------- // STAGE 8 - make originalElements (this will later be optional) @@ -832,6 +863,12 @@ std::cout << "single output type" << std::endl; } // end of execute private: + /** + * \brief Create views for the clip tables of various shapes. + * + * \param[out] views The views array that will contain the table views. + * \param dimension The dimension the topology (so we can load a subset of tables) + */ void createClipTableViews(ClipTableViews &views, int dimension) { if(dimension == 2) @@ -839,7 +876,7 @@ std::cout << "single output type" << std::endl; views[0] = m_clipTables[ST_TRI].view(); views[1] = m_clipTables[ST_QUA].view(); } - if(dimension == 2) + else if(dimension == 3) { views[2] = m_clipTables[ST_TET].view(); views[3] = m_clipTables[ST_PYR].view(); @@ -848,14 +885,30 @@ std::cout << "single output type" << std::endl; } } - void make_coordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const + /** + * \brief Make the new coordset using the blend data and the input coordset/coordsetview. + * + * \param blend The BlendData that we need to construct the new coordset. + * \param n_coordset The input coordset, which is passed for metadata. + * \param[out] n_newCoordset The new coordset. + */ + void makeCoordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { axom::mir::utilities::blueprint::CoordsetBlender cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } - void make_fields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const + /** + * \brief Make new fields for the output topology. + * + * \param blend The BlendData that we need to construct the new vertex fields. + * \param slice The SliceData we need to construct new element fields. + * \param n_fields The source fields. + * \param[out[ n_out_fields The node that will contain the new fields. + */ +// TODO: pass in a map of fields that we want to map to the new topology. + void makeFields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const { for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { @@ -877,7 +930,7 @@ std::cout << "single output type" << std::endl; } } - // NOTE: I probably want to make the clip tables be a class member so I can reuse the object without having to reload the clip tables each time. +private: TopologyView m_topologyView; CoordsetView m_coordsetView; axom::mir::clipping::ClipTableManager m_clipTables; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 17175885be..edcbd5f48b 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -68,6 +68,7 @@ TEST(mir_clipfield, uniform2d) axom::mir::clipping::ClipField clipper(topoView, coordsetView); clipper.execute(mesh, "distance", clipmesh); conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); + conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d_yaml", "yaml"); // Load a clipped baseline file & compare. } From 72d3240a9c10895b637f369d4da714c933cc18d4 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 19 Jul 2024 12:25:00 -0700 Subject: [PATCH 099/290] Populate slice data so we can map element fields to new mesh --- src/axom/mir/ClipField.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 936f46de0a..9fc696bc43 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -811,6 +811,15 @@ class ClipField // STAGE 7 - Make new fields. //----------------------------------------------------------------------- axom::mir::utilities::blueprint::SliceData slice; + axom::Array sliceIndices(finalNumZones, finalNumZones, allocatorID); + auto sliceIndicesView = sliceIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + const auto start = fragmentOffsetsView[zoneIndex]; + for(int i = 0; i < fragmentsView[zoneIndex]; i++) + sliceIndicesView[start + i] = zoneIndex; + }); + slice.m_indicesView = sliceIndicesView; makeFields(blend, slice, n_newTopo.name(), n_fields, n_newFields); //----------------------------------------------------------------------- From 7b293b025e148f72f8b7b1b174de8de136a77c8e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 19 Jul 2024 18:01:02 -0700 Subject: [PATCH 100/290] Added ClipOptions --- src/axom/mir/ClipField.hpp | 348 +++++++++++++++--- src/axom/mir/TODO.txt | 31 ++ src/axom/mir/blueprint_utilities.cpp | 4 +- src/axom/mir/blueprint_utilities.hpp | 1 + src/axom/mir/tests/mir_clipfield.cpp | 111 +++++- src/axom/mir/views/StructuredTopologyView.hpp | 17 +- .../UnstructuredTopologySingleShapeView.hpp | 12 +- 7 files changed, 459 insertions(+), 65 deletions(-) create mode 100644 src/axom/mir/TODO.txt diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9fc696bc43..0de8ade4c2 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -18,6 +18,9 @@ #include #include +#include +#include + namespace axom { namespace mir @@ -80,8 +83,9 @@ shapeMap_ValueName(const conduit::Node &n_shape_map) * * \return The Shape::id() value that matches the st_index, or 0 if there is no match. */ +template AXOM_HOST_DEVICE -int ST_Index_to_ShapeID(int st_index) +int ST_Index_to_ShapeID(IntegerType st_index) { int shapeID = 0; switch(st_index) @@ -167,11 +171,11 @@ bool shapeIsSelected(unsigned char color, int selection) } AXOM_HOST_DEVICE -float computeT(float d0, float d1) +float computeWeight(float d0, float d1, float clipValue) { const float delta = d1 - d0; const float abs_delta = (delta < 0) ? -delta : delta; - const float t = (abs_delta != 0.) ? (-d0 / delta) : 0.; + const float t = (abs_delta != 0.) ? ((clipValue - d0) / delta) : 0.; return t; } @@ -181,31 +185,245 @@ float computeT(float d0, float d1) * \brief Use the ids for the provided zone and the values from the array view to determine a clip case. * * \tparam ZoneType A class that implements the Shape interface. - * \tparam ArrayViewType The ArrayView type that contains the data. + * \tparam DataType The data type of the clip data. * * \param[in] zone The zone being clipped. * \param[in] view The view that can access the clipping distance field. + * \param[in] clipValue The value used for clipping. * * \return The index of the clipping case. */ AXOM_HOST_DEVICE -template -size_t clip_case(const ZoneType &zone, const ArrayViewType &view) +template +size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, DataType clipValue) { size_t clipcase = 0; for(size_t i = 0; i < zone.numberOfNodes(); i++) { const auto id = zone.getId(i); - clipcase |= (view[id] > 0) ? (1 << i) : 0; + const auto value = view[id] - clipValue; + clipcase |= (value > 0) ? (1 << i) : 0; } return clipcase; } } // end namespace details +/** + * \brief This class provides a kind of schema over the clipping options, as well + * as default values, and some utilities functions. + */ +template +class ClipOptions +{ +public: + ClipOptions(axom::IndexType nzones, const conduit::Node &options) : m_nzones(nzones), m_options(options), m_selectedZones() + { + } + + /** + * \brief Return a view that contains the list of selected zone ids for the mesh. + * \return A view that contains the list of selected zone ids for the mesh. + */ + axom::ArrayView selectedZonesView() + { + if(m_selectedZones.size() == 0) + buildSelectedZones(); + return m_selectedZones.view(); + } + + /** + * \brief Invalidate the selected zones array (due to options changing) so we can rebuild it. + */ + void invalidateSelectedZones() + { + m_selectedZones.clear(); + } + + /** + * \brief Return the name of the field used for clipping. + * \return The name of the field used for clipping. + */ + std::string clipField() const + { + return m_options.fetch_existing("clipField").as_string(); + } + + /** + * \brief Return the clip value. + * \return The clip value. + */ + float clipValue() const + { + return m_options.has_child("clipValue") ? m_options.fetch_existing("clipValue").to_float() : 0.f; + } + + /** + * \brief Return the name of the new topology to be created. + * \param default_value The name to use if the option is not defined. + * \return The name of the new topology to be created. + */ + std::string topologyName(const std::string &default_value = std::string()) const + { + std::string name(default_value); + if(m_options.has_child("topologyName")) + name = m_options.fetch_existing("topologyName").as_string(); + return name; + } + + /** + * \brief Return the name of the new coordset to be created. + * \param default_value The name to use if the option is not defined. + * \return The name of the new coordset to be created. + */ + std::string coordsetName(const std::string &default_value = std::string()) const + { + std::string name(default_value); + if(m_options.has_child("coordsetName")) + name = m_options.fetch_existing("coordsetName").as_string(); + return name; + } + + /** + * \brief Return the name of the new color field to be created. + * \return The name of the new color field to be created. + */ + std::string colorField() const + { + std::string name("color"); + if(m_options.has_child("colorField")) + name = m_options.fetch_existing("colorField").as_string(); + return name; + } + + /** + * \brief Whether the "inside" of the clipping field is selected. + * \return 1 of the inside clipping is selected, false otherwise. + */ + bool inside() const + { + return m_options.has_path("inside") ? (m_options.fetch_existing("inside").to_int() > 0) : true; + } + + /** + * \brief Whether the "outside" of the clipping field is selected. + * \return 1 of the outside clipping is selected, false otherwise. + */ + bool outside() const + { + return m_options.has_path("outside") ? (m_options.fetch_existing("outside").to_int() > 0) : false; + } + + /** + * \brief Extract the names of the fields to process (and their output names) from the + * options or \a n_fields if the options do not contain fields. + * + * \param n_fields The Conduit node that contains mesh fields. + * + * \return A map of the fields that will be processed, as well as their output name in the new fields. + */ + std::map fields(const conduit::Node &n_fields) const + { + std::map f; + if(m_options.has_child("fields")) + { + const conduit::Node &n_opt_fields = m_options.fetch_existing("fields"); + for(conduit::index_t i = 0; i < n_opt_fields.number_of_children(); i++) + { + if(n_opt_fields[i].dtype().is_string()) + f[n_opt_fields[i].name()] = n_opt_fields[i].as_string(); + else + f[n_opt_fields[i].name()] = n_opt_fields[i].name(); + } + } + else + { + // No options were specified. Allow all fields with same topology as clipField. + const conduit::Node &n_clipField = n_fields.fetch_existing(clipField()); + std::string topoName = n_clipField.fetch_existing("topology").as_string(); + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + if(topoName == n_fields[i].fetch_existing("topology").as_string()) + f[n_fields[i].name()] = n_fields[i].name(); + } + } + return f; + } + +private: + /** + * \brief The options may contain a "selectedZones" member that is a list of zones + * that will be operated on. If such an array is present, copy and sort it. + * If the zone list is not present, make an array that selects every zone. + */ + void buildSelectedZones() + { + const auto allocatorID = axom::execution_space::allocatorID(); + + if(m_options.has_child("selectedZones")) + { + // Store the zone list in m_selectedZones. + int badValueCount = 0; + views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) + { + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + using value_type = typename decltype(zonesView)::value_type; + + // It probably does not make sense to request more zones than we have in the mesh. + SLIC_ASSERT(zonesView.size() <= m_nzones); + + m_selectedZones = axom::Array(zonesView.size(), zonesView.size(), allocatorID); + auto szView = m_selectedZones.view(); + axom::copy(szView.data(), zonesView.data(), zonesView.size() * sizeof(value_type)); + + // Check that the selected zone values are in range. + const auto nzones = m_nzones; + RAJA::ReduceSum errReduce(0); + axom::for_all(szView.size(), AXOM_LAMBDA(auto index) + { + const int err = (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + errReduce += err; + }); + badValueCount = errReduce.get(); + + // Make sure the selectedZones are sorted. + RAJA::sort(RAJA::make_span(szView.data(), szView.size())); + }); + + if(badValueCount > 0) + { + SLIC_ERROR("Out of range selectedZones values."); + } + } + else + { + // Select all zones. + m_selectedZones = axom::Array(m_nzones, m_nzones, allocatorID); + auto szView = m_selectedZones.view(); + axom::for_all(m_nzones, AXOM_LAMBDA(auto zoneIndex) + { + szView[zoneIndex] = zoneIndex; + }); + } + } + +private: + axom::IndexType m_nzones; // The number of zones in the associated topology. + const conduit::Node &m_options; // A reference to the clipping options node. + axom::Array m_selectedZones; // Storage for a list of selected zone ids. +}; + + + + /** * \accelerated * \brief This class clips a topology using a field and puts the new topology into a new Conduit node. + * + * \tparam ExecSpace The execution space where the compute-heavy kernels run. + * \tparam TopologyView The topology view that can operate on the Blueprint topology. + * \tparam CoordsetView The coordset view that can operate on the Blueprint coordset. */ template class ClipField @@ -230,26 +448,29 @@ class ClipField * \brief Execute the clipping operation using the data stored in the specified \a clipField. * * \param[in] n_input The Conduit node that contains the topology, coordsets, and fields. - * \param[in] clipField The name of the field to use for clipping. + * \param[in] n_options A Conduit node that contains clipping options. * \param[out] n_output A Conduit node that will hold the clipped output mesh. This should be a different node from \a n_input. * * \note The clipField field must currently be vertex-associated. */ void execute(const conduit::Node &n_input, - const std::string &clipField, + const conduit::Node &n_options, conduit::Node &n_output) { + ClipOptions opts(0, n_options); + const std::string clipFieldName = opts.clipField(); + const conduit::Node &n_fields = n_input.fetch_existing("fields"); - const conduit::Node &n_clipField = n_fields.fetch_existing(clipField); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); const std::string &topoName = n_clipField["topology"].as_string(); const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); const std::string &coordsetName = n_topo["coordset"].as_string(); const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); execute(n_topo, n_coordset, n_fields, - clipField, - n_output["topologies/" + topoName], - n_output["coordsets/" + coordsetName], + n_options, + n_output["topologies/" + opts.topologyName(topoName)], + n_output["coordsets/" + opts.coordsetName(coordsetName)], n_output["fields"]); } @@ -259,7 +480,7 @@ class ClipField * \param[in] n_topo The node that contains the input mesh topology. * \param[in] n_coordset The node that contains the input mesh coordset. * \param[in] n_fields The node that contains the input fields. - * \param[in] clipField The name of the field to use for clipping. + * \param[in] n_options A Conduit node that contains clipping options. * \param[out] n_newTopo A node that will contain the new clipped topology. * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. * \param[out] n_newFields A node that will contain the new fields for the clipped topology. @@ -269,7 +490,7 @@ class ClipField void execute(const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, - const std::string &clipField, + const conduit::Node &n_options, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) @@ -282,13 +503,22 @@ class ClipField using ConnectivityType = typename TopologyView::IndexType; constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + const auto nzones = m_topologyView.numberOfZones(); + ClipOptions opts(nzones, n_options); + // Get the clip field. - const conduit::Node &n_clip_field = n_fields.fetch_existing(clipField); + std::string clipFieldName = opts.clipField(); + const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); const conduit::Node &n_clip_field_values = n_clip_field["values"]; - assert(n_clip_field["association"].as_string() == "vertex"); + SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - // TODO: process options (or add some class members to hold attributes) - int selection = -1; // All bits set. + // Determine which parts of the shapes will be kept. + int selection = 0; + if(opts.inside()) + axom::utilities::setBit(selection, 0); + if(opts.outside()) + axom::utilities::setBit(selection, 1); + SLIC_ASSERT(selection > 0); // Load clip table data and make views. m_clipTables.load(m_topologyView.dimension()); @@ -307,7 +537,6 @@ class ClipField RAJA::ReduceSum blendGroupLen_sum(0); // Allocate some memory - const auto nzones = m_topologyView.numberOfZones(); axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. @@ -324,10 +553,13 @@ class ClipField views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + using clip_value_type = typename decltype(clipFieldView)::value_type; + const auto clipValue = static_cast(opts.clipValue()); + + m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. - const auto clipcase = details::clip_case(zone, clipFieldView); + const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); clipCasesView[zoneIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. @@ -494,7 +726,8 @@ class ClipField views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + const auto clipValue = opts.clipValue(); + m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { using ZoneType = typename std::remove_reference::type; @@ -552,7 +785,7 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const float t = details::computeT(clipFieldView[id0], clipFieldView[id1]); + const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); blendIdsView[bgStart] = id0; blendIdsView[bgStart+1] = id1; @@ -608,7 +841,7 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const float t = details::computeT(clipFieldView[id0], clipFieldView[id1]); + const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); // Store blend group info blendIdsView[bgStart] = id0; @@ -645,7 +878,6 @@ class ClipField axom::mir::utilities::unique(blendNames, uNames, uIndices); auto uNamesView = uNames.view(); - auto uIndicesView = uIndices.view(); // Bundle up blend data. axom::mir::utilities::blueprint::BlendData blend; @@ -696,10 +928,19 @@ class ClipField n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); + // Allocate a color variable to keep track of the "color" of the fragments. + conduit::Node &n_color = n_newFields[opts.colorField()]; + n_color["topology"] = opts.topologyName(n_newTopo.name()); + n_color["association"] = "element"; + conduit::Node &n_color_values = n_color["values"]; + n_color_values.set_allocator(conduitAllocatorID); + n_color_values.set(conduit::DataType::int32(finalNumZones)); + auto colorView = axom::ArrayView(static_cast(n_color_values.data_ptr()), finalNumZones); + // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. RAJA::ReduceBitOr shapesUsed_reduce(0); - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. if(fragmentsView[zoneIndex] == 0) @@ -769,6 +1010,8 @@ class ClipField const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); sizesView[sizeIndex] = nIdsInFragment; shapesView[sizeIndex] = shapeID; + colorView[sizeIndex] = caseData[1] - COLOR0; + sizeIndex++; // Record which shape type was used. (ids are powers of 2) @@ -780,6 +1023,12 @@ class ClipField shapesUsed_reduce |= shapesUsed; }); + // If inside and outside are not selected, remove the color field since we should not need it. + if(!(opts.inside() && opts.outside())) + { + n_newFields.remove(opts.colorField()); + } + // Make offsets RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), finalNumZones), RAJA::make_span(offsetsView.data(), finalNumZones), @@ -820,7 +1069,7 @@ class ClipField sliceIndicesView[start + i] = zoneIndex; }); slice.m_indicesView = sliceIndicesView; - makeFields(blend, slice, n_newTopo.name(), n_fields, n_newFields); + makeFields(blend, slice, opts.topologyName(n_topo.name()), opts.fields(n_fields), n_fields, n_newFields); //----------------------------------------------------------------------- // STAGE 8 - make originalElements (this will later be optional) @@ -835,7 +1084,7 @@ class ClipField using value_type = typename decltype(origValuesView)::value_type; conduit::Node &n_origElem = n_newFields["originalElements"]; n_origElem["association"] = "element"; - n_origElem["topology"] = n_topo.name(); + n_origElem["topology"] = opts.topologyName(n_newTopo.name()); conduit::Node &n_values = n_origElem["values"]; n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); @@ -854,7 +1103,7 @@ class ClipField // Make a new node and populate originalElement. conduit::Node &n_orig = n_newFields["originalElements"]; n_orig["association"] = "element"; - n_orig["topology"] = n_topo.name(); + n_orig["topology"] = opts.topologyName(n_newTopo.name()); conduit::Node &n_values = n_orig["values"]; n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(connTypeID, finalNumZones)); @@ -913,28 +1162,33 @@ class ClipField * * \param blend The BlendData that we need to construct the new vertex fields. * \param slice The SliceData we need to construct new element fields. + * \param topologyName The name of the new field's topology. + * \param fieldMap A map containing the names of the fields that we'll operate on. * \param n_fields The source fields. - * \param[out[ n_out_fields The node that will contain the new fields. + * \param[out] n_out_fields The node that will contain the new fields. */ -// TODO: pass in a map of fields that we want to map to the new topology. - void makeFields(const BlendData &blend, const SliceData &slice, const std::string &topoName, const conduit::Node &n_fields, conduit::Node &n_out_fields) const + void makeFields(const BlendData &blend, + const SliceData &slice, + const std::string &topologyName, + const std::map &fieldMap, + const conduit::Node &n_fields, + conduit::Node &n_out_fields) const { - for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { - const conduit::Node &n_field = n_fields[i]; - if(n_field["topology"].as_string() == topoName) + const conduit::Node &n_field = n_fields.fetch_existing(it->first); + const std::string association = n_field["association"].as_string(); + if(association == "element") { - const std::string association = n_field["association"].as_string(); - if(association == "element") - { - axom::mir::utilities::blueprint::FieldSlicer s; - s.execute(slice, n_field, n_out_fields[n_field.name()]); - } - else if(association == "vertex") - { - axom::mir::utilities::blueprint::FieldBlender b; - b.execute(blend, n_field, n_out_fields[n_field.name()]); - } + axom::mir::utilities::blueprint::FieldSlicer s; + s.execute(slice, n_field, n_out_fields[it->second]); + n_out_fields[it->second]["topology"] = topologyName; + } + else if(association == "vertex") + { + axom::mir::utilities::blueprint::FieldBlender b; + b.execute(blend, n_field, n_out_fields[it->second]); + n_out_fields[it->second]["topology"] = topologyName; } } } diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt new file mode 100644 index 0000000000..6f162ed25f --- /dev/null +++ b/src/axom/mir/TODO.txt @@ -0,0 +1,31 @@ +Partial TODO list + +1. view tests +2. Conduit routine for moving data to the GPU +3. Build/run on the GPU +4. More ClipField tests (for other topologies) with baselines + - Test with the various extraction options + - Test setting the topo, coordset names + - Test storing results in same Node, use names/fields map to change names + - Make sure we can operate on a StridedStructured mesh +5. Create UnstructuredTopologyMixedShapeView and dispatch +6. Make sure ClipFilter can use the mixed topology view - it probably needs some changes +7. Code cleanup pass in ClipFilter + - Make sure array view data types are consistent, change to IndexType as needed. + - Make sure views have "View" in the name. + - Consolidate some arrays in structs to make passing data to methods easier + - Split the stages into methods +8. Write a multi-domain, dispatch-based ClipFilter that follows what the original EquiZAlgorithm was doing. +8. Make routine for making a volfrac field from a matset. +9. Create EquiZ implementation +10. Write EquiZ tests +11. Create ELVIRA MIR +12. Create routines that take 2 meshes, do MIR on mesh1, and then intersect mesh1 MIR'd geometry with the mesh2 and compute VF fields +13. Create axom abstractions for RAJA reductions, write serial non-RAJA implementation too +14. Create axom abstractions for RAJA sorts, write serial non-RAJA implementation too +15. Create axom abstractions for RAJA scans, write serial non-RAJA implementation too +16. Go back and use axom/RAJA abstractions +17. Revisit NodeArrayView code with interleaved data - make the right views. +18. [lower priority] Revisit NodeArrayView and constness/references. +19. [lower priority] Can ClipField be generalized a little into something like TopologyExtractor? Then use it to make other filters? +20. [lower priority] ConduitAllocateThroughAxom uses statics. I'd prefer to template it on ExecSpace and make the allocator a member but Conduit's API does not let us pass a void* to the allocation callbacks. diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index 1486ff070f..72ad249f88 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -42,13 +42,13 @@ conduit::index_t ConduitAllocateThroughAxom::getConduitAllocatorID() const void *ConduitAllocateThroughAxom::internal_allocate(size_t items, size_t item_size) { void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); - std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; + //std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; return ptr; } void ConduitAllocateThroughAxom::internal_free(void *ptr) { - std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; + //std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; axom::deallocate(ptr); } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 2ab15e68e5..8dc59bf3ca 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -67,6 +67,7 @@ struct cpp2conduit { using type = conduit::float64; static con * \brief This class registers a Conduit allocator that can make Conduit allocate * through Axom's allocate/deallocate functions using a specific allocator. This * permits Conduit to allocate through Axom's UMPIRE logic. + * */ class ConduitAllocateThroughAxom { diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index edcbd5f48b..1719c4fb33 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -43,6 +43,106 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) }); } +TEST(mir_clipfield, options) +{ + int nzones = 6; + + conduit::Node options; + axom::mir::clipping::ClipOptions opts(nzones, options); + + options["clipField"] = "distance"; + EXPECT_EQ(opts.clipField(), options["clipField"].as_string()); + + EXPECT_EQ(opts.clipValue(), 0.); + options["clipValue"] = 2.5f; + EXPECT_EQ(opts.clipValue(), 2.5f); + + EXPECT_EQ(opts.topologyName("default"), "default"); + options["topologyName"] = "topo"; + EXPECT_EQ(opts.topologyName("default"), "topo"); + + EXPECT_EQ(opts.coordsetName("default"), "default"); + options["coordsetName"] = "coords"; + EXPECT_EQ(opts.coordsetName("default"), "coords"); + + EXPECT_EQ(opts.colorField(), "color"); + options["colorField"] = "custom_color"; + EXPECT_EQ(opts.colorField(), "custom_color"); + + EXPECT_TRUE(opts.inside()); + options["inside"] = 1; + EXPECT_TRUE(opts.inside()); + options["inside"] = 0; + EXPECT_FALSE(opts.inside()); + + EXPECT_FALSE(opts.outside()); + options["outside"] = 1; + EXPECT_TRUE(opts.outside()); + options["outside"] = 0; + EXPECT_FALSE(opts.outside()); + + // The clip field has to be present + conduit::Node n_fields; + n_fields["distance/topology"] = "topo"; + n_fields["distance/association"] = "vertex"; + n_fields["distance/values"].set(std::vector{0.,1.,2.,3.}); + + // There are currently no fields in the options. fields should just return the clip field. + auto fields = opts.fields(n_fields); + EXPECT_EQ(fields.size(), 1); + EXPECT_EQ(fields.begin()->first, "distance"); + EXPECT_EQ(fields.begin()->second, "distance"); + + // Add an empty fields node so we select NO fields. + (void)options["fields"]; + fields = opts.fields(n_fields); + EXPECT_EQ(fields.size(), 0); + + // Add some fields + options["fields/distance"] = "distance"; + options["fields/source"] = "destination"; + options["fields/same"] = 1; + fields = opts.fields(n_fields); + EXPECT_EQ(fields.size(), 3); + int i = 0; + for(auto it = fields.begin(); it != fields.end(); it++, i++) + { + if(i == 0) + { + EXPECT_EQ(it->first, "distance"); + EXPECT_EQ(it->second, "distance"); + } + else if(i == 1) + { + EXPECT_EQ(it->first, "same"); + EXPECT_EQ(it->second, "same"); + } + else if(i == 2) + { + EXPECT_EQ(it->first, "source"); + EXPECT_EQ(it->second, "destination"); + } + } + + // There are no "selectedZones" in the options. We should get nzones values from 0 onward. + auto selectedZonesView = opts.selectedZonesView(); + EXPECT_EQ(selectedZonesView.size(), 6); + EXPECT_EQ(selectedZonesView[0], 0); + EXPECT_EQ(selectedZonesView[1], 1); + EXPECT_EQ(selectedZonesView[2], 2); + EXPECT_EQ(selectedZonesView[3], 3); + EXPECT_EQ(selectedZonesView[4], 4); + EXPECT_EQ(selectedZonesView[5], 5); + + // Put some "selectedZones" in the options. + opts.invalidateSelectedZones(); + options["selectedZones"].set(std::vector{5,4,3}); + selectedZonesView = opts.selectedZonesView(); + EXPECT_EQ(selectedZonesView.size(), 3); + EXPECT_EQ(selectedZonesView[0], 3); + EXPECT_EQ(selectedZonesView[1], 4); + EXPECT_EQ(selectedZonesView[2], 5); +} TEST(mir_clipfield, uniform2d) { @@ -66,7 +166,16 @@ TEST(mir_clipfield, uniform2d) // Clip the data conduit::Node clipmesh; axom::mir::clipping::ClipField clipper(topoView, coordsetView); - clipper.execute(mesh, "distance", clipmesh); + conduit::Node options; + options["clipField"] = "distance"; + options["inside"] = 1; + options["outside"] = 1; + options["topologyName"] = "cliptopo"; + options["coordsetName"] = "clipcoords"; + options["fields/braid"] = "new_braid"; + options["fields/radial"] = "new_radial"; + + clipper.execute(mesh, options, clipmesh); conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d_yaml", "yaml"); diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 076a48e4bf..366f494399 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -93,13 +93,13 @@ class StructuredTopologyView const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { using ShapeType = HexShape; const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); IndexType data[8]; data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; @@ -119,13 +119,12 @@ class StructuredTopologyView const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - const auto jp = nodeIndexing.jStride(); - - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { using ShapeType = QuadShape; const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); IndexType data[4]; data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; @@ -141,7 +140,7 @@ class StructuredTopologyView const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { using ShapeType = LineShape; @@ -179,14 +178,14 @@ class StructuredTopologyView const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { using ShapeType = HexShape; const auto zoneIndex = idsView[selectIndex]; const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); IndexType data[8]; data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; @@ -206,13 +205,13 @@ class StructuredTopologyView const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - const auto jp = nodeIndexing.jStride(); axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { using ShapeType = QuadShape; const auto zoneIndex = idsView[selectIndex]; const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); IndexType data[4]; data[0] = nodeIndexing.LogicalIndexToIndex(logical); data[1] = data[0] + 1; diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 6661bd6299..02f6f182c7 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -83,14 +83,14 @@ class UnstructuredTopologySingleShapeView { const auto nzones = numberOfZones(); - axom::ArrayView connectivity(m_connectivity); + axom::ArrayView connectivityView(m_connectivity); if constexpr (ShapeType::is_variable_size()) { axom::ArrayView sizes(m_sizes); axom::ArrayView offsets(m_offsets); axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { - const axom::ArrayView shapeData(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex]); + const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const ShapeType shape(shapeData); func(zoneIndex, shape); }); @@ -99,7 +99,7 @@ class UnstructuredTopologySingleShapeView { axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) { - const axom::ArrayView shapeData(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const axom::ArrayView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); const ShapeType shape(shapeData); func(zoneIndex, shape); }); @@ -120,7 +120,7 @@ class UnstructuredTopologySingleShapeView const auto nSelectedZones = selectedIdsView.size(); ViewType idsView(selectedIdsView); - axom::ArrayView connectivity(m_connectivity); + axom::ArrayView connectivityView(m_connectivity); if constexpr (ShapeType::is_variable_size()) { @@ -129,7 +129,7 @@ class UnstructuredTopologySingleShapeView axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) { const auto zoneIndex = idsView[selectIndex]; - const axom::ArrayView shapeData(connectivity.data() + offsets[zoneIndex], sizes[zoneIndex]); + const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const ShapeType shape(shapeData); func(zoneIndex, shape); }); @@ -139,7 +139,7 @@ class UnstructuredTopologySingleShapeView axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) { const auto zoneIndex = idsView[selectIndex]; - const axom::ArrayView shapeData(connectivity.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const axom::ArrayView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); const ShapeType shape(shapeData); func(zoneIndex, shape); }); From a5ff0acab86dcb0242797f2a6f88ac0828c149cc Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 22 Jul 2024 15:56:42 -0700 Subject: [PATCH 101/290] More testing --- src/axom/mir/TODO.txt | 1 + src/axom/mir/blueprint_utilities.hpp | 235 +++++------------- src/axom/mir/tests/CMakeLists.txt | 2 + .../mir/tests/mir_blueprint_utilities.cpp | 143 +++++++++++ src/axom/mir/tests/mir_views.cpp | 47 ++++ src/axom/mir/utilities.cpp | 38 --- src/axom/mir/utilities.hpp | 23 -- src/axom/mir/views/Shapes.hpp | 23 +- 8 files changed, 284 insertions(+), 228 deletions(-) create mode 100644 src/axom/mir/tests/mir_blueprint_utilities.cpp create mode 100644 src/axom/mir/tests/mir_views.cpp diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index 6f162ed25f..a6149320b2 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -29,3 +29,4 @@ Partial TODO list 18. [lower priority] Revisit NodeArrayView and constness/references. 19. [lower priority] Can ClipField be generalized a little into something like TopologyExtractor? Then use it to make other filters? 20. [lower priority] ConduitAllocateThroughAxom uses statics. I'd prefer to template it on ExecSpace and make the allocator a member but Conduit's API does not let us pass a void* to the allocation callbacks. +21. Check the edge definitions in pyr, wedge Shape types. diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 8dc59bf3ca..5dd10bd696 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -11,10 +11,18 @@ #include "axom/core/ArrayView.hpp" #include "axom/core/memory_management.hpp" #include "axom/mir/views/dispatch_structured_topology.hpp" +#include "axom/mir/views/NodeArrayView.hpp" #include #include +#if defined(AXOM_USE_RAJA) + #include +#endif + +#include +#include + namespace axom { namespace mir @@ -86,16 +94,16 @@ class ConduitAllocateThroughAxom }; //------------------------------------------------------------------------------ -// TODO: Add in a routine to migrate a Conduit node to a new memory space. -// copy(const conduit::Node &src, conduit::Node &dest); - /** * \accelerated * \brief Make an unstructured representation of a structured topology. * * \tparam ExecSpace The execution space where the work will be done. * - * \param + * \param topo The input topology to be turned into unstructured. + * \param coordset The topology's coordset. It will be referenced as an external node in the output \a mesh. + * \param topoName The name of the new topology to create. + * \param mesh The node that will contain the new topology and coordset. * * \note There are blueprint methods for this sort of thing but this one is accelerated. */ @@ -105,6 +113,7 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const { const std::string type = topo.fetch_existing("type").as_string(); const auto allocatorID = axom::execution_space::allocatorID(); + ConduitAllocateThroughAxom c2a(axom::execution_space::allocatorID()); mesh["coordsets"][coordset.name()].set_external(coordset); conduit::Node &newtopo = mesh["topologies"][topoName]; @@ -120,9 +129,9 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const conduit::Node &n_newconn = newtopo["elements/connectivity"]; conduit::Node &n_newsizes = newtopo["elements/sizes"]; conduit::Node &n_newoffsets = newtopo["elements/offsets"]; - n_newconn.set_allocator(allocatorID); - n_newsizes.set_allocator(allocatorID); - n_newoffsets.set_allocator(allocatorID); + n_newconn.set_allocator(c2a.getConduitAllocatorID()); + n_newsizes.set_allocator(c2a.getConduitAllocatorID()); + n_newoffsets.set_allocator(c2a.getConduitAllocatorID()); views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) { @@ -142,9 +151,9 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const n_newoffsets.set(conduit::DataType::index_t(nzones)); // Make views for the mesh data. - axom::ArrayView connView(reinterpret_cast(n_newconn.data_ptr()), connSize); - axom::ArrayView sizesView(reinterpret_cast(n_newsizes.data_ptr()), nzones); - axom::ArrayView offsetsView(reinterpret_cast(n_newoffsets.data_ptr()), nzones); + axom::ArrayView connView(static_cast(n_newconn.data_ptr()), connSize); + axom::ArrayView sizesView(static_cast(n_newsizes.data_ptr()), nzones); + axom::ArrayView offsetsView(static_cast(n_newoffsets.data_ptr()), nzones); // Fill in the new connectivity. topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) @@ -160,188 +169,82 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const } } -#if 0 /** - \brief This method slices a Conduit field using a node that contains index value - and stores the data in another node. The data in the nodes are assumed to - be in the memory space suitable for the ExecSpace. - - \tparam ExecSpace The execution space. - - \param[in] field The Blueprint field to be sliced. - \param[in] indices A Conduit node that contains an array of index values into the field. - \param[out] output A node that contains the new sliced field. - + * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, + * making sure to allocate array data in the appropriate memory space for + * the execution space. */ template -void -sliceField(const conduit::Node &field, const conduit::Node &indices, conduit::Node &output) +void copy(conduit::Node &dest, const conduit::Node &src) { - // Start making an output field. - output["topology"] = field["topology"].as_string(); - output["association"] = field["association"].as_string(); - conduit::Node &output_values = output["values"]; - - const conduit::Node &field_values = field["values"]; - const int allocatorID = axom::execution_space::allocatorID(); - const axom::IndexType = indices.dtype().number_of_elements(); + ConduitAllocateThroughAxom c2a(axom::execution_space::allocatorID()); - // Slice the field using the indices and store values in the output field. - if(field_values.number_of_children() > 0) + if(src.number_of_children() > 0) { - for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) + for(conduit::index_t i = 0; i < src.number_of_children(); i++) { - const conduit::Node &componentNode = field_values[i]; - const std::string componentName(componentNode.name()); - - // Allocate memory for the output view using the allocator for the ExecSpace. - conduit::Node &destNode = output_values[componentName]; - destNode.set_allocator(allocatorID); - destNode.set(conduit::DataType(componentNode.dtype().id(), numIndices)); - - views::IndexNode_to_ArrayView(indices, [&](auto indicesView) - { - views::Node_to_ArrayView(componentNode, destNode, [&](auto fieldValuesView, auto destView) - { - axom::for_all( - numIndices, - AXOM_LAMBDA(axom::IndexType i) { - destView[i] = fieldValuesView[indicesView[i]]; - }); - }); - }); + copy(dest[src[i].name()], src[i]); } } else { - // Allocate memory for the output view using the allocator for the ExecSpace. - output_values.set_allocator(allocatorID); - output_values.set(conduit::DataType(field_values.dtype().id(), numIndices)); - - IndexNodeToArrayView(indices, [&](auto indicesView) + if(!src.dtype().is_string() && src.dtype().number_of_elements() > 1) { - NodeToArrayView(field_values, output_values, [&](auto fieldValuesView, auto destView) + // Allocate the node's memory in the right place. + dest.reset(); + dest.set_allocator(c2a.getConduitAllocatorID()); + dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); + + // Copy the data to the destination node. Axom uses Umpire to manage that. + if(src.is_compact()) + axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); + else { - axom::for_all( - numIndices, - AXOM_LAMBDA(axom::IndexType i) { - destView[i] = fieldValuesView[indicesView[i]]; - }); - }); - }); + // NOTE: this assumes that src is on the host. Why would we have strided data on device? + conduit::Node tmp; + src.compact_to(tmp); + axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); + } + } + else + { + // The node data fits in the node. It's on the host. + dest.set(src); + } } } +/** + * \brief Get the min/max values for the data in a Conduit node. + * + * \param[in] n The Conduit node whose data we're checking. + * + * \return A pair containing the min,max values in the node. + */ template -void -blendField(const conduit::Node &field, const conduit::Node &indices, const conduit::Node &weights, const conduit::Node &sizes, const conduit::Node &offsets, conduit::Node &output) +std::pair minmax(const conduit::Node &n) { - // Start making an output field. - output["topology"] = field["topology"].as_string(); - output["association"] = "element"; - conduit::Node &output_values = output["values"]; - - const conduit::Node &field_values = field["values"]; - const auto n_sizes = static_cast(sizes.dtype().number_of_elements()); - int execSpaceAllocatorID = axom::execution_space::allocatorID(); + std::pair retval{0., 0.}; - // Slice the field using the indices and store values in the output field. - if(field_values.number_of_children() > 0) + axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { - for(conduit::index_t i = 0; i < field_values.number_of_children(); i++) - { - const conduit::Node &componentNode = field_values[i]; - const std::string componentName(componentNode.name()); + using value_type = typename decltype(nview)::value_type; + using reduce_policy = typename axom::execution_space::reduce_policy; - // Allocate memory for the output view using the allocator for the ExecSpace. - conduit::Node &destNode = output_values[componentName]; - destNode.set_allocator(execSpaceAllocatorID); - destNode.set(conduit::DataType(componentNode.dtype().id(), n_sizes)); + RAJA::ReduceMin vmin(std::numeric_limits::max()); + RAJA::ReduceMax vmax(std::numeric_limits::min()); - IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) - { - NodeToArrayView(componentNode, weights, destNode, [&](auto fieldValuesView, auto weightsView, auto destView) - { - axom::for_all( - n_sizes, - AXOM_LAMBDA(axom::IndexType i) { - const auto offset = offsetsView[i]; - const auto size = sizesView[i]; - destView[i] = 0; - for(int j = 0; j < size; j++) - { - const auto idx = indicesView[offset + j]; - destView[i] += fieldValuesView[idx] * weightsView[idx]; - } - }); - }); - }); - } - } - else - { - // Allocate memory for the output view using the allocator for the ExecSpace. - output_values.set_allocator(execSpaceAllocatorID); - output_values.set(conduit::DataType(field_values.dtype().id(), n_sizes)); - - IndexNodeToArrayView(indices, sizes, offsets, [&](auto indicesView, auto sizesView, auto offsetsView) + axom::for_all(nview.size(), AXOM_LAMBDA(auto index) { - NodeToArrayView(field_values, weights, output_values, [&](auto fieldValuesView, auto weightsView, auto destView) - { - axom::for_all( - n_sizes, - AXOM_LAMBDA(axom::IndexType i) { - const auto offset = offsetsView[i]; - const auto size = sizesView[i]; - destView[i] = 0; - for(int j = 0; j < size; j++) - { - const auto idx = indicesView[offset + j]; - destView[i] += fieldValuesView[idx] * weightsView[idx]; - } - }); - }); + vmin.min(nview[index]); + vmax.max(nview[index]); }); - } -} -void conduit_move(const conduit::Node &src, conduit::Node &dest, int dest_allocator) -{ - if(src.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < src.number_of_children() - { - conduit_move(src[i], dest[src[i].name()], allocator); - } - } - else - { - if(src.dtype().number_of_elements() > 1) - { - // Allocate the node's memory in the right place. - dest.reset(); - dest.set_allocator(allocator); - dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); - - // Copy the data to the destination node. Axom uses Umpire to manage that. - if(src.is_compact()) - axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); - else - { - // NOTE: this assumes that src is on the host. Why would we have strided data on device? - conduit::Node tmp; - src.compact_to(tmp); - axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); - } - } - else - { - // The node data fits in the node. It's on the host. - dest.set(src); - } - } + retval = std::pair{vmin.get(), vmax.get()}; + }); + + return retval; } -#endif } // end namespace blueprint } // end namespace utilities diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 6ae2f7a970..1457137920 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -16,8 +16,10 @@ set(gtest_mir_tests mir_cell_clipper.cpp mir_clipfield.cpp mir_utilities.cpp + mir_blueprint_utilities.cpp mir_views_indexing.cpp mir_cell_generator.cpp + mir_views.cpp ) set(mir_tests_depends_on diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp new file mode 100644 index 0000000000..88d3633720 --- /dev/null +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -0,0 +1,143 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/mir/blueprint_utilities.hpp" + +#include + +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on + + +template +void test_conduit_allocate() +{ + const auto allocatorID = axom::execution_space::allocatorID(); + axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + EXPECT_TRUE(c2a.getConduitAllocatorID() > 0); + + constexpr int nValues = 100; + conduit::Node n; + n.set_allocator(c2a.getConduitAllocatorID()); + n.set(conduit::DataType::int32(nValues)); + + // Make sure we can store some values into the data that were allocated. + int *ptr = static_cast(n.data_ptr()); + axom::for_all(nValues, AXOM_LAMBDA(auto index) + { + ptr[index] = index; + }); + + EXPECT_EQ(n.dtype().number_of_elements(), nValues); +} + +TEST(mir_blueprint_utilities, allocate) +{ + test_conduit_allocate(); +#if defined(AXOM_USE_OPENMP) + test_conduit_allocate(); +#endif +#if defined(AXOM_USE_CUDA) + test_conduit_allocate(); +#endif +#if defined(AXOM_USE_HIP) + test_conduit_allocate(); +#endif +} + +template +void test_copy_braid(const conduit::Node &mesh) +{ + // Copy the mesh to device. + conduit::Node mesh_dev; + axom::mir::utilities::blueprint::copy(mesh_dev, mesh); + + // Run some minmax operations on device (proves that the data was in the right place) and check the results. + + constexpr double eps = 1.e-7; + + auto x = axom::mir::utilities::blueprint::minmax(mesh["coordsets/coords/values/x"]); + //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; + EXPECT_NEAR(x.first, -10., eps); + EXPECT_NEAR(x.second, 10., eps); + + auto y = axom::mir::utilities::blueprint::minmax(mesh["coordsets/coords/values/y"]); + //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; + EXPECT_NEAR(y.first, -10., eps); + EXPECT_NEAR(y.second, 10., eps); + + auto c = axom::mir::utilities::blueprint::minmax(mesh["topologies/mesh/elements/connectivity"]); + //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; + EXPECT_NEAR(c.first, 0., eps); + EXPECT_NEAR(c.second, 999., eps); + + auto r = axom::mir::utilities::blueprint::minmax(mesh["fields/radial/values"]); + //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; + EXPECT_NEAR(r.first, 19.2450089729875, eps); + EXPECT_NEAR(r.second, 173.205080756888, eps); +} + +TEST(mir_blueprint_utilities, copy) +{ + const int d[3] = {10, 10, 10}; + conduit::Node mesh; + conduit::blueprint::mesh::examples::braid("hexs", d[0], d[1], d[2], mesh); + + test_copy_braid(mesh); +#if defined(AXOM_USE_OPENMP) + test_copy_braid(mesh); +#endif +#if defined(AXOM_USE_CUDA) + test_copy_braid(mesh); +#endif +#if defined(AXOM_USE_HIP) + test_copy_braid(mesh); +#endif +} + +TEST(mir_blueprint_utilities, to_unstructured) +{ + // TODO: to_unstructured +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp new file mode 100644 index 0000000000..af0b83a067 --- /dev/null +++ b/src/axom/mir/tests/mir_views.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/mir.hpp" + + +TEST(mir_views, shape2conduitName) +{ + EXPECT_EQ(axom::mir::views::LineShape::name, "line"); + EXPECT_EQ(axom::mir::views::LineShape::name, "line"); + + EXPECT_EQ(axom::mir::views::TriShape::name, "tri"); + EXPECT_EQ(axom::mir::views::TriShape::name, "tri"); + + EXPECT_EQ(axom::mir::views::QuadShape::name, "quad"); + EXPECT_EQ(axom::mir::views::QuadShape::name, "quad"); + + EXPECT_EQ(axom::mir::views::TetShape::name, "tet"); + EXPECT_EQ(axom::mir::views::TetShape::name, "tet"); + + EXPECT_EQ(axom::mir::views::PyramidShape::name, "pyramid"); + EXPECT_EQ(axom::mir::views::PyramidShape::name, "pyramid"); + + EXPECT_EQ(axom::mir::views::WedgeShape::name, "wedge"); + EXPECT_EQ(axom::mir::views::WedgeShape::name, "wedge"); + + EXPECT_EQ(axom::mir::views::HexShape::name, "hex"); + EXPECT_EQ(axom::mir::views::HexShape::name, "hex"); +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp index 1d842d348e..794c1fd0a1 100644 --- a/src/axom/mir/utilities.cpp +++ b/src/axom/mir/utilities.cpp @@ -48,44 +48,6 @@ std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) return (static_cast(hash) << 32) | hashr; } -conduit::index_t ConduitAllocateThroughAxom::conduitAllocatorID = -1; -int ConduitAllocateThroughAxom::axomAllocatorID = 0; - -ConduitAllocateThroughAxom::ConduitAllocateThroughAxom(int _allocatorID) -{ - // Stash the Axom allocatorID so we don't just use the default later. - axomAllocatorID = _allocatorID; - - // Register internal functions here as Conduit allocator functions. This returns - // the conduitAllocatorID, which is the value that we must pass to Node::set_allocator. - if(conduitAllocatorID == -1) - { - conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); - } -} - -ConduitAllocateThroughAxom::~ConduitAllocateThroughAxom() -{ -} - -conduit::index_t ConduitAllocateThroughAxom::getConduitAllocatorID() const -{ - return conduitAllocatorID; -} - -void *ConduitAllocateThroughAxom::internal_allocate(size_t items, size_t item_size) -{ - void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); - std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; - return ptr; -} - -void ConduitAllocateThroughAxom::internal_free(void *ptr) -{ - std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; - axom::deallocate(ptr); -} - } // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 8772ebb6f1..0ea61dc82e 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -262,29 +262,6 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array }); } -// TODO: move to blueprint_utilities when it has a .cpp file. - -/** - * \brief This class uses RAII to register internal functions that make Conduit allocate - * through Axom's allocate/deallocate functions using a specific allocator. This - * permits Conduit to allocate through Axom's UMPIRE logic. - */ -class ConduitAllocateThroughAxom -{ -public: - ConduitAllocateThroughAxom(int _allocatorID); - ~ConduitAllocateThroughAxom(); - - conduit::index_t getConduitAllocatorID() const; - -private: - static void *internal_allocate(size_t items, size_t item_size); - static void internal_free(void *ptr); - - static conduit::index_t conduitAllocatorID; - static int axomAllocatorID; -}; - } // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 4668b519ef..418cc18e0a 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -9,6 +9,8 @@ #include "axom/core/ArrayView.hpp" #include +#include + #include /** @@ -56,7 +58,12 @@ struct LineTraits AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } AXOM_HOST_DEVICE constexpr static IndexType faces[][2] = {{0, 1}}; - AXOM_HOST_DEVICE constexpr static IndexType edges[][2] = {{0,1}}; + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + { + return axom::StackArray{0,1}; + } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "line"; }; /* @@ -94,6 +101,8 @@ struct TriTraits const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "tri"; }; /* @@ -132,6 +141,8 @@ struct QuadTraits const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "quad"; }; /* @@ -188,6 +199,8 @@ struct PolygonShape return axom::StackArray{p0, p1}; } + AXOM_HOST_DEVICE constexpr static inline const char *name = "polygon"; + private: axom::ArrayView m_ids; }; @@ -235,6 +248,8 @@ struct TetTraits const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 0},{0, 3}, {1, 3}, {2, 3}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "tet"; }; /* @@ -281,6 +296,8 @@ struct PyramidTraits const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}, {0,4}, {1,4}, {2,4}, {3,4}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "pyramid"; }; /* @@ -329,6 +346,8 @@ struct WedgeTraits const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,3}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "wedge"; }; /* @@ -375,6 +394,8 @@ struct HexTraits const axom::StackArray edges[] = {{0, 1}, {1, 2}, {3, 2}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; return edges[edgeIndex]; } + + AXOM_HOST_DEVICE constexpr static inline const char *name = "hex"; }; /** From 07bdecf3f1e39535e726c5088f5595293b2f410e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 22 Jul 2024 16:32:46 -0700 Subject: [PATCH 102/290] templated --- src/axom/mir/TODO.txt | 2 +- src/axom/mir/tests/mir_clipfield.cpp | 83 ++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index a6149320b2..4f6ffa2974 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -1,7 +1,7 @@ Partial TODO list 1. view tests -2. Conduit routine for moving data to the GPU +(done)2. Conduit routine for moving data to the GPU 3. Build/run on the GPU 4. More ClipField tests (for other topologies) with baselines - Test with the various extraction options diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 1719c4fb33..8bcfb315ab 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -11,7 +11,31 @@ #include #include -using seq_exec = axom::SEQ_EXEC; +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on template void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) @@ -144,7 +168,8 @@ TEST(mir_clipfield, options) EXPECT_EQ(selectedZonesView[2], 5); } -TEST(mir_clipfield, uniform2d) +template +void braid2d_clip_test(const std::string &type, const std::string &name) { using Indexing = axom::mir::views::StructuredIndexing; using TopoView = axom::mir::views::StructuredTopologyView; @@ -154,18 +179,17 @@ TEST(mir_clipfield, uniform2d) axom::StackArray zoneDims{dims[0] - 1, dims[1] - 1}; // Create the data - conduit::Node mesh; - braid("uniform", dims, mesh); - conduit::relay::io::blueprint::save_mesh(mesh, "uniform2d_orig", "hdf5"); + conduit::Node hostMesh, deviceMesh; + braid(type, dims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); // Create views axom::StackArray origin{0., 0.}, spacing{1., 1.}; CoordsetView coordsetView(dims, origin, spacing); TopoView topoView(Indexing{zoneDims}); - // Clip the data - conduit::Node clipmesh; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); + // Create options to control the clipping. conduit::Node options; options["clipField"] = "distance"; options["inside"] = 1; @@ -175,13 +199,50 @@ TEST(mir_clipfield, uniform2d) options["fields/braid"] = "new_braid"; options["fields/radial"] = "new_radial"; - clipper.execute(mesh, options, clipmesh); - conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d", "hdf5"); - conduit::relay::io::blueprint::save_mesh(clipmesh, "uniform2d_yaml", "yaml"); +#if 0 + // Select the left half of zones + std::vector sel; + for(int i = 0; i < topoView.numberOfZones(); i++) + { + if(i % zoneDims[0] < 5) + sel.push_back(i); + } + options["selectedZones"].set(sel); +#endif + + // Clip the data + conduit::Node deviceClipMesh; + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + deviceClipMesh.print(); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Save data. + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); // Load a clipped baseline file & compare. } +TEST(mir_clipfield, uniform2d) +{ + braid2d_clip_test("uniform", "uniform2d"); +#if 0 +#if defined(AXOM_USE_OPENMP) + braid2d_clip_test("uniform", "uniform2d_omp"); +#endif +#if defined(AXOM_USE_CUDA) + braid2d_clip_test("uniform", "uniform2d_cuda"); +#endif +#if defined(AXOM_USE_HIP) + braid2d_clip_test("uniform", "uniform2d_hip"); +#endif + +#endif +} //------------------------------------------------------------------------------ From 395c37c87dabe24be5d180341d709a214256ce5a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 22 Jul 2024 17:18:08 -0700 Subject: [PATCH 103/290] Changes that let ClipField run on GPU with HIP --- src/axom/core/ArrayView.hpp | 4 +-- src/axom/mir/ClipField.hpp | 2 +- src/axom/mir/tests/mir_clipfield.cpp | 5 ++-- src/axom/mir/tests/mir_views.cpp | 28 ++++++++++---------- src/axom/mir/utilities.cpp | 36 ------------------------- src/axom/mir/utilities.hpp | 39 ++++++++++++++++++++++++++-- src/axom/mir/views/Shapes.hpp | 16 ++++++------ 7 files changed, 64 insertions(+), 66 deletions(-) diff --git a/src/axom/core/ArrayView.hpp b/src/axom/core/ArrayView.hpp index 42de1352ed..3ea9638788 100644 --- a/src/axom/core/ArrayView.hpp +++ b/src/axom/core/ArrayView.hpp @@ -73,7 +73,7 @@ class ArrayView : public ArrayBase> typename... Args, typename Enable = typename std::enable_if< sizeof...(Args) == DIM && detail::all_types_are_integral::value>::type> - ArrayView(T* data, Args... args); + AXOM_HOST_DEVICE ArrayView(T* data, Args... args); /*! * \brief Generic constructor for an ArrayView of arbitrary dimension with external data @@ -232,7 +232,7 @@ using MCArrayView = ArrayView; //------------------------------------------------------------------------------ template template -ArrayView::ArrayView(T* data, Args... args) +AXOM_HOST_DEVICE ArrayView::ArrayView(T* data, Args... args) : ArrayView(data, StackArray {{static_cast(args)...}}) { diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 0de8ade4c2..d0485413f0 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -193,8 +193,8 @@ float computeWeight(float d0, float d1, float clipValue) * * \return The index of the clipping case. */ -AXOM_HOST_DEVICE template +AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, DataType clipValue) { size_t clipcase = 0; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 8bcfb315ab..b0a142dba6 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -214,7 +214,6 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::Node deviceClipMesh; axom::mir::clipping::ClipField clipper(topoView, coordsetView); clipper.execute(deviceMesh, options, deviceClipMesh); - deviceClipMesh.print(); // Copy device->host conduit::Node hostClipMesh; @@ -237,10 +236,10 @@ TEST(mir_clipfield, uniform2d) #if defined(AXOM_USE_CUDA) braid2d_clip_test("uniform", "uniform2d_cuda"); #endif -#if defined(AXOM_USE_HIP) - braid2d_clip_test("uniform", "uniform2d_hip"); #endif +#if defined(AXOM_USE_HIP) + braid2d_clip_test("uniform", "uniform2d_hip"); #endif } diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index af0b83a067..e9a9ffec44 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -11,26 +11,26 @@ TEST(mir_views, shape2conduitName) { - EXPECT_EQ(axom::mir::views::LineShape::name, "line"); - EXPECT_EQ(axom::mir::views::LineShape::name, "line"); + EXPECT_EQ(axom::mir::views::LineShape::name(), "line"); + EXPECT_EQ(axom::mir::views::LineShape::name(), "line"); - EXPECT_EQ(axom::mir::views::TriShape::name, "tri"); - EXPECT_EQ(axom::mir::views::TriShape::name, "tri"); + EXPECT_EQ(axom::mir::views::TriShape::name(), "tri"); + EXPECT_EQ(axom::mir::views::TriShape::name(), "tri"); - EXPECT_EQ(axom::mir::views::QuadShape::name, "quad"); - EXPECT_EQ(axom::mir::views::QuadShape::name, "quad"); + EXPECT_EQ(axom::mir::views::QuadShape::name(), "quad"); + EXPECT_EQ(axom::mir::views::QuadShape::name(), "quad"); - EXPECT_EQ(axom::mir::views::TetShape::name, "tet"); - EXPECT_EQ(axom::mir::views::TetShape::name, "tet"); + EXPECT_EQ(axom::mir::views::TetShape::name(), "tet"); + EXPECT_EQ(axom::mir::views::TetShape::name(), "tet"); - EXPECT_EQ(axom::mir::views::PyramidShape::name, "pyramid"); - EXPECT_EQ(axom::mir::views::PyramidShape::name, "pyramid"); + EXPECT_EQ(axom::mir::views::PyramidShape::name(), "pyramid"); + EXPECT_EQ(axom::mir::views::PyramidShape::name(), "pyramid"); - EXPECT_EQ(axom::mir::views::WedgeShape::name, "wedge"); - EXPECT_EQ(axom::mir::views::WedgeShape::name, "wedge"); + EXPECT_EQ(axom::mir::views::WedgeShape::name(), "wedge"); + EXPECT_EQ(axom::mir::views::WedgeShape::name(), "wedge"); - EXPECT_EQ(axom::mir::views::HexShape::name, "hex"); - EXPECT_EQ(axom::mir::views::HexShape::name, "hex"); + EXPECT_EQ(axom::mir::views::HexShape::name(), "hex"); + EXPECT_EQ(axom::mir::views::HexShape::name(), "hex"); } //------------------------------------------------------------------------------ diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp index 794c1fd0a1..cb84c3bc98 100644 --- a/src/axom/mir/utilities.cpp +++ b/src/axom/mir/utilities.cpp @@ -12,42 +12,6 @@ namespace mir namespace utilities { -AXOM_HOST_DEVICE -std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) -{ - std::uint32_t hash = 0; - - // Build the length into the hash. - const auto ldata = reinterpret_cast(&length); - for(int e = 0; e < 4; e++) - { - hash += ldata[e]; - hash += hash << 10; - hash ^= hash >> 6; - } - - std::uint32_t hashr = hash; - for(std::uint32_t i = 0; i < length; i++) - { - hash += data[i]; - hash += hash << 10; - hash ^= hash >> 6; - - hashr += data[length - 1 - i]; - hashr += hashr << 10; - hashr ^= hashr >> 6; - } - hash += hash << 3; - hash ^= hash >> 11; - hash += hash << 15; - - hashr += hashr << 3; - hashr ^= hashr >> 11; - hashr += hashr << 15; - - return (static_cast(hash) << 32) | hashr; -} - } // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 0ea61dc82e..afb4bc2437 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -96,10 +96,45 @@ std::int32_t bsearch(T value, const axom::ArrayView &view) * backwards and the two results are merged into a uint64_t. The length is * also part of the hash to guard against a lot of repeated values in the * byte stream hashing to the same thing. - * \ + * + * \note We make this function inline since it is not a template and we want to + * use it in both host and device code. */ AXOM_HOST_DEVICE -std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length); +inline std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) +{ + std::uint32_t hash = 0; + + // Build the length into the hash. + const auto ldata = reinterpret_cast(&length); + for(int e = 0; e < 4; e++) + { + hash += ldata[e]; + hash += hash << 10; + hash ^= hash >> 6; + } + + std::uint32_t hashr = hash; + for(std::uint32_t i = 0; i < length; i++) + { + hash += data[i]; + hash += hash << 10; + hash ^= hash >> 6; + + hashr += data[length - 1 - i]; + hashr += hashr << 10; + hashr ^= hashr >> 6; + } + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + + hashr += hashr << 3; + hashr ^= hashr >> 11; + hashr += hashr << 15; + + return (static_cast(hash) << 32) | hashr; +} //------------------------------------------------------------------------------ /** diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 418cc18e0a..d87e9983af 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -63,7 +63,7 @@ struct LineTraits return axom::StackArray{0,1}; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "line"; + AXOM_HOST_DEVICE constexpr static const char *name() { return "line"; } }; /* @@ -102,7 +102,7 @@ struct TriTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "tri"; + AXOM_HOST_DEVICE constexpr static const char *name() { return "tri"; } }; /* @@ -142,7 +142,7 @@ struct QuadTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "quad"; + AXOM_HOST_DEVICE constexpr static inline const char *name() { return "quad"; } }; /* @@ -199,7 +199,7 @@ struct PolygonShape return axom::StackArray{p0, p1}; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "polygon"; + AXOM_HOST_DEVICE constexpr static const char *name() { return "polygon"; } private: axom::ArrayView m_ids; @@ -249,7 +249,7 @@ struct TetTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "tet"; + AXOM_HOST_DEVICE constexpr static inline const char *name() { return "tet"; } }; /* @@ -297,7 +297,7 @@ struct PyramidTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "pyramid"; + AXOM_HOST_DEVICE constexpr static inline const char *name() { return "pyramid"; } }; /* @@ -347,7 +347,7 @@ struct WedgeTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "wedge"; + AXOM_HOST_DEVICE constexpr static inline const char *name() { return "wedge"; } }; /* @@ -395,7 +395,7 @@ struct HexTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name = "hex"; + AXOM_HOST_DEVICE constexpr static inline const char *name() { return "hex"; } }; /** From 24a78d504145e51b1a1a63305c0e639e99ce08c3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 23 Jul 2024 18:29:58 -0700 Subject: [PATCH 104/290] Initial mixed topo support --- src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/ClipField.hpp | 37 ++- src/axom/mir/TODO.txt | 4 +- src/axom/mir/clipping/ClipTableManager.hpp | 14 +- src/axom/mir/views/Shapes.hpp | 213 ++++++++++++++- .../UnstructuredTopologyMixedShapeView.hpp | 252 ++++++++++++++++++ .../views/dispatch_unstructured_topology.hpp | 36 ++- 7 files changed, 529 insertions(+), 28 deletions(-) create mode 100644 src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index a0dc3502ad..3b3077948b 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -55,6 +55,7 @@ set(mir_headers views/StructuredTopologyView.hpp views/UniformCoordsetView.hpp views/UnstructuredTopologyPolyhedralView.hpp + views/UnstructuredTopologyMixedShapeView.hpp views/UnstructuredTopologySingleShapeView.hpp clipping/ClipCases.h diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index d0485413f0..204c5bf93c 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -137,10 +137,21 @@ shapeMap_FromFlags(std::uint64_t shapes) return sm; } +template AXOM_HOST_DEVICE -int getClipTableIndex(int dimension, int nnodes) +IndexT getClipTableIndex(IndexT shapeId) { - return (dimension == 2) ? ((nnodes == 3) ? 0 : 1) : (nnodes - 2); + IndexT index = 0; + switch(shapeId) + { + case views::TriShape::id(): index = 0; break; + case views::QuadShape::id(): index = 1; break; + case views::TetShape::id(): index = 2; break; + case views::PyramidShape::id(): index = 3; break; + case views::WedgeShape::id(): index = 4; break; + case views::HexShape::id(): index = 5; break; + } + return index; } AXOM_HOST_DEVICE @@ -563,7 +574,7 @@ class ClipField clipCasesView[zoneIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; int thisBlendGroups = 0; // The number of blend groups produced in this case. @@ -735,7 +746,7 @@ class ClipField const auto clipcase = clipCasesView[zoneIndex]; // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; const std::uint64_t ptused = pointsUsedView[zoneIndex]; @@ -988,7 +999,7 @@ class ClipField // Iterate over the selected fragments and emit connectivity for them. const auto clipcase = clipCasesView[zoneIndex]; - const auto clipTableIndex = details::getClipTableIndex(zone.dimension(), zone.numberOfNodes()); + const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto ctView = clipTableViews[clipTableIndex]; auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -1129,17 +1140,17 @@ class ClipField */ void createClipTableViews(ClipTableViews &views, int dimension) { - if(dimension == 2) + if(dimension == -1 || dimension == 2) { - views[0] = m_clipTables[ST_TRI].view(); - views[1] = m_clipTables[ST_QUA].view(); + views[details::getClipTableIndex(axom::mir::views::TriShape::id())] = m_clipTables[ST_TRI].view(); + views[details::getClipTableIndex(axom::mir::views::QuadShape::id())] = m_clipTables[ST_QUA].view(); } - else if(dimension == 3) + if(dimension == -1 || dimension == 3) { - views[2] = m_clipTables[ST_TET].view(); - views[3] = m_clipTables[ST_PYR].view(); - views[4] = m_clipTables[ST_WDG].view(); - views[5] = m_clipTables[ST_HEX].view(); + views[details::getClipTableIndex(axom::mir::views::TetShape::id())] = m_clipTables[ST_TET].view(); + views[details::getClipTableIndex(axom::mir::views::PyramidShape::id())] = m_clipTables[ST_PYR].view(); + views[details::getClipTableIndex(axom::mir::views::WedgeShape::id())] = m_clipTables[ST_WDG].view(); + views[details::getClipTableIndex(axom::mir::views::HexShape::id())] = m_clipTables[ST_HEX].view(); } } diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index 4f6ffa2974..a2633de2ab 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -1,8 +1,8 @@ Partial TODO list 1. view tests -(done)2. Conduit routine for moving data to the GPU -3. Build/run on the GPU +(done) 2. Conduit routine for moving data to the GPU +(done) 3. Build/run on the GPU 4. More ClipField tests (for other topologies) with baselines - Test with the various extraction options - Test setting the topo, coordset names diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 157b652052..6076d5da9e 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -326,10 +326,16 @@ class ClipTableManager std::vector shapes(int dim) const { std::vector s; - if(dim == 2) - s = std::vector{ST_TRI, ST_QUA}; - else if(dim == 3) - s = std::vector{ST_TET, ST_PYR, ST_WDG, ST_HEX}; + if(dim == -1 || dim == 2) + { + for(const auto value : std::vector{ST_TRI, ST_QUA}) + s.push_back(value); + } + if(dim == -1 || dim == 3) + { + for(const auto value : std::vector{ST_TET, ST_PYR, ST_WDG, ST_HEX}) + s.push_back(value); + } return s; } private: diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index d87e9983af..e23efbe8af 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -142,7 +142,7 @@ struct QuadTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name() { return "quad"; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "quad"; } }; /* @@ -249,7 +249,7 @@ struct TetTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name() { return "tet"; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "tet"; } }; /* @@ -297,7 +297,7 @@ struct PyramidTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name() { return "pyramid"; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "pyramid"; } }; /* @@ -347,7 +347,7 @@ struct WedgeTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name() { return "wedge"; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "wedge"; } }; /* @@ -395,7 +395,7 @@ struct HexTraits return edges[edgeIndex]; } - AXOM_HOST_DEVICE constexpr static inline const char *name() { return "hex"; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "hex"; } }; /** @@ -411,7 +411,7 @@ struct Shape : public ShapeTraits */ AXOM_HOST_DEVICE Shape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() { - assert(m_ids.size() == ShapeTraits::numberOfNodes()); + SLIC_ASSERT(m_ids.size() == ShapeTraits::numberOfNodes()); } /** @@ -483,6 +483,207 @@ using WedgeShape = Shape>; template using HexShape = Shape>; + +/** + * \brief This is a shape that can act as any of the other shapes. + * + * \note This is a substitute for polymorphism so we can run on device. + */ +template +struct VariableShape +{ + using IndexType = IndexT; + + /** + * \brief Constructor + * + * \param shapeId The shape id that describes the points. + * \param ids The ids that describe the shape. + */ + AXOM_HOST_DEVICE + VariableShape(int shapeId, const axom::ArrayView &ids) : m_shapeId(shapeId), m_ids(ids) + { + } + + /** + * \brief Returns the shape id of the actual shape represented by the variable shape. + * \return The actual shape represented. + */ + AXOM_HOST_DEVICE int id() const { return m_shapeId; } + + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return true; } + + AXOM_HOST_DEVICE IndexType dimension() const + { + int dim = 2; + switch(m_shapeId) + { + case LineShape::id(): dim = LineShape::dimension(); break; + case TriShape::id(): dim = TriShape::dimension(); break; + case QuadShape::id(): dim = QuadShape::dimension(); break; + case PolygonShape::id(): dim = PolygonShape::dimension(); break; + case TetShape::id(): dim = TetShape::dimension(); break; + case PyramidShape::id(): dim = PyramidShape::dimension(); break; + case WedgeShape::id(): dim = WedgeShape::dimension(); break; + case HexShape::id(): dim = HexShape::dimension(); break; + } + return dim; + } + + AXOM_HOST_DEVICE IndexType numberOfNodes() const + { + IndexType nnodes = 0; + switch(m_shapeId) + { + case LineShape::id(): nnodes = LineShape::numberOfNodes(); break; + case TriShape::id(): nnodes = TriShape::numberOfNodes(); break; + case QuadShape::id(): nnodes = QuadShape::numberOfNodes(); break; + case PolygonShape::id(): nnodes = PolygonShape::numberOfNodes(); break; + case TetShape::id(): nnodes = TetShape::numberOfNodes(); break; + case PyramidShape::id(): nnodes = PyramidShape::numberOfNodes(); break; + case WedgeShape::id(): nnodes = WedgeShape::numberOfNodes(); break; + case HexShape::id(): nnodes = HexShape::numberOfNodes(); break; + } + return nnodes; + } + + AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const + { + IndexType nnodes = 0; + switch(m_shapeId) + { + case LineShape::id(): nnodes = LineShape::numberOfNodesInFace(faceIndex); break; + case TriShape::id(): nnodes = TriShape::numberOfNodesInFace(faceIndex); break; + case QuadShape::id(): nnodes = QuadShape::numberOfNodesInFace(faceIndex); break; + case PolygonShape::id(): nnodes = PolygonShape::numberOfNodesInFace(faceIndex); break; + case TetShape::id(): nnodes = TetShape::numberOfNodesInFace(faceIndex); break; + case PyramidShape::id(): nnodes = PyramidShape::numberOfNodesInFace(faceIndex); break; + case WedgeShape::id(): nnodes = WedgeShape::numberOfNodesInFace(faceIndex); break; + case HexShape::id(): nnodes = HexShape::numberOfNodesInFace(faceIndex); break; + } + return nnodes; + } + + AXOM_HOST_DEVICE IndexType maxNodesInFace() const + { + IndexType nnodes = 0; + switch(m_shapeId) + { + case LineShape::id(): nnodes = LineShape::maxNodesInFace(); break; + case TriShape::id(): nnodes = TriShape::maxNodesInFace(); break; + case QuadShape::id(): nnodes = QuadShape::maxNodesInFace(); break; + case PolygonShape::id(): nnodes = PolygonShape::maxNodesInFace(); break; + case TetShape::id(): nnodes = TetShape::maxNodesInFace(); break; + case PyramidShape::id(): nnodes = PyramidShape::maxNodesInFace(); break; + case WedgeShape::id(): nnodes = WedgeShape::maxNodesInFace(); break; + case HexShape::id(): nnodes = HexShape::maxNodesInFace(); break; + } + return nnodes; + } + + AXOM_HOST_DEVICE IndexType numberOfFaces() const + { + IndexType nfaces = 0; + switch(m_shapeId) + { + case LineShape::id(): nfaces = LineShape::numberOfFaces(); break; + case TriShape::id(): nfaces = TriShape::numberOfFaces(); break; + case QuadShape::id(): nfaces = QuadShape::numberOfFaces(); break; + case PolygonShape::id(): nfaces = PolygonShape::numberOfFaces(); break; + case TetShape::id(): nfaces = TetShape::numberOfFaces(); break; + case PyramidShape::id(): nfaces = PyramidShape::numberOfFaces(); break; + case WedgeShape::id(): nfaces = WedgeShape::numberOfFaces(); break; + case HexShape::id(): nfaces = HexShape::numberOfFaces(); break; + } + return nfaces; + } + + AXOM_HOST_DEVICE IndexType numberOfEdges() const + { + IndexType nedges = 0; + switch(m_shapeId) + { + case LineShape::id(): nedges = LineShape::numberOfEdges(); break; + case TriShape::id(): nedges = TriShape::numberOfEdges(); break; + case QuadShape::id(): nedges = QuadShape::numberOfEdges(); break; + case PolygonShape::id(): nedges = PolygonShape::numberOfEdges(); break; + case TetShape::id(): nedges = TetShape::numberOfEdges(); break; + case PyramidShape::id(): nedges = PyramidShape::numberOfEdges(); break; + case WedgeShape::id(): nedges = WedgeShape::numberOfEdges(); break; + case HexShape::id(): nedges = HexShape::numberOfEdges(); break; + } + return nedges; + } + + AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) + { + axom::StackArray edge; + switch(m_shapeId) + { + case LineShape::id(): edge = LineShape::getEdge(edgeIndex); break; + case TriShape::id(): edge = TriShape::getEdge(edgeIndex); break; + case QuadShape::id(): edge = QuadShape::getEdge(edgeIndex); break; + case PolygonShape::id(): edge = PolygonShape::getEdge(edgeIndex); break; + case TetShape::id(): edge = TetShape::getEdge(edgeIndex); break; + case PyramidShape::id(): edge = PyramidShape::getEdge(edgeIndex); break; + case WedgeShape::id(): edge = WedgeShape::getEdge(edgeIndex); break; + case HexShape::id(): edge = HexShape::getEdge(edgeIndex); break; + } + return edge; + } + + /** + * \brief Get a specific id that makes up this shape. + * + * \return The i'th id that makes up this shape. + */ + AXOM_HOST_DEVICE IndexType getId(size_t index) const { return m_ids[index]; } + + /** + * \brief Get the ids that make up this shape. + * + * \return A view containing the ids that make up this shape. + */ + AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + + AXOM_HOST_DEVICE constexpr static const char *name() { return "mixed"; } +private: + int m_shapeId; + axom::ArrayView m_ids; +}; + +/** + * \brief Given a shape name (matches Blueprint shape name), return the Shape id() value. + * + * \param name The shape name. + * + * \return The shape id that matches the name, or 0 if there is no match. + */ +template +IndexT shapeNameToID(const std::string &name) +{ + IndexT id = 0; + if(name == LineShape::name()) + id = LineShape::id(); + else if(name == TriShape::name()) + id = TriShape::id(); + else if(name == QuadShape::name()) + id = QuadShape::id(); + else if(name == PolygonShape::name()) + id = PolygonShape::id(); + else if(name == TetShape::name()) + id = TetShape::id(); + else if(name == PyramidShape::name()) + id = PyramidShape::id(); + else if(name == WedgeShape::name()) + id = WedgeShape::id(); + else if(name == HexShape::name()) + id = HexShape::id(); + return id; +} + + } // end namespace views } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp new file mode 100644 index 0000000000..111e75794e --- /dev/null +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -0,0 +1,252 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_MIXED_SHAPE_VIEW_HPP_ +#define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_MIXED_SHAPE_VIEW_HPP_ + +#include "axom/mir/views/Shapes.hpp" +#include "axom/mir/utilities.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/** + * \brief Given a shape value, we can get the Shape::id() that is used internally. + */ +template +class ShapeMap +{ +public: + using IndexType = IndexT; + + /** + * \brief Constructor + */ + AXOM_HOST_DEVICE ShapeMap() : m_shape_values(), m_shape_ids() + { + } + + /** + * \brief Constructor + * + * \param shape_values A view of sorted values used in the Conduit data. + * \param shape_ids A view of shape ids that correspond to the values from Conduit. + */ + AXOM_HOST_DEVICE ShapeMap(const axom::ArrayView &shape_values, const axom::ArrayView &shape_ids) : m_shape_values(shape_values), m_shape_ids(shape_ids) + { + } + + /** + * \brief Return the size of the shape map. + * \return The number of entries in the shape map. + */ + AXOM_HOST_DEVICE IndexType size() const + { + return m_shape_values.size(); + } + + /** + * \brief Return whether the shape map is empty. + * \return True if the map is empty; False otherwise. + */ + AXOM_HOST_DEVICE bool empty() const + { + return m_shape_values.empty(); + } + + /** + * \brief Given a shape value (as in the Conduit shapes array), return the shape id. + * + * \param value A value from the shapes array that we want to map to a shape id. + * + * \return A shape id. + */ + AXOM_HOST_DEVICE IndexType operator[](IndexType value) const + { + const auto index = axom::mir::utilities::bsearch(value, m_shape_values); + return (index >= 0) ? m_shape_ids[index] : 0; + } + +private: + axom::ArrayView m_shape_values; + axom::ArrayView m_shape_ids; +}; + + +/** + * \brief This class provides a view for Conduit/Blueprint mixed shape unstructured grids. + * + * \tparam IndexT The index type that will be used for connectivity, etc. + * \tparam ShapeT The shape type. + */ +template +class UnstructuredTopologyMixedShapeView +{ +public: + using IndexType = IndexType; + using ShapeType = VariableShape; + + /** + * \brief Constructor + * + * \param topo A reference to the topology. + * \param conn The mesh connectivity. + * \param shapes The shape in each zone. + * \param sizes The number of nodes in each zone. + * \param offsets The offset to each zone in the connectivity. + */ + UnstructuredTopologyMixedShapeView(const conduit::Node &topo, + const axom::ArrayView &conn, + const axom::ArrayView &shapes, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) : + m_topo(topo), m_connectivity(conn), m_shapes(shapes), m_sizes(sizes), m_offsets(offsets) + { + SLIC_ASSERT(m_shapes.size() != 0); + SLIC_ASSERT(m_sizes.size() != 0); + SLIC_ASSERT(m_offsets.size() != 0); + SLIC_ASSERT(m_offsets.size() == m_sizes.size() && m_offsets.size() == m_shapes.size()); + } + + /** + * \brief Return the dimension of the shape. + * + * \return -1 for unknown dimension. We'd have to look at the shapes. + */ + static constexpr int dimension() { return -1; } + + /** + * \brief Return the number of zones. + * + * \return The number of zones. + */ + IndexType numberOfZones() const + { + return m_sizes.size(); + } + + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + void for_all_zones(FuncType &&func) const + { + const auto nzones = numberOfZones(); + + const axom::ArrayView connectivityView(m_connectivity); + const axom::ArrayView shapes(m_shapes); + const axom::ArrayView sizes(m_sizes); + const axom::ArrayView offsets(m_offsets); + + // Build a ShapeMap from the Conduit shape map. + axom::Array values, ids; + const auto allocatorID = axom::execution_space::allocatorID(); + buildShapeMap(values, ids, allocatorID); + const ShapeMap shapeMap(values.view(), ids.view()); + + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) + { + const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); + const auto shapeID = shapeMap[shapes[zoneIndex]]; + // TODO: SLIC_ASSERT(shapeID > 0); + const ShapeType shape(shapeID, shapeData); + func(zoneIndex, shape); + }); + } + + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ + template + void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + { + const auto nSelectedZones = selectedIdsView.size(); + + const ViewType idsView(selectedIdsView); + const axom::ArrayView connectivityView(m_connectivity); + const axom::ArrayView shapes(m_shapes); + const axom::ArrayView sizes(m_sizes); + const axom::ArrayView offsets(m_offsets); + + // Build a ShapeMap from the Conduit shape map. + axom::Array values, ids; + const auto allocatorID = axom::execution_space::allocatorID(); + buildShapeMap(values, ids, allocatorID); + const ShapeMap shapeMap(values.view(), ids.view()); + + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + { + const auto zoneIndex = idsView[selectIndex]; + const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); + const auto shapeID = shapeMap[shapes[zoneIndex]]; + // TODO: SLIC_ASSERT(shapeID > 0); + const ShapeType shape(shapeID, shapeData); + func(zoneIndex, shape); + }); + } + +private: + /** + * \brief Populate the shape map values/ids arrays using data in the topology's shape_map. + * + * \param[out] values The sorted values used for shapes in the topology. + * \param[out] ids The Shape ids that correspond to the shape values. + * \param allocatorID The allocator to use when creating the arrays. + */ + void buildShapeMap(axom::Array &values, axom::Array &ids, int allocatorID) const + { + // Make the map from the Conduit shape_map. Use std::map to sort the key values. + std::map sm; + const conduit::Node &m_shape_map = m_topo.fetch_existing("elements/shape_map"); + for(conduit::index_t i = 0; i < m_shape_map.number_of_children(); i++) + { + const auto value = static_cast(m_shape_map[i].to_int()); + sm[value] = axom::mir::views::shapeNameToID(m_shape_map[i].name()); + } + + // Store the map in 2 vectors so data are contiguous. + const auto n = sm.size(); + std::vector valuesvec, idsvec; + valuesvec.reserve(n); + idsvec.reserve(n); + for(auto it = sm.begin(); it != sm.end(); it++) + { + valuesvec.push_back(it->first); + idsvec.push_back(it->second); + } + + // Copy the map values to the device memory. + values = axom::Array(n, n, allocatorID); + ids = axom::Array(n, n, allocatorID); + axom::copy(values.data(), valuesvec.data(), n * sizeof(IndexType)); + axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); + } + + const conduit::Node & m_topo; + axom::ArrayView m_connectivity; + axom::ArrayView m_shapes; + axom::ArrayView m_sizes; + axom::ArrayView m_offsets; +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index a3cd3f4b36..4831911e60 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -8,6 +8,7 @@ #include "axom/mir/views/UnstructuredTopologySingleShapeView.hpp" #include "axom/mir/views/UnstructuredTopologyPolyhedralView.hpp" +#include "axom/mir/views/UnstructuredTopologyMixedShapeView.hpp" #include "axom/mir/views/NodeArrayView.hpp" #include "axom/mir/views/Shapes.hpp" @@ -48,6 +49,36 @@ void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncTy } } +/** + * \brief This function dispatches a Conduit mixed unstructured topology. + * + * \tparam FuncType The function/lambda type that will be invoked on the view. + * + * \param topo The node that contains the topology. + * \param func The function/lambda to call with the topology view. + * + * \note When this function makes the view, the view keeps a reference to + * the shape_map within the topology so we can build our own shape map + * later in the for_all_zones method. + */ +template +void dispatch_unstructured_mixed_topology(const conduit::Node &topo, FuncType &&func) +{ + const std::string shape = topo["elements/shape"].as_string(); + if(shape == "mixed") + { + IndexNode_to_ArrayView_same( + topo["elements/connectivity"], topo["elements/shapes"], topo["elements/sizes"], topo["elements/offsets"], + [&](auto connView, auto shapesView, auto sizesView, auto offsetsView) + { + using IndexType = typename decltype(connView)::value_type; + + UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, offsetsView); + func(shape, ugView); + }); + } +} + /** * \brief This function dispatches a Conduit topology to the right view type * and passes that view to the supplied function/lambda. @@ -76,6 +107,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) eligible = false; } } + #if 0 // TODO: Can't use polygon with single shape view because its sizes are not known at compile time. if constexpr (ShapeTypes & PolygonShape::id()) @@ -98,7 +130,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { if(eligible && shape == "mixed") { - // TODO: handle mixed zone types. + dispatch_unstructured_mixed_topology(topo, func); eligible = false; } } @@ -162,8 +194,6 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) } } - // TODO: handle mixed shapes. - }); } } From 41e6a465a6ec591559f0db1801f25df2176783e1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 24 Jul 2024 14:58:59 -0700 Subject: [PATCH 105/290] Try out the mixed shape view --- src/axom/mir/ClipField.hpp | 10 ++--- src/axom/mir/tests/mir_clipfield.cpp | 43 ++++++++++++++++++++- src/axom/mir/views/ExplicitCoordsetView.hpp | 33 ++++++++++------ src/axom/mir/views/Shapes.hpp | 35 +++++++++-------- src/axom/mir/views/dispatch_coordset.hpp | 4 +- 5 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 204c5bf93c..1440fa5eea 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -208,8 +208,10 @@ template AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, DataType clipValue) { + using ZoneIndex = typename ZoneType::IndexType; + size_t clipcase = 0; - for(size_t i = 0; i < zone.numberOfNodes(); i++) + for(ZoneIndex i = 0; i < zone.numberOfNodes(); i++) { const auto id = zone.getId(i); const auto value = view[id] - clipValue; @@ -740,8 +742,6 @@ class ClipField const auto clipValue = opts.clipValue(); m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - using ZoneType = typename std::remove_reference::type; - // Get the clip case for the current zone. const auto clipcase = clipCasesView[zoneIndex]; @@ -791,7 +791,7 @@ class ClipField // face point. We can store the 2 corner points in place // of the edge point (along with some blending coeff). const auto edgeIndex = ptid - EA; - const auto edge = ZoneType::getEdge(edgeIndex); + const auto edge = zone.getEdge(edgeIndex); const auto id0 = zone.getId(edge[0]); const auto id1 = zone.getId(edge[1]); @@ -847,7 +847,7 @@ class ClipField if(axom::utilities::bitIsSet(ptused, pid)) { const auto edgeIndex = pid - EA; - const auto edge = ZoneType::getEdge(edgeIndex); + const auto edge = zone.getEdge(edgeIndex); const auto id0 = zone.getId(edge[0]); const auto id1 = zone.getId(edge[1]); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index b0a142dba6..5761f36875 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -190,11 +190,12 @@ void braid2d_clip_test(const std::string &type, const std::string &name) TopoView topoView(Indexing{zoneDims}); // Create options to control the clipping. + const std::string clipTopoName("cliptopo"); conduit::Node options; options["clipField"] = "distance"; options["inside"] = 1; options["outside"] = 1; - options["topologyName"] = "cliptopo"; + options["topologyName"] = clipTopoName; options["coordsetName"] = "clipcoords"; options["fields/braid"] = "new_braid"; options["fields/radial"] = "new_radial"; @@ -223,6 +224,46 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); + // Now, take the clipped mesh and clip it again using a mixed topology view. + using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; + using ExpCoordsetView = axom::mir::views::ExplicitCoordsetView; + conduit::Node &n_x = deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x"); + conduit::Node &n_y = deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y"); + axom::ArrayView xView(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); + ExpCoordsetView mixedCoordsetView(xView, yView); + + conduit::Node &n_device_topo = deviceClipMesh.fetch_existing("topologies/" + clipTopoName); + conduit::Node &n_conn = n_device_topo.fetch_existing("elements/connectivity"); + conduit::Node &n_shapes = n_device_topo.fetch_existing("elements/shapes"); + conduit::Node &n_sizes = n_device_topo.fetch_existing("elements/sizes"); + conduit::Node &n_offsets = n_device_topo.fetch_existing("elements/offsets"); + axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); + axom::ArrayView shapesView(static_cast(n_shapes.data_ptr()), n_shapes.dtype().number_of_elements()); + axom::ArrayView sizesView(static_cast(n_sizes.data_ptr()), n_sizes.dtype().number_of_elements()); + axom::ArrayView offsetsView(static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); + MixedTopoView mixedTopoView(n_device_topo, connView, shapesView, sizesView, offsetsView); + + // Clip the data + conduit::Node deviceClipMixedMesh; + axom::mir::clipping::ClipField mixedClipper(mixedTopoView, mixedCoordsetView); + options["clipField"] = "new_braid"; + options["clipValue"] = 1.; + options["fields"].reset(); + options["fields/new_braid"] = "new_braid2"; + options["fields/color"] = "new_color"; + options["fields/new_radial"] = "new_radial2"; + + mixedClipper.execute(deviceClipMesh, options, deviceClipMixedMesh); + + // Copy device->host + conduit::Node hostClipMixedMesh; + axom::mir::utilities::blueprint::copy(hostClipMixedMesh, deviceClipMixedMesh); + + // Save data. + conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, name + "_mixed", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, name + "_mixed_yaml", "yaml"); + // Load a clipped baseline file & compare. } diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 18c469c4a8..43a433685d 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -19,10 +19,18 @@ namespace views { /** - * \class This class provides a view for Conduit/Blueprint explicit coordsets. + * \brief This class provides a view for Conduit/Blueprint explicit coordsets. + */ +template +class ExplicitCoordsetView +{ +}; + +/** + * \brief This class provides a view for Conduit/Blueprint 2d explicit coordsets. */ template -class ExplicitCoordsetView2 +class ExplicitCoordsetView { public: using IndexType = axom::IndexType; @@ -36,10 +44,10 @@ class ExplicitCoordsetView2 * \param y The second coordinate component. */ AXOM_HOST_DEVICE - ExplicitCoordsetView2(const axom::ArrayView &x, - const axom::ArrayView &y) : m_coordinates{x,y} + ExplicitCoordsetView(const axom::ArrayView &x, + const axom::ArrayView &y) : m_coordinates{x,y} { - assert(x.size() == y.size()); + SLIC_ASSERT(x.size() == y.size()); } /** @@ -61,7 +69,7 @@ class ExplicitCoordsetView2 PointType getPoint(IndexType vertex_index) const { - assert(vertex_index < size()); + SLIC_ASSERT(vertex_index < size()); return PointType(std::initializer_list{m_coordinates[0][vertex_index], m_coordinates[1][vertex_index]}); } @@ -85,10 +93,10 @@ class ExplicitCoordsetView2 }; /** - * \class This class provides a view for Conduit/Blueprint explicit coordsets. + * \brief This class provides a view for Conduit/Blueprint 3d explicit coordsets. */ template -class ExplicitCoordsetView3 +class ExplicitCoordsetView { public: using IndexType = axom::IndexType; @@ -103,11 +111,11 @@ class ExplicitCoordsetView3 * \param z The third coordinate component. */ AXOM_HOST_DEVICE - ExplicitCoordsetView3(const axom::ArrayView &x, - const axom::ArrayView &y, - const axom::ArrayView &z) : m_coordinates{x,y,z} + ExplicitCoordsetView(const axom::ArrayView &x, + const axom::ArrayView &y, + const axom::ArrayView &z) : m_coordinates{x,y,z} { - assert(x.size() == y.size() && x.size() == z.size()); + SLIC_ASSERT(x.size() == y.size() && x.size() == z.size()); } /** @@ -129,6 +137,7 @@ class ExplicitCoordsetView3 PointType getPoint(IndexType vertex_index) const { + SLIC_ASSERT(vertex_index < size()); return PointType(std::initializer_list{m_coordinates[0][vertex_index], m_coordinates[1][vertex_index], m_coordinates[2][vertex_index]}); diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index e23efbe8af..272250e673 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -533,19 +533,7 @@ struct VariableShape AXOM_HOST_DEVICE IndexType numberOfNodes() const { - IndexType nnodes = 0; - switch(m_shapeId) - { - case LineShape::id(): nnodes = LineShape::numberOfNodes(); break; - case TriShape::id(): nnodes = TriShape::numberOfNodes(); break; - case QuadShape::id(): nnodes = QuadShape::numberOfNodes(); break; - case PolygonShape::id(): nnodes = PolygonShape::numberOfNodes(); break; - case TetShape::id(): nnodes = TetShape::numberOfNodes(); break; - case PyramidShape::id(): nnodes = PyramidShape::numberOfNodes(); break; - case WedgeShape::id(): nnodes = WedgeShape::numberOfNodes(); break; - case HexShape::id(): nnodes = HexShape::numberOfNodes(); break; - } - return nnodes; + return m_ids.size(); } AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const @@ -556,7 +544,11 @@ struct VariableShape case LineShape::id(): nnodes = LineShape::numberOfNodesInFace(faceIndex); break; case TriShape::id(): nnodes = TriShape::numberOfNodesInFace(faceIndex); break; case QuadShape::id(): nnodes = QuadShape::numberOfNodesInFace(faceIndex); break; - case PolygonShape::id(): nnodes = PolygonShape::numberOfNodesInFace(faceIndex); break; + case PolygonShape::id(): + { + nnodes = (faceIndex == 0) ? m_ids.size() : 0; + break; + } case TetShape::id(): nnodes = TetShape::numberOfNodesInFace(faceIndex); break; case PyramidShape::id(): nnodes = PyramidShape::numberOfNodesInFace(faceIndex); break; case WedgeShape::id(): nnodes = WedgeShape::numberOfNodesInFace(faceIndex); break; @@ -607,7 +599,11 @@ struct VariableShape case LineShape::id(): nedges = LineShape::numberOfEdges(); break; case TriShape::id(): nedges = TriShape::numberOfEdges(); break; case QuadShape::id(): nedges = QuadShape::numberOfEdges(); break; - case PolygonShape::id(): nedges = PolygonShape::numberOfEdges(); break; + case PolygonShape::id(): + { + nedges = m_ids.size(); + break; + } case TetShape::id(): nedges = TetShape::numberOfEdges(); break; case PyramidShape::id(): nedges = PyramidShape::numberOfEdges(); break; case WedgeShape::id(): nedges = WedgeShape::numberOfEdges(); break; @@ -616,7 +612,7 @@ struct VariableShape return nedges; } - AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) const { axom::StackArray edge; switch(m_shapeId) @@ -624,7 +620,12 @@ struct VariableShape case LineShape::id(): edge = LineShape::getEdge(edgeIndex); break; case TriShape::id(): edge = TriShape::getEdge(edgeIndex); break; case QuadShape::id(): edge = QuadShape::getEdge(edgeIndex); break; - case PolygonShape::id(): edge = PolygonShape::getEdge(edgeIndex); break; + case PolygonShape::id(): + { + edge[0] = edgeIndex % m_ids.size(); + edge[1] = (edgeIndex + 1) % m_ids.size(); + break; + } case TetShape::id(): edge = TetShape::getEdge(edgeIndex); break; case PyramidShape::id(): edge = PyramidShape::getEdge(edgeIndex); break; case WedgeShape::id(): edge = WedgeShape::getEdge(edgeIndex); break; diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 27b83e50ac..1671522bc2 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -99,7 +99,7 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) { axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], [&](auto xView, auto yView) { - ExplicitCoordsetView2 coordView(xView, yView); + ExplicitCoordsetView coordView(xView, yView); func(coordView); }); } @@ -107,7 +107,7 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) { axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], values[2], [&](auto xView, auto yView, auto zView) { - ExplicitCoordsetView3 coordView(xView, yView, zView); + ExplicitCoordsetView coordView(xView, yView, zView); func(coordView); }); } From 4ba2c848e425355738e56007d2cc73539da73c19 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 24 Jul 2024 16:53:27 -0700 Subject: [PATCH 106/290] Some cleanup/refactoring --- src/axom/core/utilities/BitUtilities.hpp | 22 +- src/axom/mir/ClipField.hpp | 69 ++- src/axom/mir/NodeToZoneRelationBuilder.hpp | 1 - src/axom/mir/views/Shapes.hpp | 405 +++++++++--------- src/axom/mir/views/StructuredTopologyView.hpp | 1 + .../UnstructuredTopologyMixedShapeView.hpp | 42 +- .../UnstructuredTopologyPolyhedralView.hpp | 48 ++- .../UnstructuredTopologySingleShapeView.hpp | 63 ++- .../views/dispatch_unstructured_topology.hpp | 42 +- 9 files changed, 365 insertions(+), 328 deletions(-) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index a018debb8c..034b372ab4 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -243,7 +243,7 @@ AXOM_HOST_DEVICE inline std::int32_t countl_zero(std::int32_t word) noexcept */ template AXOM_HOST_DEVICE -bool bitIsSet(FlagType flags, BitType bit) +constexpr bool bitIsSet(FlagType flags, BitType bit) { assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); return (flags & (1 << bit)) > 0; @@ -262,13 +262,31 @@ bool bitIsSet(FlagType flags, BitType bit) */ template AXOM_HOST_DEVICE -void setBit(FlagType &flags, BitType bit, bool value = true) +constexpr void setBit(FlagType &flags, BitType bit, bool value = true) { assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); const auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } +/*! + * \brief Set the bit in flags. + * \accelerated + * + * \tparam FlagType The integer type that contains the flags. + * \tparam BitType The index type that stores the bit index. + * + * \param[inout] flags The flags whose bit we're setting. + * \param[in] bit The bit we're setting. + */ +template +AXOM_HOST_DEVICE +constexpr void setBitOn(FlagType &flags, BitType bit) +{ + assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); + flags |= (1 << bit); +} + /*! * \brief Count the number if bits that are on in the value. * \accelerated diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 1440fa5eea..d388e3b748 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -90,13 +90,13 @@ int ST_Index_to_ShapeID(IntegerType st_index) int shapeID = 0; switch(st_index) { - case ST_LIN: shapeID = views::LineShape::id(); break; - case ST_TRI: shapeID = views::TriShape::id(); break; - case ST_QUA: shapeID = views::QuadShape::id(); break; - case ST_TET: shapeID = views::TetShape::id(); break; - case ST_PYR: shapeID = views::PyramidShape::id(); break; - case ST_WDG: shapeID = views::WedgeShape::id(); break; - case ST_HEX: shapeID = views::HexShape::id(); break; + case ST_LIN: shapeID = views::Line_ShapeID; break; + case ST_TRI: shapeID = views::Tri_ShapeID; break; + case ST_QUA: shapeID = views::Quad_ShapeID; break; + case ST_TET: shapeID = views::Tet_ShapeID; break; + case ST_PYR: shapeID = views::Pyramid_ShapeID; break; + case ST_WDG: shapeID = views::Wedge_ShapeID; break; + case ST_HEX: shapeID = views::Hex_ShapeID; break; } return shapeID; } @@ -113,43 +113,42 @@ shapeMap_FromFlags(std::uint64_t shapes) { std::map sm; - if((shapes & views::LineShape::id()) > 0) - sm["line"] = views::LineShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Line_ShapeID)) + sm["line"] = views::Line_ShapeID; - if((shapes & views::TriShape::id()) > 0) - sm["tri"] = views::TriShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Tri_ShapeID)) + sm["tri"] = views::Tri_ShapeID; - if((shapes & views::QuadShape::id()) > 0) - sm["quad"] = views::QuadShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Quad_ShapeID)) + sm["quad"] = views::Quad_ShapeID; - if((shapes & views::TetShape::id()) > 0) - sm["tet"] = views::TetShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Tet_ShapeID)) + sm["tet"] = views::Tet_ShapeID; - if((shapes & views::PyramidShape::id()) > 0) - sm["pyramid"] = views::PyramidShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Pyramid_ShapeID)) + sm["pyramid"] = views::Pyramid_ShapeID; - if((shapes & views::WedgeShape::id()) > 0) - sm["wedge"] = views::WedgeShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Wedge_ShapeID)) + sm["wedge"] = views::Wedge_ShapeID; - if((shapes & views::HexShape::id()) > 0) - sm["hex"] = views::HexShape::id(); + if(axom::utilities::bitIsSet(shapes, views::Hex_ShapeID)) + sm["hex"] = views::Hex_ShapeID; return sm; } -template AXOM_HOST_DEVICE -IndexT getClipTableIndex(IndexT shapeId) +int getClipTableIndex(int shapeId) { - IndexT index = 0; + int index = 0; switch(shapeId) { - case views::TriShape::id(): index = 0; break; - case views::QuadShape::id(): index = 1; break; - case views::TetShape::id(): index = 2; break; - case views::PyramidShape::id(): index = 3; break; - case views::WedgeShape::id(): index = 4; break; - case views::HexShape::id(): index = 5; break; + case views::Tri_ShapeID: index = 0; break; + case views::Quad_ShapeID: index = 1; break; + case views::Tet_ShapeID: index = 2; break; + case views::Pyramid_ShapeID: index = 3; break; + case views::Wedge_ShapeID: index = 4; break; + case views::Hex_ShapeID: index = 5; break; } return index; } @@ -208,10 +207,8 @@ template AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, DataType clipValue) { - using ZoneIndex = typename ZoneType::IndexType; - size_t clipcase = 0; - for(ZoneIndex i = 0; i < zone.numberOfNodes(); i++) + for(IndexType i = 0; i < zone.numberOfNodes(); i++) { const auto id = zone.getId(i); const auto value = view[id] - clipValue; @@ -513,7 +510,7 @@ class ClipField using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); - using ConnectivityType = typename TopologyView::IndexType; + using ConnectivityType = typename TopologyView::ConnectivityType; constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; const auto nzones = m_topologyView.numberOfZones(); @@ -1025,8 +1022,8 @@ class ClipField sizeIndex++; - // Record which shape type was used. (ids are powers of 2) - shapesUsed |= shapeID; + // Record which shape type was used. Use a bit for each shape. + axom::utilities::setBitOn(shapesUsed, shapeID); } } } diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 4d786c80d7..40ef179aaa 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -242,7 +242,6 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit { // Shapes are all the same size. const auto nodesPerShape = shape.indices; - const auto nzones = connSize / nodesPerShape; // Allocate Conduit arrays on the device in a data type that matches the connectivity. n_zones.set(conduit::DataType(intTypeId, connSize)); diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 272250e673..688abd9476 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -31,19 +31,38 @@ namespace mir namespace views { +/// Shape ids. +enum +{ + Point_ShapeID = 0, + Line_ShapeID = 1, + Tri_ShapeID = 2, + Quad_ShapeID = 3, + Polygon_ShapeID = 4, + Tet_ShapeID = 5, + Pyramid_ShapeID = 6, + Wedge_ShapeID = 7, + Hex_ShapeID = 8, + Polyhedron_ShapeID = 9, + Mixed_ShapeID = 10, + + Invalid_ShapeID = 20 +}; + // TODO: PointTraits + /* 0*-----------* 1 */ -template +//template struct LineTraits { - using IndexType = IndexT; +// using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 2; } + AXOM_HOST_DEVICE constexpr static int id() { return Line_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -76,12 +95,12 @@ struct LineTraits 0*-----* 1 */ -template +//template struct TriTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 3; } + AXOM_HOST_DEVICE constexpr static int id() { return Tri_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -115,12 +134,12 @@ struct TriTraits 0*-----------* 1 */ -template +//template struct QuadTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 4; } + AXOM_HOST_DEVICE constexpr static int id() { return Quad_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -145,67 +164,6 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "quad"; } }; -/* - -n-1 *-... * 2 - | | - | | - 0 *-----* 1 - - */ -template -struct PolygonShape -{ - using IndexType = IndexT; - - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 5; } - AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } - AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return true; } - - AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } - - /** - * \brief Construct a shape. - */ - AXOM_HOST_DEVICE PolygonShape(const axom::ArrayView &ids) : m_ids(ids) - { - } - - AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } - - /** - * \brief Get the ids that make up this shape. - * - * \return A view containing the ids that make up this shape. - */ - AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } - - /** - * \brief Get the ids for the requested face. - * - * \param faceIndex The index of the desired face. - * - * \return An array view (wrapping m_faceIds) that contains the ids for the face. - */ - AXOM_HOST_DEVICE axom::ArrayView getFace(int /*faceIndex*/) const - { - return m_ids; - } - - AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) const - { - const auto p0 = edgeIndex % m_ids.size(); - const auto p1 = (edgeIndex + 1) % m_ids.size(); - return axom::StackArray{p0, p1}; - } - - AXOM_HOST_DEVICE constexpr static const char *name() { return "polygon"; } - -private: - axom::ArrayView m_ids; -}; - - /* 3 * @@ -222,12 +180,12 @@ struct PolygonShape 1 edge 5: 2,3 */ -template +//template struct TetTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 6; } + AXOM_HOST_DEVICE constexpr static int id() { return Tet_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -270,12 +228,12 @@ struct TetTraits edge 7: 3,4 */ -template +//template struct PyramidTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 7; } + AXOM_HOST_DEVICE constexpr static int id() { return Pyramid_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -319,12 +277,12 @@ struct PyramidTraits edge 8: 2,3 */ -template +//template struct WedgeTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 8; } + AXOM_HOST_DEVICE constexpr static int id() { return Wedge_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -366,12 +324,12 @@ struct WedgeTraits 4 5 */ -template +//template struct HexTraits { - using IndexType = IndexT; + //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 9; } + AXOM_HOST_DEVICE constexpr static int id() { return Hex_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -398,20 +356,85 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "hex"; } }; +/* + +n-1 *-... * 2 + | | + | | + 0 *-----* 1 + + */ +struct PolygonTraits +{ + AXOM_HOST_DEVICE constexpr static int id() { return Polygon_ShapeID; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return true; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 20; } + AXOM_HOST_DEVICE constexpr static const char *name() { return "polygon"; } + +}; + +template +struct PolygonShape : public PolygonTraits +{ + using ConnectivityType = ConnType; + using ConnectivityView = axom::ArrayView; + + /** + * \brief Construct a shape. + */ + AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) : m_idsView(ids) + { + } + + /** + * \brief Get the ids that make up this shape. + * + * \return A view containing the ids that make up this shape. + */ + AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } + + /** + * \brief Get the ids for the requested face. + * + * \param faceIndex The index of the desired face. + * + * \return An array view (wrapping m_faceIds) that contains the ids for the face. + */ + AXOM_HOST_DEVICE ConnectivityView getFace(int /*faceIndex*/) const + { + return m_idsView; + } + + AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) const + { + const auto p0 = edgeIndex % m_idsView.size(); + const auto p1 = (edgeIndex + 1) % m_idsView.size(); + return axom::StackArray{p0, p1}; + } + +private: + ConnectivityView m_idsView; +}; + /** * \brief This class extends the ShapeTraits with object state so it can represent a zone. */ -template +template struct Shape : public ShapeTraits { - using IndexType = typename ShapeTraits::IndexType; + using ConnectivityType = ConnType; + using ConnectivityView = axom::ArrayView; /** * \brief Construct a shape. */ - AXOM_HOST_DEVICE Shape(const axom::ArrayView &ids) : m_ids(ids), m_faceIds() + AXOM_HOST_DEVICE Shape(const ConnectivityView &ids) : m_idsView(ids), m_faceIds() { - SLIC_ASSERT(m_ids.size() == ShapeTraits::numberOfNodes()); + SLIC_ASSERT(m_idsView.size() == ShapeTraits::numberOfNodes()); } /** @@ -419,21 +442,21 @@ struct Shape : public ShapeTraits * * \return The i'th id that makes up this shape. */ - AXOM_HOST_DEVICE IndexType getId(size_t index) const { return m_ids[index]; } + AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const { return m_idsView[index]; } /** * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } /** * \brief Get the unique ids that make up this shape. For basic shapes, assume they are unique. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE axom::ArrayView getUniqueIds() const { return m_ids; } + AXOM_HOST_DEVICE ConnectivityView getUniqueIds() const { return m_idsView; } /** * \brief Get the ids for the requested face. @@ -443,45 +466,45 @@ struct Shape : public ShapeTraits * \return An array view (wrapping m_faceIds) that contains the ids for the face. */ AXOM_HOST_DEVICE - axom::ArrayView + ConnectivityView getFace(int faceIndex) const { if constexpr(ShapeTraits::dimension() == 2) - return m_ids; + return m_idsView; else { const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); for(IndexType i = 0; i < nnodes; i++) - m_faceIds[i] = m_ids[ShapeTraits::faces[faceIndex][i]]; - return axom::ArrayView(m_faceIds.m_data, nnodes); + m_faceIds[i] = m_idsView[ShapeTraits::faces[faceIndex][i]]; + return ConnectivityView(m_faceIds.m_data, nnodes); } } private: - axom::ArrayView m_ids; - mutable axom::StackArray m_faceIds; + ConnectivityView m_idsView; + mutable axom::StackArray m_faceIds; }; // Make some concrete shape classes based on the shape traits. -template -using LineShape = Shape>; +template +using LineShape = Shape; -template -using TriShape = Shape>; +template +using TriShape = Shape; -template -using QuadShape = Shape>; +template +using QuadShape = Shape; -template -using TetShape = Shape>; +template +using TetShape = Shape; -template -using PyramidShape = Shape>; +template +using PyramidShape = Shape; -template -using WedgeShape = Shape>; +template +using WedgeShape = Shape; -template -using HexShape = Shape>; +template +using HexShape = Shape; /** @@ -489,10 +512,11 @@ using HexShape = Shape>; * * \note This is a substitute for polymorphism so we can run on device. */ -template +template struct VariableShape { - using IndexType = IndexT; + using ConnectivityType = ConnType; + using ConnectivityView = axom::ArrayView; /** * \brief Constructor @@ -501,8 +525,9 @@ struct VariableShape * \param ids The ids that describe the shape. */ AXOM_HOST_DEVICE - VariableShape(int shapeId, const axom::ArrayView &ids) : m_shapeId(shapeId), m_ids(ids) + VariableShape(int shapeId, const ConnectivityView &ids) : m_shapeId(shapeId), m_idsView(ids) { + //SLIC_ASSERT(shapeId >= Point_ShapeID && shapeID <= Hex_ShapeID); } /** @@ -516,24 +541,24 @@ struct VariableShape AXOM_HOST_DEVICE IndexType dimension() const { - int dim = 2; + IndexType dim = 2; switch(m_shapeId) { - case LineShape::id(): dim = LineShape::dimension(); break; - case TriShape::id(): dim = TriShape::dimension(); break; - case QuadShape::id(): dim = QuadShape::dimension(); break; - case PolygonShape::id(): dim = PolygonShape::dimension(); break; - case TetShape::id(): dim = TetShape::dimension(); break; - case PyramidShape::id(): dim = PyramidShape::dimension(); break; - case WedgeShape::id(): dim = WedgeShape::dimension(); break; - case HexShape::id(): dim = HexShape::dimension(); break; + case Line_ShapeID: dim = LineTraits::dimension(); break; + case Tri_ShapeID: dim = TriTraits::dimension(); break; + case Quad_ShapeID: dim = QuadTraits::dimension(); break; + case Polygon_ShapeID: dim = 2; break; + case Tet_ShapeID: dim = TetTraits::dimension(); break; + case Pyramid_ShapeID: dim = PyramidTraits::dimension(); break; + case Wedge_ShapeID: dim = WedgeTraits::dimension(); break; + case Hex_ShapeID: dim = HexTraits::dimension(); break; } return dim; } AXOM_HOST_DEVICE IndexType numberOfNodes() const { - return m_ids.size(); + return m_idsView.size(); } AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const @@ -541,18 +566,14 @@ struct VariableShape IndexType nnodes = 0; switch(m_shapeId) { - case LineShape::id(): nnodes = LineShape::numberOfNodesInFace(faceIndex); break; - case TriShape::id(): nnodes = TriShape::numberOfNodesInFace(faceIndex); break; - case QuadShape::id(): nnodes = QuadShape::numberOfNodesInFace(faceIndex); break; - case PolygonShape::id(): - { - nnodes = (faceIndex == 0) ? m_ids.size() : 0; - break; - } - case TetShape::id(): nnodes = TetShape::numberOfNodesInFace(faceIndex); break; - case PyramidShape::id(): nnodes = PyramidShape::numberOfNodesInFace(faceIndex); break; - case WedgeShape::id(): nnodes = WedgeShape::numberOfNodesInFace(faceIndex); break; - case HexShape::id(): nnodes = HexShape::numberOfNodesInFace(faceIndex); break; + case Line_ShapeID: nnodes = LineTraits::numberOfNodesInFace(faceIndex); break; + case Tri_ShapeID: nnodes = TriTraits::numberOfNodesInFace(faceIndex); break; + case Quad_ShapeID: nnodes = QuadTraits::numberOfNodesInFace(faceIndex); break; + case Polygon_ShapeID: nnodes = (faceIndex == 0) ? m_idsView.size() : 0; break; + case Tet_ShapeID: nnodes = TetTraits::numberOfNodesInFace(faceIndex); break; + case Pyramid_ShapeID: nnodes = PyramidTraits::numberOfNodesInFace(faceIndex); break; + case Wedge_ShapeID: nnodes = WedgeTraits::numberOfNodesInFace(faceIndex); break; + case Hex_ShapeID: nnodes = HexTraits::numberOfNodesInFace(faceIndex); break; } return nnodes; } @@ -562,14 +583,14 @@ struct VariableShape IndexType nnodes = 0; switch(m_shapeId) { - case LineShape::id(): nnodes = LineShape::maxNodesInFace(); break; - case TriShape::id(): nnodes = TriShape::maxNodesInFace(); break; - case QuadShape::id(): nnodes = QuadShape::maxNodesInFace(); break; - case PolygonShape::id(): nnodes = PolygonShape::maxNodesInFace(); break; - case TetShape::id(): nnodes = TetShape::maxNodesInFace(); break; - case PyramidShape::id(): nnodes = PyramidShape::maxNodesInFace(); break; - case WedgeShape::id(): nnodes = WedgeShape::maxNodesInFace(); break; - case HexShape::id(): nnodes = HexShape::maxNodesInFace(); break; + case Line_ShapeID: nnodes = LineTraits::maxNodesInFace(); break; + case Tri_ShapeID: nnodes = TriTraits::maxNodesInFace(); break; + case Quad_ShapeID: nnodes = QuadTraits::maxNodesInFace(); break; + case Polygon_ShapeID: nnodes = PolygonShape::maxNodesInFace(); break; + case Tet_ShapeID: nnodes = TetTraits::maxNodesInFace(); break; + case Pyramid_ShapeID: nnodes = PyramidTraits::maxNodesInFace(); break; + case Wedge_ShapeID: nnodes = WedgeTraits::maxNodesInFace(); break; + case Hex_ShapeID: nnodes = HexTraits::maxNodesInFace(); break; } return nnodes; } @@ -579,14 +600,14 @@ struct VariableShape IndexType nfaces = 0; switch(m_shapeId) { - case LineShape::id(): nfaces = LineShape::numberOfFaces(); break; - case TriShape::id(): nfaces = TriShape::numberOfFaces(); break; - case QuadShape::id(): nfaces = QuadShape::numberOfFaces(); break; - case PolygonShape::id(): nfaces = PolygonShape::numberOfFaces(); break; - case TetShape::id(): nfaces = TetShape::numberOfFaces(); break; - case PyramidShape::id(): nfaces = PyramidShape::numberOfFaces(); break; - case WedgeShape::id(): nfaces = WedgeShape::numberOfFaces(); break; - case HexShape::id(): nfaces = HexShape::numberOfFaces(); break; + case Line_ShapeID: nfaces = LineTraits::numberOfFaces(); break; + case Tri_ShapeID: nfaces = TriTraits::numberOfFaces(); break; + case Quad_ShapeID: nfaces = QuadTraits::numberOfFaces(); break; + case Polygon_ShapeID: nfaces = 1; break; + case Tet_ShapeID: nfaces = TetTraits::numberOfFaces(); break; + case Pyramid_ShapeID: nfaces = PyramidTraits::numberOfFaces(); break; + case Wedge_ShapeID: nfaces = WedgeTraits::numberOfFaces(); break; + case Hex_ShapeID: nfaces = HexTraits::numberOfFaces(); break; } return nfaces; } @@ -596,18 +617,14 @@ struct VariableShape IndexType nedges = 0; switch(m_shapeId) { - case LineShape::id(): nedges = LineShape::numberOfEdges(); break; - case TriShape::id(): nedges = TriShape::numberOfEdges(); break; - case QuadShape::id(): nedges = QuadShape::numberOfEdges(); break; - case PolygonShape::id(): - { - nedges = m_ids.size(); - break; - } - case TetShape::id(): nedges = TetShape::numberOfEdges(); break; - case PyramidShape::id(): nedges = PyramidShape::numberOfEdges(); break; - case WedgeShape::id(): nedges = WedgeShape::numberOfEdges(); break; - case HexShape::id(): nedges = HexShape::numberOfEdges(); break; + case Line_ShapeID: nedges = LineTraits::numberOfEdges(); break; + case Tri_ShapeID: nedges = TriTraits::numberOfEdges(); break; + case Quad_ShapeID: nedges = QuadTraits::numberOfEdges(); break; + case Polygon_ShapeID: nedges = m_idsView.size(); break; + case Tet_ShapeID: nedges = TetTraits::numberOfEdges(); break; + case Pyramid_ShapeID: nedges = PyramidTraits::numberOfEdges(); break; + case Wedge_ShapeID: nedges = WedgeTraits::numberOfEdges(); break; + case Hex_ShapeID: nedges = HexTraits::numberOfEdges(); break; } return nedges; } @@ -617,19 +634,20 @@ struct VariableShape axom::StackArray edge; switch(m_shapeId) { - case LineShape::id(): edge = LineShape::getEdge(edgeIndex); break; - case TriShape::id(): edge = TriShape::getEdge(edgeIndex); break; - case QuadShape::id(): edge = QuadShape::getEdge(edgeIndex); break; - case PolygonShape::id(): + case Line_ShapeID: edge = LineTraits::getEdge(edgeIndex); break; + case Tri_ShapeID: edge = TriTraits::getEdge(edgeIndex); break; + case Quad_ShapeID: edge = QuadTraits::getEdge(edgeIndex); break; + case Polygon_ShapeID: { - edge[0] = edgeIndex % m_ids.size(); - edge[1] = (edgeIndex + 1) % m_ids.size(); + const auto n = m_idsView.size(); + edge[0] = edgeIndex % n; + edge[1] = (edgeIndex + 1) % n; break; } - case TetShape::id(): edge = TetShape::getEdge(edgeIndex); break; - case PyramidShape::id(): edge = PyramidShape::getEdge(edgeIndex); break; - case WedgeShape::id(): edge = WedgeShape::getEdge(edgeIndex); break; - case HexShape::id(): edge = HexShape::getEdge(edgeIndex); break; + case Tet_ShapeID: edge = TetTraits::getEdge(edgeIndex); break; + case Pyramid_ShapeID: edge = PyramidTraits::getEdge(edgeIndex); break; + case Wedge_ShapeID: edge = WedgeTraits::getEdge(edgeIndex); break; + case Hex_ShapeID: edge = HexTraits::getEdge(edgeIndex); break; } return edge; } @@ -639,19 +657,19 @@ struct VariableShape * * \return The i'th id that makes up this shape. */ - AXOM_HOST_DEVICE IndexType getId(size_t index) const { return m_ids[index]; } + AXOM_HOST_DEVICE ConnectivityType getId(IndexType index) const { return m_idsView[index]; } /** * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE axom::ArrayView getIds() const { return m_ids; } + AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } AXOM_HOST_DEVICE constexpr static const char *name() { return "mixed"; } private: int m_shapeId; - axom::ArrayView m_ids; + ConnectivityView m_idsView; }; /** @@ -661,26 +679,25 @@ struct VariableShape * * \return The shape id that matches the name, or 0 if there is no match. */ -template -IndexT shapeNameToID(const std::string &name) +inline int shapeNameToID(const std::string &name) { - IndexT id = 0; - if(name == LineShape::name()) - id = LineShape::id(); - else if(name == TriShape::name()) - id = TriShape::id(); - else if(name == QuadShape::name()) - id = QuadShape::id(); - else if(name == PolygonShape::name()) - id = PolygonShape::id(); - else if(name == TetShape::name()) - id = TetShape::id(); - else if(name == PyramidShape::name()) - id = PyramidShape::id(); - else if(name == WedgeShape::name()) - id = WedgeShape::id(); - else if(name == HexShape::name()) - id = HexShape::id(); + int id = 0; + if(name == LineTraits::name()) + id = Line_ShapeID; + else if(name == TriTraits::name()) + id = Tri_ShapeID; + else if(name == QuadTraits::name()) + id = Quad_ShapeID; + else if(name == PolygonTraits::name()) + id = Polygon_ShapeID; + else if(name == TetTraits::name()) + id = Tet_ShapeID; + else if(name == PyramidTraits::name()) + id = Pyramid_ShapeID; + else if(name == WedgeTraits::name()) + id = Wedge_ShapeID; + else if(name == HexTraits::name()) + id = Hex_ShapeID; return id; } diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 366f494399..c26f201d8e 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -27,6 +27,7 @@ class StructuredTopologyView using IndexingPolicy = IndexPolicy; using IndexType = typename IndexingPolicy::IndexType; using LogicalIndex = typename IndexingPolicy::LogicalIndex; + using ConnectivityType = IndexType; /** * \brief Return the number of dimensions. diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 111e75794e..163f0e60e6 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -85,11 +85,12 @@ class ShapeMap * \tparam IndexT The index type that will be used for connectivity, etc. * \tparam ShapeT The shape type. */ -template +template class UnstructuredTopologyMixedShapeView { public: - using IndexType = IndexType; + using ConnectivityType = ConnT; + using ConnectivityView = axom::ArrayView; using ShapeType = VariableShape; /** @@ -102,10 +103,10 @@ class UnstructuredTopologyMixedShapeView * \param offsets The offset to each zone in the connectivity. */ UnstructuredTopologyMixedShapeView(const conduit::Node &topo, - const axom::ArrayView &conn, - const axom::ArrayView &shapes, - const axom::ArrayView &sizes, - const axom::ArrayView &offsets) : + const ConnectivityView &conn, + const ConnectivityView &shapes, + const ConnectivityView &sizes, + const ConnectivityView &offsets) : m_topo(topo), m_connectivity(conn), m_shapes(shapes), m_sizes(sizes), m_offsets(offsets) { SLIC_ASSERT(m_shapes.size() != 0); @@ -144,20 +145,19 @@ class UnstructuredTopologyMixedShapeView { const auto nzones = numberOfZones(); - const axom::ArrayView connectivityView(m_connectivity); - const axom::ArrayView shapes(m_shapes); - const axom::ArrayView sizes(m_sizes); - const axom::ArrayView offsets(m_offsets); - // Build a ShapeMap from the Conduit shape map. axom::Array values, ids; const auto allocatorID = axom::execution_space::allocatorID(); buildShapeMap(values, ids, allocatorID); const ShapeMap shapeMap(values.view(), ids.view()); + const ConnectivityView connectivityView(m_connectivity); + const ConnectivityView shapes(m_shapes); + const ConnectivityView sizes(m_sizes); + const ConnectivityView offsets(m_offsets); axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { - const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); + const ConnectivityView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; // TODO: SLIC_ASSERT(shapeID > 0); const ShapeType shape(shapeID, shapeData); @@ -169,8 +169,10 @@ class UnstructuredTopologyMixedShapeView * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. + * \tparam ViewType A view type that contains zone indices. * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. * + * \param selectedIdsView A view that contains a list of zones to operate on. * \param func The function/lambda that will be executed for each zone in the mesh. */ template @@ -178,22 +180,20 @@ class UnstructuredTopologyMixedShapeView { const auto nSelectedZones = selectedIdsView.size(); - const ViewType idsView(selectedIdsView); - const axom::ArrayView connectivityView(m_connectivity); - const axom::ArrayView shapes(m_shapes); - const axom::ArrayView sizes(m_sizes); - const axom::ArrayView offsets(m_offsets); - // Build a ShapeMap from the Conduit shape map. axom::Array values, ids; const auto allocatorID = axom::execution_space::allocatorID(); buildShapeMap(values, ids, allocatorID); const ShapeMap shapeMap(values.view(), ids.view()); + const ConnectivityView connectivityView(m_connectivity); + const ConnectivityView shapes(m_shapes); + const ConnectivityView sizes(m_sizes); + const ConnectivityView offsets(m_offsets); axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) { - const auto zoneIndex = idsView[selectIndex]; - const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); + const auto zoneIndex = selectedIdsView[selectIndex]; + const ConnectivityView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; // TODO: SLIC_ASSERT(shapeID > 0); const ShapeType shape(shapeID, shapeData); @@ -217,7 +217,7 @@ class UnstructuredTopologyMixedShapeView for(conduit::index_t i = 0; i < m_shape_map.number_of_children(); i++) { const auto value = static_cast(m_shape_map[i].to_int()); - sm[value] = axom::mir::views::shapeNameToID(m_shape_map[i].name()); + sm[value] = axom::mir::views::shapeNameToID(m_shape_map[i].name()); } // Store the map in 2 vectors so data are contiguous. diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 4e1228b9e1..283b97bd1f 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -15,21 +15,25 @@ namespace mir namespace views { -template +/** + * \brief This class implements a view for Blueprint polyhedral topologies. + */ +template class UnstructuredTopologyPolyhedralView { public: - using IndexType = IndexT; + using ConnectivityType = ConnType; + using ConnectivityView = axom::ArrayView; struct PolyhedronData { AXOM_HOST_DEVICE - PolyhedronData(axom::ArrayView subelement_conn, - axom::ArrayView subelement_sizes, - axom::ArrayView subelement_offsets, - axom::ArrayView element_conn, - axom::ArrayView element_sizes, - axom::ArrayView element_offsets) : + PolyhedronData(const ConnectivityView &subelement_conn, + const ConnectivityView &subelement_sizes, + const ConnectivityView &subelement_offsets, + const ConnectivityView &element_conn, + const ConnectivityView &element_sizes, + const ConnectivityView &element_offsets) : m_subelement_conn(subelement_conn), m_subelement_sizes(subelement_sizes), m_subelement_offsets(subelement_offsets), m_element_conn(element_conn), m_element_sizes(element_sizes), m_element_offsets(element_offsets) { @@ -42,12 +46,12 @@ class UnstructuredTopologyPolyhedralView { } - axom::ArrayView m_subelement_conn; - axom::ArrayView m_subelement_sizes; - axom::ArrayView m_subelement_offsets; - axom::ArrayView m_element_conn; - axom::ArrayView m_element_sizes; - axom::ArrayView m_element_offsets; + ConnectivityView m_subelement_conn; + ConnectivityView m_subelement_sizes; + ConnectivityView m_subelement_offsets; + ConnectivityView m_element_conn; + ConnectivityView m_element_sizes; + ConnectivityView m_element_offsets; }; // Can we provide a way to provide data about Zone i's shape? @@ -81,7 +85,7 @@ class UnstructuredTopologyPolyhedralView return getFace(faceIndex).size(); } - AXOM_HOST_DEVICE axom::ArrayView getIds() const + AXOM_HOST_DEVICE ConnectivityView getIds() const { axom::IndexType nnodes = 0; const auto nFaces = numberOfFaces(); @@ -99,10 +103,10 @@ class UnstructuredTopologyPolyhedralView } } } - return axom::ArrayView(m_ids.m_data, nnodes); + return ConnectivityView(m_ids.m_data, nnodes); } - AXOM_HOST_DEVICE axom::ArrayView getUniqueIds() const + AXOM_HOST_DEVICE ConnectivityView getUniqueIds() const { axom::IndexType nnodes = 0; const auto nFaces = numberOfFaces(); @@ -126,16 +130,16 @@ class UnstructuredTopologyPolyhedralView return axom::ArrayView(m_ids.m_data, nnodes); } - AXOM_HOST_DEVICE axom::ArrayView getFace(int faceIndex) const + AXOM_HOST_DEVICE ConnectivityView getFace(int faceIndex) const { - const axom::ArrayView element_face_ids(m_data.m_element_conn.data() + m_data.m_element_offsets[m_zoneIndex], m_data.m_element_sizes[m_zoneIndex]); + const ConnectivityView element_face_ids(m_data.m_element_conn.data() + m_data.m_element_offsets[m_zoneIndex], m_data.m_element_sizes[m_zoneIndex]); const auto faceId = element_face_ids[faceIndex]; - return axom::ArrayView(m_data.m_subelement_conn.data() + m_data.m_subelement_offsets[faceId], m_data.m_subelement_sizes[faceId]); + return ConnectivityView(m_data.m_subelement_conn.data() + m_data.m_subelement_offsets[faceId], m_data.m_subelement_sizes[faceId]); } private: - AXOM_HOST_DEVICE bool find(const IndexType *arr, axom::IndexType n, IndexType value) const + AXOM_HOST_DEVICE bool find(const ConnectivityView *arr, axom::IndexType n, ConnectivityView value) const { bool found = false; for(axom::IndexType i = 0; i < n && !found; i++) @@ -145,7 +149,7 @@ class UnstructuredTopologyPolyhedralView PolyhedronData m_data; IndexType m_zoneIndex {0}; - mutable axom::StackArray m_ids; + mutable axom::StackArray m_ids; }; //---------------------------------------------------------------------------- diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 02f6f182c7..ae4deae10c 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -25,15 +25,16 @@ template class UnstructuredTopologySingleShapeView { public: - using IndexType = typename ShapeT::IndexType; using ShapeType = ShapeT; + using ConnectivityType = typename ShapeType::ConnectivityType; + using ConnectivityView = typename ShapeType::ConnectivityView; /** * \brief Constructor * * \param conn The mesh connectivity. */ - UnstructuredTopologySingleShapeView(const axom::ArrayView &conn) : m_connectivity(conn), m_sizes(), m_offsets() + UnstructuredTopologySingleShapeView(const ConnectivityView &conn) : m_connectivity(conn), m_sizes(), m_offsets() { } @@ -44,13 +45,13 @@ class UnstructuredTopologySingleShapeView * \param sizes The number of nodes in each zone. * \param offsets The offset to each zone in the connectivity. */ - UnstructuredTopologySingleShapeView(const axom::ArrayView &conn, - const axom::ArrayView &sizes, - const axom::ArrayView &offsets) : m_connectivity(conn), m_sizes(sizes), m_offsets(offsets) + UnstructuredTopologySingleShapeView(const ConnectivityView &conn, + const ConnectivityView &sizes, + const ConnectivityView &offsets) : m_connectivity(conn), m_sizes(sizes), m_offsets(offsets) { - assert(m_sizes.size() != 0); - assert(m_offsets.size() != 0); - assert(m_offsets.size() == m_sizes.size()); + SLIC_ASSERT(m_sizes.size() != 0); + SLIC_ASSERT(m_offsets.size() != 0); + SLIC_ASSERT(m_offsets.size() == m_sizes.size()); } /** @@ -83,24 +84,24 @@ class UnstructuredTopologySingleShapeView { const auto nzones = numberOfZones(); - axom::ArrayView connectivityView(m_connectivity); + ConnectivityView connectivityView(m_connectivity); if constexpr (ShapeType::is_variable_size()) { - axom::ArrayView sizes(m_sizes); - axom::ArrayView offsets(m_offsets); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + ConnectivityView sizesView(m_sizes); + ConnectivityView offsetsView(m_offsets); + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { - const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); - const ShapeType shape(shapeData); + const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + const ShapeType shape(shapeDataView); func(zoneIndex, shape); }); } else { - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) + axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { - const axom::ArrayView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); - const ShapeType shape(shapeData); + const ConnectivityView shapeDataView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const ShapeType shape(shapeDataView); func(zoneIndex, shape); }); } @@ -119,27 +120,25 @@ class UnstructuredTopologySingleShapeView { const auto nSelectedZones = selectedIdsView.size(); - ViewType idsView(selectedIdsView); - axom::ArrayView connectivityView(m_connectivity); - + ConnectivityView connectivityView(m_connectivity); if constexpr (ShapeType::is_variable_size()) { - axom::ArrayView sizes(m_sizes); - axom::ArrayView offsets(m_offsets); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + ConnectivityView sizesView(m_sizes); + ConnectivityView offsetsView(m_offsets); + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { - const auto zoneIndex = idsView[selectIndex]; - const axom::ArrayView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); - const ShapeType shape(shapeData); + const auto zoneIndex = selectedIdsView[selectIndex]; + const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + const ShapeType shape(shapeDataView); func(zoneIndex, shape); }); } else { - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) + axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { - const auto zoneIndex = idsView[selectIndex]; - const axom::ArrayView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const auto zoneIndex = selectedIdsView[selectIndex]; + const ConnectivityView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); const ShapeType shape(shapeData); func(zoneIndex, shape); }); @@ -147,9 +146,9 @@ class UnstructuredTopologySingleShapeView } private: - axom::ArrayView m_connectivity; - axom::ArrayView m_sizes; - axom::ArrayView m_offsets; + ConnectivityView m_connectivity; + ConnectivityView m_sizes; + ConnectivityView m_offsets; }; } // end namespace views diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 4831911e60..095f895880 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -6,6 +6,7 @@ #ifndef AXOM_MIR_DISPATCH_UNSTRUCTURED_TOPOLOGY_HPP_ #define AXOM_MIR_DISPATCH_UNSTRUCTURED_TOPOLOGY_HPP_ +#include "axom/core.hpp" #include "axom/mir/views/UnstructuredTopologySingleShapeView.hpp" #include "axom/mir/views/UnstructuredTopologyPolyhedralView.hpp" #include "axom/mir/views/UnstructuredTopologyMixedShapeView.hpp" @@ -21,6 +22,7 @@ namespace mir namespace views { +// Turn on all bits so all shapes will be enabled. constexpr int AnyShape = -1; /** @@ -42,8 +44,8 @@ void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncTy topo["elements/connectivity"], topo["elements/sizes"], topo["elements/offsets"], [&](auto seConnView, auto seSizesView, auto seOffsetsView, auto connView, auto sizesView, auto offsetsView) { - using IndexType = typename decltype(seConnView)::value_type; - UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); + using ConnType = typename decltype(seConnView)::value_type; + UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); func(shape, ugView); }); } @@ -71,9 +73,9 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, FuncType && topo["elements/connectivity"], topo["elements/shapes"], topo["elements/sizes"], topo["elements/offsets"], [&](auto connView, auto shapesView, auto sizesView, auto offsetsView) { - using IndexType = typename decltype(connView)::value_type; + using ConnType = typename decltype(connView)::value_type; - UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, offsetsView); + UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, offsetsView); func(shape, ugView); }); } @@ -99,7 +101,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) bool eligible = true; // Conditionally add polyhedron support. - if constexpr (ShapeTypes & UnstructuredTopologyPolyhedralView::PolyhedronShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Polyhedron_ShapeID)) { if(shape == "polyhedral") { @@ -110,7 +112,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) #if 0 // TODO: Can't use polygon with single shape view because its sizes are not known at compile time. - if constexpr (ShapeTypes & PolygonShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Polygon_ShapeID)) { if(eligible && shape == "polygon") { @@ -126,7 +128,7 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) } } #endif - if constexpr (ShapeTypes & AnyShape) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Mixed_ShapeID)) { if(eligible && shape == "mixed") { @@ -137,58 +139,58 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) { - using IndexType = typename decltype(connView)::value_type; + using ConnType = typename decltype(connView)::value_type; // TODO: points, lines - if constexpr (ShapeTypes & TriShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & QuadShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & TetShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & PyramidShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) { if(eligible && shape == "pyramid") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & WedgeShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) { if(eligible && shape == "wedge") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } } - if constexpr (ShapeTypes & HexShape::id()) + if constexpr (axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) { if(eligible && shape == "hex") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } From 6ff5327a281f5c189ece6e58d317d27d9f7be9c6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 24 Jul 2024 17:01:49 -0700 Subject: [PATCH 107/290] code cleanup --- src/axom/mir/views/Shapes.hpp | 53 ++++++++--------------------------- 1 file changed, 12 insertions(+), 41 deletions(-) diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 688abd9476..54bdb5da46 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -13,17 +13,6 @@ #include -/** - NOTES: I use the shapes for 2 things: - 1. As type traits to help the topology views traverse connectivity - 2. As a shape type that contains information for a given zone so we can do a few useful things with it. - - Q: I was going to ask whether a shape's getFace() should return a shape itself such as a tri/quad shape. - That won't work for zones like pyr, wdg that have faces with different shapes. - - Q: Is it worth templating the shape's index type? - */ - namespace axom { namespace mir @@ -31,7 +20,8 @@ namespace mir namespace views { -/// Shape ids. +// Shape ids. These are used to identify shapes. These are used as indices +// in bit fields in some algorithms. enum { Point_ShapeID = 0, @@ -57,11 +47,8 @@ enum 0*-----------* 1 */ -//template struct LineTraits { -// using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Line_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -76,8 +63,9 @@ struct LineTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][2] = {{0, 1}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + constexpr static IndexType faces[][2] = {{0, 1}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int /*edgeIndex*/) { return axom::StackArray{0,1}; } @@ -95,11 +83,8 @@ struct LineTraits 0*-----* 1 */ -//template struct TriTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Tri_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -114,7 +99,8 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0, 1, 2}}; + constexpr static IndexType faces[][3] = {{0, 1, 2}}; + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}}; @@ -134,11 +120,8 @@ struct TriTraits 0*-----------* 1 */ -//template struct QuadTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Quad_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -153,7 +136,7 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; + constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { @@ -180,11 +163,8 @@ struct QuadTraits 1 edge 5: 2,3 */ -//template struct TetTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Tet_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -199,7 +179,7 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 6; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; + constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { @@ -228,11 +208,8 @@ struct TetTraits edge 7: 3,4 */ -//template struct PyramidTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Pyramid_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -247,7 +224,7 @@ struct PyramidTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 8; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static int faces[][4] = {{3,2,1,0}, {0,1,4,-1}, {1,2,4,-1}, {2,3,4,-1}, {3,0,4,-1}}; + constexpr static int faces[][4] = {{3,2,1,0}, {0,1,4,-1}, {1,2,4,-1}, {2,3,4,-1}, {3,0,4,-1}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { @@ -277,11 +254,8 @@ struct PyramidTraits edge 8: 2,3 */ -//template struct WedgeTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Wedge_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } @@ -297,7 +271,7 @@ struct WedgeTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 9; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static int faces[][4] = {{0,2,1,-1}, {3,4,5,-1}, {0,1,4,3}, {1,2,5,4}, {2,0,3,5}}; + constexpr static int faces[][4] = {{0,2,1,-1}, {3,4,5,-1}, {0,1,4,3}, {1,2,5,4}, {2,0,3,5}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { @@ -324,11 +298,8 @@ struct WedgeTraits 4 5 */ -//template struct HexTraits { - //using IndexType = IndexT; - AXOM_HOST_DEVICE constexpr static int id() { return Hex_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } @@ -344,7 +315,7 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 12; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - AXOM_HOST_DEVICE constexpr static IndexType faces[][4] = { + constexpr static IndexType faces[][4] = { {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) From 721e970e70cdea78dcd04691590b5785de016e5b Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 24 Jul 2024 17:42:36 -0700 Subject: [PATCH 108/290] Adjust 3D shapes --- src/axom/mir/views/Shapes.hpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 54bdb5da46..328b1e1a86 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -179,7 +179,7 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 6; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - constexpr static IndexType faces[][3] = {{0,2,1}, {0,1,3}, {1,2,3}, {2,0,3}}; + constexpr static IndexType faces[][3] = {{0,1,3}, {1,2,3}, {2,0,3}, {0,2,1}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { @@ -283,19 +283,19 @@ struct WedgeTraits }; /* - 3*------------* 2 + 4*------------* 7 /| /| / | / | / | / | - 7*------------*6 | + 5*------------*6 | | | | | | | | | - | 0*--------|---* 1 + | 0*--------|---* 3 | / | / | / | / |/ |/ *------------* - 4 5 + 1 2 */ struct HexTraits @@ -315,12 +315,11 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 12; } AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } - constexpr static IndexType faces[][4] = { - {0, 3, 2, 1}, {0, 1, 5, 4}, {1, 2, 6, 5}, {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}}; + constexpr static IndexType faces[][4] = {{3, 0, 4, 7}, {1, 2, 6, 5}, {0, 1, 5, 4}, {3, 7, 6, 2}, {0, 3, 2, 1}, {4, 5, 6, 7}}; AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { - const axom::StackArray edges[] = {{0, 1}, {1, 2}, {3, 2}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; + const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; return edges[edgeIndex]; } From 6db7e4bc3313d1bb3e558a667cbd4df58ae65676 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 25 Jul 2024 18:34:39 -0700 Subject: [PATCH 109/290] debugging --- src/axom/mir/ClipField.hpp | 164 +++++++++++++++------ src/axom/mir/CoordsetBlender.hpp | 20 +-- src/axom/mir/FieldBlender.hpp | 58 ++++++-- src/axom/mir/TODO.txt | 9 +- src/axom/mir/clipping/ClipTableManager.hpp | 71 +++++++++ src/axom/mir/tests/mir_clipfield.cpp | 89 ++++++++++- src/axom/mir/utilities.hpp | 3 +- 7 files changed, 339 insertions(+), 75 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index d388e3b748..d47689e1c0 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -424,7 +425,37 @@ class ClipOptions axom::Array m_selectedZones; // Storage for a list of selected zone ids. }; +void print_blend_group(const IndexType *ids, const float *weights, int n, int bgStart, int bgOffset, std::uint64_t name) +{ +const std::uint64_t k = 12990793018150977444; + +std::cout << " -\n"; +std::cout << " n: " << n << std::endl; +std::cout << " bgStart: " << bgStart << std::endl; +std::cout << " bgOffset: " << bgOffset << std::endl; +std::cout << " ids: ["; +for(int bi = 0; bi < n; bi++) +{ + if(bi > 0) std::cout << ", "; + std::cout << ids[bi]; +} +std::cout << "]"; +if(name == k) +{ +std::cout << " MATCH!!!"; +} +std::cout << "\n"; +std::cout << " weights: ["; +for(int bi = 0; bi < n; bi++) +{ + if(bi > 0) std::cout << ", "; + std::cout << weights[bi]; +} +std::cout << "]\n"; +std::cout << " name: " << name; +std::cout << "\n"; +} /** @@ -505,7 +536,7 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) { - using KeyType = typename BlendData::KeyType; + using KeyType = std::uint64_t; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); @@ -537,8 +568,8 @@ class ClipField // ---------------------------------------------------------------------- // - // Stage 1: Iterate over elements and their respective clip cases to - // determine sizes of outputs. + // Stage 1: Iterate over zones and their respective fragments to + // determine sizes. // // ---------------------------------------------------------------------- RAJA::ReduceSum fragment_sum(0); @@ -587,16 +618,16 @@ class ClipField for(; it != end; it++) { // Get the current shape in the clip case. - const auto caseData = *it; + const auto fragment = *it; - if(caseData[0] == ST_PNT) + if(fragment[0] == ST_PNT) { - if(details::generatedPointIsSelected(caseData[2], selection)) + if(details::generatedPointIsSelected(fragment[2], selection)) { - const size_t nIds = caseData[3]; + const size_t nIds = fragment[3]; for(size_t ni = 4; ni < nIds; ni++) { - const auto pid = caseData[ni]; + const auto pid = fragment[ni]; // Increase the blend size to include this center point. if(pid <= P7) @@ -618,21 +649,21 @@ class ClipField thisBlendGroups++; // Mark the point used. - axom::utilities::setBit(ptused, N0 + caseData[1]); + axom::utilities::setBitOn(ptused, N0 + fragment[1]); } } else { - if(details::shapeIsSelected(caseData[1], selection)) + if(details::shapeIsSelected(fragment[1], selection)) { thisFragments++; - const int nIdsThisFragment = caseData.size() - 2; + const int nIdsThisFragment = fragment.size() - 2; thisFragmentsNumIds += nIdsThisFragment; - // Mark the points in this fragment used. - for(int i = 2; i < caseData.size(); i++) + // Mark the points this fragment used. + for(int i = 2; i < fragment.size(); i++) { - axom::utilities::setBit(ptused, caseData[i]); + axom::utilities::setBitOn(ptused, fragment[i]); } } } @@ -642,7 +673,7 @@ class ClipField pointsUsedView[zoneIndex] = ptused; // Count which points in the original cell are used. - for(unsigned char pid = P0; pid <= P7; pid++) + for(IndexType pid = P0; pid <= P7; pid++) { const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; thisBlendGroupLen += incr; // {p0} @@ -650,7 +681,7 @@ class ClipField } // Count edges that are used. - for(unsigned char pid = EA; pid <= EL; pid++) + for(IndexType pid = EA; pid <= EL; pid++) { const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; thisBlendGroupLen += 2 * incr; // {p0 p1} @@ -671,7 +702,6 @@ class ClipField }); }); - // TODO: change int to topoView::IndexType // ---------------------------------------------------------------------- @@ -681,7 +711,7 @@ class ClipField // // blendOffset : Starting offset for blending data like blendIds, blendCoeff. // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. - // fragmentOffsets : Where an element's fragments begin in the output. + // fragmentOffsets : Where an zone's fragments begin in the output. // ---------------------------------------------------------------------- axom::Array blendOffset(nzones, nzones, allocatorID); axom::Array blendGroupOffsets(nzones, nzones, allocatorID); @@ -712,7 +742,7 @@ class ClipField // ---------------------------------------------------------------------- // - // Stage 3: Iterate over the elements/cases again and fill in the blend + // Stage 3: Iterate over the zones/cases again and fill in the blend // groups that get produced: blendNames, blendGroupSizes, // blendCoeff, blendIds. These are used to produce the new points. // @@ -737,6 +767,9 @@ class ClipField views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); +#if 1 +std::cout << "clipping:\n"; +#endif m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. @@ -746,31 +779,44 @@ class ClipField const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; + // These are the points used in this zone's fragments. const std::uint64_t ptused = pointsUsedView[zoneIndex]; - // Starting offset of where we store this element's blend groups. + // Starting offset of where we store this zone's blend groups. std::uint32_t bgStart = blendOffsetView[zoneIndex]; std::uint32_t bgOffset = blendGroupOffsetsView[zoneIndex]; - +#if 1 +std::cout << " -\n"; +std::cout << " zone: " << zoneIndex << std::endl; +std::cout << " clipcase: " << clipcase << std::endl; +std::cout << " fragments:\n"; +int fi = 0; +#endif auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); for(; it != end; it++) { // Get the current shape in the clip case. - const auto caseData = *it; + const auto fragment = *it; +#if 1 +std::cout << " f" << fi << ": \""; +fi++; +it.print(std::cout); +std::cout << "\"" << std::endl; +#endif - if(caseData[0] == ST_PNT) + if(fragment[0] == ST_PNT) { - if(details::generatedPointIsSelected(caseData[2], selection)) + if(details::generatedPointIsSelected(fragment[2], selection)) { // TODO: put the ability to select on color back in... 0, 1, or both - const int nIds = static_cast(caseData[3]); + const int nIds = static_cast(fragment[3]); const auto one_over_n = 1.f / static_cast(nIds); const auto start = bgStart; for(int ni = 0; ni < nIds; ni++) { - const auto ptid = caseData[4 + ni]; + const auto ptid = fragment[4 + ni]; // Add the point to the blend group. if(ptid <= P7) @@ -794,10 +840,10 @@ class ClipField // Figure out the blend for edge. const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - + blendIdsView[bgStart] = id0; blendIdsView[bgStart+1] = id1; - blendCoeffView[bgStart] = one_over_n * (1. - t); + blendCoeffView[bgStart] = one_over_n * (1.f - t); blendCoeffView[bgStart+1] = one_over_n * t; bgStart += 2; @@ -806,18 +852,24 @@ class ClipField // Store how many points make up this blend group. Note that the // size will not necessarily be equal to npts if edges were involved. - std::uint32_t nblended = bgStart - blendGroupStartView[bgOffset]; + std::uint32_t nblended = bgStart - start; blendGroupSizesView[bgOffset] = nblended; // Store "name" of blend group. const auto blendName = axom::mir::utilities::make_name_n(blendIdsView.data() + start, nblended); - blendNamesView[bgOffset++] = blendName; + blendNamesView[bgOffset] = blendName; + +#if 1 + print_blend_group(blendIdsView.data() + start, blendCoeffView.data() + start, nblended, start, bgOffset, blendName); +#endif + + bgOffset++; } } } // Add blend group for each original point that was used. - for(unsigned char pid = P0; pid <= P7; pid++) + for(IndexType pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -832,14 +884,18 @@ class ClipField blendGroupStartView[bgOffset] = bgStart; // Store "name" of blend group. - blendNamesView[bgOffset++] = axom::mir::utilities::make_name_1(zone.getId(pid)); + blendNamesView[bgOffset] = axom::mir::utilities::make_name_1(zone.getId(pid)); +#if 1 + print_blend_group(blendIdsView.data() + bgStart, blendCoeffView.data() + bgStart, 1, bgStart, bgOffset, blendNamesView[bgOffset]); +#endif bgStart++; + bgOffset++; } } // Add blend group for each edge point that was used. - for(unsigned char pid = EA; pid <= EL; pid++) + for(IndexType pid = EA; pid <= EL; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -864,9 +920,14 @@ class ClipField blendGroupStartView[bgOffset] = bgStart; // Store "name" of blend group. - blendNamesView[bgOffset++] = axom::mir::utilities::make_name_2(id0, id1); + blendNamesView[bgOffset] = axom::mir::utilities::make_name_2(id0, id1); + +#if 1 + print_blend_group(blendIdsView.data() + bgStart, blendCoeffView.data() + bgStart, 2, bgStart, bgOffset, blendNamesView[bgOffset]); +#endif bgStart += 2; + bgOffset++; } } }); @@ -884,13 +945,11 @@ class ClipField axom::Array uNames; axom::Array uIndices; axom::mir::utilities::unique(blendNames, uNames, uIndices); - - auto uNamesView = uNames.view(); + const auto uNamesView = uNames.view(); // Bundle up blend data. axom::mir::utilities::blueprint::BlendData blend; - blend.m_blendNamesView = uNames.view(); - blend.m_uniqueIndicesView = uIndices.view(); + blend.m_selectedIndicesView = uIndices.view(); // We'll use these to select just the unique indices blend.m_blendGroupSizesView = blendGroupSizes.view(); blend.m_blendGroupStartView = blendGroupStart.view(); blend.m_blendIdsView = blendIds.view(); @@ -1003,22 +1062,22 @@ class ClipField for(; it != end; it++) { // Get the current shape in the clip case. - const auto caseData = *it; - const auto fragmentShape = static_cast(caseData[0]); + const auto fragment = *it; + const auto fragmentShape = static_cast(fragment[0]); if(fragmentShape != ST_PNT) { - if(details::shapeIsSelected(caseData[1], selection)) + if(details::shapeIsSelected(fragment[1], selection)) { // Output the nodes used in this zone. - for(int i = 2; i < caseData.size(); i++) - connView[outputIndex++] = point_2_new[caseData[i]]; + for(int i = 2; i < fragment.size(); i++) + connView[outputIndex++] = point_2_new[fragment[i]]; - const auto nIdsInFragment = caseData.size() - 2; + const auto nIdsInFragment = fragment.size() - 2; const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); sizesView[sizeIndex] = nIdsInFragment; shapesView[sizeIndex] = shapeID; - colorView[sizeIndex] = caseData[1] - COLOR0; + colorView[sizeIndex] = fragment[1] - COLOR0; sizeIndex++; @@ -1059,6 +1118,17 @@ class ClipField n_newTopo["elements/shape"] = shapeMap.begin()->first; } +#if 1 + // Save blend group. + conduit::Node bg; + bg["selectedIndicesView"].set_external(blend.m_selectedIndicesView.data(), blend.m_selectedIndicesView.size()); + bg["blendGroupSizesView"].set_external(blend.m_blendGroupSizesView.data(), blend.m_blendGroupSizesView.size()); + bg["blendGroupStartView"].set_external(blend.m_blendGroupStartView.data(), blend.m_blendGroupStartView.size()); + bg["blendIdsView"].set_external(blend.m_blendIdsView.data(), blend.m_blendIdsView.size()); + bg["blendCoeffView"].set_external(blend.m_blendCoeffView.data(), blend.m_blendCoeffView.size()); + conduit::relay::io::save(bg, "blend.yaml", "yaml"); +#endif + //----------------------------------------------------------------------- // STAGE 6 - Make new coordset. //----------------------------------------------------------------------- @@ -1160,7 +1230,7 @@ class ClipField */ void makeCoordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { - axom::mir::utilities::blueprint::CoordsetBlender cb; + axom::mir::utilities::blueprint::CoordsetBlender cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } @@ -1194,7 +1264,7 @@ class ClipField } else if(association == "vertex") { - axom::mir::utilities::blueprint::FieldBlender b; + axom::mir::utilities::blueprint::FieldBlender b; b.execute(blend, n_field, n_out_fields[it->second]); n_out_fields[it->second]["topology"] = topologyName; } diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 320ebb1572..f5e5e5600a 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -29,11 +29,12 @@ namespace blueprint * * \tparam ExecSpace The execution space for the algorithm. * \tparam CSVType The coordset view type. + * \tparam SelectionPolicy The selection policy to use. * * \brief This class uses BlendData to generate a new blended field from an input coordset. * The output coordset will be explicit. */ -template +template class CoordsetBlender { public: @@ -59,7 +60,7 @@ class CoordsetBlender const auto allocatorID = axom::execution_space::allocatorID(); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); const auto nComponents = axes.size(); - assert(PointType::DIMENSION == nComponents); + SLIC_ASSERT(PointType::DIMENSION == nComponents); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); @@ -69,7 +70,7 @@ class CoordsetBlender conduit::Node &n_values = n_output["values"]; // Make output nodes using axis names from the input coordset. Make array views too. - const auto outputSize = blend.m_uniqueIndicesView.size(); + const auto outputSize = SelectionPolicy::size(blend); axom::StackArray, PointType::DIMENSION> compViews; for(size_t i = 0; i < nComponents; i++) { @@ -84,21 +85,22 @@ class CoordsetBlender // Iterate over each blend group. axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) { - // Original blendGroup index. - const auto origBGIdx = blend.m_uniqueIndicesView[bgid]; - const auto start = blend.m_blendGroupStartView[origBGIdx]; - const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; + // Get the blend group index we want. + const auto selectedIndex = SelectionPolicy::selectedIndex(blend, bgid); + const auto start = blend.m_blendGroupStartView[selectedIndex]; + const auto end = start + blend.m_blendGroupSizesView[selectedIndex]; +std::cout << "blend" << bgid << ":\n selectedIndex: " << selectedIndex << "\n start: " << start << "\n size: " << blend.m_blendGroupSizesView[selectedIndex] << "\n end: " << end << "\n ids: ["; // Blend points for this blend group. VectorType blended{}; for(IndexType i = start; i < end; i++) { const auto index = blend.m_blendIdsView[i]; const auto weight = blend.m_blendCoeffView[i]; - +std::cout << index << ", "; blended += (VectorType(view[index]) * static_cast(weight)); } - +std::cout << "]\n"; // Store the point into the Conduit component arrays. for(int comp = 0; comp < PointType::DIMENSION; comp++) { diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index a5a61a0aa5..7e29202ec1 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -26,10 +26,7 @@ namespace blueprint */ struct BlendData { - using KeyType = std::uint64_t; - - axom::ArrayView m_blendNamesView; // Contains unique hash ids for the sorted ids in each blend group. - axom::ArrayView m_uniqueIndicesView; // Contains the index into the original blend group data for a unique blend group. + axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. @@ -37,13 +34,52 @@ struct BlendData axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; +/** + * \brief This policy can be used with FieldBlender to select all blend groups. + */ +struct SelectAllPolicy +{ + AXOM_HOST_DEVICE + static IndexType size(const BlendData &blend) + { + return blend.m_blendGroupSizesView.size(); + } + + AXOM_HOST_DEVICE + static IndexType selectedIndex(const BlendData &blend, IndexType index) + { + return index; + } +}; + +/** + * \brief This policy can be used with FieldBlender to select a subset of blend groups, according to m_selectedIndicesView. + */ +struct SelectThroughArrayView +{ + AXOM_HOST_DEVICE + static IndexType size(const BlendData &blend) + { + return blend.m_selectedIndicesView.size(); + } + + AXOM_HOST_DEVICE + static IndexType selectedIndex(const BlendData &blend, IndexType index) + { + return blend.m_selectedIndicesView[index]; + } +}; + /** * \accelerated * \class FieldBlender * * \brief This class uses BlendData to generate a new blended field from an input field. + * + * \tparam ExecSpace The execution space where the work will occur. + * \tparam SelectionPolicy The selection policy to use. */ -template +template class FieldBlender { public: @@ -88,7 +124,9 @@ class FieldBlender void blendSingleComponent(const BlendData &blend, const conduit::Node &n_values, conduit::Node &n_output_values) const { const auto allocatorID = axom::execution_space::allocatorID(); - const auto outputSize = blend.m_uniqueIndicesView.size(); + // We're allowing selectedIndicesView to be used to select specific blend + // groups. If the user did not provide that, use all blend groups. + const auto outputSize = SelectionPolicy::size(blend); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); @@ -102,10 +140,10 @@ class FieldBlender axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) { - // Original blendGroup index. - const auto origBGIdx = blend.m_uniqueIndicesView[bgid]; - const auto start = blend.m_blendGroupStartView[origBGIdx]; - const auto end = start + blend.m_blendGroupSizesView[origBGIdx]; + // Get the index we want. + const auto selectedIndex = SelectionPolicy::selectedIndex(blend, bgid); + const auto start = blend.m_blendGroupStartView[selectedIndex]; + const auto end = start + blend.m_blendGroupSizesView[selectedIndex]; accum_type blended = 0; for(IndexType i = start; i < end; i++) diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index a2633de2ab..0721f4dd8f 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -8,8 +8,8 @@ Partial TODO list - Test setting the topo, coordset names - Test storing results in same Node, use names/fields map to change names - Make sure we can operate on a StridedStructured mesh -5. Create UnstructuredTopologyMixedShapeView and dispatch -6. Make sure ClipFilter can use the mixed topology view - it probably needs some changes +(done)5. Create UnstructuredTopologyMixedShapeView and dispatch +(done)6. Make sure ClipFilter can use the mixed topology view - it probably needs some changes 7. Code cleanup pass in ClipFilter - Make sure array view data types are consistent, change to IndexType as needed. - Make sure views have "View" in the name. @@ -29,4 +29,7 @@ Partial TODO list 18. [lower priority] Revisit NodeArrayView and constness/references. 19. [lower priority] Can ClipField be generalized a little into something like TopologyExtractor? Then use it to make other filters? 20. [lower priority] ConduitAllocateThroughAxom uses statics. I'd prefer to template it on ExecSpace and make the allocator a member but Conduit's API does not let us pass a void* to the allocation callbacks. -21. Check the edge definitions in pyr, wedge Shape types. +(done)21. Check the edge definitions in pyr, wedge Shape types. +22. Compile for all compute back-ends and make sure tests run/pass. + - Right now, OMP causes the compiler to crash when linking +23. Make some timing tests so we can see how Clipfield/MIR do at various mesh sizes. diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 6076d5da9e..c3284da06d 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -116,6 +116,77 @@ class TableView const auto len = shapeLength(ptr); return TableDataView(ptr, len); } +#if 1 + private: + void printShape(std::ostream &os, TableData shape) const + { + switch(shape) + { + case ST_PNT: os << "ST_PNT"; break; + case ST_TRI: os << "ST_TRI"; break; + case ST_QUA: os << "ST_QUA"; break; + case ST_TET: os << "ST_TET"; break; + case ST_PYR: os << "ST_PYR"; break; + case ST_WDG: os << "ST_WDG"; break; + case ST_HEX: os << "ST_HEX"; break; + } + } + void printColor(std::ostream &os, TableData color) const + { + switch(color) + { + case COLOR0: os << "COLOR0"; break; + case COLOR1: os << "COLOR1"; break; + case NOCOLOR: os << "NOCOLOR"; break; + } + } + void printIds(std::ostream &os, const TableData *ids, int n) const + { + for(int i = 0; i < n; i++) + { + if(ids[i] >= P0 && ids[i] <= P7) + os << "P" << static_cast(ids[i]); + else if(ids[i] >= EA && ids[i] <= EL) + { + char c = 'A' + (ids[i] - EA); + os << "E" << c; + } + else if(ids[i] >= N0 && ids[i] <= N3) + { + os << "N" << static_cast(ids[i] - N0); + } + os << " "; + } + } + public: + void print(std::ostream &os) const + { + TableData *ptr = m_shapeStart + m_offset; + printShape(os, ptr[0]); + os << " "; + int offset = 2; + if(ptr[0] == ST_PNT) + { + os << static_cast(ptr[1]); // point number. + os << " "; + + printColor(os, ptr[2]); + os << " "; + + os << static_cast(ptr[3]); // npts + os << " "; + offset = 4; + } + else + { + printColor(os, ptr[1]); + os << " "; + } + + const auto n = shapeLength(ptr) - offset; + printIds(os, ptr + offset, n); + } +#endif private: friend class TableView; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 5761f36875..495c98e5e8 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -267,22 +267,101 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Load a clipped baseline file & compare. } +template +void braid3d_clip_test(const std::string &type, const std::string &name) +{ + using HexShape = axom::mir::views::HexShape; + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + axom::StackArray dims{8,8,8};//10, 10, 10}; + axom::StackArray zoneDims{dims[0] - 1, dims[1] - 1, dims[2] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + braid(type, dims, hostMesh); + hostMesh.print(); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + + // Create views + conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); + conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); + conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); + const axom::ArrayView x(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); + const axom::ArrayView y(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); + const axom::ArrayView z(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); + CoordsetView coordsetView(x, y, z); + + conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"); + //conduit::Node &n_sizes = deviceMesh.fetch_existing("topologies/mesh/elements/sizes"); + //conduit::Node &n_offsets = deviceMesh.fetch_existing("topologies/mesh/elements/offsets"); + const axom::ArrayView conn(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); + //const axom::ArrayView sizes(static_cast(n_sizes.data_ptr()), n_sizes.dtype().number_of_elements()); + //const axom::ArrayView offsets(static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); + TopoView topoView(conn); //, sizes, offsets); + + // Create options to control the clipping. + const std::string clipTopoName("cliptopo"); + conduit::Node options; + options["clipField"] = "distance"; + options["inside"] = 1; + options["outside"] = 0; + //options["topologyName"] = clipTopoName; + //options["coordsetName"] = "clipcoords"; + //options["fields/braid"] = "new_braid"; + //options["fields/radial"] = "new_radial"; + + // Clip the data + conduit::Node deviceClipMesh; + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Save data. + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); +} + +#if 0 TEST(mir_clipfield, uniform2d) { braid2d_clip_test("uniform", "uniform2d"); -#if 0 -#if defined(AXOM_USE_OPENMP) - braid2d_clip_test("uniform", "uniform2d_omp"); -#endif + +//#if defined(AXOM_USE_OPENMP) +// braid2d_clip_test("uniform", "uniform2d_omp"); +//#endif + #if defined(AXOM_USE_CUDA) braid2d_clip_test("uniform", "uniform2d_cuda"); #endif -#endif #if defined(AXOM_USE_HIP) braid2d_clip_test("uniform", "uniform2d_hip"); #endif } +#endif + +TEST(mir_clipfield, hex3d) +{ + const std::string type("hexs"); + braid3d_clip_test(type, "hex"); + +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "hex_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + braid3d_clip_test(type, "hex_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_clip_test(type, "hex_hip"); +#endif +} //------------------------------------------------------------------------------ diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index afb4bc2437..8c3f94cac2 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -210,7 +210,8 @@ template AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) { - assert(n < MaxValues); +if(n >= 14) std::cout << "make_name_n: " << n << std::endl; + assert(n <= MaxValues); if(n == 2) return make_name_2(values[0], values[1]); From 39b3e33f3171d3a962cf7344acd73d95c6026a68 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 26 Jul 2024 17:06:28 -0700 Subject: [PATCH 110/290] Some refactoring to see if that helps for debugging. --- src/axom/mir/ClipField.hpp | 617 ++++++++++++++++++++------- src/axom/mir/FieldBlender.hpp | 2 +- src/axom/mir/tests/mir_clipfield.cpp | 169 ++++++++ 3 files changed, 621 insertions(+), 167 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index d47689e1c0..bc75f59f6e 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -425,38 +425,386 @@ class ClipOptions axom::Array m_selectedZones; // Storage for a list of selected zone ids. }; -void print_blend_group(const IndexType *ids, const float *weights, int n, int bgStart, int bgOffset, std::uint64_t name) -{ -const std::uint64_t k = 12990793018150977444; - -std::cout << " -\n"; -std::cout << " n: " << n << std::endl; -std::cout << " bgStart: " << bgStart << std::endl; -std::cout << " bgOffset: " << bgOffset << std::endl; -std::cout << " ids: ["; -for(int bi = 0; bi < n; bi++) -{ - if(bi > 0) std::cout << ", "; - std::cout << ids[bi]; -} -std::cout << "]"; -if(name == k) +//------------------------------------------------------------------------------ +/** + * \brief This class encapsulates most of the logic for building blend groups. + * + * \note The class only contains views to data so it can be copied into lambdas. + */ +template +class BlendGroupBuilder { -std::cout << " MATCH!!!"; -} +public: + using KeyType = std::uint64_t; +private: + /** + * \brief This struct holds the views that represent data for blend groups. + */ + struct State + { + IndexType m_nzones; -std::cout << "\n"; -std::cout << " weights: ["; -for(int bi = 0; bi < n; bi++) -{ - if(bi > 0) std::cout << ", "; - std::cout << weights[bi]; -} -std::cout << "]\n"; -std::cout << " name: " << name; -std::cout << "\n"; -} + axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. + axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. + axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. + axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. + + axom::ArrayView m_blendNamesView; // Blend group names + axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. + axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. + axom::ArrayView m_blendIdsView; // blend group ids. + axom::ArrayView m_blendCoeffView; // blend group weights. + + axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. + axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. + }; + +public: + /** + * \brief Set the number of zones. + * + * \param blendGroupsView The view that holds the number of blend groups for each zone. + * \param blendGroupsLenView The view that holds the size of the blend group data for each zone. + */ + void setBlendGroupSizes(const axom::ArrayView &blendGroupsView, + const axom::ArrayView &blendGroupsLenView) + { + m_state.m_nzones = blendGroupsView.size(); + m_state.m_blendGroupsView = blendGroupsView; + m_state.m_blendGroupsLenView = blendGroupsLenView; + } + + /** + * \brief Compute the total sizes of blend group storage. + * + * \param[out] bgSum The total number of blend groups for all zones. + * \param[out] bgLenSum The total size of blend group data for all zones. + */ + void computeBlendGroupSizes(IndexType &bgSum, IndexType &bgLenSum) + { + using reduce_policy = typename axom::execution_space::reduce_policy; + RAJA::ReduceSum blendGroups_sum(0); + RAJA::ReduceSum blendGroupLen_sum(0); + axom::for_all(m_state.m_nzones, AXOM_LAMBDA(auto zoneIndex) + { + blendGroups_sum += m_state.m_blendGroupsView[zoneIndex]; + blendGroupLen_sum += m_state.m_blendGroupsLenView[zoneIndex]; + }); + bgSum = blendGroups_sum.get(); + bgLenSum = blendGroupLen_sum.get(); + } + + /** + * \brief Set the views for the blend group offsets and then fill them using a scan. + * + * \param blendOffsetView The offsets to each blend group for views sized: view[blendGroupSum]. + * \param blendGroupOffsetsView The offsets to each zone's blend groups data. + */ + void setBlendGroupOffsets(const axom::ArrayView &blendOffsetView, + const axom::ArrayView &blendGroupOffsetsView) + { + m_state.m_blendOffsetView = blendOffsetView; + m_state.m_blendGroupOffsetsView = blendGroupOffsetsView; + } + + /** + * brief Compute the blend group offsets that make it easier to store data. + */ + void computeBlendGroupOffsets() + { + using loop_policy = typename axom::execution_space::loop_policy; + // Fill in offsets via scan. + RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsLenView.data(), m_state.m_nzones), + RAJA::make_span(m_state.m_blendOffsetView.data(), m_state.m_nzones), + RAJA::operators::plus{}); + RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsView.data(), m_state.m_nzones), + RAJA::make_span(m_state.m_blendGroupOffsetsView.data(), m_state.m_nzones), + RAJA::operators::plus{}); + } + + /** + * \brief Set the views that we'll use for blend groups. + */ + void setBlendViews(const axom::ArrayView &blendNames, + const axom::ArrayView &blendGroupSizes, + const axom::ArrayView &blendGroupStart, + const axom::ArrayView &blendIds, + const axom::ArrayView &blendCoeff) + { + m_state.m_blendNamesView = blendNames; + m_state.m_blendGroupSizesView = blendGroupSizes; + m_state.m_blendGroupStartView = blendGroupStart; + m_state.m_blendIdsView = blendIds; + m_state.m_blendCoeffView = blendCoeff; + } + + /** + * \brief Set the unique names and ids views. These are used in mapping blend groups to unique blend groups. + * + * \param uniqueNames A view containing unique, sorted blend group names. + * \param uniqueIndices A view containing the original blend group index for each unique name. + */ + void setUniqueNames(const axom::ArrayView &uniqueNames, const axom::ArrayView &uniqueIndices) + { + m_state.m_blendUniqueNamesView = uniqueNames; + m_state.m_blendUniqueIndicesView = uniqueIndices; + } + + /** + * \brief Get the blend names view. + * \return The blend names view. + */ + const axom::ArrayView &blendNames() const { return m_state.m_blendNamesView; } + + /** + * \brief This class helps us manage blend group creation and usage for blend groups within a single zone. + */ + class zone_blend_groups + { + public: + /** + * \brief Return the number of blend groups for this zone. + * \return The number of blend groups for this zone. + */ + AXOM_HOST_DEVICE + IndexType size() const + { + return m_state->m_blendGroupsView[m_zoneIndex]; + } + + /** + * \brief Set the number of blend groups and total size of the blend groups for a zone. + * + * \param zoneIndex The index of the zone we're initializing. + * \param nBlendGroups The number of blend groups in this zone. + * \param blendGroupSize The size of all of the blend groups in this zone. + */ + AXOM_HOST_DEVICE + void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) + { + m_state->m_blendGroupsView[m_zoneIndex] = nBlendGroups; + m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; + } + + /** + * \brief Start creating a new blend group within the allocated space for this zone. + */ + AXOM_HOST_DEVICE + void beginGroup() + { + m_currentDataOffset = m_startOffset; + } + + /** + * \brief Add a new blend point in the current blend group. + * + * \param id The node id that will be used for blending. + * \param weight The weight that will be used for blending. + */ + AXOM_HOST_DEVICE + void add(IndexType id, float weight) + { + m_state->m_blendIdsView[m_currentDataOffset] = id; + m_state->m_blendCoeffView[m_currentDataOffset] = weight; + m_currentDataOffset++; + } + + /** + * \brief End the current blend group, storing its name, size, etc. + */ + AXOM_HOST_DEVICE + void endGroup() + { + IndexType numIds = m_currentDataOffset - m_startOffset; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + m_state->m_blendGroupStartView[m_blendGroupId] = m_startOffset; + // Save the size for this blend group. + m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; + + // Store "name" of blend group. + KeyType blendName; + if(numIds == 1) + { + blendName = axom::mir::utilities::make_name_1(m_state->m_blendIdsView[m_startOffset]); + } + else if(numIds == 2) + { + blendName = axom::mir::utilities::make_name_2(m_state->m_blendIdsView[m_startOffset], m_state->m_blendIdsView[m_startOffset + 1]); + } + else + { + blendName = axom::mir::utilities::make_name_n(m_state->m_blendIdsView.data() + m_startOffset, numIds); + } + m_state->m_blendNamesView[m_blendGroupId] = blendName; + +#if 1 + print(std::cout); +#endif + + m_blendGroupId++; + m_startOffset = m_currentDataOffset; + } + + /** + * \brief Return the name of the current blend group. + * \return The name of the current blend group. + */ + AXOM_HOST_DEVICE + KeyType name() const + { + return m_state->m_blendNamesView[m_blendGroupId]; + } + + /** + * \brief Return index of the current blend group in the unique blend groups. + * \return The unique index of the current blend group. + */ + AXOM_HOST_DEVICE + IndexType uniqueBlendGroupIndex() const + { +#define AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE +#ifdef AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE + return axom::mir::utilities::bsearch(name(), m_state->m_blendUniqueNamesView); +#else + return m_blendGroupId; +#endif + } + + /** + * \brief Advance to the next blend group. + */ + AXOM_HOST_DEVICE + void operator++() { m_blendGroupId++; } + + /** + * \brief Advance to the next blend group. + */ + AXOM_HOST_DEVICE + void operator++(int) { m_blendGroupId++; } + +#if !defined(AXOM_DEVICE_CODE) + /** + * \brief Print the current blend group to a stream. + * \param os The stream to which the blend group will print. + */ + void print(std::ostream &os) const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + os << "-\n"; + os << " zoneIndex: " << m_zoneIndex << std::endl; + os << " blendGroupId: " << m_blendGroupId << std::endl; + os << " size: " << n << std::endl; + os << " offset: " << offset << std::endl; + + const IndexType *ids = m_state->m_blendIdsView.data() + offset; + os << " ids: ["; + for(int bi = 0; bi < n; bi++) + { + if(bi > 0) os << ", "; + os << ids[bi]; + } + os << "]"; + os << "\n"; + const float *weights = m_state->m_blendCoeffView.data() + offset; + os << " weights: ["; + for(int bi = 0; bi < n; bi++) + { + if(bi > 0) os << ", "; + os << weights[bi]; + } + os << "]\n"; + os << " name: " << m_state->m_blendNamesView[m_blendGroupId]; + os << "\n"; + } +#endif + + /** + * \brief Return the current blend group's ids. + * \return The current blend group's ids. + * \note This method should not be used if blend groups are still being constructed. + */ + axom::ArrayView ids() const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + return axom::ArrayView(m_state->m_blendIdsView.data() + offset, n); + } + + /** + * \brief Return the current blend group's ids. + * \return The current blend group's ids. + * \note This method should not be used if blend groups are still being constructed. + */ + axom::ArrayView weights() const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + return axom::ArrayView(m_state->m_blendCoeffsView.data() + offset, n); + } + + private: + friend class BlendGroupBuilder; + + IndexType m_zoneIndex; // The zone that owns this set of blend groups. + IndexType m_blendGroupId; // The global blend group index within this current zone. + IndexType m_startOffset; // The data offset for the first ids/weights in this blend group. + IndexType m_currentDataOffset; // The current data offset. + State *m_state; // Pointer to the main state. + }; + + /** + * \brief Return a zone_blend_groups object for the current zone so we can add blend groups. + * + * \param zoneIndex The zone whose blend groups we want to edit. + * + * \note This method must be marked const because we can call it from an AXOM_LAMBDA. + * we pass a non-const State reference to the zone_blend_groups that we construct + * so we can write into the blend group data. + */ + AXOM_HOST_DEVICE + zone_blend_groups blendGroupsForZone(IndexType zoneIndex) const + { + zone_blend_groups groups; + // The zone that owns this set of blend groups. + groups.m_zoneIndex = zoneIndex; + + // Global blend group id for the first blend group in this zone. + groups.m_blendGroupId = m_state.m_blendGroupOffsetsView[zoneIndex]; + // Global start + groups.m_startOffset = groups.m_currentDataOffset = m_state.m_blendOffsetView[zoneIndex]; + + groups.m_state = const_cast(&m_state); + return groups; + } + + /** + * \brief Make a BlendData object from the views in this object. + * + * \return A BlendData object suitable for making new fields and coordsets. + */ + axom::mir::utilities::blueprint::BlendData + makeBlendData() const + { + axom::mir::utilities::blueprint::BlendData blend; + +#ifdef AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE + blend.m_selectedIndicesView = m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices +#endif + blend.m_blendGroupSizesView = m_state.m_blendGroupSizesView; + blend.m_blendGroupStartView = m_state.m_blendGroupStartView; + blend.m_blendIdsView = m_state.m_blendIdsView; + blend.m_blendCoeffView = m_state.m_blendCoeffView; + + return blend; + } + +private: + State m_state; +}; +//------------------------------------------------------------------------------ /** * \accelerated @@ -537,6 +885,7 @@ class ClipField conduit::Node &n_newFields) { using KeyType = std::uint64_t; + using BitSet = std::uint64_t; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); @@ -572,25 +921,32 @@ class ClipField // determine sizes. // // ---------------------------------------------------------------------- - RAJA::ReduceSum fragment_sum(0); - RAJA::ReduceSum fragment_nids_sum(0); - RAJA::ReduceSum blendGroups_sum(0); - RAJA::ReduceSum blendGroupLen_sum(0); // Allocate some memory axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. - axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone - axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. - axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. + axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone + auto clipCasesView = clipCases.view(); + auto pointsUsedView = pointsUsed.view(); + axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. + axom::Array fragmentOffsets(nzones, nzones, allocatorID); + axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); + auto fragmentsView = fragments.view(); + auto fragmentsSizeView = fragmentsSize.view(); + auto fragmentOffsetsView = fragmentOffsets.view(); + auto fragmentSizeOffsetsView = fragmentSizeOffsets.view(); - auto clipCasesView = clipCases.view(); - auto pointsUsedView = pointsUsed.view(); + axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. + axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. + axom::Array blendOffset(nzones, nzones, allocatorID); // Start of zone's blend group indices + axom::Array blendGroupOffsets(nzones, nzones, allocatorID); // Start of zone's blend group offsets in definitions. + + // Make an object to help manage building the blend groups. + BlendGroupBuilder builder; + builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); auto blendGroupsView = blendGroups.view(); auto blendGroupsLenView = blendGroupsLen.view(); - auto fragmentsView = fragments.view(); - auto fragmentsSizeView = fragmentsSize.view(); views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { @@ -611,7 +967,7 @@ class ClipField int thisBlendGroupLen = 0; // The total length of the blend groups. int thisFragments = 0; // The number of zone fragments produced in this case. int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. - std::uint64_t ptused = 0; // A bitset indicating which ST_XX nodes are used. + BitSet ptused = 0; // A bitset indicating which ST_XX nodes are used. auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -689,20 +1045,31 @@ class ClipField } // Save the results. - blendGroupsView[zoneIndex] = thisBlendGroups; - blendGroupsLenView[zoneIndex] = thisBlendGroupLen; fragmentsView[zoneIndex] = thisFragments; fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; - // Sum up the sizes overall. - fragment_sum += thisFragments; - fragment_nids_sum += thisFragmentsNumIds; - blendGroups_sum += thisBlendGroups; - blendGroupLen_sum += thisBlendGroupLen; + // Set blend group sizes for this zone. + blendGroupsView[zoneIndex] = thisBlendGroups; + blendGroupsLenView[zoneIndex] = thisBlendGroupLen; }); }); -// TODO: change int to topoView::IndexType + // Sum up the sizes. + RAJA::ReduceSum fragment_sum(0); + RAJA::ReduceSum fragment_nids_sum(0); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + fragment_sum += fragmentsView[zoneIndex]; + fragment_nids_sum += fragmentsSizeView[zoneIndex]; + }); + // Make offsets via scan. + RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), + RAJA::make_span(fragmentOffsetsView.data(), nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), + RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), + RAJA::operators::plus{}); // ---------------------------------------------------------------------- // @@ -713,32 +1080,14 @@ class ClipField // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. // fragmentOffsets : Where an zone's fragments begin in the output. // ---------------------------------------------------------------------- - axom::Array blendOffset(nzones, nzones, allocatorID); - axom::Array blendGroupOffsets(nzones, nzones, allocatorID); - axom::Array fragmentOffsets(nzones, nzones, allocatorID); - axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); - auto blendOffsetView = blendOffset.view(); - auto blendGroupOffsetsView = blendGroupOffsets.view(); - auto fragmentOffsetsView = fragmentOffsets.view(); - auto fragmentSizeOffsetsView = fragmentSizeOffsets.view(); + IndexType blendGroupsSize = 0, blendGroupLenSize = 0; + builder.computeBlendGroupSizes(blendGroupsSize, blendGroupLenSize); - // Make offsets via scan. - RAJA::exclusive_scan(RAJA::make_span(blendGroupsLenView.data(), nzones), - RAJA::make_span(blendOffsetView.data(), nzones), - RAJA::operators::plus{}); + builder.setBlendGroupOffsets(blendOffset.view(), blendGroupOffsets.view()); + builder.computeBlendGroupOffsets(); - RAJA::exclusive_scan(RAJA::make_span(blendGroupsView.data(), nzones), - RAJA::make_span(blendGroupOffsetsView.data(), nzones), - RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), - RAJA::make_span(fragmentOffsetsView.data(), nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), - RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), - RAJA::operators::plus{}); // ---------------------------------------------------------------------- // @@ -749,20 +1098,13 @@ class ClipField // NOTE: blendGroupStart is a scan of blendGroupSizes. // // ---------------------------------------------------------------------- - const auto blendGroupsSize = blendGroups_sum.get(); - const auto blendGroupLenSize = blendGroupLen_sum.get(); - axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); - auto blendNamesView = blendNames.view(); - auto blendGroupSizesView = blendGroupSizes.view(); - auto blendGroupStartView = blendGroupStart.view(); - auto blendIdsView = blendIds.view(); - auto blendCoeffView = blendCoeff.view(); + builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { @@ -780,11 +1122,10 @@ std::cout << "clipping:\n"; const auto &ctView = clipTableViews[clipTableIndex]; // These are the points used in this zone's fragments. - const std::uint64_t ptused = pointsUsedView[zoneIndex]; + const BitSet ptused = pointsUsedView[zoneIndex]; - // Starting offset of where we store this zone's blend groups. - std::uint32_t bgStart = blendOffsetView[zoneIndex]; - std::uint32_t bgOffset = blendGroupOffsetsView[zoneIndex]; + // Get the blend groups for this zone. + auto groups = builder.blendGroupsForZone(zoneIndex); #if 1 std::cout << " -\n"; std::cout << " zone: " << zoneIndex << std::endl; @@ -792,6 +1133,7 @@ std::cout << " clipcase: " << clipcase << std::endl; std::cout << " fragments:\n"; int fi = 0; #endif + auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); for(; it != end; it++) @@ -812,8 +1154,8 @@ std::cout << "\"" << std::endl; // TODO: put the ability to select on color back in... 0, 1, or both const int nIds = static_cast(fragment[3]); const auto one_over_n = 1.f / static_cast(nIds); - const auto start = bgStart; + groups.beginGroup(); for(int ni = 0; ni < nIds; ni++) { const auto ptid = fragment[4 + ni]; @@ -822,10 +1164,7 @@ std::cout << "\"" << std::endl; if(ptid <= P7) { // corner point. - blendIdsView[bgStart] = zone.getId(ptid); - blendCoeffView[bgStart] = one_over_n; - - bgStart++; + groups.add(zone.getId(ptid), one_over_n); } else if(ptid >= EA && ptid <= EL) { @@ -841,29 +1180,11 @@ std::cout << "\"" << std::endl; // Figure out the blend for edge. const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - blendIdsView[bgStart] = id0; - blendIdsView[bgStart+1] = id1; - blendCoeffView[bgStart] = one_over_n * (1.f - t); - blendCoeffView[bgStart+1] = one_over_n * t; - - bgStart += 2; + groups.add(id0, one_over_n * (1.f - t)); + groups.add(id1, one_over_n * t); } } - - // Store how many points make up this blend group. Note that the - // size will not necessarily be equal to npts if edges were involved. - std::uint32_t nblended = bgStart - start; - blendGroupSizesView[bgOffset] = nblended; - - // Store "name" of blend group. - const auto blendName = axom::mir::utilities::make_name_n(blendIdsView.data() + start, nblended); - blendNamesView[bgOffset] = blendName; - -#if 1 - print_blend_group(blendIdsView.data() + start, blendCoeffView.data() + start, nblended, start, bgOffset, blendName); -#endif - - bgOffset++; + groups.endGroup(); } } } @@ -873,24 +1194,9 @@ std::cout << "\"" << std::endl; { if(axom::utilities::bitIsSet(ptused, pid)) { - // Store blend group info - blendIdsView[bgStart] = zone.getId(pid); - blendCoeffView[bgStart] = 1.; - - // Store how many points make up this blend group. - blendGroupSizesView[bgOffset] = 1; - - // Store where this blendGroup starts in the blendIds,blendCoeff. - blendGroupStartView[bgOffset] = bgStart; - - // Store "name" of blend group. - blendNamesView[bgOffset] = axom::mir::utilities::make_name_1(zone.getId(pid)); - -#if 1 - print_blend_group(blendIdsView.data() + bgStart, blendCoeffView.data() + bgStart, 1, bgStart, bgOffset, blendNamesView[bgOffset]); -#endif - bgStart++; - bgOffset++; + groups.beginGroup(); + groups.add(zone.getId(pid), 1.f); + groups.endGroup(); } } @@ -907,27 +1213,10 @@ std::cout << "\"" << std::endl; // Figure out the blend for edge. const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - // Store blend group info - blendIdsView[bgStart] = id0; - blendIdsView[bgStart+1] = id1; - blendCoeffView[bgStart] = (1. - t); - blendCoeffView[bgStart+1] = t; - - // Store how many points make up this blend group. - blendGroupSizesView[bgOffset] = 2; - - // Store where this blendGroup starts in the blendIds,blendCoeff. - blendGroupStartView[bgOffset] = bgStart; - - // Store "name" of blend group. - blendNamesView[bgOffset] = axom::mir::utilities::make_name_2(id0, id1); - -#if 1 - print_blend_group(blendIdsView.data() + bgStart, blendCoeffView.data() + bgStart, 2, bgStart, bgOffset, blendNamesView[bgOffset]); -#endif - - bgStart += 2; - bgOffset++; + groups.beginGroup(); + groups.add(id0, 1.f - t); + groups.add(id1, t); + groups.endGroup(); } } }); @@ -944,16 +1233,11 @@ std::cout << "\"" << std::endl; // blendNames/blendGroupOffsets/blendGroupSizes. axom::Array uNames; axom::Array uIndices; - axom::mir::utilities::unique(blendNames, uNames, uIndices); - const auto uNamesView = uNames.view(); + axom::mir::utilities::unique(builder.blendNames(), uNames, uIndices); + builder.setUniqueNames(uNames.view(), uIndices.view()); // Bundle up blend data. - axom::mir::utilities::blueprint::BlendData blend; - blend.m_selectedIndicesView = uIndices.view(); // We'll use these to select just the unique indices - blend.m_blendGroupSizesView = blendGroupSizes.view(); - blend.m_blendGroupStartView = blendGroupStart.view(); - blend.m_blendIdsView = blendIds.view(); - blend.m_blendCoeffView = blendCoeff.view(); + axom::mir::utilities::blueprint::BlendData blend = builder.makeBlendData(); // ---------------------------------------------------------------------- // @@ -1006,44 +1290,45 @@ std::cout << "\"" << std::endl; // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. - RAJA::ReduceBitOr shapesUsed_reduce(0); + RAJA::ReduceBitOr shapesUsed_reduce(0); m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. if(fragmentsView[zoneIndex] == 0) return; + // Seek to the start of the blend groups for this zone. - std::uint32_t bgStart = blendGroupOffsetsView[zoneIndex]; + auto groups = builder.blendGroupsForZone(zoneIndex); // Go through the points in the order they would have been added as blend // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index // in the final points. - const std::uint64_t ptused = pointsUsedView[zoneIndex]; + const BitSet ptused = pointsUsedView[zoneIndex]; ConnectivityType point_2_new[N3 + 1]; for(unsigned char pid = N0; pid <= N3; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; } } for(unsigned char pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; } } for(unsigned char pid = EA; pid <= EL; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { - const auto name = blendNamesView[bgStart++]; - point_2_new[pid] = axom::mir::utilities::bsearch(name, uNamesView); + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; } } @@ -1051,7 +1336,7 @@ std::cout << "\"" << std::endl; int outputIndex = fragmentSizeOffsetsView[zoneIndex]; // This is where the output fragment sizes/shapes start for this zone. int sizeIndex = fragmentOffsetsView[zoneIndex]; - std::uint64_t shapesUsed = 0; + BitSet shapesUsed = 0; // Iterate over the selected fragments and emit connectivity for them. const auto clipcase = clipCasesView[zoneIndex]; diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 7e29202ec1..18f15f72ff 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -46,7 +46,7 @@ struct SelectAllPolicy } AXOM_HOST_DEVICE - static IndexType selectedIndex(const BlendData &blend, IndexType index) + static IndexType selectedIndex(const BlendData &/*blend*/, IndexType index) { return index; } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 495c98e5e8..c64b0267b3 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -67,6 +67,83 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) }); } +int conduit_to_vtk_cell(int shape_value) +{ + int vtktype = 0; + if(shape_value == axom::mir::views::Tri_ShapeID) + vtktype = 5; // VTK_TRIANGLE + else if(shape_value == axom::mir::views::Quad_ShapeID) + vtktype = 9; // VTK_QUAD + else if(shape_value == axom::mir::views::Polygon_ShapeID) + vtktype = 7; // VTK_POLYGON + else if(shape_value == axom::mir::views::Tet_ShapeID) + vtktype = 10; // VTK_TETRA + else if(shape_value == axom::mir::views::Pyramid_ShapeID) + vtktype = 14; // VTK_PYRAMID + else if(shape_value == axom::mir::views::Wedge_ShapeID) + vtktype = 13; // VTK_WEDGE + else if(shape_value == axom::mir::views::Hex_ShapeID) + vtktype = 12; // VTK_HEXAHEDRON + + return vtktype; +} + +void +conduit_save_vtk(const conduit::Node &node, const std::string &path) +{ + FILE *file = fopen(path.c_str(), "wt"); + + // Write the VTK file header + fprintf(file, "# vtk DataFile Version 3.0\n"); + fprintf(file, "Unstructured Grid Example\n"); + fprintf(file, "ASCII\n"); + fprintf(file, "DATASET UNSTRUCTURED_GRID\n"); + + // Write the points + const conduit::Node &points = node["coordsets/coords/values"]; + const conduit::Node &x = points["x"]; + const conduit::Node &y = points["y"]; + const conduit::Node &z = points["z"]; + size_t num_points = x.dtype().number_of_elements(); + + fprintf(file, "POINTS %zu float\n", num_points); + for (size_t i = 0; i < num_points; ++i) { + fprintf(file, "%f %f %f\n", x.as_float64_array()[i], y.as_float64_array()[i], z.as_float64_array()[i]); + } + + // Write the cells + const conduit::Node &topologies = node["topologies"]; + const conduit::Node &topo = topologies[0]; + const conduit::Node &elements = topo["elements"]; + const conduit::Node &connectivity = elements["connectivity"]; + size_t num_cells = elements["sizes"].dtype().number_of_elements(); + size_t total_num_indices = connectivity.dtype().number_of_elements(); + + fprintf(file, "CELLS %zu %zu\n", num_cells, total_num_indices + num_cells); + size_t index = 0; + for (size_t i = 0; i < num_cells; ++i) { + size_t cell_size = elements["sizes"].as_int32_array()[i]; + fprintf(file, "%zu", cell_size); + for (size_t j = 0; j < cell_size; ++j) { + fprintf(file, " %d", connectivity.as_int32_array()[index++]); + } + fprintf(file, "\n"); + } + + // Write the cell types + const conduit::Node &shapes = elements["shapes"]; + fprintf(file, "CELL_TYPES %zu\n", num_cells); + for (size_t i = 0; i < num_cells; ++i) { + const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); + fprintf(file, "%d\n", type); + } + + // Close the file + fclose(file); +} + + + TEST(mir_clipfield, options) { int nzones = 6; @@ -168,6 +245,95 @@ TEST(mir_clipfield, options) EXPECT_EQ(selectedZonesView[2], 5); } +TEST(mir_clipfield, blend_group_builder) +{ + using IndexType = axom::IndexType; + using KeyType = std::uint64_t; + + /* + + We'll make 2 quads + + 3 4 5 + *--8---*------* + | | | + | 6 9 | + | | | + *--7---*------* + 0 1 2 + + */ + axom::Array blendGroups{{8, 5}}; + axom::Array blendGroupsLen{{/*zone 0*/ 4+1+1+1+1+2+2+2, /*zone 1*/ 1+1+1+1+2}}; + axom::Array blendGroupOffsets{{0, 8}}; + axom::Array blendOffsets{{0, blendGroupsLen[0]}}; + + axom::Array blendNames{{/*zone 0*/ 6,0,1,3,4,7,8,9, /*zone 1*/ 1,2,4,5,9}}; + axom::Array blendGroupSizes{{/*zone 0*/4,1,1,1,1,2,2,2, /*zone 1*/1,1,1,1,2}}; + axom::Array blendGroupStart{{/*zone 0*/0,4,5,6,7,8,10,12, /*zone 1*/ 13, 14, 15, 16, 18}}; + axom::Array blendIds{{/*zone 0*/ + 0,1,2,3, // 6 (bgname) // 0 (bgindex) + 0, // 0 // 1 + 1, // 1 // 2 + 3, // 3 // 3 + 4, // 4 // 4 + 0,1, // 7 // 5 + 3,4, // 8 // 6 + 1,4, // 9 // 7 + /*zone 1*/ + 1, // 1 // 8 + 2, // 2 // 9 + 4, // 4 // 10 + 5, // 5 // 11 + 1,4 // 9 // 12 + }}; + axom::Array blendCoeff{{/*zone 0*/ + 0.25, 0.25, 0.25, 0.25, + 1., + 1., + 1., + 1., + 0.5, 0.5, + 0.5, 0.5, + 0.5, 0.5, + /*zone 1*/ + 1., + 1., + 1., + 1., + 0.5, 0.5 + }}; + axom::Array blendUniqueNames{{0,1,2,3,4,5,6,7,8,9}}; + axom::Array blendUniqueIndices{{1,2,9,3,4,11,0,5,6,7}}; + + axom::mir::clipping::BlendGroupBuilder builder; + builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); + builder.setBlendGroupOffsets(blendOffsets.view(), blendGroupOffsets.view()); + builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + + std::cout << "-------- zone 0 --------" << std::endl; + auto z0 = builder.blendGroupsForZone(0); + EXPECT_EQ(z0.size(), 8); + IndexType index = 0; + for(IndexType i = 0; i < z0.size(); i++, index++) + { + z0.print(std::cout); + EXPECT_EQ(z0.ids().size(), blendGroupSizes[index]); + + z0++; + } + + std::cout << "-------- zone 1 --------" << std::endl; + auto z1 = builder.blendGroupsForZone(1); + EXPECT_EQ(z1.size(), 5); + for(IndexType i = 0; i < z1.size(); i++, index++) + { + z1.print(std::cout); + EXPECT_EQ(z1.ids().size(), blendGroupSizes[index]); + z1++; + } +} + template void braid2d_clip_test(const std::string &type, const std::string &name) { @@ -324,6 +490,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) // Save data. conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); + conduit_save_vtk(hostClipMesh, name + ".vtk"); } #if 0 @@ -345,6 +512,7 @@ TEST(mir_clipfield, uniform2d) } #endif +#if 1 TEST(mir_clipfield, hex3d) { const std::string type("hexs"); @@ -362,6 +530,7 @@ TEST(mir_clipfield, hex3d) braid3d_clip_test(type, "hex_hip"); #endif } +#endif //------------------------------------------------------------------------------ From 8cd51b20e553016e34843eb3a090013fa488184a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 26 Jul 2024 18:12:08 -0700 Subject: [PATCH 111/290] wedge is bad too --- src/axom/mir/ClipField.hpp | 8 ++- src/axom/mir/CoordsetBlender.hpp | 4 +- src/axom/mir/TODO.txt | 19 ++++++- src/axom/mir/tests/mir_clipfield.cpp | 79 +++++++++++++++++++++++++--- 4 files changed, 99 insertions(+), 11 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index bc75f59f6e..04e0dac563 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -962,7 +962,7 @@ class ClipField // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; - +std::cout << "clipcase=" << clipcase << ", clipTableIndex=" << clipTableIndex << std::endl; int thisBlendGroups = 0; // The number of blend groups produced in this case. int thisBlendGroupLen = 0; // The total length of the blend groups. int thisFragments = 0; // The number of zone fragments produced in this case. @@ -976,6 +976,12 @@ class ClipField // Get the current shape in the clip case. const auto fragment = *it; +// ERROR! +if(fragment.size() == 0) +{ +std::cout << "zoneIndex=" << zoneIndex << "fragment.size=" << fragment.size() << std::endl; +it.print(std::cout); +} if(fragment[0] == ST_PNT) { if(details::generatedPointIsSelected(fragment[2], selection)) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index f5e5e5600a..4d1af7f58f 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -90,17 +90,15 @@ class CoordsetBlender const auto start = blend.m_blendGroupStartView[selectedIndex]; const auto end = start + blend.m_blendGroupSizesView[selectedIndex]; -std::cout << "blend" << bgid << ":\n selectedIndex: " << selectedIndex << "\n start: " << start << "\n size: " << blend.m_blendGroupSizesView[selectedIndex] << "\n end: " << end << "\n ids: ["; // Blend points for this blend group. VectorType blended{}; for(IndexType i = start; i < end; i++) { const auto index = blend.m_blendIdsView[i]; const auto weight = blend.m_blendCoeffView[i]; -std::cout << index << ", "; blended += (VectorType(view[index]) * static_cast(weight)); } -std::cout << "]\n"; + // Store the point into the Conduit component arrays. for(int comp = 0; comp < PointType::DIMENSION; comp++) { diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index 0721f4dd8f..e638a1204e 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -4,10 +4,27 @@ Partial TODO list (done) 2. Conduit routine for moving data to the GPU (done) 3. Build/run on the GPU 4. More ClipField tests (for other topologies) with baselines + - uniform2d (done) + - uniform3d + - rectilinear2d + - rectilinear3d + - structured2d + - structured3d + - stridedstructured2d + - stridedstructured3d + - unstructured2d + - tri + - quad + - mixed + - unstructured3d + - tet (done) + - pyramid - crashes + - wedge + - hex - bad nodes + - mixed - Test with the various extraction options - Test setting the topo, coordset names - Test storing results in same Node, use names/fields map to change names - - Make sure we can operate on a StridedStructured mesh (done)5. Create UnstructuredTopologyMixedShapeView and dispatch (done)6. Make sure ClipFilter can use the mixed topology view - it probably needs some changes 7. Code cleanup pass in ClipFilter diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index c64b0267b3..b7fdf8c4d2 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -433,11 +433,10 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Load a clipped baseline file & compare. } -template +template void braid3d_clip_test(const std::string &type, const std::string &name) { - using HexShape = axom::mir::views::HexShape; - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; axom::StackArray dims{8,8,8};//10, 10, 10}; @@ -449,6 +448,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) hostMesh.print(); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); // Create views conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); @@ -512,11 +512,13 @@ TEST(mir_clipfield, uniform2d) } #endif -#if 1 -TEST(mir_clipfield, hex3d) +#if 0 +TEST(mir_clipfield, hex) { + using ShapeType = axom::mir::views::HexShape; + const std::string type("hexs"); - braid3d_clip_test(type, "hex"); + braid3d_clip_test(type, "hex"); //#if defined(AXOM_USE_OPENMP) // braid3d_clip_test(type, "hex_omp"); @@ -532,6 +534,71 @@ TEST(mir_clipfield, hex3d) } #endif +#if 0 +TEST(mir_clipfield, tet) +{ + using ShapeType = axom::mir::views::TetShape; + + const std::string type("tets"); + braid3d_clip_test(type, "tet"); + +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "tet_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + braid3d_clip_test(type, "tet_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_clip_test(type, "tet_hip"); +#endif +} +#endif + +#if 0 +TEST(mir_clipfield, pyramid) +{ + using ShapeType = axom::mir::views::PyramidShape; + + const std::string type("pyramids"); + braid3d_clip_test(type, "pyr"); + +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "pyr_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + braid3d_clip_test(type, "pyr_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_clip_test(type, "pyr_hip"); +#endif +} +#endif + +#if 1 +TEST(mir_clipfield, wedge) +{ + using ShapeType = axom::mir::views::WedgeShape; + + const std::string type("wedges"); + braid3d_clip_test(type, "wdg"); + +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "wdg_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + braid3d_clip_test(type, "wdg_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_clip_test(type, "wdg_hip"); +#endif +} +#endif //------------------------------------------------------------------------------ int main(int argc, char* argv[]) From acf1ac2f47d8ab002fc340eb3e34b6b08737654f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 16:10:15 -0700 Subject: [PATCH 112/290] Added a test for explicit coordset view --- src/axom/mir/tests/mir_views.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index e9a9ffec44..2ea5950b2c 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -33,6 +33,31 @@ TEST(mir_views, shape2conduitName) EXPECT_EQ(axom::mir::views::HexShape::name(), "hex"); } +TEST(mir_views, explicit_coordsetview) +{ + axom::Array x{{0., 1., 2., 3., 4., 5.}}; + axom::Array y{{10., 11., 12., 13., 14., 15.}}; + axom::Array z{{20., 21., 22., 23., 24., 25.}}; + + axom::mir::views::ExplicitCoordsetView view2d(x.view(), y.view()); + EXPECT_EQ(view2d.size(), 6); + for(axom::IndexType i = 0; i < view2d.size(); i++) + { + axom::primal::Point P({x[i], y[i]}); + EXPECT_EQ(view2d.getPoint(i), P); + EXPECT_EQ(view2d[i], P); + } + + axom::mir::views::ExplicitCoordsetView view3d(x.view(), y.view(), z.view()); + EXPECT_EQ(view3d.size(), 6); + for(axom::IndexType i = 0; i < view3d.size(); i++) + { + axom::primal::Point P({x[i], y[i], z[i]}); + EXPECT_EQ(view3d.getPoint(i), P); + EXPECT_EQ(view3d[i], P); + } +} + //------------------------------------------------------------------------------ int main(int argc, char* argv[]) From 5331c0a1b10fffff7798715c2697175ecbdca71d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 16:10:29 -0700 Subject: [PATCH 113/290] Fix typo --- src/axom/mir/clipping/ClipTableManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index c3284da06d..393723bd96 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -462,7 +462,7 @@ class ClipTableManager axom::mir::clipping::visit::numClipShapesPyr, axom::mir::clipping::visit::startClipShapesPyr, axom::mir::clipping::visit::clipShapesPyr, - axom::mir::clipping::visit::clipShapesTetSize); + axom::mir::clipping::visit::clipShapesPyrSize); } else if(shape == ST_WDG) { From b46d992608d1b6ad29897f57a4eb79d3d2da1d89 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 16:11:24 -0700 Subject: [PATCH 114/290] Fixed a clip bug from incorrect shape traversal --- src/axom/mir/ClipField.hpp | 125 +++------- src/axom/mir/tests/mir_clipfield.cpp | 351 +++++++++++++++++++++++++-- 2 files changed, 370 insertions(+), 106 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 04e0dac563..9d1b085e79 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -639,10 +639,6 @@ class BlendGroupBuilder } m_state->m_blendNamesView[m_blendGroupId] = blendName; -#if 1 - print(std::cout); -#endif - m_blendGroupId++; m_startOffset = m_currentDataOffset; } @@ -664,12 +660,7 @@ class BlendGroupBuilder AXOM_HOST_DEVICE IndexType uniqueBlendGroupIndex() const { -#define AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE -#ifdef AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE return axom::mir::utilities::bsearch(name(), m_state->m_blendUniqueNamesView); -#else - return m_blendGroupId; -#endif } /** @@ -790,9 +781,7 @@ class BlendGroupBuilder { axom::mir::utilities::blueprint::BlendData blend; -#ifdef AXOM_CLIPFIELD_MAKE_POINTS_UNIQUE blend.m_selectedIndicesView = m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices -#endif blend.m_blendGroupSizesView = m_state.m_blendGroupSizesView; blend.m_blendGroupStartView = m_state.m_blendGroupStartView; blend.m_blendIdsView = m_state.m_blendIdsView; @@ -917,14 +906,12 @@ class ClipField // ---------------------------------------------------------------------- // - // Stage 1: Iterate over zones and their respective fragments to - // determine sizes. + // Stage 1: Allocate some memory // // ---------------------------------------------------------------------- - // Allocate some memory axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. - axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone + axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone auto clipCasesView = clipCases.view(); auto pointsUsedView = pointsUsed.view(); @@ -945,9 +932,16 @@ class ClipField // Make an object to help manage building the blend groups. BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); + + // ---------------------------------------------------------------------- + // + // Stage 2: Iterate over zones and their respective fragments to + // determine sizes for fragments and blend groups. + // + // ---------------------------------------------------------------------- + auto blendGroupsView = blendGroups.view(); auto blendGroupsLenView = blendGroupsLen.view(); - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { using clip_value_type = typename decltype(clipFieldView)::value_type; @@ -962,7 +956,7 @@ class ClipField // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; -std::cout << "clipcase=" << clipcase << ", clipTableIndex=" << clipTableIndex << std::endl; + int thisBlendGroups = 0; // The number of blend groups produced in this case. int thisBlendGroupLen = 0; // The total length of the blend groups. int thisFragments = 0; // The number of zone fragments produced in this case. @@ -976,33 +970,25 @@ std::cout << "clipcase=" << clipcase << ", clipTableIndex=" << clipTableIndex << // Get the current shape in the clip case. const auto fragment = *it; -// ERROR! -if(fragment.size() == 0) -{ -std::cout << "zoneIndex=" << zoneIndex << "fragment.size=" << fragment.size() << std::endl; -it.print(std::cout); -} if(fragment[0] == ST_PNT) { if(details::generatedPointIsSelected(fragment[2], selection)) { - const size_t nIds = fragment[3]; - for(size_t ni = 4; ni < nIds; ni++) + const int nIds = static_cast(fragment[3]); + + for(int ni = 0; ni < nIds; ni++) { - const auto pid = fragment[ni]; + const auto pid = fragment[4 + ni]; // Increase the blend size to include this center point. if(pid <= P7) { - // corner point. + // corner point thisBlendGroupLen++; } else if(pid >= EA && pid <= EL) { - // edge points are derived from 2 corner points. If - // those appear here then we're probably creating a - // face point. We can store the 2 corner points in place - // of the edge point (along with some blending coeff). + // edge point thisBlendGroupLen += 2; } } @@ -1038,6 +1024,7 @@ it.print(std::cout); for(IndexType pid = P0; pid <= P7; pid++) { const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += incr; // {p0} thisBlendGroups += incr; } @@ -1046,6 +1033,7 @@ it.print(std::cout); for(IndexType pid = EA; pid <= EL; pid++) { const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + thisBlendGroupLen += 2 * incr; // {p0 p1} thisBlendGroups += incr; } @@ -1060,7 +1048,12 @@ it.print(std::cout); }); }); - // Sum up the sizes. + // ---------------------------------------------------------------------- + // + // Stage 3: Sum up fragment sizes, make some offsets. + // + // ---------------------------------------------------------------------- + RAJA::ReduceSum fragment_sum(0); RAJA::ReduceSum fragment_nids_sum(0); axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) @@ -1068,7 +1061,6 @@ it.print(std::cout); fragment_sum += fragmentsView[zoneIndex]; fragment_nids_sum += fragmentsSizeView[zoneIndex]; }); - // Make offsets via scan. RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), RAJA::make_span(fragmentOffsetsView.data(), nzones), RAJA::operators::plus{}); @@ -1079,12 +1071,8 @@ it.print(std::cout); // ---------------------------------------------------------------------- // - // Stage 2: Do some scans to fill out blendOffset and blendGroupOffsets, - // which is where we fill in the real data. + // Stage 4: Compute total blend group sizes and offsets. // - // blendOffset : Starting offset for blending data like blendIds, blendCoeff. - // blendGroupOffset : Starting offset for blendNames, blendGroupSizes. - // fragmentOffsets : Where an zone's fragments begin in the output. // ---------------------------------------------------------------------- IndexType blendGroupsSize = 0, blendGroupLenSize = 0; @@ -1093,16 +1081,12 @@ it.print(std::cout); builder.setBlendGroupOffsets(blendOffset.view(), blendGroupOffsets.view()); builder.computeBlendGroupOffsets(); - - // ---------------------------------------------------------------------- // - // Stage 3: Iterate over the zones/cases again and fill in the blend + // Stage 5: Iterate over the zones/cases again and fill in the blend // groups that get produced: blendNames, blendGroupSizes, // blendCoeff, blendIds. These are used to produce the new points. // - // NOTE: blendGroupStart is a scan of blendGroupSizes. - // // ---------------------------------------------------------------------- axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); @@ -1115,9 +1099,7 @@ it.print(std::cout); views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); -#if 1 -std::cout << "clipping:\n"; -#endif + m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. @@ -1132,13 +1114,6 @@ std::cout << "clipping:\n"; // Get the blend groups for this zone. auto groups = builder.blendGroupsForZone(zoneIndex); -#if 1 -std::cout << " -\n"; -std::cout << " zone: " << zoneIndex << std::endl; -std::cout << " clipcase: " << clipcase << std::endl; -std::cout << " fragments:\n"; -int fi = 0; -#endif auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -1146,18 +1121,11 @@ int fi = 0; { // Get the current shape in the clip case. const auto fragment = *it; -#if 1 -std::cout << " f" << fi << ": \""; -fi++; -it.print(std::cout); -std::cout << "\"" << std::endl; -#endif if(fragment[0] == ST_PNT) { if(details::generatedPointIsSelected(fragment[2], selection)) { -// TODO: put the ability to select on color back in... 0, 1, or both const int nIds = static_cast(fragment[3]); const auto one_over_n = 1.f / static_cast(nIds); @@ -1174,10 +1142,7 @@ std::cout << "\"" << std::endl; } else if(ptid >= EA && ptid <= EL) { - // edge points are derived from 2 corner points. If - // those appear here then we're probably creating a - // face point. We can store the 2 corner points in place - // of the edge point (along with some blending coeff). + // edge point. const auto edgeIndex = ptid - EA; const auto edge = zone.getEdge(edgeIndex); const auto id0 = zone.getId(edge[0]); @@ -1185,7 +1150,6 @@ std::cout << "\"" << std::endl; // Figure out the blend for edge. const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - groups.add(id0, one_over_n * (1.f - t)); groups.add(id1, one_over_n * t); } @@ -1230,13 +1194,9 @@ std::cout << "\"" << std::endl; // ---------------------------------------------------------------------- // - // Stage 4 - Make the blend groups unique based on their blendName. + // Stage 6 - Make the blend groups unique based on their blendName. // // ---------------------------------------------------------------------- - // At this point, we have created the blend group data. We can now use the - // blendNames to make unique blend groups. uNames contains a sorted list of - // the unique blend group names while uIndices is their original index in - // blendNames/blendGroupOffsets/blendGroupSizes. axom::Array uNames; axom::Array uIndices; axom::mir::utilities::unique(builder.blendNames(), uNames, uIndices); @@ -1247,7 +1207,7 @@ std::cout << "\"" << std::endl; // ---------------------------------------------------------------------- // - // Stage 5 - Make new connectivity. + // Stage 7 - Make new connectivity. // // ---------------------------------------------------------------------- const auto finalNumZones = fragment_sum.get(); @@ -1409,24 +1369,17 @@ std::cout << "\"" << std::endl; n_newTopo["elements/shape"] = shapeMap.begin()->first; } -#if 1 - // Save blend group. - conduit::Node bg; - bg["selectedIndicesView"].set_external(blend.m_selectedIndicesView.data(), blend.m_selectedIndicesView.size()); - bg["blendGroupSizesView"].set_external(blend.m_blendGroupSizesView.data(), blend.m_blendGroupSizesView.size()); - bg["blendGroupStartView"].set_external(blend.m_blendGroupStartView.data(), blend.m_blendGroupStartView.size()); - bg["blendIdsView"].set_external(blend.m_blendIdsView.data(), blend.m_blendIdsView.size()); - bg["blendCoeffView"].set_external(blend.m_blendCoeffView.data(), blend.m_blendCoeffView.size()); - conduit::relay::io::save(bg, "blend.yaml", "yaml"); -#endif - //----------------------------------------------------------------------- - // STAGE 6 - Make new coordset. + // + // STAGE 8 - Make new coordset. + // //----------------------------------------------------------------------- makeCoordset(blend, n_coordset, n_newCoordset); //----------------------------------------------------------------------- - // STAGE 7 - Make new fields. + // + // STAGE 9 - Make new fields. + // //----------------------------------------------------------------------- axom::mir::utilities::blueprint::SliceData slice; axom::Array sliceIndices(finalNumZones, finalNumZones, allocatorID); @@ -1441,7 +1394,9 @@ std::cout << "\"" << std::endl; makeFields(blend, slice, opts.topologyName(n_topo.name()), opts.fields(n_fields), n_fields, n_newFields); //----------------------------------------------------------------------- - // STAGE 8 - make originalElements (this will later be optional) + // + // STAGE 10 - make originalElements (this will later be optional) + // //----------------------------------------------------------------------- if(n_fields.has_child("originalElements")) { diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index b7fdf8c4d2..7dd0e7169e 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -10,6 +10,7 @@ #include #include +#include // clang-format off #if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) @@ -311,29 +312,352 @@ TEST(mir_clipfield, blend_group_builder) builder.setBlendGroupOffsets(blendOffsets.view(), blendGroupOffsets.view()); builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); - std::cout << "-------- zone 0 --------" << std::endl; + //std::cout << "-------- zone 0 --------" << std::endl; auto z0 = builder.blendGroupsForZone(0); EXPECT_EQ(z0.size(), 8); IndexType index = 0; for(IndexType i = 0; i < z0.size(); i++, index++) { - z0.print(std::cout); + //z0.print(std::cout); EXPECT_EQ(z0.ids().size(), blendGroupSizes[index]); z0++; } - std::cout << "-------- zone 1 --------" << std::endl; + //std::cout << "-------- zone 1 --------" << std::endl; auto z1 = builder.blendGroupsForZone(1); EXPECT_EQ(z1.size(), 5); for(IndexType i = 0; i < z1.size(); i++, index++) { - z1.print(std::cout); + //z1.print(std::cout); EXPECT_EQ(z1.ids().size(), blendGroupSizes[index]); z1++; } } +//------------------------------------------------------------------------------ +template +bool increasing(const ArrayType &arr) +{ + bool retval = true; + for(size_t i = 1; i < arr.size(); i++) + retval &= (arr[i] >= arr[i - 1]); + return retval; +} + +std::vector permute(const std::vector &input) +{ + std::vector values, indices; + std::vector order; + + values.resize(input.size()); + indices.resize(input.size()); + order.resize(input.size()); + + std::iota(indices.begin(), indices.end(), 0); + for(size_t i = 0; i < input.size(); i++) + { + order[i] = drand48(); + } + std::sort(indices.begin(), indices.end(), [&](int a, int b) + { + return order[a] < order[b]; + }); + for(size_t i = 0; i < input.size(); i++) + values[i] = input[indices[i]]; + return values; +} + +std::vector makeUnsortedArray(int n) +{ + std::vector values; + values.resize(n); + std::iota(values.begin(), values.end(), 0); + return permute(values); +} + +std::vector makeRandomArray(int n) +{ + constexpr int largestId = 1 << 28; + std::vector values; + values.resize(n); + for(int i = 0; i < n; i++) + { + values[i] = static_cast(largestId * drand48()); + } + return permute(values); +} + +//------------------------------------------------------------------------------ +TEST(mir_clipfield, sort_values) +{ + for(int n = 1; n < 15; n++) + { + for(int trial = 1; trial <= n; trial++) + { + auto values = makeUnsortedArray(n); + axom::mir::utilities::sort_values(values.data(), values.size()); + EXPECT_TRUE(increasing(values)); + } + } +} +//------------------------------------------------------------------------------ +TEST(mir_clipfield, unique) +{ +/* + 8---9---10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + + */ + axom::Array ids{{0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10}}; + axom::Array uIds, uIndices; + + axom::mir::utilities::unique(ids.view(), uIds, uIndices); + EXPECT_EQ(uIds.size(), 12); + EXPECT_EQ(uIndices.size(), 12); + for(axom::IndexType i = 0; i < uIds.size(); i++) + { + EXPECT_EQ(uIds[i], i); + EXPECT_EQ(uIds[i], ids[uIndices[i]]); + } +} + +//------------------------------------------------------------------------------ +TEST(mir_clipfield, make_name) +{ + for(int n = 1; n < 14; n++) + { + // Make a set of scrambled ids. + auto values = makeRandomArray(n); + std::uint64_t name = 0, name2 = 0; + // Compute the name for that list of ids. + if(n == 1) + name = axom::mir::utilities::make_name_1(values[0]); + else if(n == 2) + name = axom::mir::utilities::make_name_2(values[0], values[1]); + else + name = axom::mir::utilities::make_name_n(values.data(), values.size()); + + for(int trial = 0; trial < 1000; trial++) + { + // Scramble the id list. + auto values2 = permute(values); + // Compute the name for that list of ids. + if(n == 1) + name2 = axom::mir::utilities::make_name_1(values2[0]); + else if(n == 2) + name2 = axom::mir::utilities::make_name_2(values2[0], values2[1]); + else + name2 = axom::mir::utilities::make_name_n(values2.data(), values2.size()); + + // The names for the 2 scrambled lists of numbers should be the same. + EXPECT_EQ(name, name2); + } + } +} +//------------------------------------------------------------------------------ + +void make_one_hex(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 1., 1., 0., 0., 1., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 1., 1., 0., 0., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 0., 0., 0., 1., 1., 1., 1.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "hex"; + hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4,5,6,7}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector{8}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set(std::vector{{ 1., -1., -1., -1., -1., -1., -1., -1.}}); +} + +void make_one_tet(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 0., 0.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "tet"; + hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector{4}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set(std::vector{{ -1., -1., -1., 1.}}); +} + +void make_one_pyr(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 1., 0.5}}); + hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 1., 0., 0.5}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "pyramid"; + hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector{5}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set(std::vector{{ 1., 1., -1., -1., -1.}}); +} + +void make_one_wdg(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 0., 0., 1}}); + hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 0., 0., 1., 0.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "wedge"; + hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4,5}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector{6}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set(std::vector{{ 1., 1., -1., -1., -1., -1.}}); +} + +template +void test_one_shape(conduit::Node &hostMesh, const std::string &name) +{ + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + // Copy mesh to device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + + // Make views for the device mesh. + conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); + conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); + conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); + axom::ArrayView xView(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); + axom::ArrayView zView(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); + CoordsetView coordsetView(xView, yView, zView); + + conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/topo/elements/connectivity"); + axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); + TopoView topoView(connView); + + // Clip the data + conduit::Node deviceClipMesh, options; + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + options["clipField"] = "distance"; + options["clipValue"] = 0.; + options["inside"] = 1; + options["outside"] = 1; + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Save data. + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "yaml"); +} + +TEST(mir_clipfield, onetet) +{ + using TetShape = axom::mir::views::TetShape; + + conduit::Node hostMesh; + make_one_tet(hostMesh); + + test_one_shape(hostMesh, "one_tet"); + +//#if defined(AXOM_USE_OPENMP) +// test_one_shape(hostMesh, "one_tet_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + test_one_shape(hostMesh, "one_tet_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + test_one_shape(hostMesh, "one_tet_hip"); +#endif +} + +TEST(mir_clipfield, onepyr) +{ + using PyramidShape = axom::mir::views::PyramidShape; + + conduit::Node hostMesh; + make_one_pyr(hostMesh); + + test_one_shape(hostMesh, "one_pyr"); + +//#if defined(AXOM_USE_OPENMP) +// test_one_shape(hostMesh, "one_pyr_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + test_one_shape(hostMesh, "one_pyr_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + test_one_shape(hostMesh, "one_pyr_hip"); +#endif +} + +TEST(mir_clipfield, onewdg) +{ + using WedgeShape = axom::mir::views::WedgeShape; + + conduit::Node hostMesh; + make_one_wdg(hostMesh); + + test_one_shape(hostMesh, "one_wdg"); + +//#if defined(AXOM_USE_OPENMP) +// test_one_shape(hostMesh, "one_wdg_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + test_one_shape(hostMesh, "one_wdg_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + test_one_shape(hostMesh, "one_wdg_hip"); +#endif +} + +TEST(mir_clipfield, onehex) +{ + using HexShape = axom::mir::views::HexShape; + + conduit::Node hostMesh; + make_one_hex(hostMesh); + + test_one_shape(hostMesh, "one_hex"); + +//#if defined(AXOM_USE_OPENMP) +// test_one_shape(hostMesh, "one_hex_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + test_one_shape(hostMesh, "one_hex_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + test_one_shape(hostMesh, "one_hex_hip"); +#endif +} + +//------------------------------------------------------------------------------ template void braid2d_clip_test(const std::string &type, const std::string &name) { @@ -439,13 +763,11 @@ void braid3d_clip_test(const std::string &type, const std::string &name) using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; - axom::StackArray dims{8,8,8};//10, 10, 10}; - axom::StackArray zoneDims{dims[0] - 1, dims[1] - 1, dims[2] - 1}; + axom::StackArray dims{10, 10, 10}; // Create the data conduit::Node hostMesh, deviceMesh; braid(type, dims, hostMesh); - hostMesh.print(); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); @@ -460,11 +782,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) CoordsetView coordsetView(x, y, z); conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"); - //conduit::Node &n_sizes = deviceMesh.fetch_existing("topologies/mesh/elements/sizes"); - //conduit::Node &n_offsets = deviceMesh.fetch_existing("topologies/mesh/elements/offsets"); const axom::ArrayView conn(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); - //const axom::ArrayView sizes(static_cast(n_sizes.data_ptr()), n_sizes.dtype().number_of_elements()); - //const axom::ArrayView offsets(static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); TopoView topoView(conn); //, sizes, offsets); // Create options to control the clipping. @@ -493,7 +811,6 @@ void braid3d_clip_test(const std::string &type, const std::string &name) conduit_save_vtk(hostClipMesh, name + ".vtk"); } -#if 0 TEST(mir_clipfield, uniform2d) { braid2d_clip_test("uniform", "uniform2d"); @@ -510,9 +827,7 @@ TEST(mir_clipfield, uniform2d) braid2d_clip_test("uniform", "uniform2d_hip"); #endif } -#endif -#if 0 TEST(mir_clipfield, hex) { using ShapeType = axom::mir::views::HexShape; @@ -532,9 +847,7 @@ TEST(mir_clipfield, hex) braid3d_clip_test(type, "hex_hip"); #endif } -#endif -#if 0 TEST(mir_clipfield, tet) { using ShapeType = axom::mir::views::TetShape; @@ -554,9 +867,7 @@ TEST(mir_clipfield, tet) braid3d_clip_test(type, "tet_hip"); #endif } -#endif -#if 0 TEST(mir_clipfield, pyramid) { using ShapeType = axom::mir::views::PyramidShape; @@ -576,9 +887,7 @@ TEST(mir_clipfield, pyramid) braid3d_clip_test(type, "pyr_hip"); #endif } -#endif -#if 1 TEST(mir_clipfield, wedge) { using ShapeType = axom::mir::views::WedgeShape; @@ -598,7 +907,7 @@ TEST(mir_clipfield, wedge) braid3d_clip_test(type, "wdg_hip"); #endif } -#endif + //------------------------------------------------------------------------------ int main(int argc, char* argv[]) From 40152cedf181d937a7974321b8a4817a4dae344a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 17:44:24 -0700 Subject: [PATCH 115/290] Fixed wedge edges --- src/axom/mir/views/Shapes.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 328b1e1a86..896db3a1fb 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -275,7 +275,7 @@ struct WedgeTraits AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) { - const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,3}}; + const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,5}}; return edges[edgeIndex]; } From 43d9c47dcb99a70e441f857f29ac12035955b2ee Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 17:45:30 -0700 Subject: [PATCH 116/290] Fixes for wedge clip --- src/axom/mir/ClipField.hpp | 35 ++++++++++++++++---- src/axom/mir/tests/mir_clipfield.cpp | 49 ++++++++++++---------------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9d1b085e79..8d826b6f0b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -182,15 +182,12 @@ bool shapeIsSelected(unsigned char color, int selection) } AXOM_HOST_DEVICE -float computeWeight(float d0, float d1, float clipValue) +inline float computeWeight(float d0, float d1, float clipValue) { - const float delta = d1 - d0; - const float abs_delta = (delta < 0) ? -delta : delta; - const float t = (abs_delta != 0.) ? ((clipValue - d0) / delta) : 0.; - return t; + constexpr float tiny = 1.e-09; + return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / (axom::utilities::abs(d1 - d0) + tiny), 0.f, 1.f); } - // TODO: Could we make ZoneType be a concept? /** * \brief Use the ids for the provided zone and the values from the array view to determine a clip case. @@ -638,11 +635,34 @@ class BlendGroupBuilder blendName = axom::mir::utilities::make_name_n(m_state->m_blendIdsView.data() + m_startOffset, numIds); } m_state->m_blendNamesView[m_blendGroupId] = blendName; - +#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) + const float w = weightSum(); + constexpr float EPS = 1.e-5; + if(w < (1. - EPS) || w > (1. + EPS)) + { + SLIC_ERROR(fmt::format("Invalid blend group weights w={}.", w)); + print(std::cout); + } +#endif m_blendGroupId++; m_startOffset = m_currentDataOffset; } + /** + * \brief Return the sum of the weights for the current blend group. + * \note The blendGroup must have finished construction. + * \return The sum of weights, which should be about 1. + */ + AXOM_HOST_DEVICE float weightSum() const + { + const auto numIds = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; + float w = 0.f; + for(IndexType i = 0; i < numIds; i++) + w += m_state->m_blendCoeffView[start + i]; + return w; + } + /** * \brief Return the name of the current blend group. * \return The name of the current blend group. @@ -1150,6 +1170,7 @@ class ClipField // Figure out the blend for edge. const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); + groups.add(id0, one_over_n * (1.f - t)); groups.add(id1, one_over_n * t); } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 7dd0e7169e..2d714a4a9c 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -763,9 +763,8 @@ void braid3d_clip_test(const std::string &type, const std::string &name) using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; - axom::StackArray dims{10, 10, 10}; - // Create the data + const axom::StackArray dims{10, 10, 10}; conduit::Node hostMesh, deviceMesh; braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); @@ -783,18 +782,13 @@ void braid3d_clip_test(const std::string &type, const std::string &name) conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"); const axom::ArrayView conn(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); - TopoView topoView(conn); //, sizes, offsets); + TopoView topoView(conn); // Create options to control the clipping. - const std::string clipTopoName("cliptopo"); conduit::Node options; options["clipField"] = "distance"; options["inside"] = 1; options["outside"] = 0; - //options["topologyName"] = clipTopoName; - //options["coordsetName"] = "clipcoords"; - //options["fields/braid"] = "new_braid"; - //options["fields/radial"] = "new_radial"; // Clip the data conduit::Node deviceClipMesh; @@ -828,26 +822,6 @@ TEST(mir_clipfield, uniform2d) #endif } -TEST(mir_clipfield, hex) -{ - using ShapeType = axom::mir::views::HexShape; - - const std::string type("hexs"); - braid3d_clip_test(type, "hex"); - -//#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "hex_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "hex_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "hex_hip"); -#endif -} - TEST(mir_clipfield, tet) { using ShapeType = axom::mir::views::TetShape; @@ -908,6 +882,25 @@ TEST(mir_clipfield, wedge) #endif } +TEST(mir_clipfield, hex) +{ + using ShapeType = axom::mir::views::HexShape; + + const std::string type("hexs"); + braid3d_clip_test(type, "hex"); + +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "hex_omp"); +//#endif + +#if defined(AXOM_USE_CUDA) + braid3d_clip_test(type, "hex_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_clip_test(type, "hex_hip"); +#endif +} //------------------------------------------------------------------------------ int main(int argc, char* argv[]) From e8c8474a46df4e04dd1dd6e4574ce9bc403b055f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 29 Jul 2024 18:31:00 -0700 Subject: [PATCH 117/290] start trying to work around OMP issue --- src/axom/mir/ClipField.hpp | 86 +++++++++++++------ src/axom/mir/TODO.txt | 6 +- src/axom/mir/tests/mir_clipfield.cpp | 28 +++--- .../UnstructuredTopologyPolyhedralView.hpp | 2 +- 4 files changed, 78 insertions(+), 44 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 8d826b6f0b..f50db19ddf 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -831,6 +831,12 @@ class ClipField using SliceData = axom::mir::utilities::blueprint::SliceData; using ClipTableViews = axom::StackArray; + using KeyType = std::uint64_t; + using BitSet = std::uint64_t; + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + using ConnectivityType = typename TopologyView::ConnectivityType; + /** * \brief Constructor * @@ -893,13 +899,8 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) { - using KeyType = std::uint64_t; - using BitSet = std::uint64_t; - using loop_policy = typename axom::execution_space::loop_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); - using ConnectivityType = typename TopologyView::ConnectivityType; constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; const auto nzones = m_topologyView.numberOfZones(); @@ -935,10 +936,10 @@ class ClipField auto clipCasesView = clipCases.view(); auto pointsUsedView = pointsUsed.view(); - axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. - axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. - axom::Array fragmentOffsets(nzones, nzones, allocatorID); - axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); + axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. + axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. + axom::Array fragmentOffsets(nzones, nzones, allocatorID); + axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); auto fragmentsView = fragments.view(); auto fragmentsSizeView = fragmentsSize.view(); auto fragmentOffsetsView = fragmentOffsets.view(); @@ -1073,21 +1074,10 @@ class ClipField // Stage 3: Sum up fragment sizes, make some offsets. // // ---------------------------------------------------------------------- + IndexType finalNumZones = 0, finalConnSize = 0; + computeFragmentSizesAndOffsets(fragmentsView, fragmentOffsetsView, fragmentsSizeView, fragmentSizeOffsetsView, + finalNumZones, finalConnSize); - RAJA::ReduceSum fragment_sum(0); - RAJA::ReduceSum fragment_nids_sum(0); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - fragment_sum += fragmentsView[zoneIndex]; - fragment_nids_sum += fragmentsSizeView[zoneIndex]; - }); - RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), - RAJA::make_span(fragmentOffsetsView.data(), nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), - RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), - RAJA::operators::plus{}); // ---------------------------------------------------------------------- // @@ -1231,9 +1221,6 @@ class ClipField // Stage 7 - Make new connectivity. // // ---------------------------------------------------------------------- - const auto finalNumZones = fragment_sum.get(); - const auto finalConnSize = fragment_nids_sum.get(); - n_newTopo.reset(); n_newTopo["type"] = "unstructured"; n_newTopo["coordset"] = n_newCoordset.name(); @@ -1488,6 +1475,53 @@ class ClipField } } + /** + * \brief Compute the total number of fragments and their size as well as corresponding offsets. + * + * \param[in] fragmentsView The number of fragments in each zone. + * \param[in] fragmentOffsetsView The offset to the start of each zone's fragments. + * \param[in] fragmentsSizeView The size of each zone's fragment connectivity. + * \param[in] fragmentSizeOffsetsView The offset to the start of each zone's fragment connectivity. + * \param[out] fragmentSum The sum of the elements in \a fragmentsView. + * \param[out] fragmentNIdsSum The sum of the elements in \a fragmentsSizeView. + * + */ + void computeFragmentSizesAndOffsets(const axom::ArrayView &fragmentsView, + const axom::ArrayView &fragmentOffsetsView, + const axom::ArrayView &fragmentsSizeView, + const axom::ArrayView &fragmentSizeOffsetsView, + IndexType &fragmentSum, + IndexType &fragmentNIdsSum) const + { +#if 1 + // If we're building for OpenMP, use sequential policies for now to avoid compiler crash. + using safe_loop_policy = typename std::conditional::loop_policy>::value, axom::execution_space::loop_policy, loop_policy>::type; + using safe_reduce_policy = typename std::conditional::reduce_policy>::value, axom::execution_space::reduce_policy, reduce_policy>::type; +#else + using safe_loop_policy = loop_policy; + using safe_reduce_policy = reduce_policy; +#endif + + const auto nzones = fragmentsView.size(); + RAJA::ReduceSum fragment_sum(0); + RAJA::ReduceSum fragment_nids_sum(0); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + fragment_sum += fragmentsView[zoneIndex]; + fragment_nids_sum += fragmentsSizeView[zoneIndex]; + }); + fragmentSum = fragment_sum.get(); + fragmentNIdsSum = fragment_nids_sum.get(); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), + RAJA::make_span(fragmentOffsetsView.data(), nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), + RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), + RAJA::operators::plus{}); + } + /** * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index e638a1204e..00cde61bcd 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -18,9 +18,9 @@ Partial TODO list - mixed - unstructured3d - tet (done) - - pyramid - crashes - - wedge - - hex - bad nodes + - pyramid (done) + - wedge (done) + - hex (done) - mixed - Test with the various extraction options - Test setting the topo, coordset names diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 2d714a4a9c..baf11e97e6 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -830,15 +830,15 @@ TEST(mir_clipfield, tet) braid3d_clip_test(type, "tet"); //#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "tet_omp"); +// braid3d_clip_test(type, "tet_omp"); //#endif #if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "tet_cuda"); + braid3d_clip_test(type, "tet_cuda"); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "tet_hip"); + braid3d_clip_test(type, "tet_hip"); #endif } @@ -850,15 +850,15 @@ TEST(mir_clipfield, pyramid) braid3d_clip_test(type, "pyr"); //#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "pyr_omp"); +// braid3d_clip_test(type, "pyr_omp"); //#endif #if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "pyr_cuda"); + braid3d_clip_test(type, "pyr_cuda"); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "pyr_hip"); + braid3d_clip_test(type, "pyr_hip"); #endif } @@ -870,15 +870,15 @@ TEST(mir_clipfield, wedge) braid3d_clip_test(type, "wdg"); //#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "wdg_omp"); +// braid3d_clip_test(type, "wdg_omp"); //#endif #if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "wdg_cuda"); + braid3d_clip_test(type, "wdg_cuda"); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "wdg_hip"); + braid3d_clip_test(type, "wdg_hip"); #endif } @@ -889,16 +889,16 @@ TEST(mir_clipfield, hex) const std::string type("hexs"); braid3d_clip_test(type, "hex"); -//#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "hex_omp"); -//#endif +#if defined(AXOM_USE_OPENMP) + braid3d_clip_test(type, "hex_omp"); +#endif #if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "hex_cuda"); + braid3d_clip_test(type, "hex_cuda"); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "hex_hip"); + braid3d_clip_test(type, "hex_hip"); #endif } //------------------------------------------------------------------------------ diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 283b97bd1f..3f107226d3 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -60,7 +60,7 @@ class UnstructuredTopologyPolyhedralView constexpr static IndexType MaximumNumberOfIds = 20 * 3; AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return true; } - AXOM_HOST_DEVICE constexpr static int id() { return 1 << 10; } + AXOM_HOST_DEVICE constexpr static int id() { return Polyhedron_ShapeID; } AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj), m_zoneIndex(zi), m_ids() { From 2f39c3e01917a51c58d6d8c0a883da7b5251c842 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 30 Jul 2024 12:08:52 -0700 Subject: [PATCH 118/290] Broke ClipField::execute into methods. --- src/axom/mir/ClipField.hpp | 524 ++++++++++++++------------- src/axom/mir/tests/mir_clipfield.cpp | 6 +- 2 files changed, 278 insertions(+), 252 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index f50db19ddf..e40a3d6225 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -433,7 +433,7 @@ class BlendGroupBuilder { public: using KeyType = std::uint64_t; -private: + /** * \brief This struct holds the views that represent data for blend groups. */ @@ -456,7 +456,13 @@ class BlendGroupBuilder axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. }; -public: + /** + * \brief Access the state views. + * \return A reference to the state. + */ + State &state() { return m_state; } + const State &state() const {return m_state; } + /** * \brief Set the number of zones. * @@ -900,9 +906,6 @@ class ClipField conduit::Node &n_newFields) { const auto allocatorID = axom::execution_space::allocatorID(); - - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; - const auto nzones = m_topologyView.numberOfZones(); ClipOptions opts(nzones, n_options); @@ -912,38 +915,28 @@ class ClipField const conduit::Node &n_clip_field_values = n_clip_field["values"]; SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - // Determine which parts of the shapes will be kept. - int selection = 0; - if(opts.inside()) - axom::utilities::setBit(selection, 0); - if(opts.outside()) - axom::utilities::setBit(selection, 1); - SLIC_ASSERT(selection > 0); - // Load clip table data and make views. m_clipTables.load(m_topologyView.dimension()); ClipTableViews clipTableViews; createClipTableViews(clipTableViews, m_topologyView.dimension()); - // ---------------------------------------------------------------------- - // - // Stage 1: Allocate some memory - // - // ---------------------------------------------------------------------- - + // Allocate some memory and store views in ZoneData, FragmentData. axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone - auto clipCasesView = clipCases.view(); - auto pointsUsedView = pointsUsed.view(); + ZoneData zoneData; + zoneData.m_clipCasesView = clipCases.view(); + zoneData.m_pointsUsedView = pointsUsed.view(); axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. axom::Array fragmentOffsets(nzones, nzones, allocatorID); axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); - auto fragmentsView = fragments.view(); - auto fragmentsSizeView = fragmentsSize.view(); - auto fragmentOffsetsView = fragmentOffsets.view(); - auto fragmentSizeOffsetsView = fragmentSizeOffsets.view(); + + FragmentData fragmentData; + fragmentData.m_fragmentsView = fragments.view(); + fragmentData.m_fragmentsSizeView = fragmentsSize.view(); + fragmentData.m_fragmentOffsetsView = fragmentOffsets.view(); + fragmentData.m_fragmentSizeOffsetsView = fragmentSizeOffsets.view(); axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. @@ -954,25 +947,132 @@ class ClipField BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); - // ---------------------------------------------------------------------- - // - // Stage 2: Iterate over zones and their respective fragments to - // determine sizes for fragments and blend groups. - // - // ---------------------------------------------------------------------- + // Compute sizes and offsets + computeSizes(clipTableViews, zoneData, fragmentData, builder, opts, n_clip_field_values); + computeFragmentSizesAndOffsets(fragmentData); + + IndexType blendGroupsSize = 0, blendGroupLenSize = 0; + builder.computeBlendGroupSizes(blendGroupsSize, blendGroupLenSize); + builder.setBlendGroupOffsets(blendOffset.view(), blendGroupOffsets.view()); + builder.computeBlendGroupOffsets(); + + // Allocate memory for blend groups. + axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); + axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); + axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); + + // Make the blend groups. + builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + makeBlendGroups(clipTableViews, builder, zoneData, opts, n_clip_field_values); + + // Make the blend groups unique + axom::Array uNames; + axom::Array uIndices; + axom::mir::utilities::unique(builder.blendNames(), uNames, uIndices); + builder.setUniqueNames(uNames.view(), uIndices.view()); + axom::mir::utilities::blueprint::BlendData blend = builder.makeBlendData(); + + // Make the clipped mesh + makeConnectivity(clipTableViews, builder, zoneData, fragmentData, opts, n_newTopo, n_newCoordset, n_newFields); + makeCoordset(blend, n_coordset, n_newCoordset); + + axom::mir::utilities::blueprint::SliceData slice; + axom::Array sliceIndices(fragmentData.m_finalNumZones, fragmentData.m_finalNumZones, allocatorID); + auto sliceIndicesView = sliceIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + const auto start = fragmentData.m_fragmentOffsetsView[zoneIndex]; + for(int i = 0; i < fragmentData.m_fragmentsView[zoneIndex]; i++) + sliceIndicesView[start + i] = zoneIndex; + }); + slice.m_indicesView = sliceIndicesView; + makeFields(blend, slice, opts.topologyName(n_topo.name()), opts.fields(n_fields), n_fields, n_newFields); + makeOriginalElements(fragmentData, opts, n_fields, n_newTopo, n_newFields); + } + +private: + /** + * \brief Contains data that describes the number and size of zone fragments in the output. + */ + struct FragmentData + { + IndexType m_finalNumZones{0}; + IndexType m_finalConnSize{0}; + axom::ArrayView m_fragmentsView{}; + axom::ArrayView m_fragmentsSizeView{}; + axom::ArrayView m_fragmentOffsetsView{}; + axom::ArrayView m_fragmentSizeOffsetsView{}; + }; + + /** + * \brief Contains some per-zone data that we want to hold onto between methods. + */ + struct ZoneData + { + axom::ArrayView m_clipCasesView{}; + axom::ArrayView m_pointsUsedView{}; + }; + + /** + * \brief Make a bitset that indicates the parts of the selection that are selected. + */ + int getSelection(const ClipOptions &opts) const + { + int selection = 0; + if(opts.inside()) + axom::utilities::setBit(selection, 0); + if(opts.outside()) + axom::utilities::setBit(selection, 1); + SLIC_ASSERT(selection > 0); + return selection; + } + + /** + * \brief Create views for the clip tables of various shapes. + * + * \param[out] views The views array that will contain the table views. + * \param dimension The dimension the topology (so we can load a subset of tables) + */ + void createClipTableViews(ClipTableViews &views, int dimension) + { + if(dimension == -1 || dimension == 2) + { + views[details::getClipTableIndex(views::Tri_ShapeID)] = m_clipTables[ST_TRI].view(); + views[details::getClipTableIndex(views::Quad_ShapeID)] = m_clipTables[ST_QUA].view(); + } + if(dimension == -1 || dimension == 3) + { + views[details::getClipTableIndex(views::Tet_ShapeID)] = m_clipTables[ST_TET].view(); + views[details::getClipTableIndex(views::Pyramid_ShapeID)] = m_clipTables[ST_PYR].view(); + views[details::getClipTableIndex(views::Wedge_ShapeID)] = m_clipTables[ST_WDG].view(); + views[details::getClipTableIndex(views::Hex_ShapeID)] = m_clipTables[ST_HEX].view(); + } + } - auto blendGroupsView = blendGroups.view(); - auto blendGroupsLenView = blendGroupsLen.view(); + /** + * \brief Iterate over zones and their respective fragments to determine sizes + * for fragments and blend groups. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + */ + void computeSizes(ClipTableViews clipTableViews, ZoneData zoneData, FragmentData fragmentData, BlendGroupBuilder builder, ClipOptions &opts, const conduit::Node &n_clip_field_values) const + { views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { using clip_value_type = typename decltype(clipFieldView)::value_type; const auto clipValue = static_cast(opts.clipValue()); + const auto selection = getSelection(opts); + + auto blendGroupsView = builder.state().m_blendGroupsView; + auto blendGroupsLenView = builder.state().m_blendGroupsLenView; m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); - clipCasesView[zoneIndex] = clipcase; + zoneData.m_clipCasesView[zoneIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); @@ -1039,7 +1139,7 @@ class ClipField } // Save the flags for the points that were used in this zone - pointsUsedView[zoneIndex] = ptused; + zoneData.m_pointsUsedView[zoneIndex] = ptused; // Count which points in the original cell are used. for(IndexType pid = P0; pid <= P7; pid++) @@ -1060,67 +1160,91 @@ class ClipField } // Save the results. - fragmentsView[zoneIndex] = thisFragments; - fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; + fragmentData.m_fragmentsView[zoneIndex] = thisFragments; + fragmentData.m_fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; // Set blend group sizes for this zone. blendGroupsView[zoneIndex] = thisBlendGroups; blendGroupsLenView[zoneIndex] = thisBlendGroupLen; }); }); + } - // ---------------------------------------------------------------------- - // - // Stage 3: Sum up fragment sizes, make some offsets. - // - // ---------------------------------------------------------------------- - IndexType finalNumZones = 0, finalConnSize = 0; - computeFragmentSizesAndOffsets(fragmentsView, fragmentOffsetsView, fragmentsSizeView, fragmentSizeOffsetsView, - finalNumZones, finalConnSize); - + /** + * \brief Compute the total number of fragments and their size as well as corresponding offsets. + * + * \param[in] fragmentsView The number of fragments in each zone. + * \param[in] fragmentOffsetsView The offset to the start of each zone's fragments. + * \param[in] fragmentsSizeView The size of each zone's fragment connectivity. + * \param[in] fragmentSizeOffsetsView The offset to the start of each zone's fragment connectivity. + * \param[out] fragmentSum The sum of the elements in \a fragmentsView. + * \param[out] fragmentNIdsSum The sum of the elements in \a fragmentsSizeView. + * + */ + void computeFragmentSizesAndOffsets(FragmentData &fragmentData) const + { +#if 1 + // If we're building for OpenMP, use sequential policies for now to avoid compiler crash. + using safe_loop_policy = typename std::conditional::loop_policy>::value, axom::execution_space::loop_policy, loop_policy>::type; + using safe_reduce_policy = typename std::conditional::reduce_policy>::value, axom::execution_space::reduce_policy, reduce_policy>::type; +#else + using safe_loop_policy = loop_policy; + using safe_reduce_policy = reduce_policy; +#endif - // ---------------------------------------------------------------------- - // - // Stage 4: Compute total blend group sizes and offsets. - // - // ---------------------------------------------------------------------- + const auto nzones = m_topologyView.numberOfZones(); - IndexType blendGroupsSize = 0, blendGroupLenSize = 0; - builder.computeBlendGroupSizes(blendGroupsSize, blendGroupLenSize); + // Sum the number of fragments. + RAJA::ReduceSum fragment_sum(0); + const auto fragmentsView = fragmentData.m_fragmentsView; + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + fragment_sum += fragmentsView[zoneIndex]; + }); + fragmentData.m_finalNumZones = fragment_sum.get(); - builder.setBlendGroupOffsets(blendOffset.view(), blendGroupOffsets.view()); - builder.computeBlendGroupOffsets(); + // Sum the fragment connectivity sizes. + RAJA::ReduceSum fragment_nids_sum(0); + const auto fragmentsSizeView = fragmentData.m_fragmentsSizeView; + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + fragment_nids_sum += fragmentsSizeView[zoneIndex]; + }); + fragmentData.m_finalConnSize = fragment_nids_sum.get(); - // ---------------------------------------------------------------------- - // - // Stage 5: Iterate over the zones/cases again and fill in the blend - // groups that get produced: blendNames, blendGroupSizes, - // blendCoeff, blendIds. These are used to produce the new points. - // - // ---------------------------------------------------------------------- - axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); - axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); + // Compute offsets + RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsView.data(), nzones), + RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones), + RAJA::operators::plus{}); - builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsSizeView.data(), nzones), + RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), + RAJA::operators::plus{}); + } + /** + * \brief Fill in the data for the blend group views. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + */ + void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const + { views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); + const auto selection = getSelection(opts); m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. - const auto clipcase = clipCasesView[zoneIndex]; + const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; // These are the points used in this zone's fragments. - const BitSet ptused = pointsUsedView[zoneIndex]; + const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; // Get the blend groups for this zone. auto groups = builder.blendGroupsForZone(zoneIndex); @@ -1202,25 +1326,18 @@ class ClipField } }); }); + } - // ---------------------------------------------------------------------- - // - // Stage 6 - Make the blend groups unique based on their blendName. - // - // ---------------------------------------------------------------------- - axom::Array uNames; - axom::Array uIndices; - axom::mir::utilities::unique(builder.blendNames(), uNames, uIndices); - builder.setUniqueNames(uNames.view(), uIndices.view()); - - // Bundle up blend data. - axom::mir::utilities::blueprint::BlendData blend = builder.makeBlendData(); + /** + * \brief Make connectivity for the clipped mesh. + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + */ + void makeConnectivity(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const + { + const auto allocatorID = axom::execution_space::allocatorID(); + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + const auto selection = getSelection(opts); - // ---------------------------------------------------------------------- - // - // Stage 7 - Make new connectivity. - // - // ---------------------------------------------------------------------- n_newTopo.reset(); n_newTopo["type"] = "unstructured"; n_newTopo["coordset"] = n_newCoordset.name(); @@ -1232,26 +1349,26 @@ class ClipField // Allocate connectivity. conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(conduitAllocatorID); - n_conn.set(conduit::DataType(connTypeID, finalConnSize)); - auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), finalConnSize); + n_conn.set(conduit::DataType(connTypeID,fragmentData.m_finalConnSize)); + auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), fragmentData.m_finalConnSize); // Allocate shapes. conduit::Node &n_shapes = n_newTopo["elements/shapes"]; n_shapes.set_allocator(conduitAllocatorID); - n_shapes.set(conduit::DataType(connTypeID, finalNumZones)); - auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), finalNumZones); + n_shapes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); + auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), fragmentData.m_finalNumZones); // Allocate sizes. conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(conduitAllocatorID); - n_sizes.set(conduit::DataType(connTypeID, finalNumZones)); - auto sizesView = axom::ArrayView(static_cast(n_sizes.data_ptr()), finalNumZones); + n_sizes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); + auto sizesView = axom::ArrayView(static_cast(n_sizes.data_ptr()), fragmentData.m_finalNumZones); // Allocate offsets. conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(conduitAllocatorID); - n_offsets.set(conduit::DataType(connTypeID, finalNumZones)); - auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), finalNumZones); + n_offsets.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); + auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), fragmentData.m_finalNumZones); // Allocate a color variable to keep track of the "color" of the fragments. conduit::Node &n_color = n_newFields[opts.colorField()]; @@ -1259,8 +1376,8 @@ class ClipField n_color["association"] = "element"; conduit::Node &n_color_values = n_color["values"]; n_color_values.set_allocator(conduitAllocatorID); - n_color_values.set(conduit::DataType::int32(finalNumZones)); - auto colorView = axom::ArrayView(static_cast(n_color_values.data_ptr()), finalNumZones); + n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); + auto colorView = axom::ArrayView(static_cast(n_color_values.data_ptr()), fragmentData.m_finalNumZones); // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. @@ -1268,7 +1385,7 @@ class ClipField m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. - if(fragmentsView[zoneIndex] == 0) + if(fragmentData.m_fragmentsView[zoneIndex] == 0) return; @@ -1279,7 +1396,7 @@ class ClipField // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index // in the final points. - const BitSet ptused = pointsUsedView[zoneIndex]; + const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; ConnectivityType point_2_new[N3 + 1]; for(unsigned char pid = N0; pid <= N3; pid++) { @@ -1307,13 +1424,13 @@ class ClipField } // This is where the output fragment connectivity start for this zone - int outputIndex = fragmentSizeOffsetsView[zoneIndex]; + int outputIndex = fragmentData.m_fragmentSizeOffsetsView[zoneIndex]; // This is where the output fragment sizes/shapes start for this zone. - int sizeIndex = fragmentOffsetsView[zoneIndex]; + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; BitSet shapesUsed = 0; // Iterate over the selected fragments and emit connectivity for them. - const auto clipcase = clipCasesView[zoneIndex]; + const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto ctView = clipTableViews[clipTableIndex]; auto it = ctView.begin(clipcase); @@ -1337,7 +1454,6 @@ class ClipField sizesView[sizeIndex] = nIdsInFragment; shapesView[sizeIndex] = shapeID; colorView[sizeIndex] = fragment[1] - COLOR0; - sizeIndex++; // Record which shape type was used. Use a bit for each shape. @@ -1356,8 +1472,8 @@ class ClipField } // Make offsets - RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), finalNumZones), - RAJA::make_span(offsetsView.data(), finalNumZones), + RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), fragmentData.m_finalNumZones), + RAJA::make_span(offsetsView.data(), fragmentData.m_finalNumZones), RAJA::operators::plus{}); // Add shape information to the connectivity. @@ -1376,150 +1492,6 @@ class ClipField n_newTopo["elements"].remove("shapes"); n_newTopo["elements/shape"] = shapeMap.begin()->first; } - - //----------------------------------------------------------------------- - // - // STAGE 8 - Make new coordset. - // - //----------------------------------------------------------------------- - makeCoordset(blend, n_coordset, n_newCoordset); - - //----------------------------------------------------------------------- - // - // STAGE 9 - Make new fields. - // - //----------------------------------------------------------------------- - axom::mir::utilities::blueprint::SliceData slice; - axom::Array sliceIndices(finalNumZones, finalNumZones, allocatorID); - auto sliceIndicesView = sliceIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - const auto start = fragmentOffsetsView[zoneIndex]; - for(int i = 0; i < fragmentsView[zoneIndex]; i++) - sliceIndicesView[start + i] = zoneIndex; - }); - slice.m_indicesView = sliceIndicesView; - makeFields(blend, slice, opts.topologyName(n_topo.name()), opts.fields(n_fields), n_fields, n_newFields); - - //----------------------------------------------------------------------- - // - // STAGE 10 - make originalElements (this will later be optional) - // - //----------------------------------------------------------------------- - if(n_fields.has_child("originalElements")) - { - // originalElements already exists. We need to map it forward. - const conduit::Node &n_orig = n_fields["originalElements"]; - const conduit::Node &n_orig_values = n_orig["values"]; - views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) - { - using value_type = typename decltype(origValuesView)::value_type; - conduit::Node &n_origElem = n_newFields["originalElements"]; - n_origElem["association"] = "element"; - n_origElem["topology"] = opts.topologyName(n_newTopo.name()); - conduit::Node &n_values = n_origElem["values"]; - n_values.set_allocator(conduitAllocatorID); - n_values.set(conduit::DataType(n_orig_values.dtype().id(), finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = origValuesView[zoneIndex]; - }); - }); - } - else - { - // Make a new node and populate originalElement. - conduit::Node &n_orig = n_newFields["originalElements"]; - n_orig["association"] = "element"; - n_orig["topology"] = opts.topologyName(n_newTopo.name()); - conduit::Node &n_values = n_orig["values"]; - n_values.set_allocator(conduitAllocatorID); - n_values.set(conduit::DataType(connTypeID, finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = zoneIndex; - }); - } - -//----------------------------------------------------------------------------------- - } // end of execute - -private: - /** - * \brief Create views for the clip tables of various shapes. - * - * \param[out] views The views array that will contain the table views. - * \param dimension The dimension the topology (so we can load a subset of tables) - */ - void createClipTableViews(ClipTableViews &views, int dimension) - { - if(dimension == -1 || dimension == 2) - { - views[details::getClipTableIndex(axom::mir::views::TriShape::id())] = m_clipTables[ST_TRI].view(); - views[details::getClipTableIndex(axom::mir::views::QuadShape::id())] = m_clipTables[ST_QUA].view(); - } - if(dimension == -1 || dimension == 3) - { - views[details::getClipTableIndex(axom::mir::views::TetShape::id())] = m_clipTables[ST_TET].view(); - views[details::getClipTableIndex(axom::mir::views::PyramidShape::id())] = m_clipTables[ST_PYR].view(); - views[details::getClipTableIndex(axom::mir::views::WedgeShape::id())] = m_clipTables[ST_WDG].view(); - views[details::getClipTableIndex(axom::mir::views::HexShape::id())] = m_clipTables[ST_HEX].view(); - } - } - - /** - * \brief Compute the total number of fragments and their size as well as corresponding offsets. - * - * \param[in] fragmentsView The number of fragments in each zone. - * \param[in] fragmentOffsetsView The offset to the start of each zone's fragments. - * \param[in] fragmentsSizeView The size of each zone's fragment connectivity. - * \param[in] fragmentSizeOffsetsView The offset to the start of each zone's fragment connectivity. - * \param[out] fragmentSum The sum of the elements in \a fragmentsView. - * \param[out] fragmentNIdsSum The sum of the elements in \a fragmentsSizeView. - * - */ - void computeFragmentSizesAndOffsets(const axom::ArrayView &fragmentsView, - const axom::ArrayView &fragmentOffsetsView, - const axom::ArrayView &fragmentsSizeView, - const axom::ArrayView &fragmentSizeOffsetsView, - IndexType &fragmentSum, - IndexType &fragmentNIdsSum) const - { -#if 1 - // If we're building for OpenMP, use sequential policies for now to avoid compiler crash. - using safe_loop_policy = typename std::conditional::loop_policy>::value, axom::execution_space::loop_policy, loop_policy>::type; - using safe_reduce_policy = typename std::conditional::reduce_policy>::value, axom::execution_space::reduce_policy, reduce_policy>::type; -#else - using safe_loop_policy = loop_policy; - using safe_reduce_policy = reduce_policy; -#endif - - const auto nzones = fragmentsView.size(); - RAJA::ReduceSum fragment_sum(0); - RAJA::ReduceSum fragment_nids_sum(0); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - fragment_sum += fragmentsView[zoneIndex]; - fragment_nids_sum += fragmentsSizeView[zoneIndex]; - }); - fragmentSum = fragment_sum.get(); - fragmentNIdsSum = fragment_nids_sum.get(); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsView.data(), nzones), - RAJA::make_span(fragmentOffsetsView.data(), nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentsSizeView.data(), nzones), - RAJA::make_span(fragmentSizeOffsetsView.data(), nzones), - RAJA::operators::plus{}); } /** @@ -1572,6 +1544,60 @@ class ClipField } } + void makeOriginalElements(FragmentData fragmentData, ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const + { + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + + const auto allocatorID = axom::execution_space::allocatorID(); + utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + + const auto nzones = m_topologyView.numberOfZones(); + + if(n_fields.has_child("originalElements")) + { + // originalElements already exists. We need to map it forward. + const conduit::Node &n_orig = n_fields["originalElements"]; + const conduit::Node &n_orig_values = n_orig["values"]; + views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) + { + using value_type = typename decltype(origValuesView)::value_type; + conduit::Node &n_origElem = n_newFields["originalElements"]; + n_origElem["association"] = "element"; + n_origElem["topology"] = opts.topologyName(n_newTopo.name()); + conduit::Node &n_values = n_origElem["values"]; + n_values.set_allocator(conduitAllocatorID); + n_values.set(conduit::DataType(n_orig_values.dtype().id(), fragmentData.m_finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), fragmentData.m_finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); + }); + } + else + { + // Make a new node and populate originalElement. + conduit::Node &n_orig = n_newFields["originalElements"]; + n_orig["association"] = "element"; + n_orig["topology"] = opts.topologyName(n_newTopo.name()); + conduit::Node &n_values = n_orig["values"]; + n_values.set_allocator(conduitAllocatorID); + n_values.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); + auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), fragmentData.m_finalNumZones); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = zoneIndex; + }); + } + } + private: TopologyView m_topologyView; CoordsetView m_coordsetView; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index baf11e97e6..ccbed49a32 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -889,9 +889,9 @@ TEST(mir_clipfield, hex) const std::string type("hexs"); braid3d_clip_test(type, "hex"); -#if defined(AXOM_USE_OPENMP) - braid3d_clip_test(type, "hex_omp"); -#endif +//#if defined(AXOM_USE_OPENMP) +// braid3d_clip_test(type, "hex_omp"); +//#endif #if defined(AXOM_USE_CUDA) braid3d_clip_test(type, "hex_cuda"); From a7d9e1e3b0951f6de96177bcb36106563e16b758 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 30 Jul 2024 15:16:44 -0700 Subject: [PATCH 119/290] code cleanup --- src/axom/mir/ClipField.hpp | 135 ++++++++++++++++++++++++------------- 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index e40a3d6225..0c88f9a916 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -570,7 +570,7 @@ class BlendGroupBuilder * \return The number of blend groups for this zone. */ AXOM_HOST_DEVICE - IndexType size() const + inline IndexType size() const { return m_state->m_blendGroupsView[m_zoneIndex]; } @@ -583,7 +583,7 @@ class BlendGroupBuilder * \param blendGroupSize The size of all of the blend groups in this zone. */ AXOM_HOST_DEVICE - void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) + inline void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) { m_state->m_blendGroupsView[m_zoneIndex] = nBlendGroups; m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; @@ -593,7 +593,7 @@ class BlendGroupBuilder * \brief Start creating a new blend group within the allocated space for this zone. */ AXOM_HOST_DEVICE - void beginGroup() + inline void beginGroup() { m_currentDataOffset = m_startOffset; } @@ -605,7 +605,7 @@ class BlendGroupBuilder * \param weight The weight that will be used for blending. */ AXOM_HOST_DEVICE - void add(IndexType id, float weight) + inline void add(IndexType id, float weight) { m_state->m_blendIdsView[m_currentDataOffset] = id; m_state->m_blendCoeffView[m_currentDataOffset] = weight; @@ -616,7 +616,7 @@ class BlendGroupBuilder * \brief End the current blend group, storing its name, size, etc. */ AXOM_HOST_DEVICE - void endGroup() + inline void endGroup() { IndexType numIds = m_currentDataOffset - m_startOffset; @@ -659,7 +659,8 @@ class BlendGroupBuilder * \note The blendGroup must have finished construction. * \return The sum of weights, which should be about 1. */ - AXOM_HOST_DEVICE float weightSum() const + AXOM_HOST_DEVICE + inline float weightSum() const { const auto numIds = m_state->m_blendGroupSizesView[m_blendGroupId]; const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; @@ -674,7 +675,7 @@ class BlendGroupBuilder * \return The name of the current blend group. */ AXOM_HOST_DEVICE - KeyType name() const + inline KeyType name() const { return m_state->m_blendNamesView[m_blendGroupId]; } @@ -684,7 +685,7 @@ class BlendGroupBuilder * \return The unique index of the current blend group. */ AXOM_HOST_DEVICE - IndexType uniqueBlendGroupIndex() const + inline IndexType uniqueBlendGroupIndex() const { return axom::mir::utilities::bsearch(name(), m_state->m_blendUniqueNamesView); } @@ -693,13 +694,13 @@ class BlendGroupBuilder * \brief Advance to the next blend group. */ AXOM_HOST_DEVICE - void operator++() { m_blendGroupId++; } + inline void operator++() { m_blendGroupId++; } /** * \brief Advance to the next blend group. */ AXOM_HOST_DEVICE - void operator++(int) { m_blendGroupId++; } + inline void operator++(int) { m_blendGroupId++; } #if !defined(AXOM_DEVICE_CODE) /** @@ -948,8 +949,9 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, zoneData, fragmentData, builder, opts, n_clip_field_values); - computeFragmentSizesAndOffsets(fragmentData); + computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, n_clip_field_values); + computeFragmentSizes(fragmentData); + computeFragmentOffsets(fragmentData); IndexType blendGroupsSize = 0, blendGroupLenSize = 0; builder.computeBlendGroupSizes(blendGroupsSize, blendGroupLenSize); @@ -1022,9 +1024,9 @@ class ClipField { int selection = 0; if(opts.inside()) - axom::utilities::setBit(selection, 0); + axom::utilities::setBitOn(selection, 0); if(opts.outside()) - axom::utilities::setBit(selection, 1); + axom::utilities::setBitOn(selection, 1); SLIC_ASSERT(selection > 0); return selection; } @@ -1055,9 +1057,16 @@ class ClipField * \brief Iterate over zones and their respective fragments to determine sizes * for fragments and blend groups. * - * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + * \param[in] clipTableViews An object that holds views of the clipping table data. + * \param[in] builder This object holds views to blend group data and helps with building/access. + * \param[in] zoneData This object holds views to per-zone data. + * \param[in] fragmentData This object holds views to per-fragment data. + * \param[inout] opts Clipping options. + * \param[in] n_clip_field_values The node that contains clipping field values. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void computeSizes(ClipTableViews clipTableViews, ZoneData zoneData, FragmentData fragmentData, BlendGroupBuilder builder, ClipOptions &opts, const conduit::Node &n_clip_field_values) const + void computeSizes(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const { views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { @@ -1171,31 +1180,16 @@ class ClipField } /** - * \brief Compute the total number of fragments and their size as well as corresponding offsets. - * - * \param[in] fragmentsView The number of fragments in each zone. - * \param[in] fragmentOffsetsView The offset to the start of each zone's fragments. - * \param[in] fragmentsSizeView The size of each zone's fragment connectivity. - * \param[in] fragmentSizeOffsetsView The offset to the start of each zone's fragment connectivity. - * \param[out] fragmentSum The sum of the elements in \a fragmentsView. - * \param[out] fragmentNIdsSum The sum of the elements in \a fragmentsSizeView. + * \brief Compute the total number of fragments and their size. * + * \param[inout] fragmentData The object that contains data about the zone fragments. */ - void computeFragmentSizesAndOffsets(FragmentData &fragmentData) const + void computeFragmentSizes(FragmentData &fragmentData) const { -#if 1 - // If we're building for OpenMP, use sequential policies for now to avoid compiler crash. - using safe_loop_policy = typename std::conditional::loop_policy>::value, axom::execution_space::loop_policy, loop_policy>::type; - using safe_reduce_policy = typename std::conditional::reduce_policy>::value, axom::execution_space::reduce_policy, reduce_policy>::type; -#else - using safe_loop_policy = loop_policy; - using safe_reduce_policy = reduce_policy; -#endif - const auto nzones = m_topologyView.numberOfZones(); // Sum the number of fragments. - RAJA::ReduceSum fragment_sum(0); + RAJA::ReduceSum fragment_sum(0); const auto fragmentsView = fragmentData.m_fragmentsView; axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) { @@ -1204,28 +1198,54 @@ class ClipField fragmentData.m_finalNumZones = fragment_sum.get(); // Sum the fragment connectivity sizes. - RAJA::ReduceSum fragment_nids_sum(0); + RAJA::ReduceSum fragment_nids_sum(0); const auto fragmentsSizeView = fragmentData.m_fragmentsSizeView; axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) { fragment_nids_sum += fragmentsSizeView[zoneIndex]; }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); + } + + /** + * \brief Compute fragment offsets. + * + * \param[inout] fragmentData The object that contains data about the zone fragments. + */ + void computeFragmentOffsets(FragmentData &fragmentData) const + { + const auto nzones = m_topologyView.numberOfZones(); +#if 0 + // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. - // Compute offsets - RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsView.data(), nzones), - RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones), - RAJA::operators::plus{}); + // Copy sizes into offsets + axom::copy(fragmentData.m_fragmentOffsetsView.data(), fragmentData.m_fragmentsView.data(), nzones * sizeof(IndexType)); + axom::copy(fragmentData.m_fragmentSizeOffsetsView.data(), fragmentData.m_fragmentsSizeView.data(), nzones * sizeof(IndexType)); + // Make offsets in place. + RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones)); + RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones)); - RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsSizeView.data(), nzones), - RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), - RAJA::operators::plus{}); +#else + RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsView.data(), nzones), + RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones), + RAJA::operators::plus{}); + + RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsSizeView.data(), nzones), + RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), + RAJA::operators::plus{}); +#endif } /** * \brief Fill in the data for the blend group views. * - * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + * \param[in] clipTableViews An object that holds views of the clipping table data. + * \param[in] builder This object holds views to blend group data and helps with building/access. + * \param[in] zoneData This object holds views to per-zone data. + * \param[inout] opts Clipping options. + * \param[in] n_clip_field_values The node that contains clipping field values. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const { @@ -1330,12 +1350,22 @@ class ClipField /** * \brief Make connectivity for the clipped mesh. - * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). + * + * \param[in] clipTableViews An object that holds views of the clipping table data. + * \param[in] builder This object holds views to blend group data and helps with building/access. + * \param[in] zoneData This object holds views to per-zone data. + * \param[in] fragmentData This object holds views to per-fragment data. + * \param[inout] opts Clipping options. + * \param[out] n_newTopo The node that will contain the new topology. + * \param[out] n_newCoordset The node that will contain the new coordset. + * \param[out] n_newFields The node that will contain the new fields. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeConnectivity(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { - const auto allocatorID = axom::execution_space::allocatorID(); constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + const auto allocatorID = axom::execution_space::allocatorID(); const auto selection = getSelection(opts); n_newTopo.reset(); @@ -1544,7 +1574,18 @@ class ClipField } } - void makeOriginalElements(FragmentData fragmentData, ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const + /** + * \brief Make an originalElements field so we can know each output zone's original zone number in the input mesh. + * + * \param[in] fragmentData This object holds views to per-fragment data. + * \param[in] opts Clipping options. + * \param[in[ n_fields The node that contains the input mesh's fields. + * \param[out] n_newTopo The node that will contain the new topology. + * \param[out] n_newFields The node that will contain the new fields. + * + * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. + */ + void makeOriginalElements(FragmentData fragmentData, const ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const { constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; From dd75375d2e55a409b26cbaf6c5473ed2aa1a9826 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 30 Jul 2024 17:40:24 -0700 Subject: [PATCH 120/290] Attempts to work around OMP problems. --- src/axom/mir/ClipField.hpp | 111 ++++++++++++++---- .../UnstructuredTopologyMixedShapeView.hpp | 2 +- .../UnstructuredTopologyPolyhedralView.hpp | 2 +- .../UnstructuredTopologySingleShapeView.hpp | 8 +- 4 files changed, 94 insertions(+), 29 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 0c88f9a916..c881be4538 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -22,39 +22,66 @@ #include #include -namespace axom -{ -namespace mir -{ -namespace clipping -{ -namespace details -{ +#define AXOM_CLIPFIELD_SCAN_WORKAROUND -#if 0 +#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) // NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers // so we can write serial versions for when RAJA is not enabled. namespace axom { -namespace operators +template +struct scans { -template -using plus = RAJA::operators::plus; -} // end namespace operators -} // end namespace axom + inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) + { + using loop_policy = typename axom::execution_space::loop_policy; + assert(input.size() == output.size()); + RAJA::exclusive_scan(RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size())); + } +}; -template -void exclusive_scan(ArrayViewType &input, ArrayViewType &output, OperatorType &op) +// Workaround for OpenMP +template +struct scans { - assert(input.size() == output.size()); - using loop_policy = typename axom::execution_space::loop_policy; - RAJA::exclusive_scan(RAJA::make_span(input.data(), input.size()), - RAJA::make_span(output.data(), output.size()), - op); + inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) + { + assert(input.size() == output.size()); + // RAJA is not working in OpenMP. Do serial. + output[0] = 0; + for(IndexType i = 1; i < input.size(); i++) + { + output[i] = output[i - 1] + input[i - 1]; + } + } +}; + +template +inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) +{ + scans s; + s.exclusive_scan(input, output); } +} // end namespace axom #endif +namespace axom +{ +namespace mir +{ +namespace clipping +{ +namespace details +{ + +/** + * \brief Turns a Conduit "shape_map" node into an easier STL-representation. + * + * \param n_shape_map The node that contains the shape_map. + * \return An STL-representation of the shape_map. + */ std::map shapeMap_NameValue(const conduit::Node &n_shape_map) { @@ -66,6 +93,12 @@ shapeMap_NameValue(const conduit::Node &n_shape_map) return sm; } +/** + * \brief Turns a Conduit "shape_map" node into an easier STL-representation. + * + * \param n_shape_map The node that contains the shape_map. + * \return An STL-representation of the shape_map. + */ std::map shapeMap_ValueName(const conduit::Node &n_shape_map) { @@ -138,6 +171,11 @@ shapeMap_FromFlags(std::uint64_t shapes) return sm; } +/** + * \brief Returns a clip table index for the input shapeId. + * \param shapeId A shapeID (e.g. Tet_ShapeID) + * \return The clip table index for the shape. + */ AXOM_HOST_DEVICE int getClipTableIndex(int shapeId) { @@ -181,6 +219,15 @@ bool shapeIsSelected(unsigned char color, int selection) (color1Selected(selection) && color == COLOR1); } +/** + * \brief Compute a parametric value for where clipValues occurs in [d0,d1], clamped to [0,1]. + * + * \param d0 The first data value. + * \param d1 The second data value. + * \param clipValue The data value we're looking for. + * + * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. + */ AXOM_HOST_DEVICE inline float computeWeight(float d0, float d1, float clipValue) { @@ -515,6 +562,10 @@ class BlendGroupBuilder */ void computeBlendGroupOffsets() { +#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) + axom::exclusive_scan(m_state.m_blendGroupsLenView, m_state.m_blendOffsetView); + axom::exclusive_scan(m_state.m_blendGroupsView, m_state.m_blendGroupOffsetsView); +#else using loop_policy = typename axom::execution_space::loop_policy; // Fill in offsets via scan. RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsLenView.data(), m_state.m_nzones), @@ -523,6 +574,7 @@ class BlendGroupBuilder RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsView.data(), m_state.m_nzones), RAJA::make_span(m_state.m_blendGroupOffsetsView.data(), m_state.m_nzones), RAJA::operators::plus{}); +#endif } /** @@ -1214,18 +1266,26 @@ class ClipField */ void computeFragmentOffsets(FragmentData &fragmentData) const { - const auto nzones = m_topologyView.numberOfZones(); +#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) + // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. + axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); + axom::exclusive_scan(fragmentData.m_fragmentsSizeView, fragmentData.m_fragmentSizeOffsetsView); +#endif #if 0 // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. // Copy sizes into offsets + const auto nzones = m_topologyView.numberOfZones(); axom::copy(fragmentData.m_fragmentOffsetsView.data(), fragmentData.m_fragmentsView.data(), nzones * sizeof(IndexType)); axom::copy(fragmentData.m_fragmentSizeOffsetsView.data(), fragmentData.m_fragmentsSizeView.data(), nzones * sizeof(IndexType)); // Make offsets in place. RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones)); RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones)); -#else +#endif +#if 0 + // Original code. + const auto nzones = m_topologyView.numberOfZones(); RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsView.data(), nzones), RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones), RAJA::operators::plus{}); @@ -1502,10 +1562,13 @@ class ClipField } // Make offsets +#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) + axom::exclusive_scan(sizesView, offsetsView); +#else RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), fragmentData.m_finalNumZones), RAJA::make_span(offsetsView.data(), fragmentData.m_finalNumZones), RAJA::operators::plus{}); - +#endif // Add shape information to the connectivity. const auto shapesUsed = shapesUsed_reduce.get(); const auto shapeMap = details::shapeMap_FromFlags(shapesUsed); diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 163f0e60e6..a2f5f2bc6f 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -176,7 +176,7 @@ class UnstructuredTopologyMixedShapeView * \param func The function/lambda that will be executed for each zone in the mesh. */ template - void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const { const auto nSelectedZones = selectedIdsView.size(); diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 3f107226d3..44be4b6cf3 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -192,7 +192,7 @@ class UnstructuredTopologyPolyhedralView } template - void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const { const auto nSelectedZones = selectedIdsView.size(); diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index ae4deae10c..cd6658dd4d 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -113,21 +113,23 @@ class UnstructuredTopologySingleShapeView * \tparam ExecSpace The execution space for the function body. * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. * + * \param selectedIdsView A view containing selected zone ids. * \param func The function/lambda that will be executed for each zone in the mesh. */ template - void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const { const auto nSelectedZones = selectedIdsView.size(); ConnectivityView connectivityView(m_connectivity); + const ViewType localSelectedIdsView(selectedIdsView); if constexpr (ShapeType::is_variable_size()) { ConnectivityView sizesView(m_sizes); ConnectivityView offsetsView(m_offsets); axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { - const auto zoneIndex = selectedIdsView[selectIndex]; + const auto zoneIndex = localSelectedIdsView[selectIndex]; const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); const ShapeType shape(shapeDataView); func(zoneIndex, shape); @@ -137,7 +139,7 @@ class UnstructuredTopologySingleShapeView { axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { - const auto zoneIndex = selectedIdsView[selectIndex]; + const auto zoneIndex = localSelectedIdsView[selectIndex]; const ConnectivityView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); const ShapeType shape(shapeData); func(zoneIndex, shape); From 41dd065c7d009bb000508613c14173f217bf375b Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 30 Jul 2024 18:15:23 -0700 Subject: [PATCH 121/290] Debug prints, etc --- src/axom/mir/ClipField.hpp | 29 +++++++++++++++++++++++++++- src/axom/mir/tests/mir_clipfield.cpp | 7 +++++++ src/axom/mir/utilities.hpp | 1 - 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index c881be4538..97c82ad68b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -41,6 +41,7 @@ struct scans } }; +#if defined(AXOM_USE_OPENMP) // Workaround for OpenMP template struct scans @@ -56,6 +57,7 @@ struct scans } } }; +#endif template inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) @@ -1128,7 +1130,7 @@ class ClipField auto blendGroupsView = builder.state().m_blendGroupsView; auto blendGroupsLenView = builder.state().m_blendGroupsLenView; - +std::cout << "computeSizes: start\n"; m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. @@ -1229,6 +1231,7 @@ class ClipField blendGroupsLenView[zoneIndex] = thisBlendGroupLen; }); }); +std::cout << "computeSizes: end\n"; } /** @@ -1238,6 +1241,8 @@ class ClipField */ void computeFragmentSizes(FragmentData &fragmentData) const { +std::cout << "computeFragmentSizes: start\n"; + const auto nzones = m_topologyView.numberOfZones(); // Sum the number of fragments. @@ -1257,6 +1262,9 @@ class ClipField fragment_nids_sum += fragmentsSizeView[zoneIndex]; }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); + +std::cout << "computeFragmentSizes: end\n"; + } /** @@ -1266,6 +1274,8 @@ class ClipField */ void computeFragmentOffsets(FragmentData &fragmentData) const { +std::cout << "computeFragmentOffsets: start\n"; + #if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); @@ -1294,6 +1304,7 @@ class ClipField RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), RAJA::operators::plus{}); #endif +std::cout << "computeFragmentOffsets: end\n"; } /** @@ -1309,6 +1320,8 @@ class ClipField */ void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const { +std::cout << "makeBlendGroups: start\n"; + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); @@ -1406,6 +1419,8 @@ class ClipField } }); }); +std::cout << "makeBlendGroups: end\n"; + } /** @@ -1424,6 +1439,8 @@ class ClipField */ void makeConnectivity(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { +std::cout << "makeConnectivity: start\n"; + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; const auto allocatorID = axom::execution_space::allocatorID(); const auto selection = getSelection(opts); @@ -1585,6 +1602,8 @@ class ClipField n_newTopo["elements"].remove("shapes"); n_newTopo["elements/shape"] = shapeMap.begin()->first; } +std::cout << "makeConnectivity: end\n"; + } /** @@ -1596,9 +1615,11 @@ class ClipField */ void makeCoordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { +std::cout << "makeCoordset: start\n"; axom::mir::utilities::blueprint::CoordsetBlender cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); +std::cout << "makeCoordset: end\n"; } /** @@ -1618,6 +1639,7 @@ class ClipField const conduit::Node &n_fields, conduit::Node &n_out_fields) const { +std::cout << "makeFields: start\n"; for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { const conduit::Node &n_field = n_fields.fetch_existing(it->first); @@ -1635,6 +1657,7 @@ class ClipField n_out_fields[it->second]["topology"] = topologyName; } } +std::cout << "makeFields: end\n"; } /** @@ -1650,6 +1673,8 @@ class ClipField */ void makeOriginalElements(FragmentData fragmentData, const ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const { +std::cout << "makeOriginalElements: start\n"; + constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; const auto allocatorID = axom::execution_space::allocatorID(); @@ -1700,6 +1725,8 @@ class ClipField valuesView[sizeIndex + i] = zoneIndex; }); } +std::cout << "makeOriginalElements: end\n"; + } private: diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index ccbed49a32..db802c815d 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -531,12 +531,14 @@ void make_one_wdg(conduit::Node &hostMesh) template void test_one_shape(conduit::Node &hostMesh, const std::string &name) { +std::cout << "test_one_shape: 0\n"; using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; // Copy mesh to device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +std::cout << "test_one_shape: 1\n"; // Make views for the device mesh. conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); @@ -546,10 +548,12 @@ void test_one_shape(conduit::Node &hostMesh, const std::string &name) axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); axom::ArrayView zView(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); CoordsetView coordsetView(xView, yView, zView); +std::cout << "test_one_shape: 2\n"; conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/topo/elements/connectivity"); axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); TopoView topoView(connView); +std::cout << "test_one_shape: 3\n"; // Clip the data conduit::Node deviceClipMesh, options; @@ -559,14 +563,17 @@ void test_one_shape(conduit::Node &hostMesh, const std::string &name) options["inside"] = 1; options["outside"] = 1; clipper.execute(deviceMesh, options, deviceClipMesh); +std::cout << "test_one_shape: 4\n"; // Copy device->host conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); +std::cout << "test_one_shape: 5\n"; // Save data. conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "yaml"); +std::cout << "test_one_shape: 6\n"; } TEST(mir_clipfield, onetet) diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 8c3f94cac2..c930d3d944 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -210,7 +210,6 @@ template AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) { -if(n >= 14) std::cout << "make_name_n: " << n << std::endl; assert(n <= MaxValues); if(n == 2) return make_name_2(values[0], values[1]); From e817b07bb741191f3e8cdc3dea1635748b270909 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 15:33:22 -0700 Subject: [PATCH 122/290] Changed conduit memory allocation. Fix view usage in one place. --- src/axom/mir/ClipField.hpp | 12 +++---- src/axom/mir/CoordsetBlender.hpp | 3 +- src/axom/mir/FieldBlender.hpp | 3 +- src/axom/mir/FieldSlicer.hpp | 3 +- src/axom/mir/blueprint_utilities.cpp | 3 +- src/axom/mir/blueprint_utilities.hpp | 35 +++++++++++++------ .../mir/tests/mir_blueprint_utilities.cpp | 3 +- 7 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 97c82ad68b..b0a05b5984 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -537,10 +537,12 @@ class BlendGroupBuilder using reduce_policy = typename axom::execution_space::reduce_policy; RAJA::ReduceSum blendGroups_sum(0); RAJA::ReduceSum blendGroupLen_sum(0); + const auto localBlendGroupsView = m_state.m_blendGroupsView; + const auto localBlendGroupsLenView = m_state.m_blendGroupsLenView; axom::for_all(m_state.m_nzones, AXOM_LAMBDA(auto zoneIndex) { - blendGroups_sum += m_state.m_blendGroupsView[zoneIndex]; - blendGroupLen_sum += m_state.m_blendGroupsLenView[zoneIndex]; + blendGroups_sum += localBlendGroupsView[zoneIndex]; + blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; }); bgSum = blendGroups_sum.get(); bgLenSum = blendGroupLen_sum.get(); @@ -1442,7 +1444,6 @@ std::cout << "makeBlendGroups: end\n"; std::cout << "makeConnectivity: start\n"; constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; - const auto allocatorID = axom::execution_space::allocatorID(); const auto selection = getSelection(opts); n_newTopo.reset(); @@ -1450,7 +1451,7 @@ std::cout << "makeConnectivity: start\n"; n_newTopo["coordset"] = n_newCoordset.name(); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); // Allocate connectivity. @@ -1677,8 +1678,7 @@ std::cout << "makeOriginalElements: start\n"; constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; - const auto allocatorID = axom::execution_space::allocatorID(); - utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); const auto nzones = m_topologyView.numberOfZones(); diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 4d1af7f58f..f8613aa1fc 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -57,13 +57,12 @@ class CoordsetBlender using PointType = typename CoordsetViewType::PointType; using VectorType = axom::primal::Vector; - const auto allocatorID = axom::execution_space::allocatorID(); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); const auto nComponents = axes.size(); SLIC_ASSERT(PointType::DIMENSION == nComponents); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a; n_output.reset(); n_output["type"] = "explicit"; diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 18f15f72ff..2b2e21699d 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -123,13 +123,12 @@ class FieldBlender */ void blendSingleComponent(const BlendData &blend, const conduit::Node &n_values, conduit::Node &n_output_values) const { - const auto allocatorID = axom::execution_space::allocatorID(); // We're allowing selectedIndicesView to be used to select specific blend // groups. If the user did not provide that, use all blend groups. const auto outputSize = SelectionPolicy::size(blend); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a; n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 5f8d1df600..4b84370f99 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -79,11 +79,10 @@ class FieldSlicer */ void sliceSingleComponent(const SliceData &slice, const conduit::Node &n_values, conduit::Node &n_output_values) const { - const auto allocatorID = axom::execution_space::allocatorID(); const auto outputSize = slice.m_indicesView.size(); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + utilities::blueprint::ConduitAllocateThroughAxom c2a; n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index 72ad249f88..e020da75af 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -13,7 +13,7 @@ namespace utilities { namespace blueprint { - +#if 0 conduit::index_t ConduitAllocateThroughAxom::conduitAllocatorID = -1; int ConduitAllocateThroughAxom::axomAllocatorID = 0; @@ -51,6 +51,7 @@ void ConduitAllocateThroughAxom::internal_free(void *ptr) //std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; axom::deallocate(ptr); } +#endif } // end namespace blueprint } // end namespace utilities diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 5dd10bd696..ed3ae478c4 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -77,20 +77,34 @@ struct cpp2conduit { using type = conduit::float64; static con * permits Conduit to allocate through Axom's UMPIRE logic. * */ +template class ConduitAllocateThroughAxom { public: - ConduitAllocateThroughAxom(int _allocatorID); - ~ConduitAllocateThroughAxom(); - - conduit::index_t getConduitAllocatorID() const; + static conduit::index_t getConduitAllocatorID() + { + static conduit::index_t conduitAllocatorID = -1; + if(conduitAllocatorID == -1) + { + conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); + } + return conduitAllocatorID; + } private: - static void *internal_allocate(size_t items, size_t item_size); - static void internal_free(void *ptr); + static void *internal_allocate(size_t items, size_t item_size) + { + const auto axomAllocatorID = axom::execution_space::allocatorID(); + void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); + std::cout << axom::execution_space::name() << ": Allocated for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; + return ptr; + } - static conduit::index_t conduitAllocatorID; - static int axomAllocatorID; + static void internal_free(void *ptr) + { + std::cout << axom::execution_space::name() << ": Dellocating for Conduit via axom: ptr=" << ptr << std::endl; + axom::deallocate(ptr); + } }; //------------------------------------------------------------------------------ @@ -112,8 +126,7 @@ void to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const std::string &topoName, conduit::Node &mesh) { const std::string type = topo.fetch_existing("type").as_string(); - const auto allocatorID = axom::execution_space::allocatorID(); - ConduitAllocateThroughAxom c2a(axom::execution_space::allocatorID()); + ConduitAllocateThroughAxom c2a; mesh["coordsets"][coordset.name()].set_external(coordset); conduit::Node &newtopo = mesh["topologies"][topoName]; @@ -177,7 +190,7 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const template void copy(conduit::Node &dest, const conduit::Node &src) { - ConduitAllocateThroughAxom c2a(axom::execution_space::allocatorID()); + ConduitAllocateThroughAxom c2a; if(src.number_of_children() > 0) { diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 88d3633720..e1e783231b 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -41,8 +41,7 @@ template void test_conduit_allocate() { - const auto allocatorID = axom::execution_space::allocatorID(); - axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a(allocatorID); + axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a; EXPECT_TRUE(c2a.getConduitAllocatorID() > 0); constexpr int nValues = 100; From 18f302628b90c9dd22c2c81a8532477e193f420b Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 17:29:22 -0700 Subject: [PATCH 123/290] Debugging --- src/axom/mir/ClipField.hpp | 2 +- src/axom/mir/CoordsetBlender.hpp | 11 ++-- src/axom/mir/FieldBlender.hpp | 11 ++-- src/axom/mir/FieldSlicer.hpp | 3 +- src/axom/mir/blueprint_utilities.hpp | 4 +- src/axom/mir/tests/mir_clipfield.cpp | 86 ++++++++-------------------- 6 files changed, 40 insertions(+), 77 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index b0a05b5984..ad8fbdb8c3 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -397,7 +397,7 @@ class ClipOptions { // No options were specified. Allow all fields with same topology as clipField. const conduit::Node &n_clipField = n_fields.fetch_existing(clipField()); - std::string topoName = n_clipField.fetch_existing("topology").as_string(); + const std::string topoName = n_clipField.fetch_existing("topology").as_string(); for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { if(topoName == n_fields[i].fetch_existing("topology").as_string()) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index f8613aa1fc..dc839bdb75 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -82,19 +82,20 @@ class CoordsetBlender } // Iterate over each blend group. + const BlendData blendDevice(blend); axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) { // Get the blend group index we want. - const auto selectedIndex = SelectionPolicy::selectedIndex(blend, bgid); - const auto start = blend.m_blendGroupStartView[selectedIndex]; - const auto end = start + blend.m_blendGroupSizesView[selectedIndex]; + const auto selectedIndex = SelectionPolicy::selectedIndex(blendDevice, bgid); + const auto start = blendDevice.m_blendGroupStartView[selectedIndex]; + const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; // Blend points for this blend group. VectorType blended{}; for(IndexType i = start; i < end; i++) { - const auto index = blend.m_blendIdsView[i]; - const auto weight = blend.m_blendCoeffView[i]; + const auto index = blendDevice.m_blendIdsView[i]; + const auto weight = blendDevice.m_blendCoeffView[i]; blended += (VectorType(view[index]) * static_cast(weight)); } diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 2b2e21699d..c25771cf87 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -137,18 +137,19 @@ class FieldBlender using value_type = typename decltype(compView)::value_type; using accum_type = typename axom::mir::utilities::accumulation_traits::type; + const BlendData deviceBlend(blend); axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) { // Get the index we want. - const auto selectedIndex = SelectionPolicy::selectedIndex(blend, bgid); - const auto start = blend.m_blendGroupStartView[selectedIndex]; - const auto end = start + blend.m_blendGroupSizesView[selectedIndex]; + const auto selectedIndex = SelectionPolicy::selectedIndex(deviceBlend, bgid); + const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; + const auto end = start + deviceBlend.m_blendGroupSizesView[selectedIndex]; accum_type blended = 0; for(IndexType i = start; i < end; i++) { - const auto index = blend.m_blendIdsView[i]; - const auto weight = blend.m_blendCoeffView[i]; + const auto index = deviceBlend.m_blendIdsView[i]; + const auto weight = deviceBlend.m_blendCoeffView[i]; blended += static_cast(compView[index]) * weight; } outView[bgid] = static_cast(blended); diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 4b84370f99..96d0adcc15 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -88,9 +88,10 @@ class FieldSlicer views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto valuesView, auto outputView) { + const SliceData deviceSlice(slice); axom::for_all(outputSize, AXOM_LAMBDA(auto index) { - outputView[index] = valuesView[slice.m_indicesView[index]]; + outputView[index] = valuesView[deviceSlice.m_indicesView[index]]; }); }); } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index ed3ae478c4..251150cb9c 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -96,13 +96,13 @@ class ConduitAllocateThroughAxom { const auto axomAllocatorID = axom::execution_space::allocatorID(); void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); - std::cout << axom::execution_space::name() << ": Allocated for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; + //std::cout << axom::execution_space::name() << ": Allocated for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; return ptr; } static void internal_free(void *ptr) { - std::cout << axom::execution_space::name() << ": Dellocating for Conduit via axom: ptr=" << ptr << std::endl; + //std::cout << axom::execution_space::name() << ": Dellocating for Conduit via axom: ptr=" << ptr << std::endl; axom::deallocate(ptr); } }; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index db802c815d..5e8204e01d 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -812,102 +812,62 @@ void braid3d_clip_test(const std::string &type, const std::string &name) conduit_save_vtk(hostClipMesh, name + ".vtk"); } -TEST(mir_clipfield, uniform2d) +/// Execute the braid3d test for a single shape on multiple ExecSpaces +template +void braid3d_clip_test_exec(const std::string &type, const std::string &name) { - braid2d_clip_test("uniform", "uniform2d"); + braid3d_clip_test(type, name); -//#if defined(AXOM_USE_OPENMP) -// braid2d_clip_test("uniform", "uniform2d_omp"); -//#endif +#if defined(AXOM_USE_OPENMP) + braid3d_clip_test(type, name + "_omp"); +#endif #if defined(AXOM_USE_CUDA) - braid2d_clip_test("uniform", "uniform2d_cuda"); + braid3d_clip_test(type, name + "_cuda"); #endif #if defined(AXOM_USE_HIP) - braid2d_clip_test("uniform", "uniform2d_hip"); + braid3d_clip_test(type, name + "_hip"); #endif } -TEST(mir_clipfield, tet) +TEST(mir_clipfield, uniform2d) { - using ShapeType = axom::mir::views::TetShape; - - const std::string type("tets"); - braid3d_clip_test(type, "tet"); + braid2d_clip_test("uniform", "uniform2d"); //#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "tet_omp"); +// braid2d_clip_test("uniform", "uniform2d_omp"); //#endif #if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "tet_cuda"); + braid2d_clip_test("uniform", "uniform2d_cuda"); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "tet_hip"); + braid2d_clip_test("uniform", "uniform2d_hip"); #endif } -TEST(mir_clipfield, pyramid) +TEST(mir_clipfield, tet) { - using ShapeType = axom::mir::views::PyramidShape; - - const std::string type("pyramids"); - braid3d_clip_test(type, "pyr"); - -//#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "pyr_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "pyr_cuda"); -#endif + braid3d_clip_test_exec>("tets", "tet"); +} -#if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "pyr_hip"); -#endif +TEST(mir_clipfield, pyramid) +{ + braid3d_clip_test_exec>("pyramids", "pyr"); } TEST(mir_clipfield, wedge) { - using ShapeType = axom::mir::views::WedgeShape; - - const std::string type("wedges"); - braid3d_clip_test(type, "wdg"); - -//#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "wdg_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "wdg_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "wdg_hip"); -#endif + braid3d_clip_test_exec>("wedges", "wdg"); } TEST(mir_clipfield, hex) { - using ShapeType = axom::mir::views::HexShape; - - const std::string type("hexs"); - braid3d_clip_test(type, "hex"); - -//#if defined(AXOM_USE_OPENMP) -// braid3d_clip_test(type, "hex_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - braid3d_clip_test(type, "hex_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - braid3d_clip_test(type, "hex_hip"); -#endif + braid3d_clip_test_exec>("hexs", "hex"); } + //------------------------------------------------------------------------------ int main(int argc, char* argv[]) From 56341412f52f18ec5566dfa9fdf50115d64da18f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 18:39:10 -0700 Subject: [PATCH 124/290] Fix for HIP --- src/axom/mir/ClipField.hpp | 33 +------ src/axom/mir/EquiZAlgorithm.cpp | 4 + .../mir/tests/mir_blueprint_utilities.cpp | 11 ++- src/axom/mir/tests/mir_clipfield.cpp | 98 +++++-------------- .../UnstructuredTopologySingleShapeView.hpp | 38 +++---- 5 files changed, 61 insertions(+), 123 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index ad8fbdb8c3..c661c373c8 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -425,14 +425,16 @@ class ClipOptions { using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; - using value_type = typename decltype(zonesView)::value_type; // It probably does not make sense to request more zones than we have in the mesh. SLIC_ASSERT(zonesView.size() <= m_nzones); m_selectedZones = axom::Array(zonesView.size(), zonesView.size(), allocatorID); auto szView = m_selectedZones.view(); - axom::copy(szView.data(), zonesView.data(), zonesView.size() * sizeof(value_type)); + axom::for_all(szView.size(), AXOM_LAMBDA(auto index) + { + szView[index] = zonesView[index]; + }); // Check that the selected zone values are in range. const auto nzones = m_nzones; @@ -1132,7 +1134,7 @@ class ClipField auto blendGroupsView = builder.state().m_blendGroupsView; auto blendGroupsLenView = builder.state().m_blendGroupsLenView; -std::cout << "computeSizes: start\n"; + m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. @@ -1233,7 +1235,6 @@ std::cout << "computeSizes: start\n"; blendGroupsLenView[zoneIndex] = thisBlendGroupLen; }); }); -std::cout << "computeSizes: end\n"; } /** @@ -1243,8 +1244,6 @@ std::cout << "computeSizes: end\n"; */ void computeFragmentSizes(FragmentData &fragmentData) const { -std::cout << "computeFragmentSizes: start\n"; - const auto nzones = m_topologyView.numberOfZones(); // Sum the number of fragments. @@ -1264,9 +1263,6 @@ std::cout << "computeFragmentSizes: start\n"; fragment_nids_sum += fragmentsSizeView[zoneIndex]; }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); - -std::cout << "computeFragmentSizes: end\n"; - } /** @@ -1276,8 +1272,6 @@ std::cout << "computeFragmentSizes: end\n"; */ void computeFragmentOffsets(FragmentData &fragmentData) const { -std::cout << "computeFragmentOffsets: start\n"; - #if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); @@ -1306,7 +1300,6 @@ std::cout << "computeFragmentOffsets: start\n"; RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), RAJA::operators::plus{}); #endif -std::cout << "computeFragmentOffsets: end\n"; } /** @@ -1322,8 +1315,6 @@ std::cout << "computeFragmentOffsets: end\n"; */ void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const { -std::cout << "makeBlendGroups: start\n"; - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); @@ -1421,8 +1412,6 @@ std::cout << "makeBlendGroups: start\n"; } }); }); -std::cout << "makeBlendGroups: end\n"; - } /** @@ -1441,8 +1430,6 @@ std::cout << "makeBlendGroups: end\n"; */ void makeConnectivity(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { -std::cout << "makeConnectivity: start\n"; - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; const auto selection = getSelection(opts); @@ -1603,8 +1590,6 @@ std::cout << "makeConnectivity: start\n"; n_newTopo["elements"].remove("shapes"); n_newTopo["elements/shape"] = shapeMap.begin()->first; } -std::cout << "makeConnectivity: end\n"; - } /** @@ -1616,11 +1601,9 @@ std::cout << "makeConnectivity: end\n"; */ void makeCoordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { -std::cout << "makeCoordset: start\n"; axom::mir::utilities::blueprint::CoordsetBlender cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); -std::cout << "makeCoordset: end\n"; } /** @@ -1640,7 +1623,6 @@ std::cout << "makeCoordset: end\n"; const conduit::Node &n_fields, conduit::Node &n_out_fields) const { -std::cout << "makeFields: start\n"; for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { const conduit::Node &n_field = n_fields.fetch_existing(it->first); @@ -1658,7 +1640,6 @@ std::cout << "makeFields: start\n"; n_out_fields[it->second]["topology"] = topologyName; } } -std::cout << "makeFields: end\n"; } /** @@ -1674,8 +1655,6 @@ std::cout << "makeFields: end\n"; */ void makeOriginalElements(FragmentData fragmentData, const ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const { -std::cout << "makeOriginalElements: start\n"; - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; utilities::blueprint::ConduitAllocateThroughAxom c2a; @@ -1725,8 +1704,6 @@ std::cout << "makeOriginalElements: start\n"; valuesView[sizeIndex + i] = zoneIndex; }); } -std::cout << "makeOriginalElements: end\n"; - } private: diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index add59a5ec5..57d0402193 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -3,6 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +#include "axom/config.hpp" +#include "axom/core.hpp" #include "axom/mir/EquiZAlgorithm.hpp" #include "axom/core/ArrayView.hpp" #include "axom/mir/views/StructuredTopologyView.hpp" @@ -20,6 +22,7 @@ #include "RAJA/RAJA.hpp" #endif +#if 0 // clang-format off #if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) using seq_exec = axom::SEQ_EXEC; @@ -45,6 +48,7 @@ #endif #endif // clang-format on +#endif namespace axom { diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index e1e783231b..19a37b4b59 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -11,6 +11,11 @@ #include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + // clang-format off #if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) using seq_exec = axom::SEQ_EXEC; @@ -21,7 +26,7 @@ using omp_exec = seq_exec; #endif - #if defined(AXOM_USE_CUDA) + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; #else @@ -65,7 +70,7 @@ TEST(mir_blueprint_utilities, allocate) #if defined(AXOM_USE_OPENMP) test_conduit_allocate(); #endif -#if defined(AXOM_USE_CUDA) +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) test_conduit_allocate(); #endif #if defined(AXOM_USE_HIP) @@ -115,7 +120,7 @@ TEST(mir_blueprint_utilities, copy) #if defined(AXOM_USE_OPENMP) test_copy_braid(mesh); #endif -#if defined(AXOM_USE_CUDA) +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) test_copy_braid(mesh); #endif #if defined(AXOM_USE_HIP) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 5e8204e01d..43b69ca11c 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -22,7 +22,7 @@ using omp_exec = seq_exec; #endif - #if defined(AXOM_USE_CUDA) + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; #else @@ -529,16 +529,14 @@ void make_one_wdg(conduit::Node &hostMesh) } template -void test_one_shape(conduit::Node &hostMesh, const std::string &name) +void test_one_shape(const conduit::Node &hostMesh, const std::string &name) { -std::cout << "test_one_shape: 0\n"; using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; // Copy mesh to device conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); -std::cout << "test_one_shape: 1\n"; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); // Make views for the device mesh. conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); @@ -548,12 +546,10 @@ std::cout << "test_one_shape: 1\n"; axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); axom::ArrayView zView(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); CoordsetView coordsetView(xView, yView, zView); -std::cout << "test_one_shape: 2\n"; conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/topo/elements/connectivity"); axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); TopoView topoView(connView); -std::cout << "test_one_shape: 3\n"; // Clip the data conduit::Node deviceClipMesh, options; @@ -563,105 +559,61 @@ std::cout << "test_one_shape: 3\n"; options["inside"] = 1; options["outside"] = 1; clipper.execute(deviceMesh, options, deviceClipMesh); -std::cout << "test_one_shape: 4\n"; // Copy device->host conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); -std::cout << "test_one_shape: 5\n"; // Save data. conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "yaml"); -std::cout << "test_one_shape: 6\n"; } -TEST(mir_clipfield, onetet) +template +void test_one_shape_exec(const conduit::Node &hostMesh, const std::string &name) { - using TetShape = axom::mir::views::TetShape; - - conduit::Node hostMesh; - make_one_tet(hostMesh); + test_one_shape(hostMesh, name); - test_one_shape(hostMesh, "one_tet"); - -//#if defined(AXOM_USE_OPENMP) -// test_one_shape(hostMesh, "one_tet_omp"); -//#endif +#if defined(AXOM_USE_OPENMP) + test_one_shape(hostMesh, name + "_omp"); +#endif -#if defined(AXOM_USE_CUDA) - test_one_shape(hostMesh, "one_tet_cuda"); +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_one_shape(hostMesh, name + "_cuda"); #endif #if defined(AXOM_USE_HIP) - test_one_shape(hostMesh, "one_tet_hip"); + test_one_shape(hostMesh, name + "_hip"); #endif } -TEST(mir_clipfield, onepyr) + +TEST(mir_clipfield, onetet) { - using PyramidShape = axom::mir::views::PyramidShape; + conduit::Node hostMesh; + make_one_tet(hostMesh); + test_one_shape_exec >(hostMesh, "one_tet"); +} +TEST(mir_clipfield, onepyr) +{ conduit::Node hostMesh; make_one_pyr(hostMesh); - - test_one_shape(hostMesh, "one_pyr"); - -//#if defined(AXOM_USE_OPENMP) -// test_one_shape(hostMesh, "one_pyr_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - test_one_shape(hostMesh, "one_pyr_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - test_one_shape(hostMesh, "one_pyr_hip"); -#endif + test_one_shape_exec >(hostMesh, "one_pyr"); } TEST(mir_clipfield, onewdg) { - using WedgeShape = axom::mir::views::WedgeShape; - conduit::Node hostMesh; make_one_wdg(hostMesh); - - test_one_shape(hostMesh, "one_wdg"); - -//#if defined(AXOM_USE_OPENMP) -// test_one_shape(hostMesh, "one_wdg_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - test_one_shape(hostMesh, "one_wdg_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - test_one_shape(hostMesh, "one_wdg_hip"); -#endif + test_one_shape_exec >(hostMesh, "one_wdg"); } TEST(mir_clipfield, onehex) { - using HexShape = axom::mir::views::HexShape; - conduit::Node hostMesh; make_one_hex(hostMesh); - - test_one_shape(hostMesh, "one_hex"); - -//#if defined(AXOM_USE_OPENMP) -// test_one_shape(hostMesh, "one_hex_omp"); -//#endif - -#if defined(AXOM_USE_CUDA) - test_one_shape(hostMesh, "one_hex_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - test_one_shape(hostMesh, "one_hex_hip"); -#endif + test_one_shape_exec >(hostMesh, "one_hex"); } //------------------------------------------------------------------------------ @@ -822,7 +774,7 @@ void braid3d_clip_test_exec(const std::string &type, const std::string &name) braid3d_clip_test(type, name + "_omp"); #endif -#if defined(AXOM_USE_CUDA) +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) braid3d_clip_test(type, name + "_cuda"); #endif @@ -839,7 +791,7 @@ TEST(mir_clipfield, uniform2d) // braid2d_clip_test("uniform", "uniform2d_omp"); //#endif -#if defined(AXOM_USE_CUDA) +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) braid2d_clip_test("uniform", "uniform2d_cuda"); #endif diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index cd6658dd4d..cf8d26cdc0 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -34,7 +34,7 @@ class UnstructuredTopologySingleShapeView * * \param conn The mesh connectivity. */ - UnstructuredTopologySingleShapeView(const ConnectivityView &conn) : m_connectivity(conn), m_sizes(), m_offsets() + UnstructuredTopologySingleShapeView(const ConnectivityView &conn) : m_connectivityView(conn), m_sizesView(), m_offsetsView() { } @@ -47,11 +47,11 @@ class UnstructuredTopologySingleShapeView */ UnstructuredTopologySingleShapeView(const ConnectivityView &conn, const ConnectivityView &sizes, - const ConnectivityView &offsets) : m_connectivity(conn), m_sizes(sizes), m_offsets(offsets) + const ConnectivityView &offsets) : m_connectivityView(conn), m_sizesView(sizes), m_offsetsView(offsets) { - SLIC_ASSERT(m_sizes.size() != 0); - SLIC_ASSERT(m_offsets.size() != 0); - SLIC_ASSERT(m_offsets.size() == m_sizes.size()); + SLIC_ASSERT(m_sizesView.size() != 0); + SLIC_ASSERT(m_offsetsView.size() != 0); + SLIC_ASSERT(m_offsetsView.size() == m_sizesView.size()); } /** @@ -68,7 +68,7 @@ class UnstructuredTopologySingleShapeView */ IndexType numberOfZones() const { - return (m_sizes.size() != 0) ? m_sizes.size() : (m_connectivity.size() / ShapeType::numberOfNodes()); + return (m_sizesView.size() != 0) ? m_sizesView.size() : (m_connectivityView.size() / ShapeType::numberOfNodes()); } /** @@ -84,11 +84,11 @@ class UnstructuredTopologySingleShapeView { const auto nzones = numberOfZones(); - ConnectivityView connectivityView(m_connectivity); + ConnectivityView connectivityView(m_connectivityView); if constexpr (ShapeType::is_variable_size()) { - ConnectivityView sizesView(m_sizes); - ConnectivityView offsetsView(m_offsets); + ConnectivityView sizesView(m_sizesView); + ConnectivityView offsetsView(m_offsetsView); axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) { const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); @@ -121,17 +121,17 @@ class UnstructuredTopologySingleShapeView { const auto nSelectedZones = selectedIdsView.size(); - ConnectivityView connectivityView(m_connectivity); + ConnectivityView connectivityView(m_connectivityView); const ViewType localSelectedIdsView(selectedIdsView); if constexpr (ShapeType::is_variable_size()) { - ConnectivityView sizesView(m_sizes); - ConnectivityView offsetsView(m_offsets); + ConnectivityView sizesView(m_sizesView); + ConnectivityView offsetsView(m_offsetsView); axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); - const ShapeType shape(shapeDataView); + const ConnectivityView shapeIdsView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + const ShapeType shape(shapeIdsView); func(zoneIndex, shape); }); } @@ -140,17 +140,17 @@ class UnstructuredTopologySingleShapeView axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeData(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); - const ShapeType shape(shapeData); + const ConnectivityView shapeIdsView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + const ShapeType shape(shapeIdsView); func(zoneIndex, shape); }); } } private: - ConnectivityView m_connectivity; - ConnectivityView m_sizes; - ConnectivityView m_offsets; + ConnectivityView m_connectivityView; + ConnectivityView m_sizesView; + ConnectivityView m_offsetsView; }; } // end namespace views From 3418c07c528ca6b0ea5e9b0abab6e393c9e06f28 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 18:44:45 -0700 Subject: [PATCH 125/290] Always use wrapped scan --- src/axom/mir/ClipField.hpp | 66 +------------------------------------- 1 file changed, 1 insertion(+), 65 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index c661c373c8..3964a0c6c2 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -22,9 +22,6 @@ #include #include -#define AXOM_CLIPFIELD_SCAN_WORKAROUND - -#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) // NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers // so we can write serial versions for when RAJA is not enabled. namespace axom @@ -41,24 +38,6 @@ struct scans } }; -#if defined(AXOM_USE_OPENMP) -// Workaround for OpenMP -template -struct scans -{ - inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) - { - assert(input.size() == output.size()); - // RAJA is not working in OpenMP. Do serial. - output[0] = 0; - for(IndexType i = 1; i < input.size(); i++) - { - output[i] = output[i - 1] + input[i - 1]; - } - } -}; -#endif - template inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) { @@ -67,7 +46,6 @@ inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) } } // end namespace axom -#endif namespace axom { @@ -568,19 +546,8 @@ class BlendGroupBuilder */ void computeBlendGroupOffsets() { -#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) axom::exclusive_scan(m_state.m_blendGroupsLenView, m_state.m_blendOffsetView); axom::exclusive_scan(m_state.m_blendGroupsView, m_state.m_blendGroupOffsetsView); -#else - using loop_policy = typename axom::execution_space::loop_policy; - // Fill in offsets via scan. - RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsLenView.data(), m_state.m_nzones), - RAJA::make_span(m_state.m_blendOffsetView.data(), m_state.m_nzones), - RAJA::operators::plus{}); - RAJA::exclusive_scan(RAJA::make_span(m_state.m_blendGroupsView.data(), m_state.m_nzones), - RAJA::make_span(m_state.m_blendGroupOffsetsView.data(), m_state.m_nzones), - RAJA::operators::plus{}); -#endif } /** @@ -1272,34 +1239,8 @@ class ClipField */ void computeFragmentOffsets(FragmentData &fragmentData) const { -#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) - // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); axom::exclusive_scan(fragmentData.m_fragmentsSizeView, fragmentData.m_fragmentSizeOffsetsView); -#endif -#if 0 - // NOTE: I was trying to work aroung a RAJA runtime error with OpenMP. No good. - - // Copy sizes into offsets - const auto nzones = m_topologyView.numberOfZones(); - axom::copy(fragmentData.m_fragmentOffsetsView.data(), fragmentData.m_fragmentsView.data(), nzones * sizeof(IndexType)); - axom::copy(fragmentData.m_fragmentSizeOffsetsView.data(), fragmentData.m_fragmentsSizeView.data(), nzones * sizeof(IndexType)); - // Make offsets in place. - RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones)); - RAJA::exclusive_scan_inplace(RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones)); - -#endif -#if 0 - // Original code. - const auto nzones = m_topologyView.numberOfZones(); - RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsView.data(), nzones), - RAJA::make_span(fragmentData.m_fragmentOffsetsView.data(), nzones), - RAJA::operators::plus{}); - - RAJA::exclusive_scan(RAJA::make_span(fragmentData.m_fragmentsSizeView.data(), nzones), - RAJA::make_span(fragmentData.m_fragmentSizeOffsetsView.data(), nzones), - RAJA::operators::plus{}); -#endif } /** @@ -1567,13 +1508,8 @@ class ClipField } // Make offsets -#if defined(AXOM_CLIPFIELD_SCAN_WORKAROUND) axom::exclusive_scan(sizesView, offsetsView); -#else - RAJA::exclusive_scan(RAJA::make_span(sizesView.data(), fragmentData.m_finalNumZones), - RAJA::make_span(offsetsView.data(), fragmentData.m_finalNumZones), - RAJA::operators::plus{}); -#endif + // Add shape information to the connectivity. const auto shapesUsed = shapesUsed_reduce.get(); const auto shapeMap = details::shapeMap_FromFlags(shapesUsed); From f45ebb73cebfd594b042a661563309c1d005d533 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 18:54:01 -0700 Subject: [PATCH 126/290] Disable formatting in some borrowed data tables --- src/axom/mir/clipping/ClipCases.h | 2 ++ src/axom/mir/clipping/ClipCasesHex.cpp | 2 ++ src/axom/mir/clipping/ClipCasesPyr.cpp | 2 ++ src/axom/mir/clipping/ClipCasesQua.cpp | 2 ++ src/axom/mir/clipping/ClipCasesTet.cpp | 2 ++ src/axom/mir/clipping/ClipCasesTri.cpp | 2 ++ src/axom/mir/clipping/ClipCasesWdg.cpp | 2 ++ 7 files changed, 14 insertions(+) diff --git a/src/axom/mir/clipping/ClipCases.h b/src/axom/mir/clipping/ClipCases.h index 8db2e39d3f..6b7f7c4210 100644 --- a/src/axom/mir/clipping/ClipCases.h +++ b/src/axom/mir/clipping/ClipCases.h @@ -6,6 +6,7 @@ #define AXOM_VISIT_CLIP_CASES_H //--------------------------------------------------------------------------- // Axom modifications +// clang-format off //#include #define VISIT_VTK_LIGHT_API #include @@ -171,6 +172,7 @@ extern const size_t clipShapesHexSize; } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- #endif diff --git a/src/axom/mir/clipping/ClipCasesHex.cpp b/src/axom/mir/clipping/ClipCasesHex.cpp index 7efe45e1d5..f10e3d140a 100644 --- a/src/axom/mir/clipping/ClipCasesHex.cpp +++ b/src/axom/mir/clipping/ClipCasesHex.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -3503,4 +3504,5 @@ const size_t clipShapesHexSize = sizeof(clipShapesHex) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesPyr.cpp b/src/axom/mir/clipping/ClipCasesPyr.cpp index 983624a8d7..158990badc 100644 --- a/src/axom/mir/clipping/ClipCasesPyr.cpp +++ b/src/axom/mir/clipping/ClipCasesPyr.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -224,4 +225,5 @@ const size_t clipShapesPyrSize = sizeof(clipShapesPyr) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesQua.cpp b/src/axom/mir/clipping/ClipCasesQua.cpp index 7794a036c8..e870378465 100644 --- a/src/axom/mir/clipping/ClipCasesQua.cpp +++ b/src/axom/mir/clipping/ClipCasesQua.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -100,4 +101,5 @@ const size_t clipShapesQuaSize = sizeof(clipShapesQua) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesTet.cpp b/src/axom/mir/clipping/ClipCasesTet.cpp index 7c26ba8d8c..fc04e6ca86 100644 --- a/src/axom/mir/clipping/ClipCasesTet.cpp +++ b/src/axom/mir/clipping/ClipCasesTet.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -92,4 +93,5 @@ const size_t clipShapesTetSize = sizeof(clipShapesTet) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesTri.cpp b/src/axom/mir/clipping/ClipCasesTri.cpp index b5e0f5f405..68c27a94d7 100644 --- a/src/axom/mir/clipping/ClipCasesTri.cpp +++ b/src/axom/mir/clipping/ClipCasesTri.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -62,4 +63,5 @@ const size_t clipShapesTriSize = sizeof(clipShapesTri) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- diff --git a/src/axom/mir/clipping/ClipCasesWdg.cpp b/src/axom/mir/clipping/ClipCasesWdg.cpp index 031fbde43c..03d3dba8a5 100644 --- a/src/axom/mir/clipping/ClipCasesWdg.cpp +++ b/src/axom/mir/clipping/ClipCasesWdg.cpp @@ -5,6 +5,7 @@ #include "ClipCases.h" //--------------------------------------------------------------------------- // Axom modifications +// clang-format off namespace axom { namespace mir { namespace clipping { @@ -620,4 +621,5 @@ const size_t clipShapesWdgSize = sizeof(clipShapesWdg) / sizeof(unsigned char); } // namespace clipping } // namespace mir } // namespace axom +// clang-format on //--------------------------------------------------------------------------- From 5fd8a62e0f1046bb5fdc8672726754fb76018035 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 31 Jul 2024 19:00:55 -0700 Subject: [PATCH 127/290] clang-format is the worst! clang-ugly is more like it. --- src/axom/core/StaticArray.hpp | 37 +- src/axom/core/tests/core_bit_utilities.hpp | 10 +- src/axom/core/tests/utils_utilities.cpp | 71 +- src/axom/core/utilities/Utilities.hpp | 7 +- src/axom/mir/CellClipper.cpp | 136 +- src/axom/mir/CellClipper.hpp | 61 +- src/axom/mir/CellData.cpp | 138 +- src/axom/mir/CellData.hpp | 72 +- src/axom/mir/CellGenerator.cpp | 166 +-- src/axom/mir/CellGenerator.hpp | 72 +- src/axom/mir/ClipField.hpp | 1062 ++++++++------ src/axom/mir/CoordsetBlender.hpp | 66 +- src/axom/mir/EquiZAlgorithm.cpp | 21 +- src/axom/mir/EquiZAlgorithm.hpp | 43 +- src/axom/mir/FieldBlender.hpp | 82 +- src/axom/mir/FieldSlicer.hpp | 35 +- src/axom/mir/InterfaceReconstructor.cpp | 258 ++-- src/axom/mir/InterfaceReconstructor.hpp | 70 +- src/axom/mir/MIRAlgorithm.cpp | 52 +- src/axom/mir/MIRAlgorithm.hpp | 81 +- src/axom/mir/MIRMesh.cpp | 601 ++++---- src/axom/mir/MIRMesh.hpp | 184 ++- src/axom/mir/MIRMeshTypes.hpp | 80 +- src/axom/mir/MIRUtilities.hpp | 680 ++++++--- src/axom/mir/MeshTester.cpp | 908 ++++++------ src/axom/mir/MeshTester.hpp | 117 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 246 ++-- src/axom/mir/RecenterField.hpp | 86 +- src/axom/mir/ZooClippingTables.cpp | 1238 ++++++++++------- src/axom/mir/ZooClippingTables.hpp | 22 +- src/axom/mir/blueprint_utilities.cpp | 8 +- src/axom/mir/blueprint_utilities.hpp | 193 ++- src/axom/mir/clipping/ClipTableManager.hpp | 150 +- .../mir/examples/mir_concentric_circles.cpp | 28 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 298 ++-- .../mir/tests/mir_blueprint_utilities.cpp | 26 +- src/axom/mir/tests/mir_cell_clipper.cpp | 718 +++++----- src/axom/mir/tests/mir_cell_generator.cpp | 270 ++-- src/axom/mir/tests/mir_clipfield.cpp | 465 ++++--- .../mir/tests/mir_interface_reconstructor.cpp | 3 +- src/axom/mir/tests/mir_mesh.cpp | 292 ++-- src/axom/mir/tests/mir_smoke.cpp | 13 +- src/axom/mir/tests/mir_utilities.cpp | 107 +- src/axom/mir/tests/mir_views.cpp | 11 +- src/axom/mir/tests/mir_views_indexing.cpp | 55 +- src/axom/mir/utilities.cpp | 8 +- src/axom/mir/utilities.hpp | 104 +- src/axom/mir/views/ExplicitCoordsetView.hpp | 41 +- src/axom/mir/views/MaterialView.cpp | 9 +- src/axom/mir/views/MaterialView.hpp | 51 +- src/axom/mir/views/NodeArrayView.hpp | 761 ++++++---- .../mir/views/RectilinearCoordsetView.hpp | 64 +- src/axom/mir/views/Shapes.hpp | 419 ++++-- .../mir/views/StridedStructuredIndexing.hpp | 79 +- src/axom/mir/views/StructuredIndexing.hpp | 57 +- src/axom/mir/views/StructuredTopologyView.hpp | 240 ++-- src/axom/mir/views/UniformCoordsetView.hpp | 38 +- .../UnstructuredTopologyMixedShapeView.hpp | 97 +- .../UnstructuredTopologyPolyhedralView.hpp | 116 +- .../UnstructuredTopologySingleShapeView.hpp | 96 +- src/axom/mir/views/dispatch_coordset.hpp | 75 +- src/axom/mir/views/dispatch_material.hpp | 38 +- .../views/dispatch_rectilinear_topology.hpp | 46 +- .../views/dispatch_structured_topology.hpp | 73 +- src/axom/mir/views/dispatch_topology.hpp | 13 +- .../mir/views/dispatch_uniform_topology.hpp | 28 +- .../views/dispatch_unstructured_topology.hpp | 187 +-- src/axom/mir/views/dispatch_utilities.hpp | 11 +- src/axom/slam/Map.hpp | 13 +- 69 files changed, 6807 insertions(+), 5196 deletions(-) diff --git a/src/axom/core/StaticArray.hpp b/src/axom/core/StaticArray.hpp index f80db5fb63..6efa38d837 100644 --- a/src/axom/core/StaticArray.hpp +++ b/src/axom/core/StaticArray.hpp @@ -6,13 +6,12 @@ #ifndef AXOM_STATICARRAY_HPP_ #define AXOM_STATICARRAY_HPP_ -#include "axom/config.hpp" // for compile-time defines +#include "axom/config.hpp" // for compile-time defines #include "axom/core/Macros.hpp" #include "axom/core/StackArray.hpp" namespace axom { - /** * \brief This class extends StackArray with some std::vector-like convenience methods. * @@ -25,54 +24,36 @@ class StaticArray : public StackArray { public: AXOM_HOST_DEVICE - constexpr size_t capacity() const - { - return static_cast(N); - } + constexpr size_t capacity() const { return static_cast(N); } AXOM_HOST_DEVICE - size_t size() const - { - return m_size; - } + size_t size() const { return m_size; } AXOM_HOST_DEVICE void push_back(const T &e) { - if(m_size + 1 < capacity()) - StackArray::m_data[m_size++] = e; + if(m_size + 1 < capacity()) StackArray::m_data[m_size++] = e; } AXOM_HOST_DEVICE - void pop_back() - { - m_size = (m_size > 0) ? (m_size - 1) : 0; - } + void pop_back() { m_size = (m_size > 0) ? (m_size - 1) : 0; } AXOM_HOST_DEVICE - void clear() - { - m_size = 0; - } + void clear() { m_size = 0; } AXOM_HOST_DEVICE - bool empty() const - { - return m_size == 0; - } + bool empty() const { return m_size == 0; } AXOM_HOST_DEVICE void fill(const T &e) { - for(size_t i = 0; i < capacity(); i++) - StackArray::m_data[i] = e; + for(size_t i = 0; i < capacity(); i++) StackArray::m_data[i] = e; } private: - size_t m_size{0}; + size_t m_size {0}; }; - } /* namespace axom */ #endif /* AXOM_STATICARRAY_HPP_ */ diff --git a/src/axom/core/tests/core_bit_utilities.hpp b/src/axom/core/tests/core_bit_utilities.hpp index 1c91942de2..ed49d3170d 100644 --- a/src/axom/core/tests/core_bit_utilities.hpp +++ b/src/axom/core/tests/core_bit_utilities.hpp @@ -184,14 +184,17 @@ TEST(core_bit_utilities, countl_zero) TEST(core_bit_utilities, setbit_bitisset) { const std::uint32_t pattern = 0xaaaaaaaa; - for(size_t bit = 0; bit < axom::utilities::BitTraits::BITS_PER_WORD; bit++) + for(size_t bit = 0; + bit < axom::utilities::BitTraits::BITS_PER_WORD; + bit++) { EXPECT_EQ(axom::utilities::bitIsSet(pattern, bit), ((bit & 1) == 1)); } const bool bitvals[] = {false, true, true, false, true, true, false, true}; std::uint8_t value = 0; - for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; i++) + for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; + i++) { axom::utilities::setBit(value, i, bitvals[i]); for(size_t b = 0; b <= i; b++) @@ -203,7 +206,8 @@ TEST(core_bit_utilities, setbit_bitisset) EXPECT_EQ(axom::utilities::countBits(pattern), 16); EXPECT_EQ(axom::utilities::countBits(value), 5); - for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; i++) + for(size_t i = 0; i < axom::utilities::BitTraits::BITS_PER_WORD; + i++) { axom::utilities::setBit(value, i, false); EXPECT_EQ(axom::utilities::bitIsSet(value, i), false); diff --git a/src/axom/core/tests/utils_utilities.cpp b/src/axom/core/tests/utils_utilities.cpp index 1b11d57a25..005729a2da 100644 --- a/src/axom/core/tests/utils_utilities.cpp +++ b/src/axom/core/tests/utils_utilities.cpp @@ -7,9 +7,9 @@ #include "axom/core/utilities/Utilities.hpp" -TEST(core_Utilities,log2) +TEST(core_Utilities, log2) { - std::cout<<"Testing log2 functions."<< std::endl; + std::cout << "Testing log2 functions." << std::endl; // Test integer log2 value of type int { @@ -20,7 +20,7 @@ TEST(core_Utilities,log2) // Test non-integer log2 value of type int { - int val = 72; // not a power of 2 + int val = 72; // not a power of 2 int exp = 6; EXPECT_EQ(exp, axom::utilities::log2(val)); } @@ -34,23 +34,23 @@ TEST(core_Utilities,log2) // Test non-integer log2 value of type double { - double val = 20.; // not a power of 2 + double val = 20.; // not a power of 2 double exp = 4.3219281; EXPECT_NEAR(exp, axom::utilities::log2(val), 1e-5); } } -TEST(core_Utilities,random_real) +TEST(core_Utilities, random_real) { - std::cout<<"Testing random_real functions (non-deterministic)."<< std::endl; + std::cout << "Testing random_real functions (non-deterministic)." << std::endl; int min = 0; int max = 1; - for (int offset = 0 ; offset < 10 ; ++offset) + for(int offset = 0; offset < 10; ++offset) { int cur_min = min - offset; int cur_max = max + offset; - for (int i = 0 ; i < 100 ; ++i) + for(int i = 0; i < 100; ++i) { float f_val = axom::utilities::random_real(cur_min, cur_max); EXPECT_GE(f_val, cur_min); @@ -60,51 +60,48 @@ TEST(core_Utilities,random_real) EXPECT_GE(d_val, cur_min); EXPECT_LT(d_val, cur_max); - long double ld_val = axom::utilities::random_real(cur_min, - cur_max); + long double ld_val = + axom::utilities::random_real(cur_min, cur_max); EXPECT_GE(ld_val, cur_min); EXPECT_LT(ld_val, cur_max); } } } -TEST( core_Utilities,random_real_with_seed ) +TEST(core_Utilities, random_real_with_seed) { - std::cout<<"Testing random_real functions (deterministic)."<< std::endl; + std::cout << "Testing random_real functions (deterministic)." << std::endl; constexpr unsigned int seed = 123456789; constexpr double a = -5.0; - constexpr double b = 5.0; + constexpr double b = 5.0; - const double expected_reals[ 5 ] = { - -1.5112829544380526, - -2.3311429024686219, - -3.6335370551231403, - -4.714431326610093, - 3.6893326916732878 - }; + const double expected_reals[5] = {-1.5112829544380526, + -2.3311429024686219, + -3.6335370551231403, + -4.714431326610093, + 3.6893326916732878}; - for ( int i=0 ; i < 5 ; ++i ) + for(int i = 0; i < 5; ++i) { - const double real = axom::utilities::random_real( a, b, seed ); - EXPECT_DOUBLE_EQ( real, expected_reals[ i ] ); + const double real = axom::utilities::random_real(a, b, seed); + EXPECT_DOUBLE_EQ(real, expected_reals[i]); EXPECT_GE(real, a); EXPECT_LT(real, b); } - } -TEST(core_Utilities,minmax) +TEST(core_Utilities, minmax) { - std::cout<<"Testing min and max functions."<< std::endl; + std::cout << "Testing min and max functions." << std::endl; // Test simple min, max comparisons on ints { int a = 5; int b = 7; - EXPECT_EQ(a, axom::utilities::min(a,b)); - EXPECT_EQ(b, axom::utilities::max(a,b)); + EXPECT_EQ(a, axom::utilities::min(a, b)); + EXPECT_EQ(b, axom::utilities::max(a, b)); } // Test simple min, max comparisons on doubles @@ -112,38 +109,38 @@ TEST(core_Utilities,minmax) double a = 5.2; double b = -1.7; - EXPECT_EQ(b, axom::utilities::min(a,b)); - EXPECT_EQ(a, axom::utilities::max(a,b)); + EXPECT_EQ(b, axom::utilities::min(a, b)); + EXPECT_EQ(a, axom::utilities::max(a, b)); } } TEST(core_Utilities, lerp) { - std::cout<<"Testing linear interpolation (lerp) function."<< std::endl; + std::cout << "Testing linear interpolation (lerp) function." << std::endl; double f0 = 50.0; double f1 = 100.0; // Test end points { - EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f0, f1, 0.) ); - EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f1, f0, 0.) ); + EXPECT_DOUBLE_EQ(f0, axom::utilities::lerp(f0, f1, 0.)); + EXPECT_DOUBLE_EQ(f1, axom::utilities::lerp(f1, f0, 0.)); - EXPECT_DOUBLE_EQ( f1, axom::utilities::lerp(f0, f1, 1.) ); - EXPECT_DOUBLE_EQ( f0, axom::utilities::lerp(f1, f0, 1.) ); + EXPECT_DOUBLE_EQ(f1, axom::utilities::lerp(f0, f1, 1.)); + EXPECT_DOUBLE_EQ(f0, axom::utilities::lerp(f1, f0, 1.)); } // Test midpoint { double t = 0.5; double exp = 75.; - EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + EXPECT_DOUBLE_EQ(exp, axom::utilities::lerp(f0, f1, t)); } // Another test { double t = 0.66; double exp = 83.; - EXPECT_DOUBLE_EQ( exp, axom::utilities::lerp(f0, f1, t)); + EXPECT_DOUBLE_EQ(exp, axom::utilities::lerp(f0, f1, t)); } } diff --git a/src/axom/core/utilities/Utilities.hpp b/src/axom/core/utilities/Utilities.hpp index 5447052ee0..f7662bfb6f 100644 --- a/src/axom/core/utilities/Utilities.hpp +++ b/src/axom/core/utilities/Utilities.hpp @@ -126,12 +126,11 @@ inline T log2(T val) * \param [in] t The interpolation parameter. * \return The interpolated value */ -template < typename T > -inline AXOM_HOST_DEVICE -T lerp( T v0, T v1, T t) +template +inline AXOM_HOST_DEVICE T lerp(T v0, T v1, T t) { constexpr T one = T(1); - return (one-t)*v0 + t*v1; + return (one - t) * v0 + t * v1; } /*! diff --git a/src/axom/mir/CellClipper.cpp b/src/axom/mir/CellClipper.cpp index 644b3f31c4..0fc83071ba 100644 --- a/src/axom/mir/CellClipper.cpp +++ b/src/axom/mir/CellClipper.cpp @@ -9,60 +9,59 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- -CellClipper::CellClipper() -{ - -} +CellClipper::CellClipper() { } //-------------------------------------------------------------------------------- -CellClipper::~CellClipper() -{ - -} +CellClipper::~CellClipper() { } //-------------------------------------------------------------------------------- // Computes the t-values where each edge is clipped, as well as the topology of the new output cells after clipping the original cell // Outputs the newElements, newVertices maps and the verticesClippingTValue array[] -void CellClipper::computeClippingPoints(const mir::Shape shapeType, - const std::vector >& vertexVF, - std::map >& newElements, - std::map >& newVertices, - axom::float64* tValues) +void CellClipper::computeClippingPoints( + const mir::Shape shapeType, + const std::vector>& vertexVF, + std::map>& newElements, + std::map>& newVertices, + axom::float64* tValues) { // Determine the clipping case for the current element - unsigned int caseIndex = determineClippingCase( shapeType, vertexVF[0], vertexVF[1] ); + unsigned int caseIndex = + determineClippingCase(shapeType, vertexVF[0], vertexVF[1]); - std::vector > clipTable = getClipTable(shapeType); + std::vector> clipTable = getClipTable(shapeType); // Create the new polygons based on the clipping case - int currentElementIndex = 0; // the next available element index + int currentElementIndex = 0; // the next available element index int i = 0; int numVertices = clipTable[caseIndex][i]; - + // for each new element in the current clipping case - while (numVertices != -1) + while(numVertices != -1) { // for each vertex of the new element - for (int j = 0; j < numVertices; ++j) + for(int j = 0; j < numVertices; ++j) { // Find the id of the next vertex of the new element - int vID = clipTable[caseIndex][i + (j+1)]; + int vID = clipTable[caseIndex][i + (j + 1)]; // Associate the vertex and element together newElements[currentElementIndex].push_back(vID); newVertices[vID].push_back(currentElementIndex); - if ( vID >= mir::utilities::numVerts(shapeType) && vID < (mir::utilities::maxPossibleNumVerts(shapeType) - 1) ) + if(vID >= mir::utilities::numVerts(shapeType) && + vID < (mir::utilities::maxPossibleNumVerts(shapeType) - 1)) { int vertexOneID = mir::utilities::getEdgeEndpoint(shapeType, vID, true); int vertexTwoID = mir::utilities::getEdgeEndpoint(shapeType, vID, false); - tValues[vID] = computeTValueOnEdge( vertexVF[0][vertexOneID], vertexVF[1][vertexOneID], vertexVF[0][vertexTwoID], vertexVF[1][vertexTwoID] ); + tValues[vID] = computeTValueOnEdge(vertexVF[0][vertexOneID], + vertexVF[1][vertexOneID], + vertexVF[0][vertexTwoID], + vertexVF[1][vertexTwoID]); } } @@ -77,22 +76,23 @@ void CellClipper::computeClippingPoints(const mir::Shape shapeType, //-------------------------------------------------------------------------------- -unsigned int CellClipper::determineClippingCase(const mir::Shape shapeType, - const std::vector& matOneVF, - const std::vector& matTwoVF) +unsigned int CellClipper::determineClippingCase( + const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF) { unsigned int caseIndex = 0; int numVertices = mir::utilities::numVerts(shapeType); - for (int vID = 0; vID < numVertices; ++vID) + for(int vID = 0; vID < numVertices; ++vID) { - if (matOneVF[vID] > matTwoVF[vID]) + if(matOneVF[vID] > matTwoVF[vID]) { unsigned int bitIndex = (numVertices - 1) - vID; caseIndex |= (1 << bitIndex); - } + } } return caseIndex; @@ -100,29 +100,42 @@ unsigned int CellClipper::determineClippingCase(const mir::Shape shapeType, //-------------------------------------------------------------------------------- -axom::float64 CellClipper::computeTValueOnEdge(axom::float64 vfMatOneVertexOne, - axom::float64 vfMatTwoVertexOne, - axom::float64 vfMatOneVertexTwo, - axom::float64 vfMatTwoVertexTwo) +axom::float64 CellClipper::computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo) { axom::float64 ret = 0.0; // TODO: Perhaps just handle NULL_MAT by return 0, since that is what will happen anyways? // Handle NULL_MAT, which has a vf of -1.0, but which needs to be 0.0 for the purposes of computing the clipping point - if (vfMatOneVertexOne < 0.0) { vfMatOneVertexOne = 0.0; }; - if (vfMatTwoVertexOne < 0.0) { vfMatTwoVertexOne = 0.0; }; - if (vfMatOneVertexTwo < 0.0) { vfMatOneVertexTwo = 0.0; }; - if (vfMatTwoVertexTwo < 0.0) { vfMatTwoVertexTwo = 0.0; }; + if(vfMatOneVertexOne < 0.0) + { + vfMatOneVertexOne = 0.0; + }; + if(vfMatTwoVertexOne < 0.0) + { + vfMatTwoVertexOne = 0.0; + }; + if(vfMatOneVertexTwo < 0.0) + { + vfMatOneVertexTwo = 0.0; + }; + if(vfMatTwoVertexTwo < 0.0) + { + vfMatTwoVertexTwo = 0.0; + }; axom::float64 numerator = vfMatTwoVertexOne - vfMatOneVertexOne; - axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + vfMatTwoVertexOne - vfMatTwoVertexTwo; - - if (denominator != 0.0) + axom::float64 denominator = -vfMatOneVertexOne + vfMatOneVertexTwo + + vfMatTwoVertexOne - vfMatTwoVertexTwo; + + if(denominator != 0.0) { ret = numerator / denominator; } - - if (ret > 1.0 || ret < 0.0) + + if(ret > 1.0 || ret < 0.0) { // This shouldn't happen... printf(" OUT OF BOUNDS T VALUE: %f\n", ret); @@ -137,29 +150,30 @@ axom::float64 CellClipper::computeTValueOnEdge(axom::float64 vfMatOneVertexOne, //-------------------------------------------------------------------------------- -const std::vector >& CellClipper::getClipTable(const mir::Shape shapeType) +const std::vector>& CellClipper::getClipTable( + const mir::Shape shapeType) { - switch ( shapeType ) + switch(shapeType) { - case mir::Shape::Triangle: - return triangleClipTableVec; - case mir::Shape::Quad: - return quadClipTableVec; - case mir::Shape::Tetrahedron: - return tetrahedronClipTableVec; - case mir::Shape::Pyramid: - return pyramidClipTableVec; - case mir::Shape::Triangular_Prism: - return triangularPrismClipTableVec; - case mir::Shape::Hexahedron: - return hexahedronClipTableVec; - default: - printf("No clipping table for this shape type.\n"); - return triangleClipTableVec; + case mir::Shape::Triangle: + return triangleClipTableVec; + case mir::Shape::Quad: + return quadClipTableVec; + case mir::Shape::Tetrahedron: + return tetrahedronClipTableVec; + case mir::Shape::Pyramid: + return pyramidClipTableVec; + case mir::Shape::Triangular_Prism: + return triangularPrismClipTableVec; + case mir::Shape::Hexahedron: + return hexahedronClipTableVec; + default: + printf("No clipping table for this shape type.\n"); + return triangleClipTableVec; } } //-------------------------------------------------------------------------------- -} -} \ No newline at end of file +} // namespace mir +} // namespace axom \ No newline at end of file diff --git a/src/axom/mir/CellClipper.hpp b/src/axom/mir/CellClipper.hpp index fcdd650db3..26b0519cf7 100644 --- a/src/axom/mir/CellClipper.hpp +++ b/src/axom/mir/CellClipper.hpp @@ -29,30 +29,29 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- - /** +/** * \class CellClipper * * \brief A class that contains the functionality for taking an input cell * and determining how it should be clipped. * */ - class CellClipper - { - public: - /** +class CellClipper +{ +public: + /** * \brief Default constructor. */ - CellClipper(); + CellClipper(); - /** + /** * \brief Default destructor. */ - ~CellClipper(); + ~CellClipper(); - /** + /** * \brief Computes the elements, vertices, and t values resulting from splitting the element with the given vertex volume fractions. * * \param shapeType The shape type of the element. @@ -62,13 +61,13 @@ namespace mir * \param tValues An array of t values where each of the midpoint vertices are for the newly generated elements. * */ - void computeClippingPoints(const mir::Shape shapeType, - const std::vector >& vertexVF, - std::map >& newElements, - std::map >& newVertices, - axom::float64* tValues); + void computeClippingPoints(const mir::Shape shapeType, + const std::vector>& vertexVF, + std::map>& newElements, + std::map>& newVertices, + axom::float64* tValues); - /** + /** * \brief Determines the index into the clipping table of the given shape based on the volume fractions at its vertices. * * \param shapeType The shape type of the element. @@ -77,12 +76,11 @@ namespace mir * * \return The index into the clipping table. */ - unsigned int determineClippingCase(const mir::Shape shapeType, - const std::vector& matOneVF, - const std::vector& matTwoVF); - + unsigned int determineClippingCase(const mir::Shape shapeType, + const std::vector& matOneVF, + const std::vector& matTwoVF); - /** + /** * \brief Computes the t value as a percent from vertex one to vertex two based on the materials given. * * \param vfMatOneVertexOne The volume fraction of material one present at vertex one. @@ -95,26 +93,25 @@ namespace mir * \note When one material's volume fractions dominates the other material's, then the edge should not be clipped and this function will return 0.0. * \note When one of the materials is the NULL_MAT (and has vf = -1.0), these values are set to 0 in order to interpolate properly. */ - axom::float64 computeTValueOnEdge(axom::float64 vfMatOneVertexOne, - axom::float64 vfMatTwoVertexOne, - axom::float64 vfMatOneVertexTwo, - axom::float64 vfMatTwoVertexTwo); + axom::float64 computeTValueOnEdge(axom::float64 vfMatOneVertexOne, + axom::float64 vfMatTwoVertexOne, + axom::float64 vfMatOneVertexTwo, + axom::float64 vfMatTwoVertexTwo); - private: - /** +private: + /** * \brief Returns a reference to the appropriate clipping table to use for the shape type. * * \param The shape type of the element. * * \return A reference to the clipping table. */ - const std::vector >& getClipTable(const mir::Shape shapeType); - - }; + const std::vector>& getClipTable(const mir::Shape shapeType); +}; //-------------------------------------------------------------------------------- -} -} +} // namespace mir +} // namespace axom #endif \ No newline at end of file diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/CellData.cpp index c9bd25ecb5..524e7dc9c7 100644 --- a/src/axom/mir/CellData.cpp +++ b/src/axom/mir/CellData.cpp @@ -9,85 +9,95 @@ namespace axom { namespace mir { +//-------------------------------------------------------------------------------- - //-------------------------------------------------------------------------------- +CellData::CellData() : m_numVerts(0), m_numElems(0) { } - CellData::CellData() - : m_numVerts(0) - , m_numElems(0) - {} +//-------------------------------------------------------------------------------- - //-------------------------------------------------------------------------------- - - void CellData::mergeCell(const CellData& cellToMerge) - { - // Initialize index offsets - int evBeginsOffset = m_topology.m_evInds.size(); - int veBeginsOffset = m_topology.m_veInds.size(); - - int vertexIndexOffset = m_numVerts; - int elementIndexOffset = m_numElems; +void CellData::mergeCell(const CellData& cellToMerge) +{ + // Initialize index offsets + int evBeginsOffset = m_topology.m_evInds.size(); + int veBeginsOffset = m_topology.m_veInds.size(); - // Merge the cell topology information - for (unsigned long i = 0; i < cellToMerge.m_topology.m_evInds.size(); ++i) - { - m_topology.m_evInds.push_back(cellToMerge.m_topology.m_evInds[i] + vertexIndexOffset); - } + int vertexIndexOffset = m_numVerts; + int elementIndexOffset = m_numElems; - for (unsigned long i = 1; i < cellToMerge.m_topology.m_evBegins.size(); ++i) - { - m_topology.m_evBegins.push_back(cellToMerge.m_topology.m_evBegins[i] + evBeginsOffset); - } + // Merge the cell topology information + for(unsigned long i = 0; i < cellToMerge.m_topology.m_evInds.size(); ++i) + { + m_topology.m_evInds.push_back(cellToMerge.m_topology.m_evInds[i] + + vertexIndexOffset); + } - for (unsigned long i = 0; i < cellToMerge.m_topology.m_veInds.size(); ++i) - { - m_topology.m_veInds.push_back(cellToMerge.m_topology.m_veInds[i] + elementIndexOffset); - } + for(unsigned long i = 1; i < cellToMerge.m_topology.m_evBegins.size(); ++i) + { + m_topology.m_evBegins.push_back(cellToMerge.m_topology.m_evBegins[i] + + evBeginsOffset); + } - for (unsigned long i = 1; i < cellToMerge.m_topology.m_veBegins.size(); ++i) - { - m_topology.m_veBegins.push_back(cellToMerge.m_topology.m_veBegins[i] + veBeginsOffset); - } + for(unsigned long i = 0; i < cellToMerge.m_topology.m_veInds.size(); ++i) + { + m_topology.m_veInds.push_back(cellToMerge.m_topology.m_veInds[i] + + elementIndexOffset); + } - // Merge the vertex positions - for (unsigned long i = 0; i < cellToMerge.m_mapData.m_vertexPositions.size(); ++i) - { - m_mapData.m_vertexPositions.push_back(cellToMerge.m_mapData.m_vertexPositions[i]); - } + for(unsigned long i = 1; i < cellToMerge.m_topology.m_veBegins.size(); ++i) + { + m_topology.m_veBegins.push_back(cellToMerge.m_topology.m_veBegins[i] + + veBeginsOffset); + } - // Merge the vertex volume fractions - for (unsigned long matID = 0; matID < m_mapData.m_vertexVolumeFractions.size(); ++matID) - { - for (unsigned long vID = 0; vID < cellToMerge.m_mapData.m_vertexVolumeFractions[matID].size(); ++vID) - { - m_mapData.m_vertexVolumeFractions[matID].push_back(cellToMerge.m_mapData.m_vertexVolumeFractions[matID][vID]); - } - } + // Merge the vertex positions + for(unsigned long i = 0; i < cellToMerge.m_mapData.m_vertexPositions.size(); + ++i) + { + m_mapData.m_vertexPositions.push_back( + cellToMerge.m_mapData.m_vertexPositions[i]); + } - // Merge the elements' dominant materials - for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementDominantMaterials.size(); ++i) + // Merge the vertex volume fractions + for(unsigned long matID = 0; matID < m_mapData.m_vertexVolumeFractions.size(); + ++matID) + { + for(unsigned long vID = 0; + vID < cellToMerge.m_mapData.m_vertexVolumeFractions[matID].size(); + ++vID) { - m_mapData.m_elementDominantMaterials.push_back(cellToMerge.m_mapData.m_elementDominantMaterials[i]); + m_mapData.m_vertexVolumeFractions[matID].push_back( + cellToMerge.m_mapData.m_vertexVolumeFractions[matID][vID]); } + } - // Merge the elements' parent ids - for (unsigned long i = 0; i < cellToMerge.m_mapData.m_elementParents.size(); ++i) - { - m_mapData.m_elementParents.push_back(cellToMerge.m_mapData.m_elementParents[i]); - } - - // Merge the elements' shape types - for (unsigned long i = 0; i < cellToMerge.m_mapData.m_shapeTypes.size(); ++i) - { - m_mapData.m_shapeTypes.push_back(cellToMerge.m_mapData.m_shapeTypes[i]); - } + // Merge the elements' dominant materials + for(unsigned long i = 0; + i < cellToMerge.m_mapData.m_elementDominantMaterials.size(); + ++i) + { + m_mapData.m_elementDominantMaterials.push_back( + cellToMerge.m_mapData.m_elementDominantMaterials[i]); + } - // Merge the total number of verts and elems in the resulting cell - m_numVerts += cellToMerge.m_numVerts; - m_numElems += cellToMerge.m_numElems; + // Merge the elements' parent ids + for(unsigned long i = 0; i < cellToMerge.m_mapData.m_elementParents.size(); ++i) + { + m_mapData.m_elementParents.push_back( + cellToMerge.m_mapData.m_elementParents[i]); } - //-------------------------------------------------------------------------------- + // Merge the elements' shape types + for(unsigned long i = 0; i < cellToMerge.m_mapData.m_shapeTypes.size(); ++i) + { + m_mapData.m_shapeTypes.push_back(cellToMerge.m_mapData.m_shapeTypes[i]); + } + // Merge the total number of verts and elems in the resulting cell + m_numVerts += cellToMerge.m_numVerts; + m_numElems += cellToMerge.m_numElems; } -} + +//-------------------------------------------------------------------------------- + +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/CellData.hpp index 93caca8fb4..168a66bde6 100644 --- a/src/axom/mir/CellData.hpp +++ b/src/axom/mir/CellData.hpp @@ -25,35 +25,35 @@ namespace axom { namespace mir { - - /** +/** * \struct CellTopologyData * * \brief Struct for collecting data that specifies a mesh or cell's connectivity/topology. */ - struct CellTopologyData - { - std::vector m_evInds; - std::vector m_evBegins; - std::vector m_veInds; - std::vector m_veBegins; - }; +struct CellTopologyData +{ + std::vector m_evInds; + std::vector m_evBegins; + std::vector m_veInds; + std::vector m_veBegins; +}; - /** +/** * \struct CellMapData * * \brief Struct for collecting the data that will populate a mesh's map data structures. */ - struct CellMapData - { - std::vector m_vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap - std::vector > m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap - std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap - std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap - std::vector m_shapeTypes; // Data that goes into MIRMesh's shapeType IntMap - }; +struct CellMapData +{ + std::vector m_vertexPositions; // Data that goes into MIRMesh's vertexPositions PointMap + std::vector> + m_vertexVolumeFractions; // Data that goes into MIRMesh's materialVolumeFractionsVertex ScalarMap + std::vector m_elementDominantMaterials; // Data that goes into MIRMesh's elementDominantColors IntMap + std::vector m_elementParents; // Data that goes into MIRMesh's elementParentIDs IntMap + std::vector m_shapeTypes; // Data that goes into MIRMesh's shapeType IntMap +}; - /** +/** * \class CellData * * \brief The CellData class represents an arbitrary number of cells that are @@ -64,34 +64,34 @@ namespace mir * data while processing a mesh, and to be used as input to the MIRMesh class * to fully initialize it. */ - class CellData - { - public: - /** +class CellData +{ +public: + /** * \brief Default constructor. */ - CellData(); + CellData(); - /** + /** * \brief Default destructor. */ - ~CellData() = default; + ~CellData() = default; - /** + /** * \brief Merges the cell data from the given cell into this cell. * * \param cellToMerge The cell whose data will be taken and merged in. */ - void mergeCell(const CellData& cellToMerge); + void mergeCell(const CellData& cellToMerge); - public: - int m_numVerts; - int m_numElems; +public: + int m_numVerts; + int m_numElems; - CellTopologyData m_topology; - CellMapData m_mapData; - }; -} -} + CellTopologyData m_topology; + CellMapData m_mapData; +}; +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/CellGenerator.cpp index 888bfb24a2..a18dbdc9e6 100644 --- a/src/axom/mir/CellGenerator.cpp +++ b/src/axom/mir/CellGenerator.cpp @@ -5,92 +5,86 @@ #include "CellGenerator.hpp" - namespace axom { namespace mir { - //-------------------------------------------------------------------------------- -CellGenerator::CellGenerator() -{ - -} +CellGenerator::CellGenerator() { } //-------------------------------------------------------------------------------- -CellGenerator::~CellGenerator() -{ - -} +CellGenerator::~CellGenerator() { } //-------------------------------------------------------------------------------- -void CellGenerator::generateTopologyData(const std::map >& newElements, - const std::map >& newVertices, - CellData& out_cellData) +void CellGenerator::generateTopologyData( + const std::map>& newElements, + const std::map>& newVertices, + CellData& out_cellData) { - // Store the evInds and evBegins data in the output vectors - int currentEVBeginIndex = 0; - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) - { - // Push the start index of the next element - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); + // Store the evInds and evBegins data in the output vectors + int currentEVBeginIndex = 0; + for(auto itr = newElements.begin(); itr != newElements.end(); itr++) + { + // Push the start index of the next element + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - // Push the next element's vertices - for (unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) - { - out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); - ++currentEVBeginIndex; - } + // Push the next element's vertices + for(unsigned int vIndex = 0; vIndex < itr->second.size(); ++vIndex) + { + out_cellData.m_topology.m_evInds.push_back(itr->second[vIndex]); + ++currentEVBeginIndex; } + } - // Push the index that occurs after the last vertex - out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - - // Store the veInds and veBegins data in the output vectors - int currentVEBeginIndex = 0; - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) - { - // Push the start index of the vertex's elements - out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); + // Push the index that occurs after the last vertex + out_cellData.m_topology.m_evBegins.push_back(currentEVBeginIndex); - // Push the next vertex's elements - for (unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) - { - out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); - ++currentVEBeginIndex; - } - } - - // Push the index that occurs after the last element + // Store the veInds and veBegins data in the output vectors + int currentVEBeginIndex = 0; + for(auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + { + // Push the start index of the vertex's elements out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); + + // Push the next vertex's elements + for(unsigned int eIndex = 0; eIndex < itr->second.size(); eIndex++) + { + out_cellData.m_topology.m_veInds.push_back(itr->second[eIndex]); + ++currentVEBeginIndex; + } + } + + // Push the index that occurs after the last element + out_cellData.m_topology.m_veBegins.push_back(currentVEBeginIndex); } //-------------------------------------------------------------------------------- -void CellGenerator::generateVertexPositions(const mir::Shape shapeType, - const std::map>& newVertices, - const std::vector& vertexPositions, - axom::float64* tValues, - CellData& out_cellData) +void CellGenerator::generateVertexPositions( + const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData) { - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + for(auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - if ( vID < mir::utilities::numVerts(shapeType) ) + if(vID < mir::utilities::numVerts(shapeType)) { // This vertex is one of the shape's original vertices - out_cellData.m_mapData.m_vertexPositions.push_back( vertexPositions[vID] ); + out_cellData.m_mapData.m_vertexPositions.push_back(vertexPositions[vID]); } - else if ( mir::utilities::isCenterVertex(shapeType, vID) ) + else if(mir::utilities::isCenterVertex(shapeType, vID)) { // Average the vertex position values at the corners of the shape mir::Point2 centroid = mir::utilities::computeAveragePoint(vertexPositions); - out_cellData.m_mapData.m_vertexPositions.push_back( centroid ); + out_cellData.m_mapData.m_vertexPositions.push_back(centroid); } else { @@ -99,38 +93,45 @@ void CellGenerator::generateVertexPositions(const mir::Shape shapeType, int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); out_cellData.m_mapData.m_vertexPositions.push_back( - mir::Point2::lerp( vertexPositions[vIDFrom], vertexPositions[vIDTo], tValues[vID] ) ); + mir::Point2::lerp(vertexPositions[vIDFrom], + vertexPositions[vIDTo], + tValues[vID])); } } } //-------------------------------------------------------------------------------- -void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, - const std::map>& newVertices, - const std::vector >& vertexVF, - axom::float64* tValues, - CellData& out_cellData) +void CellGenerator::generateVertexVolumeFractions( + const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector>& vertexVF, + axom::float64* tValues, + CellData& out_cellData) { - out_cellData.m_mapData.m_vertexVolumeFractions.resize(vertexVF.size()); // vertexVF size is the number of materials + out_cellData.m_mapData.m_vertexVolumeFractions.resize( + vertexVF.size()); // vertexVF size is the number of materials - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + for(auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; - - for (unsigned long matID = 0; matID < vertexVF.size(); ++matID) + + for(unsigned long matID = 0; matID < vertexVF.size(); ++matID) { - if ( vID < mir::utilities::numVerts(shapeType) ) + if(vID < mir::utilities::numVerts(shapeType)) { // This vertex is one of the shape's original vertices - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( vertexVF[matID][vID] ); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( + vertexVF[matID][vID]); } - else if ( mir::utilities::isCenterVertex(shapeType, vID) ) + else if(mir::utilities::isCenterVertex(shapeType, vID)) { // Average the vertex volume fractions values at the corners of the shape - axom::float64 averageValue = mir::utilities::computeAverageFloat( vertexVF[matID] ); + axom::float64 averageValue = + mir::utilities::computeAverageFloat(vertexVF[matID]); - out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( averageValue ); + out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( + averageValue); } else { @@ -139,7 +140,9 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, int vIDTo = mir::utilities::getEdgeEndpoint(shapeType, vID, false); out_cellData.m_mapData.m_vertexVolumeFractions[matID].push_back( - axom::utilities::lerp( vertexVF[matID][vIDFrom], vertexVF[matID][vIDTo], tValues[vID] ) ); + axom::utilities::lerp(vertexVF[matID][vIDFrom], + vertexVF[matID][vIDTo], + tValues[vID])); } } } @@ -147,28 +150,29 @@ void CellGenerator::generateVertexVolumeFractions(const mir::Shape shapeType, //-------------------------------------------------------------------------------- -int CellGenerator::determineCleanCellMaterial(const Shape elementShape, - const std::vector& vertexIDs, - const int matOne, - const int matTwo, - const std::vector >& vertexVF) +int CellGenerator::determineCleanCellMaterial( + const Shape elementShape, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector>& vertexVF) { int dominantMaterial = matOne; axom::float64 matOneVF = -1.0; axom::float64 matTwoVF = -1.0; - for (unsigned long it = 0; it < vertexIDs.size(); ++it) + for(unsigned long it = 0; it < vertexIDs.size(); ++it) { int vID = vertexIDs[it]; - if ( vID >= 0 && vID < mir::utilities::numVerts(elementShape) ) + if(vID >= 0 && vID < mir::utilities::numVerts(elementShape)) { - if (matOne != NULL_MAT) + if(matOne != NULL_MAT) { matOneVF = vertexVF[matOne][vID]; } - if (matTwo != NULL_MAT) + if(matTwo != NULL_MAT) { matTwoVF = vertexVF[matTwo][vID]; } @@ -176,11 +180,11 @@ int CellGenerator::determineCleanCellMaterial(const Shape elementShape, dominantMaterial = (matOneVF > matTwoVF) ? matOne : matTwo; } } - + return dominantMaterial; } //-------------------------------------------------------------------------------- -} -} +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/CellGenerator.hpp b/src/axom/mir/CellGenerator.hpp index 94df6a3f3e..cabf4840ef 100644 --- a/src/axom/mir/CellGenerator.hpp +++ b/src/axom/mir/CellGenerator.hpp @@ -30,42 +30,39 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- - /** +/** * \class CellGenerator * * \brief A class that generates that uses the clipping information to generate * the data needed in order to produce a clean, reconstructed mesh. */ - class CellGenerator - { - public: - - /** +class CellGenerator +{ +public: + /** * \brief Default constructor. */ - CellGenerator(); + CellGenerator(); - /** + /** * \brief Default destructor. */ - ~CellGenerator(); + ~CellGenerator(); - /** + /** * \brief Generates the topology of the new elements resulting from a split. * * \param newElements An ordered map of the generated elements' IDs to a list of the vertex IDs in the local frame of the output element. * \param newVertices An order map of the vertex IDs in the local frame of the output element to the generated elements' IDs it is associated with. * \param out_cellData Container to store the topology data of the generated elements. */ - void generateTopologyData(const std::map >& newElements, - const std::map >& newVertices, - CellData& out_cellData); - + void generateTopologyData(const std::map>& newElements, + const std::map>& newVertices, + CellData& out_cellData); - /** + /** * \brief Generates the vertex positions values for each of the new vertices of the generated element. * * \param shapeType The shape type of the element. @@ -76,14 +73,13 @@ namespace mir * * \note New vertex positions are interpolated from the original vertex positions. */ - void generateVertexPositions(const mir::Shape shapeType, - const std::map>& newVertices, - const std::vector& vertexPositions, - axom::float64* tValues, - CellData& out_cellData); + void generateVertexPositions(const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector& vertexPositions, + axom::float64* tValues, + CellData& out_cellData); - - /** + /** * \brief Generates the vertex volume fractions for each of the new vertices of the generated element. * * \param shapeType The shape type of the element. @@ -94,13 +90,14 @@ namespace mir * * \note New vertex positions are interpolated from the original vertex volume fractions. */ - void generateVertexVolumeFractions(const mir::Shape shapeType, - const std::map>& newVertices, - const std::vector >& vertexVF, - axom::float64* tValues, - CellData& out_cellData); + void generateVertexVolumeFractions( + const mir::Shape shapeType, + const std::map>& newVertices, + const std::vector>& vertexVF, + axom::float64* tValues, + CellData& out_cellData); - /** + /** * \brief Determines the more dominant material of the two given for the given element. * * \param shapeType An enumerator denoting the element's shape. @@ -117,16 +114,17 @@ namespace mir * * \note It is assumed that the given cell is one that results from splitting its parent cell. */ - int determineCleanCellMaterial(const Shape shapeType, - const std::vector& vertexIDs, - const int matOne, - const int matTwo, - const std::vector >& vertexVF); - }; + int determineCleanCellMaterial( + const Shape shapeType, + const std::vector& vertexIDs, + const int matOne, + const int matTwo, + const std::vector>& vertexVF); +}; //-------------------------------------------------------------------------------- -} -} +} // namespace mir +} // namespace axom #endif \ No newline at end of file diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 3964a0c6c2..20eaec804a 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -13,7 +13,7 @@ #include "axom/mir/CoordsetBlender.hpp" #include "axom/mir/FieldSlicer.hpp" #include "axom/mir/blueprint_utilities.hpp" -#include "axom/mir/utilities.hpp" // for cpp2conduit +#include "axom/mir/utilities.hpp" // for cpp2conduit #include #include @@ -33,8 +33,9 @@ struct scans { using loop_policy = typename axom::execution_space::loop_policy; assert(input.size() == output.size()); - RAJA::exclusive_scan(RAJA::make_span(input.data(), input.size()), - RAJA::make_span(output.data(), output.size())); + RAJA::exclusive_scan( + RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size())); } }; @@ -45,7 +46,7 @@ inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) s.exclusive_scan(input, output); } -} // end namespace axom +} // end namespace axom namespace axom { @@ -55,15 +56,13 @@ namespace clipping { namespace details { - /** * \brief Turns a Conduit "shape_map" node into an easier STL-representation. * * \param n_shape_map The node that contains the shape_map. * \return An STL-representation of the shape_map. */ -std::map -shapeMap_NameValue(const conduit::Node &n_shape_map) +std::map shapeMap_NameValue(const conduit::Node &n_shape_map) { std::map sm; for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) @@ -79,8 +78,7 @@ shapeMap_NameValue(const conduit::Node &n_shape_map) * \param n_shape_map The node that contains the shape_map. * \return An STL-representation of the shape_map. */ -std::map -shapeMap_ValueName(const conduit::Node &n_shape_map) +std::map shapeMap_ValueName(const conduit::Node &n_shape_map) { std::map sm; for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) @@ -98,19 +96,32 @@ shapeMap_ValueName(const conduit::Node &n_shape_map) * \return The Shape::id() value that matches the st_index, or 0 if there is no match. */ template -AXOM_HOST_DEVICE -int ST_Index_to_ShapeID(IntegerType st_index) +AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) { int shapeID = 0; switch(st_index) { - case ST_LIN: shapeID = views::Line_ShapeID; break; - case ST_TRI: shapeID = views::Tri_ShapeID; break; - case ST_QUA: shapeID = views::Quad_ShapeID; break; - case ST_TET: shapeID = views::Tet_ShapeID; break; - case ST_PYR: shapeID = views::Pyramid_ShapeID; break; - case ST_WDG: shapeID = views::Wedge_ShapeID; break; - case ST_HEX: shapeID = views::Hex_ShapeID; break; + case ST_LIN: + shapeID = views::Line_ShapeID; + break; + case ST_TRI: + shapeID = views::Tri_ShapeID; + break; + case ST_QUA: + shapeID = views::Quad_ShapeID; + break; + case ST_TET: + shapeID = views::Tet_ShapeID; + break; + case ST_PYR: + shapeID = views::Pyramid_ShapeID; + break; + case ST_WDG: + shapeID = views::Wedge_ShapeID; + break; + case ST_HEX: + shapeID = views::Hex_ShapeID; + break; } return shapeID; } @@ -122,8 +133,7 @@ int ST_Index_to_ShapeID(IntegerType st_index) * * \return A map of Conduit shape name to Shape::id() value. */ -std::map -shapeMap_FromFlags(std::uint64_t shapes) +std::map shapeMap_FromFlags(std::uint64_t shapes) { std::map sm; @@ -162,12 +172,24 @@ int getClipTableIndex(int shapeId) int index = 0; switch(shapeId) { - case views::Tri_ShapeID: index = 0; break; - case views::Quad_ShapeID: index = 1; break; - case views::Tet_ShapeID: index = 2; break; - case views::Pyramid_ShapeID: index = 3; break; - case views::Wedge_ShapeID: index = 4; break; - case views::Hex_ShapeID: index = 5; break; + case views::Tri_ShapeID: + index = 0; + break; + case views::Quad_ShapeID: + index = 1; + break; + case views::Tet_ShapeID: + index = 2; + break; + case views::Pyramid_ShapeID: + index = 3; + break; + case views::Wedge_ShapeID: + index = 4; + break; + case views::Hex_ShapeID: + index = 5; + break; } return index; } @@ -187,16 +209,15 @@ bool color1Selected(int selection) AXOM_HOST_DEVICE bool generatedPointIsSelected(unsigned char color, int selection) { - return color == NOCOLOR || - (color0Selected(selection) && color == COLOR0) || - (color1Selected(selection) && color == COLOR1); + return color == NOCOLOR || (color0Selected(selection) && color == COLOR0) || + (color1Selected(selection) && color == COLOR1); } AXOM_HOST_DEVICE bool shapeIsSelected(unsigned char color, int selection) { return (color0Selected(selection) && color == COLOR0) || - (color1Selected(selection) && color == COLOR1); + (color1Selected(selection) && color == COLOR1); } /** @@ -212,7 +233,10 @@ AXOM_HOST_DEVICE inline float computeWeight(float d0, float d1, float clipValue) { constexpr float tiny = 1.e-09; - return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / (axom::utilities::abs(d1 - d0) + tiny), 0.f, 1.f); + return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / + (axom::utilities::abs(d1 - d0) + tiny), + 0.f, + 1.f); } // TODO: Could we make ZoneType be a concept? @@ -229,8 +253,9 @@ inline float computeWeight(float d0, float d1, float clipValue) * \return The index of the clipping case. */ template -AXOM_HOST_DEVICE -size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, DataType clipValue) +AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, + const axom::ArrayView &view, + DataType clipValue) { size_t clipcase = 0; for(IndexType i = 0; i < zone.numberOfNodes(); i++) @@ -242,7 +267,7 @@ size_t clip_case(const ZoneType &zone, const axom::ArrayView &view, Da return clipcase; } -} // end namespace details +} // end namespace details /** * \brief This class provides a kind of schema over the clipping options, as well @@ -252,9 +277,11 @@ template class ClipOptions { public: - ClipOptions(axom::IndexType nzones, const conduit::Node &options) : m_nzones(nzones), m_options(options), m_selectedZones() - { - } + ClipOptions(axom::IndexType nzones, const conduit::Node &options) + : m_nzones(nzones) + , m_options(options) + , m_selectedZones() + { } /** * \brief Return a view that contains the list of selected zone ids for the mesh. @@ -262,18 +289,14 @@ class ClipOptions */ axom::ArrayView selectedZonesView() { - if(m_selectedZones.size() == 0) - buildSelectedZones(); + if(m_selectedZones.size() == 0) buildSelectedZones(); return m_selectedZones.view(); } /** * \brief Invalidate the selected zones array (due to options changing) so we can rebuild it. */ - void invalidateSelectedZones() - { - m_selectedZones.clear(); - } + void invalidateSelectedZones() { m_selectedZones.clear(); } /** * \brief Return the name of the field used for clipping. @@ -290,7 +313,9 @@ class ClipOptions */ float clipValue() const { - return m_options.has_child("clipValue") ? m_options.fetch_existing("clipValue").to_float() : 0.f; + return m_options.has_child("clipValue") + ? m_options.fetch_existing("clipValue").to_float() + : 0.f; } /** @@ -337,7 +362,9 @@ class ClipOptions */ bool inside() const { - return m_options.has_path("inside") ? (m_options.fetch_existing("inside").to_int() > 0) : true; + return m_options.has_path("inside") + ? (m_options.fetch_existing("inside").to_int() > 0) + : true; } /** @@ -346,7 +373,9 @@ class ClipOptions */ bool outside() const { - return m_options.has_path("outside") ? (m_options.fetch_existing("outside").to_int() > 0) : false; + return m_options.has_path("outside") + ? (m_options.fetch_existing("outside").to_int() > 0) + : false; } /** @@ -375,7 +404,8 @@ class ClipOptions { // No options were specified. Allow all fields with same topology as clipField. const conduit::Node &n_clipField = n_fields.fetch_existing(clipField()); - const std::string topoName = n_clipField.fetch_existing("topology").as_string(); + const std::string topoName = + n_clipField.fetch_existing("topology").as_string(); for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { if(topoName == n_fields[i].fetch_existing("topology").as_string()) @@ -383,7 +413,7 @@ class ClipOptions } } return f; - } + } private: /** @@ -399,29 +429,33 @@ class ClipOptions { // Store the zone list in m_selectedZones. int badValueCount = 0; - views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) - { - using loop_policy = typename axom::execution_space::loop_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; + views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) { + using loop_policy = + typename axom::execution_space::loop_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; // It probably does not make sense to request more zones than we have in the mesh. SLIC_ASSERT(zonesView.size() <= m_nzones); - m_selectedZones = axom::Array(zonesView.size(), zonesView.size(), allocatorID); + m_selectedZones = axom::Array(zonesView.size(), + zonesView.size(), + allocatorID); auto szView = m_selectedZones.view(); - axom::for_all(szView.size(), AXOM_LAMBDA(auto index) - { - szView[index] = zonesView[index]; - }); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); // Check that the selected zone values are in range. const auto nzones = m_nzones; RAJA::ReduceSum errReduce(0); - axom::for_all(szView.size(), AXOM_LAMBDA(auto index) - { - const int err = (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; - errReduce += err; - }); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { + const int err = + (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + errReduce += err; + }); badValueCount = errReduce.get(); // Make sure the selectedZones are sorted. @@ -436,19 +470,19 @@ class ClipOptions else { // Select all zones. - m_selectedZones = axom::Array(m_nzones, m_nzones, allocatorID); + m_selectedZones = + axom::Array(m_nzones, m_nzones, allocatorID); auto szView = m_selectedZones.view(); - axom::for_all(m_nzones, AXOM_LAMBDA(auto zoneIndex) - { - szView[zoneIndex] = zoneIndex; - }); + axom::for_all( + m_nzones, + AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); } } private: - axom::IndexType m_nzones; // The number of zones in the associated topology. - const conduit::Node &m_options; // A reference to the clipping options node. - axom::Array m_selectedZones; // Storage for a list of selected zone ids. + axom::IndexType m_nzones; // The number of zones in the associated topology. + const conduit::Node &m_options; // A reference to the clipping options node. + axom::Array m_selectedZones; // Storage for a list of selected zone ids. }; //------------------------------------------------------------------------------ @@ -468,21 +502,22 @@ class BlendGroupBuilder */ struct State { - IndexType m_nzones; - - axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. - axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. - axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. - axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. - - axom::ArrayView m_blendNamesView; // Blend group names - axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. - axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. - axom::ArrayView m_blendIdsView; // blend group ids. - axom::ArrayView m_blendCoeffView; // blend group weights. - - axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. + IndexType m_nzones; + // clang-format off + axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. + axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. + axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. + axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. + + axom::ArrayView m_blendNamesView; // Blend group names + axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. + axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. + axom::ArrayView m_blendIdsView; // blend group ids. + axom::ArrayView m_blendCoeffView; // blend group weights. + + axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. + // clang-format on }; /** @@ -490,7 +525,7 @@ class BlendGroupBuilder * \return A reference to the state. */ State &state() { return m_state; } - const State &state() const {return m_state; } + const State &state() const { return m_state; } /** * \brief Set the number of zones. @@ -514,16 +549,18 @@ class BlendGroupBuilder */ void computeBlendGroupSizes(IndexType &bgSum, IndexType &bgLenSum) { - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; RAJA::ReduceSum blendGroups_sum(0); RAJA::ReduceSum blendGroupLen_sum(0); const auto localBlendGroupsView = m_state.m_blendGroupsView; const auto localBlendGroupsLenView = m_state.m_blendGroupsLenView; - axom::for_all(m_state.m_nzones, AXOM_LAMBDA(auto zoneIndex) - { - blendGroups_sum += localBlendGroupsView[zoneIndex]; - blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; - }); + axom::for_all( + m_state.m_nzones, + AXOM_LAMBDA(auto zoneIndex) { + blendGroups_sum += localBlendGroupsView[zoneIndex]; + blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; + }); bgSum = blendGroups_sum.get(); bgLenSum = blendGroupLen_sum.get(); } @@ -546,8 +583,10 @@ class BlendGroupBuilder */ void computeBlendGroupOffsets() { - axom::exclusive_scan(m_state.m_blendGroupsLenView, m_state.m_blendOffsetView); - axom::exclusive_scan(m_state.m_blendGroupsView, m_state.m_blendGroupOffsetsView); + axom::exclusive_scan(m_state.m_blendGroupsLenView, + m_state.m_blendOffsetView); + axom::exclusive_scan(m_state.m_blendGroupsView, + m_state.m_blendGroupOffsetsView); } /** @@ -572,7 +611,8 @@ class BlendGroupBuilder * \param uniqueNames A view containing unique, sorted blend group names. * \param uniqueIndices A view containing the original blend group index for each unique name. */ - void setUniqueNames(const axom::ArrayView &uniqueNames, const axom::ArrayView &uniqueIndices) + void setUniqueNames(const axom::ArrayView &uniqueNames, + const axom::ArrayView &uniqueIndices) { m_state.m_blendUniqueNamesView = uniqueNames; m_state.m_blendUniqueIndicesView = uniqueIndices; @@ -582,7 +622,10 @@ class BlendGroupBuilder * \brief Get the blend names view. * \return The blend names view. */ - const axom::ArrayView &blendNames() const { return m_state.m_blendNamesView; } + const axom::ArrayView &blendNames() const + { + return m_state.m_blendNamesView; + } /** * \brief This class helps us manage blend group creation and usage for blend groups within a single zone. @@ -618,10 +661,7 @@ class BlendGroupBuilder * \brief Start creating a new blend group within the allocated space for this zone. */ AXOM_HOST_DEVICE - inline void beginGroup() - { - m_currentDataOffset = m_startOffset; - } + inline void beginGroup() { m_currentDataOffset = m_startOffset; } /** * \brief Add a new blend point in the current blend group. @@ -655,15 +695,20 @@ class BlendGroupBuilder KeyType blendName; if(numIds == 1) { - blendName = axom::mir::utilities::make_name_1(m_state->m_blendIdsView[m_startOffset]); + blendName = axom::mir::utilities::make_name_1( + m_state->m_blendIdsView[m_startOffset]); } else if(numIds == 2) { - blendName = axom::mir::utilities::make_name_2(m_state->m_blendIdsView[m_startOffset], m_state->m_blendIdsView[m_startOffset + 1]); + blendName = axom::mir::utilities::make_name_2( + m_state->m_blendIdsView[m_startOffset], + m_state->m_blendIdsView[m_startOffset + 1]); } else { - blendName = axom::mir::utilities::make_name_n(m_state->m_blendIdsView.data() + m_startOffset, numIds); + blendName = axom::mir::utilities::make_name_n( + m_state->m_blendIdsView.data() + m_startOffset, + numIds); } m_state->m_blendNamesView[m_blendGroupId] = blendName; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) @@ -712,7 +757,8 @@ class BlendGroupBuilder AXOM_HOST_DEVICE inline IndexType uniqueBlendGroupIndex() const { - return axom::mir::utilities::bsearch(name(), m_state->m_blendUniqueNamesView); + return axom::mir::utilities::bsearch(name(), + m_state->m_blendUniqueNamesView); } /** @@ -773,7 +819,8 @@ class BlendGroupBuilder { const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; - return axom::ArrayView(m_state->m_blendIdsView.data() + offset, n); + return axom::ArrayView(m_state->m_blendIdsView.data() + offset, + n); } /** @@ -785,17 +832,18 @@ class BlendGroupBuilder { const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; - return axom::ArrayView(m_state->m_blendCoeffsView.data() + offset, n); + return axom::ArrayView(m_state->m_blendCoeffsView.data() + offset, + n); } private: friend class BlendGroupBuilder; - IndexType m_zoneIndex; // The zone that owns this set of blend groups. - IndexType m_blendGroupId; // The global blend group index within this current zone. - IndexType m_startOffset; // The data offset for the first ids/weights in this blend group. - IndexType m_currentDataOffset; // The current data offset. - State *m_state; // Pointer to the main state. + IndexType m_zoneIndex; // The zone that owns this set of blend groups. + IndexType m_blendGroupId; // The global blend group index within this current zone. + IndexType m_startOffset; // The data offset for the first ids/weights in this blend group. + IndexType m_currentDataOffset; // The current data offset. + State *m_state; // Pointer to the main state. }; /** @@ -817,7 +865,8 @@ class BlendGroupBuilder // Global blend group id for the first blend group in this zone. groups.m_blendGroupId = m_state.m_blendGroupOffsetsView[zoneIndex]; // Global start - groups.m_startOffset = groups.m_currentDataOffset = m_state.m_blendOffsetView[zoneIndex]; + groups.m_startOffset = groups.m_currentDataOffset = + m_state.m_blendOffsetView[zoneIndex]; groups.m_state = const_cast(&m_state); return groups; @@ -828,12 +877,12 @@ class BlendGroupBuilder * * \return A BlendData object suitable for making new fields and coordsets. */ - axom::mir::utilities::blueprint::BlendData - makeBlendData() const + axom::mir::utilities::blueprint::BlendData makeBlendData() const { axom::mir::utilities::blueprint::BlendData blend; - blend.m_selectedIndicesView = m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices + blend.m_selectedIndicesView = + m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices blend.m_blendGroupSizesView = m_state.m_blendGroupSizesView; blend.m_blendGroupStartView = m_state.m_blendGroupStartView; blend.m_blendIdsView = m_state.m_blendIdsView; @@ -876,9 +925,11 @@ class ClipField * \param coordsetView A coordset view suitable for the supplied coordset. * */ - ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) : m_topologyView(topoView), m_coordsetView(coordsetView), m_clipTables() - { - } + ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) + : m_topologyView(topoView) + , m_coordsetView(coordsetView) + , m_clipTables() + { } /** * \brief Execute the clipping operation using the data stored in the specified \a clipField. @@ -893,21 +944,25 @@ class ClipField const conduit::Node &n_options, conduit::Node &n_output) { - ClipOptions opts(0, n_options); - const std::string clipFieldName = opts.clipField(); - - const conduit::Node &n_fields = n_input.fetch_existing("fields"); - const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); - const std::string &topoName = n_clipField["topology"].as_string(); - const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); - const std::string &coordsetName = n_topo["coordset"].as_string(); - const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); - - execute(n_topo, n_coordset, n_fields, - n_options, - n_output["topologies/" + opts.topologyName(topoName)], - n_output["coordsets/" + opts.coordsetName(coordsetName)], - n_output["fields"]); + ClipOptions opts(0, n_options); + const std::string clipFieldName = opts.clipField(); + + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); + const std::string &topoName = n_clipField["topology"].as_string(); + const conduit::Node &n_topo = + n_input.fetch_existing("topologies/" + topoName); + const std::string &coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); + + execute(n_topo, + n_coordset, + n_fields, + n_options, + n_output["topologies/" + opts.topologyName(topoName)], + n_output["coordsets/" + opts.coordsetName(coordsetName)], + n_output["fields"]); } /** @@ -947,14 +1002,25 @@ class ClipField createClipTableViews(clipTableViews, m_topologyView.dimension()); // Allocate some memory and store views in ZoneData, FragmentData. - axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. - axom::Array pointsUsed(nzones, nzones, allocatorID); // Which points are used over all selected fragments in a zone + axom::Array clipCases(nzones, + nzones, + allocatorID); // The clip case for a zone. + axom::Array pointsUsed( + nzones, + nzones, + allocatorID); // Which points are used over all selected fragments in a zone ZoneData zoneData; zoneData.m_clipCasesView = clipCases.view(); zoneData.m_pointsUsedView = pointsUsed.view(); - axom::Array fragments(nzones, nzones, allocatorID); // The number of fragments (child zones) produced for a zone. - axom::Array fragmentsSize(nzones, nzones, allocatorID); // The connectivity size for all selected fragments in a zone. + axom::Array fragments( + nzones, + nzones, + allocatorID); // The number of fragments (child zones) produced for a zone. + axom::Array fragmentsSize( + nzones, + nzones, + allocatorID); // The connectivity size for all selected fragments in a zone. axom::Array fragmentOffsets(nzones, nzones, allocatorID); axom::Array fragmentSizeOffsets(nzones, nzones, allocatorID); @@ -964,17 +1030,34 @@ class ClipField fragmentData.m_fragmentOffsetsView = fragmentOffsets.view(); fragmentData.m_fragmentSizeOffsetsView = fragmentSizeOffsets.view(); - axom::Array blendGroups(nzones, nzones, allocatorID); // Number of blend groups in a zone. - axom::Array blendGroupsLen(nzones, nzones, allocatorID); // Length of the blend groups in a zone. - axom::Array blendOffset(nzones, nzones, allocatorID); // Start of zone's blend group indices - axom::Array blendGroupOffsets(nzones, nzones, allocatorID); // Start of zone's blend group offsets in definitions. + axom::Array blendGroups( + nzones, + nzones, + allocatorID); // Number of blend groups in a zone. + axom::Array blendGroupsLen( + nzones, + nzones, + allocatorID); // Length of the blend groups in a zone. + axom::Array blendOffset( + nzones, + nzones, + allocatorID); // Start of zone's blend group indices + axom::Array blendGroupOffsets( + nzones, + nzones, + allocatorID); // Start of zone's blend group offsets in definitions. // Make an object to help manage building the blend groups. BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, n_clip_field_values); + computeSizes(clipTableViews, + builder, + zoneData, + fragmentData, + opts, + n_clip_field_values); computeFragmentSizes(fragmentData); computeFragmentOffsets(fragmentData); @@ -985,37 +1068,66 @@ class ClipField // Allocate memory for blend groups. axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendGroupStart(blendGroupsSize, blendGroupsSize, allocatorID); - axom::Array blendIds(blendGroupLenSize, blendGroupLenSize, allocatorID); - axom::Array blendCoeff(blendGroupLenSize, blendGroupLenSize, allocatorID); + axom::Array blendGroupSizes(blendGroupsSize, + blendGroupsSize, + allocatorID); + axom::Array blendGroupStart(blendGroupsSize, + blendGroupsSize, + allocatorID); + axom::Array blendIds(blendGroupLenSize, + blendGroupLenSize, + allocatorID); + axom::Array blendCoeff(blendGroupLenSize, + blendGroupLenSize, + allocatorID); // Make the blend groups. - builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + builder.setBlendViews(blendNames.view(), + blendGroupSizes.view(), + blendGroupStart.view(), + blendIds.view(), + blendCoeff.view()); makeBlendGroups(clipTableViews, builder, zoneData, opts, n_clip_field_values); // Make the blend groups unique axom::Array uNames; axom::Array uIndices; - axom::mir::utilities::unique(builder.blendNames(), uNames, uIndices); - builder.setUniqueNames(uNames.view(), uIndices.view()); + axom::mir::utilities::unique(builder.blendNames(), + uNames, + uIndices); + builder.setUniqueNames(uNames.view(), uIndices.view()); axom::mir::utilities::blueprint::BlendData blend = builder.makeBlendData(); // Make the clipped mesh - makeConnectivity(clipTableViews, builder, zoneData, fragmentData, opts, n_newTopo, n_newCoordset, n_newFields); + makeConnectivity(clipTableViews, + builder, + zoneData, + fragmentData, + opts, + n_newTopo, + n_newCoordset, + n_newFields); makeCoordset(blend, n_coordset, n_newCoordset); axom::mir::utilities::blueprint::SliceData slice; - axom::Array sliceIndices(fragmentData.m_finalNumZones, fragmentData.m_finalNumZones, allocatorID); + axom::Array sliceIndices(fragmentData.m_finalNumZones, + fragmentData.m_finalNumZones, + allocatorID); auto sliceIndicesView = sliceIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - const auto start = fragmentData.m_fragmentOffsetsView[zoneIndex]; - for(int i = 0; i < fragmentData.m_fragmentsView[zoneIndex]; i++) - sliceIndicesView[start + i] = zoneIndex; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const auto start = fragmentData.m_fragmentOffsetsView[zoneIndex]; + for(int i = 0; i < fragmentData.m_fragmentsView[zoneIndex]; i++) + sliceIndicesView[start + i] = zoneIndex; + }); slice.m_indicesView = sliceIndicesView; - makeFields(blend, slice, opts.topologyName(n_topo.name()), opts.fields(n_fields), n_fields, n_newFields); + makeFields(blend, + slice, + opts.topologyName(n_topo.name()), + opts.fields(n_fields), + n_fields, + n_newFields); makeOriginalElements(fragmentData, opts, n_fields, n_newTopo, n_newFields); } @@ -1025,12 +1137,12 @@ class ClipField */ struct FragmentData { - IndexType m_finalNumZones{0}; - IndexType m_finalConnSize{0}; - axom::ArrayView m_fragmentsView{}; - axom::ArrayView m_fragmentsSizeView{}; - axom::ArrayView m_fragmentOffsetsView{}; - axom::ArrayView m_fragmentSizeOffsetsView{}; + IndexType m_finalNumZones {0}; + IndexType m_finalConnSize {0}; + axom::ArrayView m_fragmentsView {}; + axom::ArrayView m_fragmentsSizeView {}; + axom::ArrayView m_fragmentOffsetsView {}; + axom::ArrayView m_fragmentSizeOffsetsView {}; }; /** @@ -1038,8 +1150,8 @@ class ClipField */ struct ZoneData { - axom::ArrayView m_clipCasesView{}; - axom::ArrayView m_pointsUsedView{}; + axom::ArrayView m_clipCasesView {}; + axom::ArrayView m_pointsUsedView {}; }; /** @@ -1048,10 +1160,8 @@ class ClipField int getSelection(const ClipOptions &opts) const { int selection = 0; - if(opts.inside()) - axom::utilities::setBitOn(selection, 0); - if(opts.outside()) - axom::utilities::setBitOn(selection, 1); + if(opts.inside()) axom::utilities::setBitOn(selection, 0); + if(opts.outside()) axom::utilities::setBitOn(selection, 1); SLIC_ASSERT(selection > 0); return selection; } @@ -1066,15 +1176,21 @@ class ClipField { if(dimension == -1 || dimension == 2) { - views[details::getClipTableIndex(views::Tri_ShapeID)] = m_clipTables[ST_TRI].view(); - views[details::getClipTableIndex(views::Quad_ShapeID)] = m_clipTables[ST_QUA].view(); + views[details::getClipTableIndex(views::Tri_ShapeID)] = + m_clipTables[ST_TRI].view(); + views[details::getClipTableIndex(views::Quad_ShapeID)] = + m_clipTables[ST_QUA].view(); } if(dimension == -1 || dimension == 3) { - views[details::getClipTableIndex(views::Tet_ShapeID)] = m_clipTables[ST_TET].view(); - views[details::getClipTableIndex(views::Pyramid_ShapeID)] = m_clipTables[ST_PYR].view(); - views[details::getClipTableIndex(views::Wedge_ShapeID)] = m_clipTables[ST_WDG].view(); - views[details::getClipTableIndex(views::Hex_ShapeID)] = m_clipTables[ST_HEX].view(); + views[details::getClipTableIndex(views::Tet_ShapeID)] = + m_clipTables[ST_TET].view(); + views[details::getClipTableIndex(views::Pyramid_ShapeID)] = + m_clipTables[ST_PYR].view(); + views[details::getClipTableIndex(views::Wedge_ShapeID)] = + m_clipTables[ST_WDG].view(); + views[details::getClipTableIndex(views::Hex_ShapeID)] = + m_clipTables[ST_HEX].view(); } } @@ -1091,10 +1207,14 @@ class ClipField * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void computeSizes(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const + void computeSizes(ClipTableViews clipTableViews, + BlendGroupBuilder builder, + ZoneData zoneData, + FragmentData fragmentData, + ClipOptions &opts, + const conduit::Node &n_clip_field_values) const { - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) - { + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { using clip_value_type = typename decltype(clipFieldView)::value_type; const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); @@ -1102,105 +1222,110 @@ class ClipField auto blendGroupsView = builder.state().m_blendGroupsView; auto blendGroupsLenView = builder.state().m_blendGroupsLenView; - m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // Get the clip case for the current zone. - const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); - zoneData.m_clipCasesView[zoneIndex] = clipcase; - - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto &ctView = clipTableViews[clipTableIndex]; - - int thisBlendGroups = 0; // The number of blend groups produced in this case. - int thisBlendGroupLen = 0; // The total length of the blend groups. - int thisFragments = 0; // The number of zone fragments produced in this case. - int thisFragmentsNumIds = 0;// The number of points used to make all the fragment zones. - BitSet ptused = 0; // A bitset indicating which ST_XX nodes are used. - - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) - { - // Get the current shape in the clip case. - const auto fragment = *it; - - if(fragment[0] == ST_PNT) + m_topologyView.template for_selected_zones( + opts.selectedZonesView(), + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + // Get the clip case for the current zone. + const auto clipcase = + details::clip_case(zone, clipFieldView, clipValue); + zoneData.m_clipCasesView[zoneIndex] = clipcase; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto &ctView = clipTableViews[clipTableIndex]; + + int thisBlendGroups = + 0; // The number of blend groups produced in this case. + int thisBlendGroupLen = 0; // The total length of the blend groups. + int thisFragments = + 0; // The number of zone fragments produced in this case. + int thisFragmentsNumIds = + 0; // The number of points used to make all the fragment zones. + BitSet ptused = 0; // A bitset indicating which ST_XX nodes are used. + + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) { - if(details::generatedPointIsSelected(fragment[2], selection)) - { - const int nIds = static_cast(fragment[3]); + // Get the current shape in the clip case. + const auto fragment = *it; - for(int ni = 0; ni < nIds; ni++) + if(fragment[0] == ST_PNT) + { + if(details::generatedPointIsSelected(fragment[2], selection)) { - const auto pid = fragment[4 + ni]; + const int nIds = static_cast(fragment[3]); - // Increase the blend size to include this center point. - if(pid <= P7) - { - // corner point - thisBlendGroupLen++; - } - else if(pid >= EA && pid <= EL) + for(int ni = 0; ni < nIds; ni++) { - // edge point - thisBlendGroupLen += 2; + const auto pid = fragment[4 + ni]; + + // Increase the blend size to include this center point. + if(pid <= P7) + { + // corner point + thisBlendGroupLen++; + } + else if(pid >= EA && pid <= EL) + { + // edge point + thisBlendGroupLen += 2; + } } - } - // This center or face point counts as a blend group. - thisBlendGroups++; + // This center or face point counts as a blend group. + thisBlendGroups++; - // Mark the point used. - axom::utilities::setBitOn(ptused, N0 + fragment[1]); + // Mark the point used. + axom::utilities::setBitOn(ptused, N0 + fragment[1]); + } } - } - else - { - if(details::shapeIsSelected(fragment[1], selection)) + else { - thisFragments++; - const int nIdsThisFragment = fragment.size() - 2; - thisFragmentsNumIds += nIdsThisFragment; - - // Mark the points this fragment used. - for(int i = 2; i < fragment.size(); i++) + if(details::shapeIsSelected(fragment[1], selection)) { - axom::utilities::setBitOn(ptused, fragment[i]); + thisFragments++; + const int nIdsThisFragment = fragment.size() - 2; + thisFragmentsNumIds += nIdsThisFragment; + + // Mark the points this fragment used. + for(int i = 2; i < fragment.size(); i++) + { + axom::utilities::setBitOn(ptused, fragment[i]); + } } } } - } - // Save the flags for the points that were used in this zone - zoneData.m_pointsUsedView[zoneIndex] = ptused; + // Save the flags for the points that were used in this zone + zoneData.m_pointsUsedView[zoneIndex] = ptused; - // Count which points in the original cell are used. - for(IndexType pid = P0; pid <= P7; pid++) - { - const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + // Count which points in the original cell are used. + for(IndexType pid = P0; pid <= P7; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += incr; // {p0} - thisBlendGroups += incr; - } + thisBlendGroupLen += incr; // {p0} + thisBlendGroups += incr; + } - // Count edges that are used. - for(IndexType pid = EA; pid <= EL; pid++) - { - const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + // Count edges that are used. + for(IndexType pid = EA; pid <= EL; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += 2 * incr; // {p0 p1} - thisBlendGroups += incr; - } + thisBlendGroupLen += 2 * incr; // {p0 p1} + thisBlendGroups += incr; + } - // Save the results. - fragmentData.m_fragmentsView[zoneIndex] = thisFragments; - fragmentData.m_fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; + // Save the results. + fragmentData.m_fragmentsView[zoneIndex] = thisFragments; + fragmentData.m_fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; - // Set blend group sizes for this zone. - blendGroupsView[zoneIndex] = thisBlendGroups; - blendGroupsLenView[zoneIndex] = thisBlendGroupLen; - }); + // Set blend group sizes for this zone. + blendGroupsView[zoneIndex] = thisBlendGroups; + blendGroupsLenView[zoneIndex] = thisBlendGroupLen; + }); }); } @@ -1216,19 +1341,19 @@ class ClipField // Sum the number of fragments. RAJA::ReduceSum fragment_sum(0); const auto fragmentsView = fragmentData.m_fragmentsView; - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - fragment_sum += fragmentsView[zoneIndex]; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { fragment_sum += fragmentsView[zoneIndex]; }); fragmentData.m_finalNumZones = fragment_sum.get(); // Sum the fragment connectivity sizes. RAJA::ReduceSum fragment_nids_sum(0); const auto fragmentsSizeView = fragmentData.m_fragmentsSizeView; - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - fragment_nids_sum += fragmentsSizeView[zoneIndex]; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + fragment_nids_sum += fragmentsSizeView[zoneIndex]; + }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); } @@ -1239,8 +1364,10 @@ class ClipField */ void computeFragmentOffsets(FragmentData &fragmentData) const { - axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); - axom::exclusive_scan(fragmentData.m_fragmentsSizeView, fragmentData.m_fragmentSizeOffsetsView); + axom::exclusive_scan(fragmentData.m_fragmentsView, + fragmentData.m_fragmentOffsetsView); + axom::exclusive_scan(fragmentData.m_fragmentsSizeView, + fragmentData.m_fragmentSizeOffsetsView); } /** @@ -1254,104 +1381,112 @@ class ClipField * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const + void makeBlendGroups(ClipTableViews clipTableViews, + BlendGroupBuilder builder, + ZoneData zoneData, + ClipOptions &opts, + const conduit::Node &n_clip_field_values) const { - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) - { + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); const auto selection = getSelection(opts); - m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // Get the clip case for the current zone. - const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; - - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto &ctView = clipTableViews[clipTableIndex]; + m_topologyView.template for_selected_zones( + opts.selectedZonesView(), + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + // Get the clip case for the current zone. + const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; - // These are the points used in this zone's fragments. - const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto &ctView = clipTableViews[clipTableIndex]; - // Get the blend groups for this zone. - auto groups = builder.blendGroupsForZone(zoneIndex); + // These are the points used in this zone's fragments. + const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) - { - // Get the current shape in the clip case. - const auto fragment = *it; + // Get the blend groups for this zone. + auto groups = builder.blendGroupsForZone(zoneIndex); - if(fragment[0] == ST_PNT) + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) { - if(details::generatedPointIsSelected(fragment[2], selection)) - { - const int nIds = static_cast(fragment[3]); - const auto one_over_n = 1.f / static_cast(nIds); + // Get the current shape in the clip case. + const auto fragment = *it; - groups.beginGroup(); - for(int ni = 0; ni < nIds; ni++) + if(fragment[0] == ST_PNT) + { + if(details::generatedPointIsSelected(fragment[2], selection)) { - const auto ptid = fragment[4 + ni]; + const int nIds = static_cast(fragment[3]); + const auto one_over_n = 1.f / static_cast(nIds); - // Add the point to the blend group. - if(ptid <= P7) - { - // corner point. - groups.add(zone.getId(ptid), one_over_n); - } - else if(ptid >= EA && ptid <= EL) + groups.beginGroup(); + for(int ni = 0; ni < nIds; ni++) { - // edge point. - const auto edgeIndex = ptid - EA; - const auto edge = zone.getEdge(edgeIndex); - const auto id0 = zone.getId(edge[0]); - const auto id1 = zone.getId(edge[1]); - - // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - - groups.add(id0, one_over_n * (1.f - t)); - groups.add(id1, one_over_n * t); + const auto ptid = fragment[4 + ni]; + + // Add the point to the blend group. + if(ptid <= P7) + { + // corner point. + groups.add(zone.getId(ptid), one_over_n); + } + else if(ptid >= EA && ptid <= EL) + { + // edge point. + const auto edgeIndex = ptid - EA; + const auto edge = zone.getEdge(edgeIndex); + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); + + groups.add(id0, one_over_n * (1.f - t)); + groups.add(id1, one_over_n * t); + } } + groups.endGroup(); } - groups.endGroup(); } } - } - // Add blend group for each original point that was used. - for(IndexType pid = P0; pid <= P7; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + // Add blend group for each original point that was used. + for(IndexType pid = P0; pid <= P7; pid++) { - groups.beginGroup(); - groups.add(zone.getId(pid), 1.f); - groups.endGroup(); + if(axom::utilities::bitIsSet(ptused, pid)) + { + groups.beginGroup(); + groups.add(zone.getId(pid), 1.f); + groups.endGroup(); + } } - } - // Add blend group for each edge point that was used. - for(IndexType pid = EA; pid <= EL; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + // Add blend group for each edge point that was used. + for(IndexType pid = EA; pid <= EL; pid++) { - const auto edgeIndex = pid - EA; - const auto edge = zone.getEdge(edgeIndex); - const auto id0 = zone.getId(edge[0]); - const auto id1 = zone.getId(edge[1]); - - // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], clipFieldView[id1], clipValue); - - groups.beginGroup(); - groups.add(id0, 1.f - t); - groups.add(id1, t); - groups.endGroup(); + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto edgeIndex = pid - EA; + const auto edge = zone.getEdge(edgeIndex); + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); + + groups.beginGroup(); + groups.add(id0, 1.f - t); + groups.add(id1, t); + groups.endGroup(); + } } - } - }); + }); }); } @@ -1369,9 +1504,17 @@ class ClipField * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void makeConnectivity(ClipTableViews clipTableViews, BlendGroupBuilder builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const + void makeConnectivity(ClipTableViews clipTableViews, + BlendGroupBuilder builder, + ZoneData zoneData, + FragmentData fragmentData, + ClipOptions &opts, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) const { - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + constexpr auto connTypeID = + axom::mir::utilities::blueprint::cpp2conduit::id; const auto selection = getSelection(opts); n_newTopo.reset(); @@ -1385,26 +1528,34 @@ class ClipField // Allocate connectivity. conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(conduitAllocatorID); - n_conn.set(conduit::DataType(connTypeID,fragmentData.m_finalConnSize)); - auto connView = axom::ArrayView(static_cast(n_conn.data_ptr()), fragmentData.m_finalConnSize); + n_conn.set(conduit::DataType(connTypeID, fragmentData.m_finalConnSize)); + auto connView = axom::ArrayView( + static_cast(n_conn.data_ptr()), + fragmentData.m_finalConnSize); // Allocate shapes. conduit::Node &n_shapes = n_newTopo["elements/shapes"]; n_shapes.set_allocator(conduitAllocatorID); n_shapes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto shapesView = axom::ArrayView(static_cast(n_shapes.data_ptr()), fragmentData.m_finalNumZones); + auto shapesView = axom::ArrayView( + static_cast(n_shapes.data_ptr()), + fragmentData.m_finalNumZones); // Allocate sizes. conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(conduitAllocatorID); n_sizes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto sizesView = axom::ArrayView(static_cast(n_sizes.data_ptr()), fragmentData.m_finalNumZones); + auto sizesView = axom::ArrayView( + static_cast(n_sizes.data_ptr()), + fragmentData.m_finalNumZones); // Allocate offsets. conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(conduitAllocatorID); n_offsets.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto offsetsView = axom::ArrayView(static_cast(n_offsets.data_ptr()), fragmentData.m_finalNumZones); + auto offsetsView = axom::ArrayView( + static_cast(n_offsets.data_ptr()), + fragmentData.m_finalNumZones); // Allocate a color variable to keep track of the "color" of the fragments. conduit::Node &n_color = n_newFields[opts.colorField()]; @@ -1413,93 +1564,94 @@ class ClipField conduit::Node &n_color_values = n_color["values"]; n_color_values.set_allocator(conduitAllocatorID); n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); - auto colorView = axom::ArrayView(static_cast(n_color_values.data_ptr()), fragmentData.m_finalNumZones); + auto colorView = axom::ArrayView( + static_cast(n_color_values.data_ptr()), + fragmentData.m_finalNumZones); // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. RAJA::ReduceBitOr shapesUsed_reduce(0); - m_topologyView.template for_selected_zones(opts.selectedZonesView(), AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - // If there are no fragments, return from lambda. - if(fragmentData.m_fragmentsView[zoneIndex] == 0) - return; - + m_topologyView.template for_selected_zones( + opts.selectedZonesView(), + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + // If there are no fragments, return from lambda. + if(fragmentData.m_fragmentsView[zoneIndex] == 0) return; - // Seek to the start of the blend groups for this zone. - auto groups = builder.blendGroupsForZone(zoneIndex); + // Seek to the start of the blend groups for this zone. + auto groups = builder.blendGroupsForZone(zoneIndex); - // Go through the points in the order they would have been added as blend - // groups, get their blendName, and then overall index of that blendName - // in uNames, the unique list of new dof names. That will be their index - // in the final points. - const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; - ConnectivityType point_2_new[N3 + 1]; - for(unsigned char pid = N0; pid <= N3; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + // Go through the points in the order they would have been added as blend + // groups, get their blendName, and then overall index of that blendName + // in uNames, the unique list of new dof names. That will be their index + // in the final points. + const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; + ConnectivityType point_2_new[N3 + 1]; + for(unsigned char pid = N0; pid <= N3; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - for(unsigned char pid = P0; pid <= P7; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + for(unsigned char pid = P0; pid <= P7; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - for(unsigned char pid = EA; pid <= EL; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + for(unsigned char pid = EA; pid <= EL; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - // This is where the output fragment connectivity start for this zone - int outputIndex = fragmentData.m_fragmentSizeOffsetsView[zoneIndex]; - // This is where the output fragment sizes/shapes start for this zone. - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; - BitSet shapesUsed = 0; - - // Iterate over the selected fragments and emit connectivity for them. - const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto ctView = clipTableViews[clipTableIndex]; - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) - { - // Get the current shape in the clip case. - const auto fragment = *it; - const auto fragmentShape = static_cast(fragment[0]); + // This is where the output fragment connectivity start for this zone + int outputIndex = fragmentData.m_fragmentSizeOffsetsView[zoneIndex]; + // This is where the output fragment sizes/shapes start for this zone. + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + BitSet shapesUsed = 0; - if(fragmentShape != ST_PNT) + // Iterate over the selected fragments and emit connectivity for them. + const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto ctView = clipTableViews[clipTableIndex]; + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) { - if(details::shapeIsSelected(fragment[1], selection)) + // Get the current shape in the clip case. + const auto fragment = *it; + const auto fragmentShape = static_cast(fragment[0]); + + if(fragmentShape != ST_PNT) { - // Output the nodes used in this zone. - for(int i = 2; i < fragment.size(); i++) - connView[outputIndex++] = point_2_new[fragment[i]]; - - const auto nIdsInFragment = fragment.size() - 2; - const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); - sizesView[sizeIndex] = nIdsInFragment; - shapesView[sizeIndex] = shapeID; - colorView[sizeIndex] = fragment[1] - COLOR0; - sizeIndex++; - - // Record which shape type was used. Use a bit for each shape. - axom::utilities::setBitOn(shapesUsed, shapeID); + if(details::shapeIsSelected(fragment[1], selection)) + { + // Output the nodes used in this zone. + for(int i = 2; i < fragment.size(); i++) + connView[outputIndex++] = point_2_new[fragment[i]]; + + const auto nIdsInFragment = fragment.size() - 2; + const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); + sizesView[sizeIndex] = nIdsInFragment; + shapesView[sizeIndex] = shapeID; + colorView[sizeIndex] = fragment[1] - COLOR0; + sizeIndex++; + + // Record which shape type was used. Use a bit for each shape. + axom::utilities::setBitOn(shapesUsed, shapeID); + } } } - } - shapesUsed_reduce |= shapesUsed; - }); + shapesUsed_reduce |= shapesUsed; + }); // If inside and outside are not selected, remove the color field since we should not need it. if(!(opts.inside() && opts.outside())) @@ -1518,7 +1670,7 @@ class ClipField n_newTopo["elements/shape"] = "mixed"; conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; for(auto it = shapeMap.cbegin(); it != shapeMap.cend(); it++) - n_shape_map[it->first] = it->second; + n_shape_map[it->first] = it->second; } else { @@ -1535,9 +1687,15 @@ class ClipField * \param n_coordset The input coordset, which is passed for metadata. * \param[out] n_newCoordset The new coordset. */ - void makeCoordset(const BlendData &blend, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const + void makeCoordset(const BlendData &blend, + const conduit::Node &n_coordset, + conduit::Node &n_newCoordset) const { - axom::mir::utilities::blueprint::CoordsetBlender cb; + axom::mir::utilities::blueprint::CoordsetBlender< + ExecSpace, + CoordsetView, + axom::mir::utilities::blueprint::SelectThroughArrayView> + cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } @@ -1571,7 +1729,10 @@ class ClipField } else if(association == "vertex") { - axom::mir::utilities::blueprint::FieldBlender b; + axom::mir::utilities::blueprint::FieldBlender< + ExecSpace, + axom::mir::utilities::blueprint::SelectThroughArrayView> + b; b.execute(blend, n_field, n_out_fields[it->second]); n_out_fields[it->second]["topology"] = topologyName; } @@ -1589,9 +1750,14 @@ class ClipField * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void makeOriginalElements(FragmentData fragmentData, const ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const + void makeOriginalElements(FragmentData fragmentData, + const ClipOptions &opts, + const conduit::Node &n_fields, + conduit::Node &n_newTopo, + conduit::Node &n_newFields) const { - constexpr auto connTypeID = axom::mir::utilities::blueprint::cpp2conduit::id; + constexpr auto connTypeID = + axom::mir::utilities::blueprint::cpp2conduit::id; utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); @@ -1603,23 +1769,26 @@ class ClipField // originalElements already exists. We need to map it forward. const conduit::Node &n_orig = n_fields["originalElements"]; const conduit::Node &n_orig_values = n_orig["values"]; - views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) - { + views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) { using value_type = typename decltype(origValuesView)::value_type; - conduit::Node &n_origElem = n_newFields["originalElements"]; + conduit::Node &n_origElem = n_newFields["originalElements"]; n_origElem["association"] = "element"; n_origElem["topology"] = opts.topologyName(n_newTopo.name()); conduit::Node &n_values = n_origElem["values"]; n_values.set_allocator(conduitAllocatorID); - n_values.set(conduit::DataType(n_orig_values.dtype().id(), fragmentData.m_finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), fragmentData.m_finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentData.m_fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = origValuesView[zoneIndex]; - }); + n_values.set(conduit::DataType(n_orig_values.dtype().id(), + fragmentData.m_finalNumZones)); + auto valuesView = axom::ArrayView( + static_cast(n_values.data_ptr()), + fragmentData.m_finalNumZones); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); }); } else @@ -1631,14 +1800,17 @@ class ClipField conduit::Node &n_values = n_orig["values"]; n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto valuesView = axom::ArrayView(static_cast(n_values.data_ptr()), fragmentData.m_finalNumZones); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentData.m_fragmentsView[zoneIndex]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = zoneIndex; - }); + auto valuesView = axom::ArrayView( + static_cast(n_values.data_ptr()), + fragmentData.m_finalNumZones); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = zoneIndex; + }); } } @@ -1648,8 +1820,8 @@ class ClipField axom::mir::clipping::ClipTableManager m_clipTables; }; -} // end namespace clipping -} // end namespace mir -} // end namespace axom +} // end namespace clipping +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index dc839bdb75..0218212d2e 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -6,8 +6,8 @@ #define AXOM_MIR_COORDSET_BLENDER_HPP_ #include "axom/core.hpp" -#include "axom/mir/FieldBlender.hpp" // for BlendData -#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit +#include "axom/mir/FieldBlender.hpp" // for BlendData +#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" @@ -22,7 +22,6 @@ namespace utilities { namespace blueprint { - /** * \accelerated * \class FieldBlender @@ -36,7 +35,7 @@ namespace blueprint */ template class CoordsetBlender -{ +{ public: using CoordsetViewType = CSVType; @@ -51,7 +50,10 @@ class CoordsetBlender * a view and the coordset node since the view may not be able to contain * come coordset metadata and remain trivially copyable. */ - void execute(const BlendData &blend, const CoordsetViewType &view, const conduit::Node &n_input, conduit::Node &n_output) const + void execute(const BlendData &blend, + const CoordsetViewType &view, + const conduit::Node &n_input, + conduit::Node &n_output) const { using value_type = typename CoordsetViewType::value_type; using PointType = typename CoordsetViewType::PointType; @@ -76,41 +78,45 @@ class CoordsetBlender // Allocate data in the Conduit node and make a view. conduit::Node &comp = n_values[axes[i]]; comp.set_allocator(c2a.getConduitAllocatorID()); - comp.set(conduit::DataType(axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); + comp.set(conduit::DataType( + axom::mir::utilities::blueprint::cpp2conduit::id, + outputSize)); auto *comp_data = static_cast(comp.data_ptr()); compViews[i] = axom::ArrayView(comp_data, outputSize); } // Iterate over each blend group. const BlendData blendDevice(blend); - axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) - { - // Get the blend group index we want. - const auto selectedIndex = SelectionPolicy::selectedIndex(blendDevice, bgid); - const auto start = blendDevice.m_blendGroupStartView[selectedIndex]; - const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; + axom::for_all( + outputSize, + AXOM_LAMBDA(auto bgid) { + // Get the blend group index we want. + const auto selectedIndex = + SelectionPolicy::selectedIndex(blendDevice, bgid); + const auto start = blendDevice.m_blendGroupStartView[selectedIndex]; + const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; - // Blend points for this blend group. - VectorType blended{}; - for(IndexType i = start; i < end; i++) - { - const auto index = blendDevice.m_blendIdsView[i]; - const auto weight = blendDevice.m_blendCoeffView[i]; - blended += (VectorType(view[index]) * static_cast(weight)); - } + // Blend points for this blend group. + VectorType blended {}; + for(IndexType i = start; i < end; i++) + { + const auto index = blendDevice.m_blendIdsView[i]; + const auto weight = blendDevice.m_blendCoeffView[i]; + blended += (VectorType(view[index]) * static_cast(weight)); + } - // Store the point into the Conduit component arrays. - for(int comp = 0; comp < PointType::DIMENSION; comp++) - { - compViews[comp][bgid] = blended[comp]; - } - }); + // Store the point into the Conduit component arrays. + for(int comp = 0; comp < PointType::DIMENSION; comp++) + { + compViews[comp][bgid] = blended[comp]; + } + }); } }; -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 57d0402193..11bbc5cd51 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -54,7 +54,6 @@ namespace axom { namespace mir { - void EquiZAlgorithm::execute(const conduit::Node &topo, const conduit::Node &coordset, const conduit::Node &matset, @@ -64,31 +63,31 @@ void EquiZAlgorithm::execute(const conduit::Node &topo, conduit::Node &new_matset) { #if 0 -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) switch(m_execPolicy) { - #if defined(AXOM_USE_OPENMP) + #if defined(AXOM_USE_OPENMP) case RuntimePolicy::omp: executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; - #endif - #if defined(AXOM_USE_CUDA) + #endif + #if defined(AXOM_USE_CUDA) case RuntimePolicy::cuda: executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; - #endif - #if defined(AXOM_USE_HIP) + #endif + #if defined(AXOM_USE_HIP) case RuntimePolicy::hip: executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; - #endif + #endif default: // Falls through case RuntimePolicy::seq: executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); break; } -#endif + #endif #endif } @@ -183,5 +182,5 @@ void EquiZAlgorithm::executeImpl(const conduit::Node &topo, #endif } -} // namespace mir -} // namespace axom +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index f1f2ab9955..af4190ad04 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -13,10 +13,8 @@ namespace axom { - namespace mir { - /** * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. @@ -40,30 +38,29 @@ class EquiZAlgorithm : public MIRAlgorithm void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } protected: - /// Implement the EquiZ algorithm on a single domain. - virtual void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) override; + /// Implement the EquiZ algorithm on a single domain. + virtual void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &matset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset, + conduit::Node &new_matset) override; - - /// Implement the EquiZ algorithm on a single domain for a given ExecSpace. - template - void executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset); + /// Implement the EquiZ algorithm on a single domain for a given ExecSpace. + template + void executeImpl(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &matset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset, + conduit::Node &new_matset); - RuntimePolicy m_execPolicy{RuntimePolicy::seq}; + RuntimePolicy m_execPolicy {RuntimePolicy::seq}; }; -} // end namespace mir -} // end namespace axom +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index c25771cf87..fab733c843 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -20,18 +20,18 @@ namespace utilities { namespace blueprint { - /** * \brief This class contains views of blend data. */ struct BlendData { - axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. + axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. - axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. - axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. - axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups - axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. + axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. + axom::ArrayView + m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; /** @@ -46,7 +46,7 @@ struct SelectAllPolicy } AXOM_HOST_DEVICE - static IndexType selectedIndex(const BlendData &/*blend*/, IndexType index) + static IndexType selectedIndex(const BlendData & /*blend*/, IndexType index) { return index; } @@ -81,7 +81,7 @@ struct SelectThroughArrayView */ template class FieldBlender -{ +{ public: /** * \brief Create a new blended field from the \a n_input field and place it in \a n_output. @@ -90,7 +90,9 @@ class FieldBlender * \param n_input The input field that we're blending. * \param n_output The output node that will contain the new field. */ - void execute(const BlendData &blend, const conduit::Node &n_input, conduit::Node &n_output) const + void execute(const BlendData &blend, + const conduit::Node &n_input, + conduit::Node &n_output) const { n_output.reset(); n_output["association"] = n_input["association"]; @@ -121,7 +123,9 @@ class FieldBlender * \param n_values The input values that we're blending. * \param n_output_values The output node that will contain the new field. */ - void blendSingleComponent(const BlendData &blend, const conduit::Node &n_values, conduit::Node &n_output_values) const + void blendSingleComponent(const BlendData &blend, + const conduit::Node &n_values, + conduit::Node &n_output_values) const { // We're allowing selectedIndicesView to be used to select specific blend // groups. If the user did not provide that, use all blend groups. @@ -132,35 +136,41 @@ class FieldBlender n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); - views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto compView, auto outView) - { - using value_type = typename decltype(compView)::value_type; - using accum_type = typename axom::mir::utilities::accumulation_traits::type; - - const BlendData deviceBlend(blend); - axom::for_all(outputSize, AXOM_LAMBDA(auto bgid) - { - // Get the index we want. - const auto selectedIndex = SelectionPolicy::selectedIndex(deviceBlend, bgid); - const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; - const auto end = start + deviceBlend.m_blendGroupSizesView[selectedIndex]; - - accum_type blended = 0; - for(IndexType i = start; i < end; i++) - { - const auto index = deviceBlend.m_blendIdsView[i]; - const auto weight = deviceBlend.m_blendCoeffView[i]; - blended += static_cast(compView[index]) * weight; - } - outView[bgid] = static_cast(blended); + views::Node_to_ArrayView_same( + n_values, + n_output_values, + [&](auto compView, auto outView) { + using value_type = typename decltype(compView)::value_type; + using accum_type = + typename axom::mir::utilities::accumulation_traits::type; + + const BlendData deviceBlend(blend); + axom::for_all( + outputSize, + AXOM_LAMBDA(auto bgid) { + // Get the index we want. + const auto selectedIndex = + SelectionPolicy::selectedIndex(deviceBlend, bgid); + const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; + const auto end = + start + deviceBlend.m_blendGroupSizesView[selectedIndex]; + + accum_type blended = 0; + for(IndexType i = start; i < end; i++) + { + const auto index = deviceBlend.m_blendIdsView[i]; + const auto weight = deviceBlend.m_blendCoeffView[i]; + blended += static_cast(compView[index]) * weight; + } + outView[bgid] = static_cast(blended); + }); }); - }); } }; -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 96d0adcc15..13d424bc3d 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -19,7 +19,6 @@ namespace utilities { namespace blueprint { - /** * \brief Contains the indices to be sliced out of a Blueprint field. */ @@ -47,7 +46,9 @@ class FieldSlicer * * \note We assume for now that n_input != n_output. */ - void execute(const SliceData &slice, const conduit::Node &n_input, conduit::Node &n_output) + void execute(const SliceData &slice, + const conduit::Node &n_input, + conduit::Node &n_output) { n_output.reset(); n_output["association"] = n_input["association"]; @@ -69,6 +70,7 @@ class FieldSlicer sliceSingleComponent(slice, n_input_values, n_output_values); } } + private: /** * \brief Slice data for a single field component. @@ -77,7 +79,9 @@ class FieldSlicer * \param n_values The input values that we're slicing. * \param n_output_values The output node that will contain the new field. */ - void sliceSingleComponent(const SliceData &slice, const conduit::Node &n_values, conduit::Node &n_output_values) const + void sliceSingleComponent(const SliceData &slice, + const conduit::Node &n_values, + conduit::Node &n_output_values) const { const auto outputSize = slice.m_indicesView.size(); @@ -86,20 +90,23 @@ class FieldSlicer n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); - views::Node_to_ArrayView_same(n_values, n_output_values, [&](auto valuesView, auto outputView) - { - const SliceData deviceSlice(slice); - axom::for_all(outputSize, AXOM_LAMBDA(auto index) - { - outputView[index] = valuesView[deviceSlice.m_indicesView[index]]; + views::Node_to_ArrayView_same( + n_values, + n_output_values, + [&](auto valuesView, auto outputView) { + const SliceData deviceSlice(slice); + axom::for_all( + outputSize, + AXOM_LAMBDA(auto index) { + outputView[index] = valuesView[deviceSlice.m_indicesView[index]]; + }); }); - }); } }; -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/InterfaceReconstructor.cpp index 1c81c47589..2774dad523 100644 --- a/src/axom/mir/InterfaceReconstructor.cpp +++ b/src/axom/mir/InterfaceReconstructor.cpp @@ -9,24 +9,17 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- -InterfaceReconstructor::InterfaceReconstructor() -{ - -} +InterfaceReconstructor::InterfaceReconstructor() { } //-------------------------------------------------------------------------------- -InterfaceReconstructor::~InterfaceReconstructor() -{ - -} +InterfaceReconstructor::~InterfaceReconstructor() { } //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh, +void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMesh, mir::MIRMesh& outputMesh) { // Store a reference to the original mesh @@ -36,7 +29,7 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe mir::MIRMesh finalMesh(m_originalMesh); // For each material in the mesh, split the mesh on the current material and generate a new mesh (input into next iteration) - for (int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) + for(int matID = 0; matID < m_originalMesh.m_numMaterials; ++matID) { // Copy the mesh to be split mir::MIRMesh intermediateMesh(finalMesh); @@ -45,7 +38,7 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe CellData temp_cellData[intermediateMesh.m_elems.size()]; // Process/split each element - for (int eID = 0; eID < intermediateMesh.m_elems.size(); ++eID) + for(int eID = 0; eID < intermediateMesh.m_elems.size(); ++eID) { // Update the materials upon which the split will occur int currentDominantMat = intermediateMesh.m_elementDominantMaterials[eID]; @@ -53,55 +46,62 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe // Extract the data for the current element std::vector elementVertices; - for (int vID = 0; vID < intermediateMesh.m_bdry[eID].size(); ++vID) + for(int vID = 0; vID < intermediateMesh.m_bdry[eID].size(); ++vID) { elementVertices.push_back(intermediateMesh.m_bdry[eID][vID]); } // Extract the vertex volume fractions of the element to split - std::vector > originalElementVertexVF; - for (int matID = 0; matID < intermediateMesh.m_numMaterials; ++matID) + std::vector> originalElementVertexVF; + for(int matID = 0; matID < intermediateMesh.m_numMaterials; ++matID) { std::vector materialVertexVF; - for (unsigned long vID = 0; vID < elementVertices.size(); ++vID) + for(unsigned long vID = 0; vID < elementVertices.size(); ++vID) { int originalVID = elementVertices[vID]; - materialVertexVF.push_back( intermediateMesh.m_materialVolumeFractionsVertex[matID][originalVID] ); + materialVertexVF.push_back( + intermediateMesh.m_materialVolumeFractionsVertex[matID][originalVID]); } - originalElementVertexVF.push_back( materialVertexVF ); + originalElementVertexVF.push_back(materialVertexVF); } // Extract the vertex positions of the element to split std::vector originalElementVertexPositions; - for (unsigned long vID = 0; vID < elementVertices.size(); ++vID) + for(unsigned long vID = 0; vID < elementVertices.size(); ++vID) { int originalVID = elementVertices[vID]; - originalElementVertexPositions.push_back( intermediateMesh.m_vertexPositions[originalVID] ); + originalElementVertexPositions.push_back( + intermediateMesh.m_vertexPositions[originalVID]); } - generateCleanCells( (mir::Shape) intermediateMesh.m_shapeTypes[eID], - intermediateMesh.m_elementParentIDs[eID], - currentDominantMat, - matOne, - elementVertices, - originalElementVertexVF, - originalElementVertexPositions, - temp_cellData[eID]); + generateCleanCells((mir::Shape)intermediateMesh.m_shapeTypes[eID], + intermediateMesh.m_elementParentIDs[eID], + currentDominantMat, + matOne, + elementVertices, + originalElementVertexVF, + originalElementVertexPositions, + temp_cellData[eID]); } // Merge each of the cells into the first CellData struct - for (int eID = 1; eID < intermediateMesh.m_elems.size(); ++eID) + for(int eID = 1; eID < intermediateMesh.m_elems.size(); ++eID) { temp_cellData[0].mergeCell(temp_cellData[eID]); } - + mir::VertSet combined_verts(temp_cellData[0].m_numVerts); mir::ElemSet combined_elems(temp_cellData[0].m_numElems); // Create the final, processed mesh mir::MIRMesh processedMesh; - processedMesh.initializeMesh(combined_verts, combined_elems, intermediateMesh.m_numMaterials, temp_cellData[0].m_topology, temp_cellData[0].m_mapData); - processedMesh.constructMeshVolumeFractionsVertex(temp_cellData[0].m_mapData.m_vertexVolumeFractions); + processedMesh.initializeMesh(combined_verts, + combined_elems, + intermediateMesh.m_numMaterials, + temp_cellData[0].m_topology, + temp_cellData[0].m_mapData); + processedMesh.constructMeshVolumeFractionsVertex( + temp_cellData[0].m_mapData.m_vertexVolumeFractions); // Store the current mesh to be passed into the next iteration finalMesh = processedMesh; @@ -114,10 +114,11 @@ void InterfaceReconstructor::computeReconstructedInterface(mir::MIRMesh& inputMe //-------------------------------------------------------------------------------- -void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, - const int numIterations, - const axom::float64 percent, - mir::MIRMesh& outputMesh) +void InterfaceReconstructor::computeReconstructedInterfaceIterative( + mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh) { int numElems = inputMesh.m_elems.size(); int numMaterials = inputMesh.m_numMaterials; @@ -128,23 +129,28 @@ void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh // Calculate the reconstruction on the unmodified, input mesh computeReconstructedInterface(meshToImprove, outputMesh); - for (int it = 0; it < numIterations; ++it) + for(int it = 0; it < numIterations; ++it) { printf("iteration %d\n", it); // Calculate the output element volume fractions of the resulting output mesh - std::vector > resultingElementVF = outputMesh.computeOriginalElementVolumeFractions(); + std::vector> resultingElementVF = + outputMesh.computeOriginalElementVolumeFractions(); // Initialize the vector to store the improved element volume fractions - std::vector > improvedElementVF; + std::vector> improvedElementVF; improvedElementVF.resize(numMaterials); - + // Modify the copy of the original mesh's element volume fractions by percent difference (modify meshToImprove's elementVF) - for (int matID = 0; matID < numMaterials; ++matID) + for(int matID = 0; matID < numMaterials; ++matID) { - for (int eID = 0; eID < numElems; ++eID) + for(int eID = 0; eID < numElems; ++eID) { - axom::float64 difference = inputMesh.m_materialVolumeFractionsElement[matID][eID] - resultingElementVF[matID][eID]; - improvedElementVF[matID].push_back( inputMesh.m_materialVolumeFractionsElement[matID][eID] + ( difference * percent ) ); + axom::float64 difference = + inputMesh.m_materialVolumeFractionsElement[matID][eID] - + resultingElementVF[matID][eID]; + improvedElementVF[matID].push_back( + inputMesh.m_materialVolumeFractionsElement[matID][eID] + + (difference * percent)); } } @@ -158,24 +164,31 @@ void InterfaceReconstructor::computeReconstructedInterfaceIterative(mir::MIRMesh //-------------------------------------------------------------------------------- -void InterfaceReconstructor::generateCleanCells(mir::Shape shapeType, - const int parentElementID, - const int matOne, - const int matTwo, - const std::vector& elementVertices, - const std::vector >& originalElementVertexVF, - const std::vector& originalElementVertexPositions, - CellData& out_cellData) +void InterfaceReconstructor::generateCleanCells( + mir::Shape shapeType, + const int parentElementID, + const int matOne, + const int matTwo, + const std::vector& elementVertices, + const std::vector>& originalElementVertexVF, + const std::vector& originalElementVertexPositions, + CellData& out_cellData) { // Set up data structures needed to compute how element should be clipped - std::map > newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets - std::map > newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets - std::vector verticesPresent(mir::utilities::maxPossibleNumVerts(shapeType), 0); // Vector of flags denoting whether the vertex is present in the current case or not - axom::float64* tValues = new axom::float64[ mir::utilities::maxPossibleNumVerts(shapeType) ]{0}; // Array of t values that denote the percent value of where the edge should be clipped + std::map> + newElements; // hashmap of the new elements' vertices | Note: maps are ordered sets + std::map> + newVertices; // hashmap of the new vertices' elements | Note: maps are ordered sets + std::vector verticesPresent( + mir::utilities::maxPossibleNumVerts(shapeType), + 0); // Vector of flags denoting whether the vertex is present in the current case or not + axom::float64* tValues = + new axom::float64[mir::utilities::maxPossibleNumVerts(shapeType)] { + 0}; // Array of t values that denote the percent value of where the edge should be clipped // Set up the volume fractions for the current element for the two materials currently being considered - std::vector > vertexVF(2); - if (matOne == NULL_MAT) + std::vector> vertexVF(2); + if(matOne == NULL_MAT) { std::vector nullVF(elementVertices.size(), -1); vertexVF[0] = nullVF; @@ -184,7 +197,7 @@ void InterfaceReconstructor::generateCleanCells(mir::Shape shapeType, { vertexVF[0] = originalElementVertexVF[matOne]; } - if (matTwo == NULL_MAT) + if(matTwo == NULL_MAT) { std::vector nullVF(elementVertices.size(), -1); vertexVF[1] = nullVF; @@ -196,74 +209,83 @@ void InterfaceReconstructor::generateCleanCells(mir::Shape shapeType, // Clip the element CellClipper clipper; - clipper.computeClippingPoints(shapeType, vertexVF, newElements, newVertices, tValues); - + clipper.computeClippingPoints(shapeType, + vertexVF, + newElements, + newVertices, + tValues); + // Determine which vertices are present - for (auto itr = newVertices.begin(); itr != newVertices.end(); itr++) + for(auto itr = newVertices.begin(); itr != newVertices.end(); itr++) { int vID = itr->first; verticesPresent[vID] = 1; } int centerVertexID = mir::utilities::getCenterVertex(shapeType); - if (centerVertexID != -1 && verticesPresent[centerVertexID] == 1) + if(centerVertexID != -1 && verticesPresent[centerVertexID] == 1) { // Set up the cell data struct for the results of the decomposed element being clipped CellData decomposed_cellData[newElements.size()]; int decompElementID = 0; // The clipping is one a tough one, and the element needs to decompose further before splitting with the two materials - for (auto itr = newElements.begin(); itr != newElements.end(); itr++, decompElementID++) + for(auto itr = newElements.begin(); itr != newElements.end(); + itr++, decompElementID++) { // Determine the shape type of the decomposed element - mir::Shape decomposedShapeType = mir::utilities::determineElementShapeType(shapeType, itr->second.size()); + mir::Shape decomposedShapeType = + mir::utilities::determineElementShapeType(shapeType, itr->second.size()); // Extract the data for the current element - std::vector decomposedElementVertices;// = itr->second; - for (int i = 0; i < mir::utilities::numVerts(decomposedShapeType); ++i) + std::vector decomposedElementVertices; // = itr->second; + for(int i = 0; i < mir::utilities::numVerts(decomposedShapeType); ++i) { decomposedElementVertices.push_back(i); } // Extract the vertex volume fractions of the element to split - std::vector > decomposedElementVertexVF; - for (unsigned long matID = 0; matID < originalElementVertexVF.size(); ++matID) + std::vector> decomposedElementVertexVF; + for(unsigned long matID = 0; matID < originalElementVertexVF.size(); ++matID) { std::vector materialVertexVF; - for (unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) + for(unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) { int originalVID = itr->second[vID]; - if ( mir::utilities::isCenterVertex(shapeType, originalVID) ) + if(mir::utilities::isCenterVertex(shapeType, originalVID)) { - // Compute the central vertex volume fraction as the average of the other - axom::float64 averageMaterialVF = mir::utilities::computeAverageFloat(originalElementVertexVF[matID]); - materialVertexVF.push_back( averageMaterialVF ); + // Compute the central vertex volume fraction as the average of the other + axom::float64 averageMaterialVF = + mir::utilities::computeAverageFloat(originalElementVertexVF[matID]); + materialVertexVF.push_back(averageMaterialVF); } else { // Use the values that is at one of the original local vertices - materialVertexVF.push_back( originalElementVertexVF[matID][originalVID] ); + materialVertexVF.push_back( + originalElementVertexVF[matID][originalVID]); } } - decomposedElementVertexVF.push_back( materialVertexVF ); + decomposedElementVertexVF.push_back(materialVertexVF); } // Extract the vertex positions of the element to split std::vector decomposedElementVertexPositions; - for (unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) + for(unsigned long vID = 0; vID < decomposedElementVertices.size(); ++vID) { int originalVID = itr->second[vID]; - if ( mir::utilities::isCenterVertex(shapeType, originalVID) ) + if(mir::utilities::isCenterVertex(shapeType, originalVID)) { // Compute the central vertex position as the centroid of the other - mir::Point2 centroid = mir::utilities::computeAveragePoint(originalElementVertexPositions); - decomposedElementVertexPositions.push_back( centroid ); + mir::Point2 centroid = + mir::utilities::computeAveragePoint(originalElementVertexPositions); + decomposedElementVertexPositions.push_back(centroid); } else { // Use the vertex position that is at one of the original local vertices - decomposedElementVertexPositions.push_back( originalElementVertexPositions[originalVID] ); + decomposedElementVertexPositions.push_back( + originalElementVertexPositions[originalVID]); } - } generateCleanCells(decomposedShapeType, @@ -273,94 +295,112 @@ void InterfaceReconstructor::generateCleanCells(mir::Shape shapeType, decomposedElementVertices, decomposedElementVertexVF, decomposedElementVertexPositions, - decomposed_cellData[decompElementID] - ); + decomposed_cellData[decompElementID]); } // Merge the decomposed cell data with the outermost cellData - out_cellData.m_mapData.m_vertexVolumeFractions.resize(originalElementVertexVF.size()); + out_cellData.m_mapData.m_vertexVolumeFractions.resize( + originalElementVertexVF.size()); out_cellData.m_topology.m_evBegins.push_back(0); out_cellData.m_topology.m_veBegins.push_back(0); - for (int i = 0; i < decompElementID; i++) + for(int i = 0; i < decompElementID; i++) out_cellData.mergeCell(decomposed_cellData[i]); } else { // Calculate the total number of elements and vertices that were generated from splitting the current element - out_cellData.m_numElems = (int) newElements.size(); - out_cellData.m_numVerts = (int) newVertices.size(); + out_cellData.m_numElems = (int)newElements.size(); + out_cellData.m_numVerts = (int)newVertices.size(); // Generate the topology and connectivity of the newly split element CellGenerator cellGenerator; cellGenerator.generateTopologyData(newElements, newVertices, out_cellData); // Generate the vertex position values of the newly split element - cellGenerator.generateVertexPositions( shapeType, newVertices, originalElementVertexPositions, tValues, out_cellData); + cellGenerator.generateVertexPositions(shapeType, + newVertices, + originalElementVertexPositions, + tValues, + out_cellData); // Generate the vertex volume fractions of the newly split elements - cellGenerator.generateVertexVolumeFractions( shapeType, newVertices, originalElementVertexVF, tValues, out_cellData); + cellGenerator.generateVertexVolumeFractions(shapeType, + newVertices, + originalElementVertexVF, + tValues, + out_cellData); // Determine and store the dominant material of this element - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + for(auto itr = newElements.begin(); itr != newElements.end(); itr++) { - int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, itr->second, matOne, matTwo, out_cellData.m_mapData.m_vertexVolumeFractions); - out_cellData.m_mapData.m_elementDominantMaterials.push_back(dominantMaterial); + int dominantMaterial = cellGenerator.determineCleanCellMaterial( + shapeType, + itr->second, + matOne, + matTwo, + out_cellData.m_mapData.m_vertexVolumeFractions); + out_cellData.m_mapData.m_elementDominantMaterials.push_back( + dominantMaterial); } // Determine and store the parent of this element out_cellData.m_mapData.m_elementParents.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + for(auto itr = newElements.begin(); itr != newElements.end(); itr++) { out_cellData.m_mapData.m_elementParents[itr->first] = parentElementID; } // Determine the generated elements' shape types out_cellData.m_mapData.m_shapeTypes.resize(newElements.size()); - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + for(auto itr = newElements.begin(); itr != newElements.end(); itr++) { - out_cellData.m_mapData.m_shapeTypes[itr->first] = mir::utilities::determineElementShapeType(shapeType, itr->second.size()); + out_cellData.m_mapData.m_shapeTypes[itr->first] = + mir::utilities::determineElementShapeType(shapeType, itr->second.size()); } // Check that each element is dominated by a material that is actually present in the original parent cell - for (auto itr = newElements.begin(); itr != newElements.end(); itr++) + for(auto itr = newElements.begin(); itr != newElements.end(); itr++) { int parentElementID = out_cellData.m_mapData.m_elementParents[itr->first]; - int currentDominantMaterial = out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; + int currentDominantMaterial = + out_cellData.m_mapData.m_elementDominantMaterials[itr->first]; - if (m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial][parentElementID] == 0) + if(m_originalMesh.m_materialVolumeFractionsElement[currentDominantMaterial] + [parentElementID] == 0) { // This material is not present in the original element from which the current element comes from - if (currentDominantMaterial == matOne) + if(currentDominantMaterial == matOne) out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matTwo; else out_cellData.m_mapData.m_elementDominantMaterials[itr->first] = matOne; } } - + // Modify the evIndex values to account for the fact that perhaps not all possible vertices are present std::vector evIndexSubtract; evIndexSubtract.resize(mir::utilities::maxPossibleNumVerts(shapeType), 0); - for (unsigned long i = 0; i < evIndexSubtract.size(); ++i) + for(unsigned long i = 0; i < evIndexSubtract.size(); ++i) { - if (verticesPresent[i] == 1) + if(verticesPresent[i] == 1) evIndexSubtract[i] = 0; else evIndexSubtract[i] = 1; } - for (unsigned long i = 1; i < evIndexSubtract.size(); ++i) + for(unsigned long i = 1; i < evIndexSubtract.size(); ++i) evIndexSubtract[i] += evIndexSubtract[i - 1]; - for (unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) + for(unsigned long i = 0; i < out_cellData.m_topology.m_evInds.size(); ++i) { - out_cellData.m_topology.m_evInds[i] -= evIndexSubtract[ out_cellData.m_topology.m_evInds[i] ]; + out_cellData.m_topology.m_evInds[i] -= + evIndexSubtract[out_cellData.m_topology.m_evInds[i]]; } } - + // Memory management delete[] tValues; } //-------------------------------------------------------------------------------- -} -} +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/InterfaceReconstructor.hpp index 19363f0177..ec9af1530a 100644 --- a/src/axom/mir/InterfaceReconstructor.hpp +++ b/src/axom/mir/InterfaceReconstructor.hpp @@ -32,8 +32,7 @@ namespace axom { namespace mir { - - /** +/** * \class InterfaceReconstructor * * \brief A class that contains the functionality for taking an input mesh @@ -45,33 +44,29 @@ namespace mir * a zoo-based algorithm described in Meredith 2004, and the other * is the iterative version described in Meredith and Childs 2010. */ - class InterfaceReconstructor - { - public: - - /** +class InterfaceReconstructor +{ +public: + /** * \brief Default constructor. */ - InterfaceReconstructor(); + InterfaceReconstructor(); - - /** + /** * \brief Default destructor. */ - ~InterfaceReconstructor(); + ~InterfaceReconstructor(); - - /** + /** * \brief Performs material interface reconstruction using the zoo-based algorithm. * * \param inputMesh The mesh composed of mixed cells. * \param outputMesh The mesh composed of clean cells. */ - void computeReconstructedInterface(mir::MIRMesh& inputMesh, - mir::MIRMesh& outputMesh); + void computeReconstructedInterface(mir::MIRMesh& inputMesh, + mir::MIRMesh& outputMesh); - - /** + /** * \brief Performs material interface reconstruction using an iterative version of the zoo-based algorithm. * * \param inputMesh The mesh made up of mixed cells. @@ -79,12 +74,12 @@ namespace mir * \param percent The percent of the difference to use when modifying the original element volume fractions. * \param outputMesh The mesh composed of clean cells. */ - void computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, - const int numIterations, - const axom::float64 percent, - mir::MIRMesh& outputMesh); + void computeReconstructedInterfaceIterative(mir::MIRMesh& inputMesh, + const int numIterations, + const axom::float64 percent, + mir::MIRMesh& outputMesh); - /** + /** * \brief Generates a set of clean cells by splitting the element with the two given materials. * * \param shapeType The shape type of the element to be split. @@ -95,19 +90,20 @@ namespace mir * \param originalElementVertexVF The original vertex volume fractions associated with the vertices of the element to be split. * \param originalElementVertexPositions The original vertex positions associated with the vertices of the element to be split. * \param out_cellData Container to store the data of the generated elements. - */ - void generateCleanCells(mir::Shape shapeType, - const int parentElementID, - const int matOne, - const int matTwo, - const std::vector& elementVertices, - const std::vector >& originalElementVertexVF, - const std::vector& originalElementVertexPositions, - CellData& out_cellData); - - private: - mir::MIRMesh m_originalMesh; - }; -} -} + */ + void generateCleanCells( + mir::Shape shapeType, + const int parentElementID, + const int matOne, + const int matTwo, + const std::vector& elementVertices, + const std::vector>& originalElementVertexVF, + const std::vector& originalElementVertexPositions, + CellData& out_cellData); + +private: + mir::MIRMesh m_originalMesh; +}; +} // namespace mir +} // namespace axom #endif \ No newline at end of file diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index 7612d63ad7..34390edae3 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -12,10 +12,8 @@ namespace axom { - namespace mir { - void MIRAlgorithm::execute(const conduit::Node &root, const conduit::Node &options, conduit::Node &output) @@ -35,7 +33,8 @@ void MIRAlgorithm::execute(const conduit::Node &root, conduit::Node &newDomain = output.append(); const conduit::Node &topologies = dom.fetch_existing("topologies"); const conduit::Node &topo = topologies.fetch_existing(topoName); - const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *cset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); const conduit::Node &mset = matset(dom, options); conduit::Node &newTopo = newDomain["topologies/" + newTopoName]; @@ -57,7 +56,8 @@ void MIRAlgorithm::execute(const conduit::Node &root, const conduit::Node &topologies = dom.fetch_existing("topologies"); const conduit::Node &topo = topologies.fetch_existing(topoName); - const conduit::Node *cset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *cset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); const conduit::Node &mset = matset(root, options); conduit::Node &newTopo = output["topologies/" + newTopoName]; @@ -68,15 +68,14 @@ void MIRAlgorithm::execute(const conduit::Node &root, } } -void -MIRAlgorithm::copyState(const conduit::Node &mesh, conduit::Node &destMesh) const +void MIRAlgorithm::copyState(const conduit::Node &mesh, + conduit::Node &destMesh) const { - if(mesh.has_path("state")) - destMesh["state"].set(mesh["state"]); + if(mesh.has_path("state")) destMesh["state"].set(mesh["state"]); } -std::string -MIRAlgorithm::topologyName(const conduit::Node &mesh, const conduit::Node &options) const +std::string MIRAlgorithm::topologyName(const conduit::Node &mesh, + const conduit::Node &options) const { std::string topoName; if(options.has_path("topology")) @@ -89,8 +88,8 @@ MIRAlgorithm::topologyName(const conduit::Node &mesh, const conduit::Node &optio return topoName; } -std::string -MIRAlgorithm::newTopologyName(const conduit::Node &mesh, const conduit::Node &options) const +std::string MIRAlgorithm::newTopologyName(const conduit::Node &mesh, + const conduit::Node &options) const { std::string topoName; if(options.has_path("new_topology")) @@ -100,8 +99,8 @@ MIRAlgorithm::newTopologyName(const conduit::Node &mesh, const conduit::Node &op return topoName; } -std::string -MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, const conduit::Node &options) const +std::string MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, + const conduit::Node &options) const { std::string csetName; if(options.has_path("new_coordset")) @@ -117,8 +116,8 @@ MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, const conduit::Node &op return csetName; } -std::string -MIRAlgorithm::matsetName(const conduit::Node &mesh, const conduit::Node &options) const +std::string MIRAlgorithm::matsetName(const conduit::Node &mesh, + const conduit::Node &options) const { std::string matName; if(options.has_path("matset")) @@ -131,8 +130,8 @@ MIRAlgorithm::matsetName(const conduit::Node &mesh, const conduit::Node &options return matName; } -std::string -MIRAlgorithm::newMatsetName(const conduit::Node &mesh, const conduit::Node &options) const +std::string MIRAlgorithm::newMatsetName(const conduit::Node &mesh, + const conduit::Node &options) const { std::string matName; if(options.has_path("new_matset")) @@ -142,14 +141,16 @@ MIRAlgorithm::newMatsetName(const conduit::Node &mesh, const conduit::Node &opti return matName; } -const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, const conduit::Node &options) const +const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, + const conduit::Node &options) const { const std::string topoName = topologyName(mesh, options); const conduit::Node &topologies = mesh.fetch_existing("topologies"); return topologies.fetch_existing(topoName); } -const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, const conduit::Node &options) const +const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, + const conduit::Node &options) const { const std::string matName = matsetName(mesh, options); #if 0 @@ -169,8 +170,8 @@ const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, const condu #endif } -std::vector -MIRAlgorithm::fieldNames(const conduit::Node &mesh, const conduit::Node &options) const +std::vector MIRAlgorithm::fieldNames(const conduit::Node &mesh, + const conduit::Node &options) const { std::vector names; if(options.has_path("fields")) @@ -183,8 +184,7 @@ MIRAlgorithm::fieldNames(const conduit::Node &mesh, const conduit::Node &options } else { - if(fields.dtype().is_char8_str()) - names.push_back(fields.as_string()); + if(fields.dtype().is_char8_str()) names.push_back(fields.as_string()); } } else if(mesh.has_child("fields")) @@ -196,5 +196,5 @@ MIRAlgorithm::fieldNames(const conduit::Node &mesh, const conduit::Node &options return names; } -} // namespace mir -} // namespace axom +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index 600ed7e54a..83553c30c8 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -3,7 +3,6 @@ // // SPDX-License-Identifier: (BSD-3-Clause) - #ifndef AXOM_MIR_ALGORITHM_HPP_ #define AXOM_MIR_ALGORITHM_HPP_ @@ -18,20 +17,18 @@ namespace axom { - namespace mir { - /** \brief Base class for Material Interface Reconstruction (MIR) algorithms. */ class MIRAlgorithm { public: - MIRAlgorithm() = default; - virtual ~MIRAlgorithm() = default; + MIRAlgorithm() = default; + virtual ~MIRAlgorithm() = default; - /** + /** \brief Perform material interface reconstruction on the meshes supplied in the root node. Root can either be a mesh domain or a node that contains multiple domains. @@ -63,11 +60,12 @@ class MIRAlgorithm \param[out] output A node that will contain the new entities. */ - virtual void execute(const conduit::Node &root, - const conduit::Node &options, - conduit::Node &output); + virtual void execute(const conduit::Node &root, + const conduit::Node &options, + conduit::Node &output); + protected: - /** + /** \brief Perform material interface reconstruction on a single domain. Derived classes must implement this method and any device-specific coding gets handled under it. @@ -79,32 +77,39 @@ class MIRAlgorithm \param[out] new_coordset A Conduit node that will contain the new coordset. */ - virtual void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) = 0; - - // Utility methods for derived types. - void copyState(const conduit::Node &mesh, conduit::Node &destMesh) const; - std::string topologyName(const conduit::Node &mesh, const conduit::Node &options) const; - std::string newTopologyName(const conduit::Node &mesh, const conduit::Node &options) const; - - std::string newCoordsetName(const conduit::Node &mesh, const conduit::Node &options) const; - - std::string matsetName(const conduit::Node &mesh, const conduit::Node &options) const; - std::string newMatsetName(const conduit::Node &mesh, const conduit::Node &options) const; - - std::vector fieldNames(const conduit::Node &mesh, const conduit::Node &options) const; - - const conduit::Node &topology(const conduit::Node &input, const conduit::Node &options) const; - const conduit::Node &matset(const conduit::Node &input, const conduit::Node &options) const; - - - // TODO: method for mapping element field to new topo - // TODO: method for mapping vertex field to new topo + virtual void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const conduit::Node &matset, + const conduit::Node &options, + conduit::Node &new_topo, + conduit::Node &new_coordset, + conduit::Node &new_matset) = 0; + + // Utility methods for derived types. + void copyState(const conduit::Node &mesh, conduit::Node &destMesh) const; + std::string topologyName(const conduit::Node &mesh, + const conduit::Node &options) const; + std::string newTopologyName(const conduit::Node &mesh, + const conduit::Node &options) const; + + std::string newCoordsetName(const conduit::Node &mesh, + const conduit::Node &options) const; + + std::string matsetName(const conduit::Node &mesh, + const conduit::Node &options) const; + std::string newMatsetName(const conduit::Node &mesh, + const conduit::Node &options) const; + + std::vector fieldNames(const conduit::Node &mesh, + const conduit::Node &options) const; + + const conduit::Node &topology(const conduit::Node &input, + const conduit::Node &options) const; + const conduit::Node &matset(const conduit::Node &input, + const conduit::Node &options) const; + + // TODO: method for mapping element field to new topo + // TODO: method for mapping vertex field to new topo }; #if 0 @@ -139,7 +144,7 @@ class ElviraMIRAlgorithm : public MIRAlgorithm }; #endif -} // end namespace mir -} // end namespace axom +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/MIRMesh.cpp index 2da1b10f5b..a71116466a 100644 --- a/src/axom/mir/MIRMesh.cpp +++ b/src/axom/mir/MIRMesh.cpp @@ -14,13 +14,7 @@ namespace axom { namespace mir { - - -MIRMesh::MIRMesh() - : m_verts(0) - , m_elems(0) - , m_numMaterials(0) -{} +MIRMesh::MIRMesh() : m_verts(0), m_elems(0), m_numMaterials(0) { } //-------------------------------------------------------------------------------- @@ -32,50 +26,54 @@ MIRMesh::MIRMesh(const MIRMesh& other) , m_numMaterials(other.m_numMaterials) , m_meshTopology(other.m_meshTopology) { - constructMeshRelations(); - constructVertexPositionMap(other.m_vertexPositions.data()); - constructElementParentMap(other.m_elementParentIDs.data()); - constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); - - m_shapeTypes= IntMap( &m_elems ); - for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); + m_shapeTypes = IntMap(&m_elems); + for(auto el : m_elems) + { + m_shapeTypes[el] = other.m_shapeTypes[el]; + } } MIRMesh& MIRMesh::operator=(const MIRMesh& other) { - if(this != &other) - { - m_verts = other.m_verts; - m_elems = other.m_elems; - - m_meshTopology = other.m_meshTopology; + if(this != &other) + { + m_verts = other.m_verts; + m_elems = other.m_elems; - constructMeshRelations(); - constructVertexPositionMap(other.m_vertexPositions.data()); - constructElementParentMap(other.m_elementParentIDs.data()); - constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); + m_meshTopology = other.m_meshTopology; + constructMeshRelations(); + constructVertexPositionMap(other.m_vertexPositions.data()); + constructElementParentMap(other.m_elementParentIDs.data()); + constructElementDominantMaterialMap(other.m_elementDominantMaterials.data()); - m_shapeTypes = IntMap( &m_elems ); - for(auto el: m_elems) { m_shapeTypes[el] = other.m_shapeTypes[el]; } + m_shapeTypes = IntMap(&m_elems); + for(auto el : m_elems) + { + m_shapeTypes[el] = other.m_shapeTypes[el]; + } - m_materialVolumeFractionsElement = other.m_materialVolumeFractionsElement; - m_materialVolumeFractionsVertex = other.m_materialVolumeFractionsVertex; + m_materialVolumeFractionsElement = other.m_materialVolumeFractionsElement; + m_materialVolumeFractionsVertex = other.m_materialVolumeFractionsVertex; - m_numMaterials = other.m_numMaterials; - } - return *this; + m_numMaterials = other.m_numMaterials; + } + return *this; } //-------------------------------------------------------------------------------- -void MIRMesh::initializeMesh(const VertSet _verts, - const ElemSet _elems, - const int _numMaterials, - const CellTopologyData& _topology, - const CellMapData& _mapData, - const std::vector >& _elementVF) +void MIRMesh::initializeMesh(const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector>& _elementVF) { // Initialize the vertex and element sets m_verts = _verts; @@ -99,12 +97,11 @@ void MIRMesh::initializeMesh(const VertSet _verts, constructElementShapeTypesMap(_mapData.m_shapeTypes); // Initialize the element and vertex volume fraction maps - if (_elementVF.size() > 0) - constructMeshVolumeFractionsMaps(_elementVF); + if(_elementVF.size() > 0) constructMeshVolumeFractionsMaps(_elementVF); // Check validity of the sets - SLIC_ASSERT_MSG( m_verts.isValid(), "Vertex set is not valid."); - SLIC_ASSERT_MSG( m_elems.isValid(), "Element set is not valid."); + SLIC_ASSERT_MSG(m_verts.isValid(), "Vertex set is not valid."); + SLIC_ASSERT_MSG(m_elems.isValid(), "Element set is not valid."); } //-------------------------------------------------------------------------------- @@ -115,75 +112,76 @@ void MIRMesh::constructMeshRelations() { using RelationBuilder = ElemToVertRelation::RelationBuilder; m_bdry = RelationBuilder() - .fromSet( &m_elems ) - .toSet( &m_verts ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( m_elems.size() ) - .data( m_meshTopology.m_evBegins.data() ) ) - .indices ( RelationBuilder::IndicesSetBuilder() - .size( m_meshTopology.m_evInds.size() ) - .data( m_meshTopology.m_evInds.data() ) ); + .fromSet(&m_elems) + .toSet(&m_verts) + .begins(RelationBuilder::BeginsSetBuilder() + .size(m_elems.size()) + .data(m_meshTopology.m_evBegins.data())) + .indices(RelationBuilder::IndicesSetBuilder() + .size(m_meshTopology.m_evInds.size()) + .data(m_meshTopology.m_evInds.data())); } - { // construct coboundary relation from vertices to elements using RelationBuilder = VertToElemRelation::RelationBuilder; m_cobdry = RelationBuilder() - .fromSet( &m_verts ) - .toSet( &m_elems ) - .begins( RelationBuilder::BeginsSetBuilder() - .size( m_verts.size() ) - .data( m_meshTopology.m_veBegins.data() ) ) - .indices( RelationBuilder::IndicesSetBuilder() - .size( m_meshTopology.m_veInds.size() ) - .data( m_meshTopology.m_veInds.data() ) ); + .fromSet(&m_verts) + .toSet(&m_elems) + .begins(RelationBuilder::BeginsSetBuilder() + .size(m_verts.size()) + .data(m_meshTopology.m_veBegins.data())) + .indices(RelationBuilder::IndicesSetBuilder() + .size(m_meshTopology.m_veInds.size()) + .data(m_meshTopology.m_veInds.data())); } - SLIC_ASSERT_MSG( m_bdry.isValid(), "Boundary relation is not valid."); - SLIC_ASSERT_MSG( m_cobdry.isValid(), "Coboundary relation is not valid."); + SLIC_ASSERT_MSG(m_bdry.isValid(), "Boundary relation is not valid."); + SLIC_ASSERT_MSG(m_cobdry.isValid(), "Coboundary relation is not valid."); // SLIC_DEBUG("Elem-Vert relation has size " << m_bdry.totalSize()); // SLIC_DEBUG("Vert-Elem relation has size " << m_cobdry.totalSize()); } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- -void MIRMesh::constructMeshVolumeFractionsMaps(const std::vector >& elementVF) +void MIRMesh::constructMeshVolumeFractionsMaps( + const std::vector>& elementVF) { // Clear the old maps m_materialVolumeFractionsElement.clear(); m_materialVolumeFractionsVertex.clear(); // Initialize the maps for all of the materials with the input volume fraction data for each material - for (int matID = 0; matID < m_numMaterials; ++matID) + for(int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - m_materialVolumeFractionsElement.push_back(ScalarMap( &m_elems )); + m_materialVolumeFractionsElement.push_back(ScalarMap(&m_elems)); // Copy the data for the current material - for (int eID = 0; eID < m_elems.size(); ++eID) + for(int eID = 0; eID < m_elems.size(); ++eID) { m_materialVolumeFractionsElement[matID][eID] = elementVF[matID][eID]; } - SLIC_ASSERT_MSG( m_materialVolumeFractionsElement[matID].isValid(), "Element volume fraction map is not valid."); + SLIC_ASSERT_MSG(m_materialVolumeFractionsElement[matID].isValid(), + "Element volume fraction map is not valid."); } // Initialize the maps for all of the vertex volume fractions - for (int matID = 0; matID < m_numMaterials; ++matID) + for(int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the new map for the volume fractions - m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts ) ); + m_materialVolumeFractionsVertex.push_back(ScalarMap(&m_verts)); // Calculate the average volume fraction value for the current vertex for the current material - for (int vID = 0; vID < m_verts.size(); ++vID) + for(int vID = 0; vID < m_verts.size(); ++vID) { // Compute the per vertex volume fractions for the green material axom::float64 sum = 0; auto vertexElements = m_cobdry[vID]; - for (int i = 0; i < vertexElements.size(); ++i) + for(int i = 0; i < vertexElements.size(); ++i) { auto eID = vertexElements[i]; sum += m_materialVolumeFractionsElement[matID][eID]; @@ -192,71 +190,77 @@ void MIRMesh::constructMeshVolumeFractionsMaps(const std::vector >& vertexVF) +void MIRMesh::constructMeshVolumeFractionsVertex( + const std::vector>& vertexVF) { m_materialVolumeFractionsVertex.clear(); // Initialize the maps for all of the materials with the input volume fraction data for each vertex - for (int matID = 0; matID < m_numMaterials; ++matID) + for(int matID = 0; matID < m_numMaterials; ++matID) { // Initialize the map for the current material - m_materialVolumeFractionsVertex.push_back(ScalarMap( &m_verts )); + m_materialVolumeFractionsVertex.push_back(ScalarMap(&m_verts)); // Copy the data for the current material - for (int vID = 0; vID < m_verts.size(); ++vID) + for(int vID = 0; vID < m_verts.size(); ++vID) { m_materialVolumeFractionsVertex[matID][vID] = vertexVF[matID][vID]; } - SLIC_ASSERT_MSG( m_materialVolumeFractionsVertex[matID].isValid(), "Vertex volume fraction map is not valid."); - } + SLIC_ASSERT_MSG(m_materialVolumeFractionsVertex[matID].isValid(), + "Vertex volume fraction map is not valid."); + } } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- void MIRMesh::constructVertexPositionMap(const std::vector& data) { // construct the position map on the vertices - m_vertexPositions = PointMap( &m_verts ); + m_vertexPositions = PointMap(&m_verts); - for (int vID = 0; vID < m_verts.size(); ++vID) + for(int vID = 0; vID < m_verts.size(); ++vID) m_vertexPositions[vID] = data[vID]; - SLIC_ASSERT_MSG( m_vertexPositions.isValid(), "Position map is not valid."); + SLIC_ASSERT_MSG(m_vertexPositions.isValid(), "Position map is not valid."); } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- void MIRMesh::constructElementParentMap(const std::vector& elementParents) { // Initialize the map for the elements' parent IDs - m_elementParentIDs = IntMap( &m_elems ); + m_elementParentIDs = IntMap(&m_elems); // Copy the data for the elements - for (int eID = 0; eID < m_elems.size(); ++eID) + for(int eID = 0; eID < m_elems.size(); ++eID) m_elementParentIDs[eID] = elementParents[eID]; - SLIC_ASSERT_MSG( m_elementParentIDs.isValid(), "Element parent map is not valid."); + SLIC_ASSERT_MSG(m_elementParentIDs.isValid(), + "Element parent map is not valid."); } -//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- -void MIRMesh::constructElementDominantMaterialMap(const std::vector& dominantMaterials) +void MIRMesh::constructElementDominantMaterialMap( + const std::vector& dominantMaterials) { // Initialize the map for the elements' dominant colors - m_elementDominantMaterials = IntMap( &m_elems ); + m_elementDominantMaterials = IntMap(&m_elems); // Copy the dat for the elements - for (int eID = 0; eID < m_elems.size(); ++eID) + for(int eID = 0; eID < m_elems.size(); ++eID) m_elementDominantMaterials[eID] = dominantMaterials[eID]; - SLIC_ASSERT_MSG( m_elementDominantMaterials.isValid(), "Element dominant materials map is not valid."); + SLIC_ASSERT_MSG(m_elementDominantMaterials.isValid(), + "Element dominant materials map is not valid."); } //-------------------------------------------------------------------------------- @@ -264,87 +268,90 @@ void MIRMesh::constructElementDominantMaterialMap(const std::vector& domina void MIRMesh::constructElementShapeTypesMap(const std::vector& shapeTypes) { // Initialize the map for the elements' dominant colors - m_shapeTypes = IntMap( &m_elems ); + m_shapeTypes = IntMap(&m_elems); // Copy the data for the elements - for (int eID = 0; eID < m_elems.size(); ++eID) + for(int eID = 0; eID < m_elems.size(); ++eID) m_shapeTypes[eID] = shapeTypes[eID]; - SLIC_ASSERT_MSG( m_shapeTypes.isValid(), "Element dominant materials map is not valid."); + SLIC_ASSERT_MSG(m_shapeTypes.isValid(), + "Element dominant materials map is not valid."); } //-------------------------------------------------------------------------------- void MIRMesh::print() { - printf("\n------------------------Printing Mesh Information:------------------------\n"); + printf( + "\n------------------------Printing Mesh " + "Information:------------------------\n"); printf("number of vertices: %d\n", m_verts.size()); printf("number of elements: %d\n", m_elems.size()); printf("number of materials: %d\n", m_numMaterials); printf("evInds: { "); - for (unsigned long i = 0; i < m_meshTopology.m_evInds.size(); i++) + for(unsigned long i = 0; i < m_meshTopology.m_evInds.size(); i++) { printf("%d ", m_meshTopology.m_evInds[i]); } printf("}\n"); printf("evBegins: { "); - for (unsigned long i = 0; i < m_meshTopology.m_evBegins.size(); i++) + for(unsigned long i = 0; i < m_meshTopology.m_evBegins.size(); i++) { printf("%d ", m_meshTopology.m_evBegins[i]); } printf("}\n"); printf("veInds: { "); - for (unsigned long i = 0; i < m_meshTopology.m_veInds.size(); i++) + for(unsigned long i = 0; i < m_meshTopology.m_veInds.size(); i++) { printf("%d ", m_meshTopology.m_veInds[i]); } printf("}\n"); printf("veBegins: { "); - for (unsigned long i = 0; i < m_meshTopology.m_veBegins.size(); i++) + for(unsigned long i = 0; i < m_meshTopology.m_veBegins.size(); i++) { printf("%d ", m_meshTopology.m_veBegins[i]); } printf("}\n"); printf("vertexPositions: { "); - for (int i = 0; i < m_verts.size(); ++i) + for(int i = 0; i < m_verts.size(); ++i) { std::stringstream sstr; - sstr<< m_vertexPositions[i]; + sstr << m_vertexPositions[i]; printf("%s", sstr.str().c_str()); } printf("}\n"); printf("elementParentIDs: { "); - for (int i = 0; i < m_elems.size(); ++i) + for(int i = 0; i < m_elems.size(); ++i) { printf("%d ", m_elementParentIDs[i]); } printf("}\n"); printf("elementDominantMaterials: { "); - for (int i = 0; i < m_elems.size(); ++i) + for(int i = 0; i < m_elems.size(); ++i) { printf("%d ", m_elementDominantMaterials[i]); } printf("}\n"); printf("shapeTypes: { "); - for (int i = 0; i < m_elems.size(); ++i) + for(int i = 0; i < m_elems.size(); ++i) { printf("%d ", m_shapeTypes[i]); } printf("}\n"); printf("elementVolumeFractions: { \n"); - for (unsigned long i = 0; i < m_materialVolumeFractionsElement.size(); ++i) + for(unsigned long i = 0; i < m_materialVolumeFractionsElement.size(); ++i) { printf(" { "); - for (int j = 0; j < m_elems.size(); ++j) + for(int j = 0; j < m_elems.size(); ++j) { printf("%.3f, ", m_materialVolumeFractionsElement[i][j]); } @@ -353,25 +360,28 @@ void MIRMesh::print() printf("}\n"); printf("vertexVolumeFractions: { \n"); - for (unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) + for(unsigned long i = 0; i < m_materialVolumeFractionsVertex.size(); ++i) { printf(" { "); - for (int j = 0; j < m_verts.size(); ++j) + for(int j = 0; j < m_verts.size(); ++j) { printf("%.3f, ", m_materialVolumeFractionsVertex[i][j]); } printf("}\n"); } printf("}\n"); - printf("--------------------------------------------------------------------------\n"); + printf( + "--------------------------------------------------------------------------" + "\n"); } //-------------------------------------------------------------------------------- void MIRMesh::readMeshFromFile(std::string filename) { - printf("Mesh reading functionality not implemented yet. Can't read file: %s", filename.c_str()); - + printf("Mesh reading functionality not implemented yet. Can't read file: %s", + filename.c_str()); + // Read in header // Read in POINTS @@ -390,12 +400,13 @@ void MIRMesh::writeMeshToFile(const std::string& dirName, const std::string& separator) { // Ensure that the directory to write to exists - if ( !axom::utilities::filesystem::pathExists( dirName ) ) + if(!axom::utilities::filesystem::pathExists(dirName)) { - axom::utilities::filesystem::makeDirsForPath( dirName ); + axom::utilities::filesystem::makeDirsForPath(dirName); } - std::string outputLocation = axom::utilities::filesystem::joinPath(dirName, fileName, separator); + std::string outputLocation = + axom::utilities::filesystem::joinPath(dirName, fileName, separator); std::ofstream meshfile; meshfile.open(outputLocation); @@ -403,106 +414,116 @@ void MIRMesh::writeMeshToFile(const std::string& dirName, // write header meshfile << "# vtk DataFile Version 3.0\n" - << "vtk output\n" - << "ASCII\n" - << "DATASET UNSTRUCTURED_GRID\n" - << "POINTS " << m_verts.size() << " double\n"; + << "vtk output\n" + << "ASCII\n" + << "DATASET UNSTRUCTURED_GRID\n" + << "POINTS " << m_verts.size() << " double\n"; // write positions - for (int vID = 0; vID < m_verts.size(); ++vID) - { + for(int vID = 0; vID < m_verts.size(); ++vID) + { auto& pt = m_vertexPositions[vID]; - for(int i=0; i < pt.dimension(); ++i) + for(int i = 0; i < pt.dimension(); ++i) { meshfile << pt[i] << " "; } meshfile << "\n"; } - meshfile << "\nCELLS " << m_elems.size() << " " << m_meshTopology.m_evInds.size() + m_elems.size(); - for (int i = 0; i < m_elems.size(); ++i) + meshfile << "\nCELLS " << m_elems.size() << " " + << m_meshTopology.m_evInds.size() + m_elems.size(); + for(int i = 0; i < m_elems.size(); ++i) { - int nVerts = m_meshTopology.m_evBegins[i+1] - m_meshTopology.m_evBegins[i]; + int nVerts = m_meshTopology.m_evBegins[i + 1] - m_meshTopology.m_evBegins[i]; meshfile << "\n" << nVerts; - for (int j = 0; j < nVerts; ++j) + for(int j = 0; j < nVerts; ++j) { int startIndex = m_meshTopology.m_evBegins[i]; meshfile << " " << m_meshTopology.m_evInds[startIndex + j]; } - } + } meshfile << "\n\nCELL_TYPES " << m_elems.size() << "\n"; - for (int i = 0; i < m_elems.size(); ++i) + for(int i = 0; i < m_elems.size(); ++i) { - if (m_shapeTypes[i] == mir::Shape::Triangle) + if(m_shapeTypes[i] == mir::Shape::Triangle) meshfile << "5\n"; - else if (m_shapeTypes[i] == mir::Shape::Quad) + else if(m_shapeTypes[i] == mir::Shape::Quad) meshfile << "9\n"; - else if (m_shapeTypes[i] == mir::Shape::Tetrahedron) + else if(m_shapeTypes[i] == mir::Shape::Tetrahedron) meshfile << "10\n"; - else if (m_shapeTypes[i] == mir::Shape::Triangular_Prism) + else if(m_shapeTypes[i] == mir::Shape::Triangular_Prism) meshfile << "13\n"; - else if (m_shapeTypes[i] == mir::Shape::Pyramid) + else if(m_shapeTypes[i] == mir::Shape::Pyramid) meshfile << "14\n"; - else if (m_shapeTypes[i] == mir::Shape::Hexahedron) + else if(m_shapeTypes[i] == mir::Shape::Hexahedron) meshfile << "12\n"; } // write element materials - meshfile << "\n\nCELL_DATA " << m_elems.size() - << "\nSCALARS cellIds int 1" - << "\nLOOKUP_TABLE default \n"; - for(int i=0 ; i< m_elems.size() ; ++i) + meshfile << "\n\nCELL_DATA " << m_elems.size() << "\nSCALARS cellIds int 1" + << "\nLOOKUP_TABLE default \n"; + for(int i = 0; i < m_elems.size(); ++i) { meshfile << m_elementDominantMaterials[i] << " "; } - meshfile <<"\n"; + meshfile << "\n"; } //-------------------------------------------------------------------------------- -std::vector > MIRMesh::computeOriginalElementVolumeFractions() +std::vector> MIRMesh::computeOriginalElementVolumeFractions() { - std::map totalAreaOriginalElements; // the total area of the original elements - std::map newElementAreas; // the area of each of the generated child elements - std::map numChildren; // the number of child elements generated from one of the original elements + std::map totalAreaOriginalElements; // the total area of the original elements + std::map newElementAreas; // the area of each of the generated child elements + std::map numChildren; // the number of child elements generated from one of the original elements // Compute the total area of each element of the original mesh and also the area of each new element - for (int eID = 0; eID < m_elems.size(); ++eID) + for(int eID = 0; eID < m_elems.size(); ++eID) { - int numVertices = m_meshTopology.m_evBegins[eID + 1] - m_meshTopology.m_evBegins[eID]; - if (numVertices == 3) + int numVertices = + m_meshTopology.m_evBegins[eID + 1] - m_meshTopology.m_evBegins[eID]; + if(numVertices == 3) { Point2 trianglePoints[3]; - for (int i = 0; i < 3; ++i) - trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; - newElementAreas[eID] = computeTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]); + for(int i = 0; i < 3; ++i) + trianglePoints[i] = + m_vertexPositions[m_meshTopology.m_evInds[m_meshTopology.m_evBegins[eID] + i]]; + newElementAreas[eID] = computeTriangleArea(trianglePoints[0], + trianglePoints[1], + trianglePoints[2]); } - if (numVertices == 4) + if(numVertices == 4) { Point2 trianglePoints[4]; - for (int i = 0; i < 4; ++i) - trianglePoints[i] = m_vertexPositions[m_meshTopology.m_evInds[ m_meshTopology.m_evBegins[eID] + i] ]; - newElementAreas[eID] = computeQuadArea(trianglePoints[0], trianglePoints[1], trianglePoints[2], trianglePoints[3]); + for(int i = 0; i < 4; ++i) + trianglePoints[i] = + m_vertexPositions[m_meshTopology.m_evInds[m_meshTopology.m_evBegins[eID] + i]]; + newElementAreas[eID] = computeQuadArea(trianglePoints[0], + trianglePoints[1], + trianglePoints[2], + trianglePoints[3]); } - - totalAreaOriginalElements[ m_elementParentIDs[eID] ] += newElementAreas[eID]; - numChildren[ m_elementParentIDs[eID] ] += .4; + + totalAreaOriginalElements[m_elementParentIDs[eID]] += newElementAreas[eID]; + numChildren[m_elementParentIDs[eID]] += .4; } - + // Intialize the element volume fraction vectors - std::vector > elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction - elementVolumeFractions.resize( m_numMaterials ); - for (unsigned long i = 0; i < elementVolumeFractions.size(); ++i) - elementVolumeFractions[i].resize( totalAreaOriginalElements.size(), 0.0 ); + std::vector> + elementVolumeFractions; // indexed as: elementVolumeFractions[material][originalElementID] = volumeFraction + elementVolumeFractions.resize(m_numMaterials); + for(unsigned long i = 0; i < elementVolumeFractions.size(); ++i) + elementVolumeFractions[i].resize(totalAreaOriginalElements.size(), 0.0); // Compute the volume fractions for each of the original mesh elements - for (auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) + for(auto itr = newElementAreas.begin(); itr != newElementAreas.end(); itr++) { int parentElementID = m_elementParentIDs[itr->first]; int materialID = m_elementDominantMaterials[itr->first]; - elementVolumeFractions[materialID][parentElementID] += (itr->second / totalAreaOriginalElements[parentElementID]); + elementVolumeFractions[materialID][parentElementID] += + (itr->second / totalAreaOriginalElements[parentElementID]); } return elementVolumeFractions; @@ -510,19 +531,14 @@ std::vector > MIRMesh::computeOriginalElementVolumeFr //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeTriangleArea(Point2 p0, - Point2 p1, - Point2 p2) +axom::float64 MIRMesh::computeTriangleArea(Point2 p0, Point2 p1, Point2 p2) { - return primal::Triangle(p0,p1,p2).area(); + return primal::Triangle(p0, p1, p2).area(); } //-------------------------------------------------------------------------------- -axom::float64 MIRMesh::computeQuadArea(Point2 p0, - Point2 p1, - Point2 p2, - Point2 p3) +axom::float64 MIRMesh::computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3) { return computeTriangleArea(p0, p1, p2) + computeTriangleArea(p2, p3, p0); } @@ -531,38 +547,38 @@ axom::float64 MIRMesh::computeQuadArea(Point2 p0, bool MIRMesh::isValid(bool verbose) const { - bool bValid = true; + bool bValid = true; - // Check the topology data - bValid = bValid && isTopologyValid(verbose); + // Check the topology data + bValid = bValid && isTopologyValid(verbose); - // Check the volume fraction data - bValid = bValid && areVolumeFractionsValid(verbose); + // Check the volume fraction data + bValid = bValid && areVolumeFractionsValid(verbose); - // Check the map data - bValid = bValid && areMapsValid(verbose); + // Check the map data + bValid = bValid && areMapsValid(verbose); - return bValid; + return bValid; } bool MIRMesh::isTopologyValid(bool verbose) const { - bool bValid = true; + bool bValid = true; - // check the vertex and element sets - bValid = bValid && m_verts.isValid(verbose); - bValid = bValid && m_elems.isValid(verbose); + // check the vertex and element sets + bValid = bValid && m_verts.isValid(verbose); + bValid = bValid && m_elems.isValid(verbose); - // check the relations - if(!m_verts.empty() && !m_elems.empty() ) - { - bValid = bValid && m_bdry.isValid(verbose); - bValid = bValid && m_cobdry.isValid(verbose); + // check the relations + if(!m_verts.empty() && !m_elems.empty()) + { + bValid = bValid && m_bdry.isValid(verbose); + bValid = bValid && m_cobdry.isValid(verbose); } if(!bValid && verbose) { - SLIC_WARNING("MIRMesh invalid: Invalid topology"); + SLIC_WARNING("MIRMesh invalid: Invalid topology"); } return bValid; @@ -573,129 +589,130 @@ bool MIRMesh::areVolumeFractionsValid(bool verbose) const bool bValid = true; int vfElemSize = m_materialVolumeFractionsElement.size(); - if( vfElemSize != m_numMaterials) + if(vfElemSize != m_numMaterials) { - bValid = false; - if(verbose) - { - SLIC_WARNING("MIRMesh invalid: Invalid number of materials in volume fraction array."); - } - return bValid; - } + bValid = false; + if(verbose) + { + SLIC_WARNING( + "MIRMesh invalid: Invalid number of materials in volume fraction " + "array."); + } + return bValid; + } // Check the sizes of the volume fraction arrays - for(int i=0; i < m_numMaterials; ++i) - { - const auto& mats = m_materialVolumeFractionsElement[i]; - if( mats.size() != m_elems.size()) - { - bValid = false; - if(verbose) - { - SLIC_WARNING("MIRMesh invalid: Material " << i <<" has wrong " - << " number of materials in volume fraction array." - << " Expected " << m_elems.size() - << ", got " << mats.size() << "."); - } - } - } - - if(!bValid) - { - return false; - } - - // Check that the volume fractions sum to 1.0 - for(auto el : m_elems.positions()) - { - double sum = 0; - for(int m=0; m < m_numMaterials; ++m) - { - sum += m_materialVolumeFractionsElement[m][el]; - } - - if(! axom::utilities::isNearlyEqual(sum, 1.)) + for(int i = 0; i < m_numMaterials; ++i) + { + const auto& mats = m_materialVolumeFractionsElement[i]; + if(mats.size() != m_elems.size()) + { + bValid = false; + if(verbose) { - bValid = false; - if(verbose) - { - std::stringstream sstr; - - for(int m=0; m < m_numMaterials; ++m) - { - sstr << m_materialVolumeFractionsElement[m][el] << " "; - } - - SLIC_WARNING("MIRMesh invalid: Sum of materials in element" - << el << " was " << sum << ". Expected 1." - << " Values were: " << sstr.str() ); - } + SLIC_WARNING("MIRMesh invalid: Material " + << i << " has wrong " + << " number of materials in volume fraction array." + << " Expected " << m_elems.size() << ", got " + << mats.size() << "."); } - } + } + } - return bValid; -} + if(!bValid) + { + return false; + } -bool MIRMesh::areMapsValid(bool verbose) const -{ - bool bValid = true; + // Check that the volume fractions sum to 1.0 + for(auto el : m_elems.positions()) + { + double sum = 0; + for(int m = 0; m < m_numMaterials; ++m) + { + sum += m_materialVolumeFractionsElement[m][el]; + } - // Check the positions map - if( m_vertexPositions.size() != m_verts.size()) - { + if(!axom::utilities::isNearlyEqual(sum, 1.)) + { bValid = false; if(verbose) { - SLIC_WARNING("MIRMesh invalid: Incorrect number of vertex positions." - << " Expected " << m_verts.size() - << ". Got " << m_vertexPositions.size()); + std::stringstream sstr; + + for(int m = 0; m < m_numMaterials; ++m) + { + sstr << m_materialVolumeFractionsElement[m][el] << " "; + } + + SLIC_WARNING("MIRMesh invalid: Sum of materials in element" + << el << " was " << sum << ". Expected 1." + << " Values were: " << sstr.str()); } - } + } + } - bValid = bValid && m_vertexPositions.isValid(verbose); + return bValid; +} - // Check the element maps - bValid = bValid && m_elementParentIDs.isValid(verbose); - bValid = bValid && m_elementDominantMaterials.isValid(verbose); - bValid = bValid && m_shapeTypes.isValid(verbose); +bool MIRMesh::areMapsValid(bool verbose) const +{ + bool bValid = true; - if( m_elementParentIDs.size() != m_elems.size() ) - { - bValid = false; - if(verbose) - { - SLIC_WARNING("MIRMesh invalid: Incorrect number of elem parent IDs." - << " Expected " << m_elems.size() - << ". Got " << m_elementParentIDs.size()); - } - } + // Check the positions map + if(m_vertexPositions.size() != m_verts.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of vertex positions." + << " Expected " << m_verts.size() << ". Got " + << m_vertexPositions.size()); + } + } - if( m_elementDominantMaterials.size() != m_elems.size() ) - { - bValid = false; - if(verbose) - { - SLIC_WARNING("MIRMesh invalid: Incorrect number of elem dominant mats." - << " Expected " << m_elems.size() - << ". Got " << m_elementDominantMaterials.size()); - } - } + bValid = bValid && m_vertexPositions.isValid(verbose); - if( m_shapeTypes.size() != m_elems.size() ) - { - bValid = false; - if(verbose) - { - SLIC_WARNING("MIRMesh invalid: Incorrect number of elem shape types." - << " Expected " << m_elems.size() - << ". Got " << m_shapeTypes.size()); - } - } + // Check the element maps + bValid = bValid && m_elementParentIDs.isValid(verbose); + bValid = bValid && m_elementDominantMaterials.isValid(verbose); + bValid = bValid && m_shapeTypes.isValid(verbose); - return bValid; + if(m_elementParentIDs.size() != m_elems.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem parent IDs." + << " Expected " << m_elems.size() << ". Got " + << m_elementParentIDs.size()); + } + } -} + if(m_elementDominantMaterials.size() != m_elems.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem dominant mats." + << " Expected " << m_elems.size() << ". Got " + << m_elementDominantMaterials.size()); + } + } + if(m_shapeTypes.size() != m_elems.size()) + { + bValid = false; + if(verbose) + { + SLIC_WARNING("MIRMesh invalid: Incorrect number of elem shape types." + << " Expected " << m_elems.size() << ". Got " + << m_shapeTypes.size()); + } + } + return bValid; } -} + +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/MIRMesh.hpp index faccaa0b84..851f8de8e5 100644 --- a/src/axom/mir/MIRMesh.hpp +++ b/src/axom/mir/MIRMesh.hpp @@ -13,7 +13,6 @@ #ifndef __MIR_MESH_H__ #define __MIR_MESH_H__ - #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions @@ -21,7 +20,7 @@ #include "CellData.hpp" // C/C++ includes -#include // for definition of M_PI, exp() +#include // for definition of M_PI, exp() #include #include #include @@ -34,12 +33,11 @@ namespace axom { namespace mir { +const int NULL_MAT = -1; - const int NULL_MAT = -1; - - //-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- - /** +/** * \class MIRMesh * * \brief The MIRMesh class represents a finite element mesh containing element volume fractions. @@ -48,30 +46,30 @@ namespace mir * to process the mesh. * */ - class MIRMesh - { - public: - /** +class MIRMesh +{ +public: + /** * \brief Default constructor. */ - MIRMesh(); + MIRMesh(); - /** + /** * \brief Copy constructor. */ - MIRMesh(const MIRMesh& other); + MIRMesh(const MIRMesh& other); - /** + /** * \brief Default destructor. */ - ~MIRMesh() = default; + ~MIRMesh() = default; - /** + /** * \brief Copy assignment. */ - MIRMesh& operator=(const MIRMesh& other); + MIRMesh& operator=(const MIRMesh& other); - /** + /** * \brief Initializes a mesh with the provided data and topology. * * \param _verts The set of vertices of the mesh. @@ -81,120 +79,119 @@ namespace mir * \param _mapData The data used to initialized the maps associated with the vertex and element sets. * \param _elementVF The volume fractions of each element. Note that this is an optional parameter. */ - void initializeMesh(const VertSet _verts, - const ElemSet _elems, - const int _numMaterials, - const CellTopologyData& _topology, - const CellMapData& _mapData, - const std::vector >& _elementVF = {}); + void initializeMesh( + const VertSet _verts, + const ElemSet _elems, + const int _numMaterials, + const CellTopologyData& _topology, + const CellMapData& _mapData, + const std::vector>& _elementVF = {}); - /** + /** * \brief Constructs the mesh boundary and coboundary relations. * * \note The boundary relation is from each element to its vertices. * \note The coboundary relation is from each vertex to its elements. */ - void constructMeshRelations(); + void constructMeshRelations(); - /** + /** * \brief Constructs the element and vertex volume fraction maps. * * \param elementVF The volume fractions of each element. */ - void constructMeshVolumeFractionsMaps(const std::vector >& elementVF); - - /** + void constructMeshVolumeFractionsMaps( + const std::vector>& elementVF); + + /** * \brief Constructs the vertex volume fraction map. * * \param vertexVF The volume fractions of each vertex. */ - void constructMeshVolumeFractionsVertex(const std::vector >& vertexVF); + void constructMeshVolumeFractionsVertex( + const std::vector>& vertexVF); - /** + /** * \brief Prints out the data contained within this mesh in a nice format. */ - void print(); + void print(); - /** + /** * \brief Reads in a mesh specified by the given file. * * \param filename The location where the mesh file will be read from. */ - void readMeshFromFile(const std::string filename); + void readMeshFromFile(const std::string filename); - /** + /** * \brief Writes out the mesh to the given file. * * \param filename The location where the mesh file will be written. * * \note Currently reads in an ASCII, UNSTRUCTURED_GRID .vtk file. */ - void writeMeshToFile(const std::string& dirName, - const std::string& fileName, - const std::string& separator = "/"); + void writeMeshToFile(const std::string& dirName, + const std::string& fileName, + const std::string& separator = "/"); - - /** + /** * \brief Computes the volume fractions of the elements of the original mesh. */ - std::vector > computeOriginalElementVolumeFractions(); - - /** + std::vector> computeOriginalElementVolumeFractions(); + + /** * \brief Checks that this instance of MIRMesh is valid * * \return True if this instance is valid, false otherwise */ - bool isValid(bool verbose) const; + bool isValid(bool verbose) const; - private: +private: + /// Utility predicate to check if the mesh's topology is valid + bool isTopologyValid(bool verbose) const; + /// Utility predicate to check if the mesh's volume fractions are valid + bool areVolumeFractionsValid(bool verbose) const; + /// Utility predicate to check if the mesh's maps are valid + bool areMapsValid(bool verbose) const; - /// Utility predicate to check if the mesh's topology is valid - bool isTopologyValid(bool verbose) const; - /// Utility predicate to check if the mesh's volume fractions are valid - bool areVolumeFractionsValid(bool verbose) const; - /// Utility predicate to check if the mesh's maps are valid - bool areMapsValid(bool verbose) const; - - /** + /** * \brief Constructs the positions map on the vertices. * * \param data The vector of position data for each vertex. */ - void constructVertexPositionMap(const std::vector& data); + void constructVertexPositionMap(const std::vector& data); - /** + /** * \brief Constructs the map of elements to their original element parent. * * \param cellParents The vector of parent IDs for each element of the mesh. */ - void constructElementParentMap(const std::vector& elementParents); + void constructElementParentMap(const std::vector& elementParents); - /** + /** * \brief Constructs the map of elements to their dominant materials. * * \param dominantMaterials A vector of material ids that are the dominant material of each element. */ - void constructElementDominantMaterialMap(const std::vector& dominantMaterials); + void constructElementDominantMaterialMap(const std::vector& dominantMaterials); - /** + /** * \brief Constructs the map of elements to their shape types. * * \param shapeTypes A vector of shape enumerators that are the shape type of each element. */ - void constructElementShapeTypesMap(const std::vector& shapeTypes); + void constructElementShapeTypesMap(const std::vector& shapeTypes); - /** + /** * \brief Computes the area of the triangle defined by the given three vertex positions using Heron's formula. * * \param p0 The position of the first vertex. * \param p1 The position of the second vertex. * \param p2 The position of the third vertex. */ - axom::float64 computeTriangleArea(Point2 p0, - Point2 p1, - Point2 p2); + axom::float64 computeTriangleArea(Point2 p0, Point2 p1, Point2 p2); - /** + /** * \brief Computes the area of the quad defined by the given four vertex positions. * * \param p0 The position of the first vertex. @@ -204,36 +201,33 @@ namespace mir * * \note It is assumed that the points are given in consecutive, counter-clockwise order. */ - axom::float64 computeQuadArea(Point2 p0, - Point2 p1, - Point2 p2, - Point2 p3); + axom::float64 computeQuadArea(Point2 p0, Point2 p1, Point2 p2, Point2 p3); - /**************************************************************** + /**************************************************************** * VARIABLES ****************************************************************/ - public: - // Mesh Set Definitions - VertSet m_verts; // the set of vertices in the mesh - ElemSet m_elems; // the set of elements in the mesh - - // Mesh Relation Definitions - ElemToVertRelation m_bdry; // Boundary relation from elements to vertices - VertToElemRelation m_cobdry; // Coboundary relation from vertices to elements - - // Mesh Map Definitions - PointMap m_vertexPositions; // vertex position for each vertex - std::vector m_materialVolumeFractionsElement; // the volume fractions of each material for each element - std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex - IntMap m_elementParentIDs; // the ID of the parent element from the original mesh - IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) - IntMap m_shapeTypes; // the int enumerator of what type of shape each element is - - int m_numMaterials; // the number of materials present in the mesh - CellTopologyData m_meshTopology; // the topology/connectivity of the mesh - }; - - //-------------------------------------------------------------------------------- -} -} +public: + // Mesh Set Definitions + VertSet m_verts; // the set of vertices in the mesh + ElemSet m_elems; // the set of elements in the mesh + + // Mesh Relation Definitions + ElemToVertRelation m_bdry; // Boundary relation from elements to vertices + VertToElemRelation m_cobdry; // Coboundary relation from vertices to elements + + // Mesh Map Definitions + PointMap m_vertexPositions; // vertex position for each vertex + std::vector m_materialVolumeFractionsElement; // the volume fractions of each material for each element + std::vector m_materialVolumeFractionsVertex; // the volume fractions of each material for each vertex + IntMap m_elementParentIDs; // the ID of the parent element from the original mesh + IntMap m_elementDominantMaterials; // the dominant material of the cell (a processed mesh should have only one material per cell) + IntMap m_shapeTypes; // the int enumerator of what type of shape each element is + + int m_numMaterials; // the number of materials present in the mesh + CellTopologyData m_meshTopology; // the topology/connectivity of the mesh +}; + +//-------------------------------------------------------------------------------- +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/MIRMeshTypes.hpp index 96450cc1d4..34259da7cf 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/MIRMeshTypes.hpp @@ -12,53 +12,51 @@ #ifndef __MIR_MESH_TYPES_H__ #define __MIR_MESH_TYPES_H__ - #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" #include "axom/primal.hpp" - namespace axom { namespace mir { - - enum Shape - { - Triangle, - Quad, - Tetrahedron, - Triangular_Prism, - Pyramid, - Hexahedron - }; - - - using Point2 = primal::Point; - - // SET TYPE ALIASES - using PosType = slam::DefaultPositionType; - using ElemType = slam::DefaultElementType; - - using ArrayIndir = slam::policies::CArrayIndirection< PosType, ElemType >; - - using VertSet = slam::PositionSet< PosType, ElemType >; - using ElemSet = slam::PositionSet< PosType, ElemType >; - - // RELATION TYPE ALIASES - using VarCard = slam::policies::VariableCardinality< PosType, ArrayIndir >; - - // Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. - // Note: It is the relation of the elements to the vertices. - - using ElemToVertRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, ElemSet, VertSet >; - using VertToElemRelation = slam::StaticRelation< PosType, ElemType, VarCard, ArrayIndir, VertSet, ElemSet >; - - // MAP TYPE ALIASES - using BaseSet = slam::Set< PosType, ElemType >; - using ScalarMap = slam::Map< axom::float64, BaseSet >; - using PointMap = slam::Map< Point2, BaseSet >; - using IntMap = slam::Map< int, BaseSet >; -} -} +enum Shape +{ + Triangle, + Quad, + Tetrahedron, + Triangular_Prism, + Pyramid, + Hexahedron +}; + +using Point2 = primal::Point; + +// SET TYPE ALIASES +using PosType = slam::DefaultPositionType; +using ElemType = slam::DefaultElementType; + +using ArrayIndir = slam::policies::CArrayIndirection; + +using VertSet = slam::PositionSet; +using ElemSet = slam::PositionSet; + +// RELATION TYPE ALIASES +using VarCard = slam::policies::VariableCardinality; + +// Note: This is the actual relation type, which takes in a bunch of policies and data entries to relate to each other. +// Note: It is the relation of the elements to the vertices. + +using ElemToVertRelation = + slam::StaticRelation; +using VertToElemRelation = + slam::StaticRelation; + +// MAP TYPE ALIASES +using BaseSet = slam::Set; +using ScalarMap = slam::Map; +using PointMap = slam::Map; +using IntMap = slam::Map; +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/MIRUtilities.hpp index f2a9314776..aee6db156f 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/MIRUtilities.hpp @@ -24,48 +24,47 @@ namespace mir { namespace utilities { - //-------------------------------------------------------------------------------- - /** +/** * \brief Determines the number of vertices of the given shape in the finite element zoo. * * \param shape The shape type from the finite element zoo. * * \return THe number of vertices of the shape. */ - inline int numVerts(mir::Shape shape) +inline int numVerts(mir::Shape shape) +{ + int numVertices = 0; + switch(shape) { - int numVertices = 0; - switch (shape) - { - case mir::Shape::Triangle: - numVertices = 3; - break; - case mir::Shape::Quad: - numVertices = 4; - break; - case mir::Shape::Tetrahedron: - numVertices = 4; - break; - case mir::Shape::Pyramid: - numVertices = 5; - break; - case mir::Shape::Triangular_Prism: - numVertices = 6; - break; - case mir::Shape::Hexahedron: - numVertices = 8; - break; - default: - printf("Invalid shape. Cannot determine numVerts().\n"); - } - return numVertices; + case mir::Shape::Triangle: + numVertices = 3; + break; + case mir::Shape::Quad: + numVertices = 4; + break; + case mir::Shape::Tetrahedron: + numVertices = 4; + break; + case mir::Shape::Pyramid: + numVertices = 5; + break; + case mir::Shape::Triangular_Prism: + numVertices = 6; + break; + case mir::Shape::Hexahedron: + numVertices = 8; + break; + default: + printf("Invalid shape. Cannot determine numVerts().\n"); } + return numVertices; +} //-------------------------------------------------------------------------------- - /** +/** * \brief Determines the maximum number of possible vertices of the given shape in the finite element zoo. * This number includes the midpoint vertices between each of the original shape's vertices. * @@ -73,39 +72,38 @@ namespace utilities * * \return THe number of vertices of the shape. */ - inline int maxPossibleNumVerts(mir::Shape shape) +inline int maxPossibleNumVerts(mir::Shape shape) +{ + int numVertices = -1; + switch(shape) { - int numVertices = -1; - switch (shape) - { - case mir::Shape::Triangle: - numVertices = 6 + 1; // add one for the central vertex (not used) - break; - case mir::Shape::Quad: - numVertices = 8 + 1; // add one for the central vertex (not used) - break; - case mir::Shape::Tetrahedron: - numVertices = 10 + 1; // add one for the central vertex (not used) - break; - case mir::Shape::Pyramid: - numVertices = 13 + 1; // add one for the central vertex - break; - case mir::Shape::Triangular_Prism: - numVertices = 15 + 1; // add one for the central vertex - break; - case mir::Shape::Hexahedron: - numVertices = 20 + 1; // add one for the central vertex - break; - default: - printf("Invalid shape. Cannot determine maxPossibleNumVerts().\n"); - } - return numVertices; + case mir::Shape::Triangle: + numVertices = 6 + 1; // add one for the central vertex (not used) + break; + case mir::Shape::Quad: + numVertices = 8 + 1; // add one for the central vertex (not used) + break; + case mir::Shape::Tetrahedron: + numVertices = 10 + 1; // add one for the central vertex (not used) + break; + case mir::Shape::Pyramid: + numVertices = 13 + 1; // add one for the central vertex + break; + case mir::Shape::Triangular_Prism: + numVertices = 15 + 1; // add one for the central vertex + break; + case mir::Shape::Hexahedron: + numVertices = 20 + 1; // add one for the central vertex + break; + default: + printf("Invalid shape. Cannot determine maxPossibleNumVerts().\n"); } - + return numVertices; +} //-------------------------------------------------------------------------------- - /** +/** * \brief Returns the local vertex ID of the from/to vertex that is one of * the two endpoints that the edge the given midpoint is on. * @@ -115,116 +113,368 @@ namespace utilities * * \return The vertex ID of one of the endpoints. */ - inline int getEdgeEndpoint(const mir::Shape shapeType, - const int midpointVertexID, - const bool isFromVertex) +inline int getEdgeEndpoint(const mir::Shape shapeType, + const int midpointVertexID, + const bool isFromVertex) +{ + switch(shapeType) { - switch(shapeType) - { - case mir::Shape::Triangle: - if ( midpointVertexID == 3 && isFromVertex ) { return 0; } - if ( midpointVertexID == 3 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 4 && isFromVertex ) { return 1; } - if ( midpointVertexID == 4 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 5 && isFromVertex ) { return 2; } - if ( midpointVertexID == 5 && !isFromVertex ) { return 0; } - break; - case mir::Shape::Quad: - if ( midpointVertexID == 4 && isFromVertex ) { return 0; } - if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 5 && isFromVertex ) { return 1; } - if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 6 && isFromVertex ) { return 2; } - if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 7 && isFromVertex ) { return 3; } - if ( midpointVertexID == 7 && !isFromVertex ) { return 0; } - break; - case mir::Shape::Tetrahedron: - if ( midpointVertexID == 4 && isFromVertex ) { return 0; } - if ( midpointVertexID == 4 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 5 && isFromVertex ) { return 0; } - if ( midpointVertexID == 5 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 6 && isFromVertex ) { return 0; } - if ( midpointVertexID == 6 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 7 && isFromVertex ) { return 1; } - if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 8 && isFromVertex ) { return 2; } - if ( midpointVertexID == 8 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 9 && isFromVertex ) { return 3; } - if ( midpointVertexID == 9 && !isFromVertex ) { return 1; } - case mir::Shape::Pyramid: - if ( midpointVertexID == 5 && isFromVertex ) { return 0; } - if ( midpointVertexID == 5 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 6 && isFromVertex ) { return 1; } - if ( midpointVertexID == 6 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 7 && isFromVertex ) { return 2; } - if ( midpointVertexID == 7 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 8 && isFromVertex ) { return 3; } - if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + case mir::Shape::Triangle: + if(midpointVertexID == 3 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 3 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 4 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 4 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 5 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 5 && !isFromVertex) + { + return 0; + } + break; + case mir::Shape::Quad: + if(midpointVertexID == 4 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 4 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 5 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 5 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 6 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 6 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 7 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 7 && !isFromVertex) + { + return 0; + } + break; + case mir::Shape::Tetrahedron: + if(midpointVertexID == 4 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 4 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 5 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 5 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 6 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 6 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 7 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 7 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 8 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 8 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 9 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 9 && !isFromVertex) + { + return 1; + } + case mir::Shape::Pyramid: + if(midpointVertexID == 5 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 5 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 6 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 6 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 7 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 7 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 8 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 8 && !isFromVertex) + { + return 0; + } - if ( midpointVertexID == 9 && isFromVertex ) { return 0; } - if ( midpointVertexID == 9 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 10 && isFromVertex ) { return 1; } - if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 11 && isFromVertex ) { return 2; } - if ( midpointVertexID == 11 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 12 && isFromVertex ) { return 3; } - if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } - case mir::Shape::Triangular_Prism: - if ( midpointVertexID == 6 && isFromVertex ) { return 0; } - if ( midpointVertexID == 6 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 7 && isFromVertex ) { return 1; } - if ( midpointVertexID == 7 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 8 && isFromVertex ) { return 2; } - if ( midpointVertexID == 8 && !isFromVertex ) { return 0; } + if(midpointVertexID == 9 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 9 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 10 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 10 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 11 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 11 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 12 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 12 && !isFromVertex) + { + return 4; + } + case mir::Shape::Triangular_Prism: + if(midpointVertexID == 6 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 6 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 7 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 7 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 8 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 8 && !isFromVertex) + { + return 0; + } - if ( midpointVertexID == 9 && isFromVertex ) { return 0; } - if ( midpointVertexID == 9 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 10 && isFromVertex ) { return 1; } - if ( midpointVertexID == 10 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 11 && isFromVertex ) { return 2; } - if ( midpointVertexID == 11 && !isFromVertex ) { return 5; } + if(midpointVertexID == 9 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 9 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 10 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 10 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 11 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 11 && !isFromVertex) + { + return 5; + } - if ( midpointVertexID == 12 && isFromVertex ) { return 3; } - if ( midpointVertexID == 12 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 13 && isFromVertex ) { return 4; } - if ( midpointVertexID == 13 && !isFromVertex ) { return 5; } - if ( midpointVertexID == 14 && isFromVertex ) { return 5; } - if ( midpointVertexID == 14 && !isFromVertex ) { return 3; } - case mir::Shape::Hexahedron: - if ( midpointVertexID == 8 && isFromVertex ) { return 0; } - if ( midpointVertexID == 8 && !isFromVertex ) { return 1; } - if ( midpointVertexID == 9 && isFromVertex ) { return 1; } - if ( midpointVertexID == 9 && !isFromVertex ) { return 2; } - if ( midpointVertexID == 10 && isFromVertex ) { return 2; } - if ( midpointVertexID == 10 && !isFromVertex ) { return 3; } - if ( midpointVertexID == 11 && isFromVertex ) { return 3; } - if ( midpointVertexID == 11 && !isFromVertex ) { return 0; } + if(midpointVertexID == 12 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 12 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 13 && isFromVertex) + { + return 4; + } + if(midpointVertexID == 13 && !isFromVertex) + { + return 5; + } + if(midpointVertexID == 14 && isFromVertex) + { + return 5; + } + if(midpointVertexID == 14 && !isFromVertex) + { + return 3; + } + case mir::Shape::Hexahedron: + if(midpointVertexID == 8 && isFromVertex) + { + return 0; + } + if(midpointVertexID == 8 && !isFromVertex) + { + return 1; + } + if(midpointVertexID == 9 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 9 && !isFromVertex) + { + return 2; + } + if(midpointVertexID == 10 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 10 && !isFromVertex) + { + return 3; + } + if(midpointVertexID == 11 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 11 && !isFromVertex) + { + return 0; + } - if ( midpointVertexID == 12 && isFromVertex ) { return 4; } - if ( midpointVertexID == 12 && !isFromVertex ) { return 5; } - if ( midpointVertexID == 13 && isFromVertex ) { return 5; } - if ( midpointVertexID == 13 && !isFromVertex ) { return 6; } - if ( midpointVertexID == 14 && isFromVertex ) { return 6; } - if ( midpointVertexID == 14 && !isFromVertex ) { return 7; } - if ( midpointVertexID == 15 && isFromVertex ) { return 7; } - if ( midpointVertexID == 15 && !isFromVertex ) { return 4; } + if(midpointVertexID == 12 && isFromVertex) + { + return 4; + } + if(midpointVertexID == 12 && !isFromVertex) + { + return 5; + } + if(midpointVertexID == 13 && isFromVertex) + { + return 5; + } + if(midpointVertexID == 13 && !isFromVertex) + { + return 6; + } + if(midpointVertexID == 14 && isFromVertex) + { + return 6; + } + if(midpointVertexID == 14 && !isFromVertex) + { + return 7; + } + if(midpointVertexID == 15 && isFromVertex) + { + return 7; + } + if(midpointVertexID == 15 && !isFromVertex) + { + return 4; + } - if ( midpointVertexID == 16 && isFromVertex ) { return 0; } - if ( midpointVertexID == 16 && !isFromVertex ) { return 4; } - if ( midpointVertexID == 17 && isFromVertex ) { return 1; } - if ( midpointVertexID == 17 && !isFromVertex ) { return 5; } - if ( midpointVertexID == 18 && isFromVertex ) { return 2; } - if ( midpointVertexID == 18 && !isFromVertex ) { return 6; } - if ( midpointVertexID == 19 && isFromVertex ) { return 3; } - if ( midpointVertexID == 19 && !isFromVertex ) { return 7; } - default: - printf("Edge endpoint case not implemented.\n"); - return -1; - break; + if(midpointVertexID == 16 && isFromVertex) + { + return 0; } + if(midpointVertexID == 16 && !isFromVertex) + { + return 4; + } + if(midpointVertexID == 17 && isFromVertex) + { + return 1; + } + if(midpointVertexID == 17 && !isFromVertex) + { + return 5; + } + if(midpointVertexID == 18 && isFromVertex) + { + return 2; + } + if(midpointVertexID == 18 && !isFromVertex) + { + return 6; + } + if(midpointVertexID == 19 && isFromVertex) + { + return 3; + } + if(midpointVertexID == 19 && !isFromVertex) + { + return 7; + } + default: + printf("Edge endpoint case not implemented.\n"); return -1; + break; } + return -1; +} //-------------------------------------------------------------------------------- @@ -237,7 +487,8 @@ namespace utilities */ inline bool isShapeThreeDimensional(const mir::Shape shapeType) { - return (shapeType == mir::Tetrahedron || shapeType == mir::Pyramid || shapeType == mir::Triangular_Prism || shapeType == mir::Hexahedron); + return (shapeType == mir::Tetrahedron || shapeType == mir::Pyramid || + shapeType == mir::Triangular_Prism || shapeType == mir::Hexahedron); } //-------------------------------------------------------------------------------- @@ -255,16 +506,16 @@ inline int getCenterVertex(const mir::Shape shapeType) { switch(shapeType) { - case mir::Tetrahedron: - return 10; - case mir::Pyramid: - return 13; - case mir::Triangular_Prism: - return 15; - case mir::Hexahedron: - return 20; - default: - return -1; + case mir::Tetrahedron: + return 10; + case mir::Pyramid: + return 13; + case mir::Triangular_Prism: + return 15; + case mir::Hexahedron: + return 20; + default: + return -1; } } @@ -282,13 +533,13 @@ inline int getCenterVertex(const mir::Shape shapeType) */ inline bool isCenterVertex(const mir::Shape shapeType, const int vID) { - if (shapeType == mir::Tetrahedron && vID == 10) + if(shapeType == mir::Tetrahedron && vID == 10) return true; - else if (shapeType == mir::Pyramid && vID == 13) + else if(shapeType == mir::Pyramid && vID == 13) return true; - else if (shapeType == mir::Triangular_Prism && vID == 15) + else if(shapeType == mir::Triangular_Prism && vID == 15) return true; - else if (shapeType == mir::Hexahedron && vID == 20) + else if(shapeType == mir::Hexahedron && vID == 20) return true; else return false; @@ -306,11 +557,11 @@ inline bool isCenterVertex(const mir::Shape shapeType, const int vID) inline axom::float64 computeAverageFloat(const std::vector& values) { axom::float64 sum = 0.0; - for (unsigned long i = 0; i < values.size(); ++i) + for(unsigned long i = 0; i < values.size(); ++i) { sum += values[i]; } - return sum / (axom::float64) values.size(); + return sum / (axom::float64)values.size(); } //-------------------------------------------------------------------------------- @@ -325,11 +576,11 @@ inline axom::float64 computeAverageFloat(const std::vector& value inline mir::Point2 computeAveragePoint(const std::vector& points) { mir::Point2 centroid; - for (auto i = 0u; i < points.size(); ++i) + for(auto i = 0u; i < points.size(); ++i) { centroid.array() += points[i].array(); } - centroid.array() /= (axom::float64) points.size(); + centroid.array() /= (axom::float64)points.size(); return centroid; } @@ -348,53 +599,58 @@ inline mir::Shape determineElementShapeType(const Shape parentShapeType, const int numVerts) { mir::Shape newShapeType; - if (parentShapeType == mir::Shape::Triangle || parentShapeType == mir::Shape::Quad) + if(parentShapeType == mir::Shape::Triangle || + parentShapeType == mir::Shape::Quad) { // Handle the two-dimensional case - switch (numVerts) - { - case 3: - newShapeType = mir::Shape::Triangle; - break; - case 4: - newShapeType = mir::Shape::Quad; - break; - default: - newShapeType = mir::Shape::Triangle; - printf("2D Case: Invalid number of vertices in determineElementShapeType().\n"); - break; + switch(numVerts) + { + case 3: + newShapeType = mir::Shape::Triangle; + break; + case 4: + newShapeType = mir::Shape::Quad; + break; + default: + newShapeType = mir::Shape::Triangle; + printf( + "2D Case: Invalid number of vertices in " + "determineElementShapeType().\n"); + break; } } else { // Handle the three-dimensional case - switch (numVerts) - { - case 4: - newShapeType = mir::Shape::Tetrahedron; - break; - case 5: - newShapeType = mir::Shape::Pyramid; - break; - case 6: - newShapeType = mir::Shape::Triangular_Prism; - break; - case 8: - newShapeType = mir::Shape::Hexahedron; - break; - default: - newShapeType = mir::Shape::Tetrahedron; - printf("3D Case: Invalid number of vertices in determineElementShapeType().\n"); - break; + switch(numVerts) + { + case 4: + newShapeType = mir::Shape::Tetrahedron; + break; + case 5: + newShapeType = mir::Shape::Pyramid; + break; + case 6: + newShapeType = mir::Shape::Triangular_Prism; + break; + case 8: + newShapeType = mir::Shape::Hexahedron; + break; + default: + newShapeType = mir::Shape::Tetrahedron; + printf( + "3D Case: Invalid number of vertices in " + "determineElementShapeType().\n"); + break; } } - return newShapeType; + return newShapeType; } //-------------------------------------------------------------------------------- -} -} -} +} // namespace utilities +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index dfb570ba87..c8de116006 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -12,93 +12,90 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- MIRMesh MeshTester::initTestCaseOne() { - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; int numElements = 9; int numVertices = 16; - mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems(numElements); // Construct an element set with 9 elements + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information topoData.m_evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; + 0, 4, 5, 1, // elem 0, card 4, start 0 + 1, 5, 6, 2, // elem 1, card 4, start 4 + 2, 6, 7, 3, // elem 2, card 4, start 8 + 4, 8, 9, 5, // elem 3, card 4, start 12 + 5, 9, 10, 6, // elem 4, card 4, start 16 + 6, 10, 11, 7, // elem 5, card 4, start 20 + 8, 12, 13, 9, // elem 6, card 4, start 24 + 9, 13, 14, 10, // elem 7, card 4, start 28 + 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 + }; + + topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData.m_veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; + 0, // vert 0, card 1, start 0 + 0, 1, // vert 1, card 2, start 1 + 1, 2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0, 3, // vert 4, card 2, start 6 + 0, 1, 3, 4, // vert 5, card 4, start 8 + 1, 2, 4, 5, // vert 6, card 4, start 12 + 2, 5, // vert 7, card 2, start 16 + 3, 6, // vert 8, card 2, start 18 + 3, 4, 6, 7, // vert 9, card 4, start 20 + 4, 5, 7, 8, // vert 10, card 4, start 24 + 5, 8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6, 7, // vert 13, card 2, start 31 + 7, 8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + topoData + .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; int numMaterials = 2; - enum { GREEN = 0, BLUE = 1 }; + enum + { + GREEN = 0, + BLUE = 1 + }; volFracs.resize(numMaterials); volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), + mir::Point2::make_point(1.0, 3.0), + mir::Point2::make_point(2.0, 3.0), + mir::Point2::make_point(3.0, 3.0), - mapData.m_vertexPositions = - { - mir::Point2::make_point( 0.0, 3.0 ), - mir::Point2::make_point( 1.0, 3.0 ), - mir::Point2::make_point( 2.0, 3.0 ), - mir::Point2::make_point( 3.0, 3.0 ), - - mir::Point2::make_point( 0.0, 2.0 ), - mir::Point2::make_point( 1.0, 2.0 ), - mir::Point2::make_point( 2.0, 2.0 ), - mir::Point2::make_point( 3.0, 2.0 ), - - mir::Point2::make_point( 0.0, 1.0 ), - mir::Point2::make_point( 1.0, 1.0 ), - mir::Point2::make_point( 2.0, 1.0 ), - mir::Point2::make_point( 3.0, 1.0 ), - - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ), - mir::Point2::make_point( 2.0, 0.0 ), - mir::Point2::make_point( 3.0, 0.0 ) - }; + mir::Point2::make_point(0.0, 2.0), + mir::Point2::make_point(1.0, 2.0), + mir::Point2::make_point(2.0, 2.0), + mir::Point2::make_point(3.0, 2.0), + + mir::Point2::make_point(0.0, 1.0), + mir::Point2::make_point(1.0, 1.0), + mir::Point2::make_point(2.0, 1.0), + mir::Point2::make_point(3.0, 1.0), + + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0), + mir::Point2::make_point(2.0, 0.0), + mir::Point2::make_point(3.0, 0.0)}; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_elementParents = + {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh @@ -119,80 +116,80 @@ mir::MIRMesh MeshTester::initTestCaseTwo() int numElements = 9; int numVertices = 16; - mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems(numElements); // Construct an element set with 9 elements + mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information topoData.m_evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; + 0, 4, 5, 1, // elem 0, card 4, start 0 + 1, 5, 6, 2, // elem 1, card 4, start 4 + 2, 6, 7, 3, // elem 2, card 4, start 8 + 4, 8, 9, 5, // elem 3, card 4, start 12 + 5, 9, 10, 6, // elem 4, card 4, start 16 + 6, 10, 11, 7, // elem 5, card 4, start 20 + 8, 12, 13, 9, // elem 6, card 4, start 24 + 9, 13, 14, 10, // elem 7, card 4, start 28 + 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 + }; + + topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData.m_veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; + 0, // vert 0, card 1, start 0 + 0, 1, // vert 1, card 2, start 1 + 1, 2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0, 3, // vert 4, card 2, start 6 + 0, 1, 3, 4, // vert 5, card 4, start 8 + 1, 2, 4, 5, // vert 6, card 4, start 12 + 2, 5, // vert 7, card 2, start 16 + 3, 6, // vert 8, card 2, start 18 + 3, 4, 6, 7, // vert 9, card 4, start 20 + 4, 5, 7, 8, // vert 10, card 4, start 24 + 5, 8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6, 7, // vert 13, card 2, start 31 + 7, 8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + topoData + .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; int numMaterials = 3; - enum { BLUE = 0, RED = 1, ORANGE = 2 }; + enum + { + BLUE = 0, + RED = 1, + ORANGE = 2 + }; volFracs.resize(numMaterials); - volFracs[BLUE] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - volFracs[RED] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; + volFracs[BLUE] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + volFracs[RED] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; volFracs[ORANGE] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - mapData.m_vertexPositions = - { - mir::Point2::make_point( 0.0, 3.0 ), - mir::Point2::make_point( 1.0, 3.0 ), - mir::Point2::make_point( 2.0, 3.0 ), - mir::Point2::make_point( 3.0, 3.0 ), - - mir::Point2::make_point( 0.0, 2.0 ), - mir::Point2::make_point( 1.0, 2.0 ), - mir::Point2::make_point( 2.0, 2.0 ), - mir::Point2::make_point( 3.0, 2.0 ), - - mir::Point2::make_point( 0.0, 1.0 ), - mir::Point2::make_point( 1.0, 1.0 ), - mir::Point2::make_point( 2.0, 1.0 ), - mir::Point2::make_point( 3.0, 1.0 ), - - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ), - mir::Point2::make_point( 2.0, 0.0 ), - mir::Point2::make_point( 3.0, 0.0 ) - }; + mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), + mir::Point2::make_point(1.0, 3.0), + mir::Point2::make_point(2.0, 3.0), + mir::Point2::make_point(3.0, 3.0), + + mir::Point2::make_point(0.0, 2.0), + mir::Point2::make_point(1.0, 2.0), + mir::Point2::make_point(2.0, 2.0), + mir::Point2::make_point(3.0, 2.0), + + mir::Point2::make_point(0.0, 1.0), + mir::Point2::make_point(1.0, 1.0), + mir::Point2::make_point(2.0, 1.0), + mir::Point2::make_point(3.0, 1.0), + + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0), + mir::Point2::make_point(2.0, 0.0), + mir::Point2::make_point(3.0, 0.0)}; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_elementParents = + {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh @@ -206,60 +203,71 @@ mir::MIRMesh MeshTester::initTestCaseTwo() mir::MIRMesh MeshTester::initTestCaseThree() { - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; + mir::CellTopologyData topoData; + mir::CellMapData mapData; + mir::CellData cellData; + VolumeFractions volFracs; - int numElements = 4; - int numVertices = 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material + int numElements = 4; + int numVertices = + 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Create the mesh connectivity information topoData.m_evInds = { - 0,1,2, // elem 0, card 3, start 0 - 1,3,4, // elem 1, card 3, start 3 - 1,4,2, // elem 2, card 3, start 6 - 2,4,5 // elem 3, card 3, start 9, end 12 - }; - - topoData.m_evBegins = { - 0,3,6,9,12 - }; - topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0,1,2, // vert 1, card 3, start 1 - 0,2,3, // vert 2, card 3, start 4 - 1, // vert 3, card 1, start 7 - 1,2,3, // vert 4, card 3, start 8 - 3 // vert 5, card 1, start 11, end 12 - }; - topoData.m_veBegins = { - 0,1,4,7,8,11,12 - }; + 0, + 1, + 2, // elem 0, card 3, start 0 + 1, + 3, + 4, // elem 1, card 3, start 3 + 1, + 4, + 2, // elem 2, card 3, start 6 + 2, + 4, + 5 // elem 3, card 3, start 9, end 12 + }; + topoData.m_evBegins = {0, 3, 6, 9, 12}; + topoData.m_veInds = { + 0, // vert 0, card 1, start 0 + 0, + 1, + 2, // vert 1, card 3, start 1 + 0, + 2, + 3, // vert 2, card 3, start 4 + 1, // vert 3, card 1, start 7 + 1, + 2, + 3, // vert 4, card 3, start 8 + 3 // vert 5, card 1, start 11, end 12 + }; + topoData.m_veBegins = {0, 1, 4, 7, 8, 11, 12}; int numMaterials = 2; - enum { BLUE = 0, RED = 1, }; + enum + { + BLUE = 0, + RED = 1, + }; volFracs.resize(numMaterials); volFracs[BLUE] = {0.0, 0.5, 0.8, 0.5}; - volFracs[RED] = {1.0, 0.5, 0.2, 0.5}; + volFracs[RED] = {1.0, 0.5, 0.2, 0.5}; - mapData.m_vertexPositions = - { - mir::Point2::make_point( 1.0, 2.0 ), - mir::Point2::make_point( 0.5, 1.0 ), - mir::Point2::make_point( 1.5, 1.0 ), - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ), - mir::Point2::make_point( 2.0, 0.0 ) - }; + mapData.m_vertexPositions = {mir::Point2::make_point(1.0, 2.0), + mir::Point2::make_point(0.5, 1.0), + mir::Point2::make_point(1.5, 1.0), + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0), + mir::Point2::make_point(2.0, 0.0)}; mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = { 0,1,2,3 }; // For the base mesh, the parents are always themselves + mapData.m_elementParents = {0, 1, 2, 3}; // For the base mesh, the parents are always themselves mapData.m_shapeTypes = Vec(numElements, mir::Shape::Triangle); // Build the mesh @@ -280,73 +288,72 @@ mir::MIRMesh MeshTester::initTestCaseFour() int numElements = 9; int numVertices = 16; - mir::VertSet verts = mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = mir::ElemSet(numElements); // Construct an element set with 9 elements + mir::VertSet verts = + mir::VertSet(numVertices); // Construct a vertex set with 16 vertices + mir::ElemSet elems = + mir::ElemSet(numElements); // Construct an element set with 9 elements // Create the mesh connectivity information topoData.m_evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; + 0, 4, 5, 1, // elem 0, card 4, start 0 + 1, 5, 6, 2, // elem 1, card 4, start 4 + 2, 6, 7, 3, // elem 2, card 4, start 8 + 4, 8, 9, 5, // elem 3, card 4, start 12 + 5, 9, 10, 6, // elem 4, card 4, start 16 + 6, 10, 11, 7, // elem 5, card 4, start 20 + 8, 12, 13, 9, // elem 6, card 4, start 24 + 9, 13, 14, 10, // elem 7, card 4, start 28 + 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 + }; + + topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData.m_veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; - - - mapData.m_vertexPositions = - { - mir::Point2::make_point( 0.0, 3.0 ), - mir::Point2::make_point( 1.0, 3.0 ), - mir::Point2::make_point( 2.0, 3.0 ), - mir::Point2::make_point( 3.0, 3.0 ), - - mir::Point2::make_point( 0.0, 2.0 ), - mir::Point2::make_point( 1.0, 2.0 ), - mir::Point2::make_point( 2.0, 2.0 ), - mir::Point2::make_point( 3.0, 2.0 ), - - mir::Point2::make_point( 0.0, 1.0 ), - mir::Point2::make_point( 1.0, 1.0 ), - mir::Point2::make_point( 2.0, 1.0 ), - mir::Point2::make_point( 3.0, 1.0 ), - - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ), - mir::Point2::make_point( 2.0, 0.0 ), - mir::Point2::make_point( 3.0, 0.0 ) + 0, // vert 0, card 1, start 0 + 0, 1, // vert 1, card 2, start 1 + 1, 2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0, 3, // vert 4, card 2, start 6 + 0, 1, 3, 4, // vert 5, card 4, start 8 + 1, 2, 4, 5, // vert 6, card 4, start 12 + 2, 5, // vert 7, card 2, start 16 + 3, 6, // vert 8, card 2, start 18 + 3, 4, 6, 7, // vert 9, card 4, start 20 + 4, 5, 7, 8, // vert 10, card 4, start 24 + 5, 8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6, 7, // vert 13, card 2, start 31 + 7, 8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 }; + topoData + .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; + + mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), + mir::Point2::make_point(1.0, 3.0), + mir::Point2::make_point(2.0, 3.0), + mir::Point2::make_point(3.0, 3.0), + + mir::Point2::make_point(0.0, 2.0), + mir::Point2::make_point(1.0, 2.0), + mir::Point2::make_point(2.0, 2.0), + mir::Point2::make_point(3.0, 2.0), + + mir::Point2::make_point(0.0, 1.0), + mir::Point2::make_point(1.0, 1.0), + mir::Point2::make_point(2.0, 1.0), + mir::Point2::make_point(3.0, 1.0), + + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0), + mir::Point2::make_point(2.0, 0.0), + mir::Point2::make_point(3.0, 0.0)}; int numMaterials = 2; - enum { GREEN = 0, BLUE = 1 }; + enum + { + GREEN = 0, + BLUE = 1 + }; volFracs.resize(numMaterials); @@ -362,7 +369,7 @@ mir::MIRMesh MeshTester::initTestCaseFour() auto circleCenter = mir::Point2::make_point(1.5, 1.5); axom::float64 circleRadius = 1.25; int gridSize = 1000; - for (int i = 0; i < numElements; ++i) + for(int i = 0; i < numElements; ++i) { auto vf = calculatePercentOverlapMonteCarlo(gridSize, circleCenter, @@ -376,7 +383,8 @@ mir::MIRMesh MeshTester::initTestCaseFour() } mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = { 0,1,2,3,4,5,6,7,8 }; // For the base mesh, the parents are always themselves + mapData.m_elementParents = + {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); // Build the mesh @@ -388,16 +396,25 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- -mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, const mir::Point2& circleCenter, axom::float64 circleRadius) +mir::MIRMesh MeshTester::createUniformGridTestCaseMesh( + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius) { // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + mir::VertSet verts = + mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = + mir::ElemSet(cellData.m_numElems); // Construct the element set int numMaterials = 2; - enum { GREEN = 0, BLUE = 1 }; + enum + { + GREEN = 0, + BLUE = 1 + }; VolumeFractions volFracs; volFracs.resize(numMaterials); @@ -408,30 +425,37 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, const mir:: const int numMonteCarloSamples = 100; auto& pos = cellData.m_mapData.m_vertexPositions; const auto& evInds = cellData.m_topology.m_evInds; - for (int i = 0; i < cellData.m_numElems; ++i) + for(int i = 0; i < cellData.m_numElems; ++i) { - auto vf = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, - circleCenter, - circleRadius, - pos[evInds[i * 4 + 0]], - pos[evInds[i * 4 + 1]], - pos[evInds[i * 4 + 2]], - pos[evInds[i * 4 + 3]]); - volFracs[GREEN][i] = vf; - volFracs[BLUE][i] = 1.0 - vf; + auto vf = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, + circleCenter, + circleRadius, + pos[evInds[i * 4 + 0]], + pos[evInds[i * 4 + 1]], + pos[evInds[i * 4 + 2]], + pos[evInds[i * 4 + 3]]); + volFracs[GREEN][i] = vf; + volFracs[BLUE][i] = 1.0 - vf; } - cellData.m_mapData.m_elementDominantMaterials = Vec(cellData.m_numVerts, NULL_MAT); - cellData.m_mapData.m_shapeTypes = Vec(cellData.m_numVerts, mir::Shape::Quad); + cellData.m_mapData.m_elementDominantMaterials = + Vec(cellData.m_numVerts, NULL_MAT); + cellData.m_mapData.m_shapeTypes = + Vec(cellData.m_numVerts, mir::Shape::Quad); cellData.m_mapData.m_elementParents.resize(cellData.m_numVerts); - for (auto i : elems.positions() ) + for(auto i : elems.positions()) { - cellData.m_mapData.m_elementParents[i] = i; + cellData.m_mapData.m_elementParents[i] = i; } // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, volFracs); + testMesh.initializeMesh(verts, + elems, + numMaterials, + cellData.m_topology, + cellData.m_mapData, + volFracs); return testMesh; } @@ -439,13 +463,13 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh(int gridSize, const mir:: //-------------------------------------------------------------------------------- axom::float64 MeshTester::calculatePercentOverlapMonteCarlo( - int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3) + int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle auto d0Sq = primal::squared_distance(quadP0, circleCenter); @@ -454,35 +478,36 @@ axom::float64 MeshTester::calculatePercentOverlapMonteCarlo( auto d3Sq = primal::squared_distance(quadP3, circleCenter); auto dRSq = circleRadius * circleRadius; - int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) - + ((d1Sq < dRSq) ? 1 << 1 : 0) - + ((d2Sq < dRSq) ? 1 << 2 : 0) - + ((d3Sq < dRSq) ? 1 << 3 : 0); + int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) + ((d1Sq < dRSq) ? 1 << 1 : 0) + + ((d2Sq < dRSq) ? 1 << 2 : 0) + ((d3Sq < dRSq) ? 1 << 3 : 0); const int allFlags = 15; const int noFlags = 0; - if (inFlags == allFlags ) + if(inFlags == allFlags) { // The entire quad overlaps the circle return 1.; } - else if( inFlags == noFlags) + else if(inFlags == noFlags) { - return 0.; + return 0.; } else { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / static_cast(gridSize - 1); - axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / static_cast(gridSize - 1); + axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / + static_cast(gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / + static_cast(gridSize - 1); int countOverlap = 0; - for (int y = 0; y < gridSize; ++y) + for(int y = 0; y < gridSize; ++y) { - for (int x = 0; x < gridSize; ++x) + for(int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + quadP1[0], - delta_y * y + quadP1[1]); - if (primal::squared_distance(samplePoint, circleCenter) < dRSq) + mir::Point2 samplePoint = + mir::Point2::make_point(delta_x * x + quadP1[0], + delta_y * y + quadP1[1]); + if(primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } } @@ -498,29 +523,29 @@ mir::CellData MeshTester::generateGrid(int gridSize) int numElements = gridSize * gridSize; int numVertices = (gridSize + 1) * (gridSize + 1); - mir::CellData data; + mir::CellData data; data.m_numVerts = numVertices; data.m_numElems = numElements; // Generate the evInds auto& evInds = data.m_topology.m_evInds; - for (int eID = 0; eID < numElements; ++eID) + for(int eID = 0; eID < numElements; ++eID) { int row = eID / gridSize; // note the integer division int vertsPerRow = gridSize + 1; int elemsPerRow = gridSize; - evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 0); - evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); - evInds.push_back( (eID % elemsPerRow) + (row + 1) * vertsPerRow + 1); - evInds.push_back( (eID % elemsPerRow) + row * vertsPerRow + 1); + evInds.push_back((eID % elemsPerRow) + row * vertsPerRow + 0); + evInds.push_back((eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); + evInds.push_back((eID % elemsPerRow) + (row + 1) * vertsPerRow + 1); + evInds.push_back((eID % elemsPerRow) + row * vertsPerRow + 1); } // Generate the evBegins auto& evBegins = data.m_topology.m_evBegins; evBegins.push_back(0); - for (int i = 0; i < numElements; ++i) + for(int i = 0; i < numElements; ++i) { evBegins.push_back((i + 1) * 4); } @@ -528,37 +553,37 @@ mir::CellData MeshTester::generateGrid(int gridSize) // Generate the veInds auto& veInds = data.m_topology.m_veInds; auto& veBegins = data.m_topology.m_veBegins; - std::map > veInds_data; - for (int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) + std::map> veInds_data; + for(int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) { - int currentElementID = evInd_itr / 4; // note the integer division + int currentElementID = evInd_itr / 4; // note the integer division veInds_data[evInds[evInd_itr]].push_back(currentElementID); } - - for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + + for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) { // Sort the vector std::sort(itr->second.begin(), itr->second.end()); // Add the elements associated with the current vertex to veInds - for (unsigned long i = 0; i < itr->second.size(); ++i) + for(unsigned long i = 0; i < itr->second.size(); ++i) veInds.push_back(itr->second[i]); } // Generate the veBegins veBegins.push_back(0); int currentIndexCount = 0; - for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) { currentIndexCount += itr->second.size(); veBegins.push_back(currentIndexCount); } // Generate the vertex positions - auto& points = data.m_mapData.m_vertexPositions; - for (int y = gridSize; y > -1; --y) + auto& points = data.m_mapData.m_vertexPositions; + for(int y = gridSize; y > -1; --y) { - for (int x = 0; x < gridSize + 1; ++x) + for(int x = 0; x < gridSize + 1; ++x) { points.push_back(mir::Point2::make_point(x, y)); } @@ -609,38 +634,44 @@ mir::CellData MeshTester::generateGrid(int gridSize) mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) { - // Generate the mesh topology mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + mir::VertSet verts = + mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = + mir::ElemSet(cellData.m_numElems); // Construct the element set // Generate the element volume fractions with concentric circles int numMaterials = numCircles + 1; - int defaultMaterialID = numMaterials - 1; // default material is always the last index + int defaultMaterialID = + numMaterials - 1; // default material is always the last index - mir::Point2 circleCenter = mir::Point2::make_point(gridSize / 2.0, gridSize / 2.0); // all circles are centered around the same point + mir::Point2 circleCenter = mir::Point2::make_point( + gridSize / 2.0, + gridSize / 2.0); // all circles are centered around the same point // Initialize the radii of the circles - std::vector circleRadii; - axom::float64 maxRadius = gridSize / 2.4; // Note: The choice of divisor is arbitrary - axom::float64 minRadius = gridSize / 8; // Note: The choice of divisor is arbitrary + std::vector circleRadii; + axom::float64 maxRadius = + gridSize / 2.4; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = + gridSize / 8; // Note: The choice of divisor is arbitrary axom::float64 radiusDelta; - if (numCircles <= 1) + if(numCircles <= 1) radiusDelta = (maxRadius - minRadius); else - radiusDelta = (maxRadius - minRadius) / (double) (numCircles - 1); + radiusDelta = (maxRadius - minRadius) / (double)(numCircles - 1); - for (int i = 0; i < numCircles; ++i) + for(int i = 0; i < numCircles; ++i) { - circleRadii.push_back( minRadius + (i * radiusDelta) ); + circleRadii.push_back(minRadius + (i * radiusDelta)); } // Initialize all material volume fractions to 0 - std::vector > materialVolumeFractionsData; - for (int i = 0; i < numMaterials; ++i) + std::vector> materialVolumeFractionsData; + for(int i = 0; i < numMaterials; ++i) { std::vector tempVec; tempVec.resize(cellData.m_numElems); @@ -649,40 +680,51 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) // Use the uniform sampling method to generate volume fractions for each material // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation - for (int eID = 0; eID < cellData.m_numElems; ++eID) + for(int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2& v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; - mir::Point2& v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; - mir::Point2& v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; + mir::Point2& v0 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; + mir::Point2& v1 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; + mir::Point2& v2 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; //mir::Point2& v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; // Run the uniform sampling to determine how much of the current cell is composed of each material - int materialCount[numMaterials]; for (int i = 0; i < numMaterials; ++i) materialCount[i] = 0; + int materialCount[numMaterials]; + for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - for (int matID = 0; matID < numMaterials; ++matID) + for(int matID = 0; matID < numMaterials; ++matID) { - materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + materialVolumeFractionsData[matID][eID] = + materialCount[matID] / (double)(gridSize * gridSize); } - axom::float64 delta_x = axom::utilities::abs(v2[0] - v1[1]) / (double) (gridSize - 1); - axom::float64 delta_y = axom::utilities::abs(v0[1] - v1[1]) / (double) (gridSize - 1); + axom::float64 delta_x = + axom::utilities::abs(v2[0] - v1[1]) / (double)(gridSize - 1); + axom::float64 delta_y = + axom::utilities::abs(v0[1] - v1[1]) / (double)(gridSize - 1); - for (int y = 0; y < gridSize; ++y) + for(int y = 0; y < gridSize; ++y) { - for (int x = 0; x < gridSize; ++x) + for(int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], delta_y * y + v1[1]); + mir::Point2 samplePoint = + mir::Point2::make_point(delta_x * x + v1[0], delta_y * y + v1[1]); bool isPointSampled = false; - for (int cID = 0; cID < numCircles && !isPointSampled; ++cID) + for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) { const auto r = circleRadii[cID]; - if (primal::squared_distance(samplePoint, circleCenter) < r*r) + if(primal::squared_distance(samplePoint, circleCenter) < r * r) { materialCount[cID]++; isPointSampled = true; } } - if (!isPointSampled) + if(!isPointSampled) { // The point was not within any of the circles, so increment the count for the default material materialCount[defaultMaterialID]++; @@ -691,16 +733,17 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } // Assign the element volume fractions based on the count of the samples in each circle - for (int matID = 0; matID < numMaterials; ++matID) + for(int matID = 0; matID < numMaterials; ++matID) { - materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize); + materialVolumeFractionsData[matID][eID] = + materialCount[matID] / (double)(gridSize * gridSize); } } - std::vector elementParents; // For the base mesh, the parents are always themselves + std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; std::vector elementShapeTypes; - for (int i = 0; i < cellData.m_numElems; ++i) + for(int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); @@ -721,18 +764,23 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, materialVolumeFractionsData); + testMesh.initializeMesh(verts, + elems, + numMaterials, + topology, + mapData, + materialVolumeFractionsData); return testMesh; } //-------------------------------------------------------------------------------- -int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, +int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, const mir::Point2& quadP3) { // Check if any of the quad's corners are within the circle @@ -744,14 +792,10 @@ int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, int numCorners = 0; - if (d0Sq < dRSq) - numCorners++; - if (d1Sq < dRSq) - numCorners++; - if (d2Sq < dRSq) - numCorners++; - if (d3Sq < dRSq) - numCorners++; + if(d0Sq < dRSq) numCorners++; + if(d1Sq < dRSq) numCorners++; + if(d2Sq < dRSq) numCorners++; + if(d3Sq < dRSq) numCorners++; return numCorners; } @@ -764,12 +808,14 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() int gridSize = 3; mir::CellData cellData = generateGrid(gridSize); - mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + mir::VertSet verts = + mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = + mir::ElemSet(cellData.m_numElems); // Construct the element set int numMaterials = 2; - std::vector > elementVF; + std::vector> elementVF; elementVF.resize(numMaterials); elementVF[0] = {1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0}; elementVF[1] = {0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0}; @@ -777,62 +823,73 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() std::vector elementParents; std::vector elementDominantMaterials; std::vector elementShapeTypes; - for (int i = 0; i < cellData.m_numElems; ++i) + for(int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); elementShapeTypes.push_back(mir::Shape::Quad); } - + cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; cellData.m_mapData.m_elementParents = elementParents; cellData.m_mapData.m_shapeTypes = elementShapeTypes; // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, cellData.m_topology, cellData.m_mapData, elementVF); + testMesh.initializeMesh(verts, + elems, + numMaterials, + cellData.m_topology, + cellData.m_mapData, + elementVF); return testMesh; } //-------------------------------------------------------------------------------- -mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) +mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) { // Generate the mesh topology mir::CellData cellData = generateGrid3D(gridSize); - mir::VertSet verts = mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = mir::ElemSet(cellData.m_numElems); // Construct the element set + mir::VertSet verts = + mir::VertSet(cellData.m_numVerts); // Construct the vertex set + mir::ElemSet elems = + mir::ElemSet(cellData.m_numElems); // Construct the element set // // Generate the element volume fractions with concentric spheres int numMaterials = numSpheres + 1; - int defaultMaterialID = numMaterials - 1; // default material is always the last index + int defaultMaterialID = + numMaterials - 1; // default material is always the last index - mir::Point2 sphereCenter = mir::Point2::make_point(gridSize / 2.0, - gridSize / 2.0, - gridSize / 2.0); // all spheres are centered around the same point + mir::Point2 sphereCenter = mir::Point2::make_point( + gridSize / 2.0, + gridSize / 2.0, + gridSize / 2.0); // all spheres are centered around the same point // Initialize the radii of the circles - std::vector sphereRadii; - axom::float64 maxRadius = gridSize / 2.0; // Note: The choice of divisor is arbitrary - axom::float64 minRadius = gridSize / 4.0; // Note: The choice of divisor is arbitrary + std::vector sphereRadii; + axom::float64 maxRadius = + gridSize / 2.0; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = + gridSize / 4.0; // Note: The choice of divisor is arbitrary axom::float64 radiusDelta; - if (numSpheres <= 1) + if(numSpheres <= 1) radiusDelta = (maxRadius - minRadius); else - radiusDelta = (maxRadius - minRadius) / (double) (numSpheres - 1); + radiusDelta = (maxRadius - minRadius) / (double)(numSpheres - 1); - for (int i = 0; i < numSpheres; ++i) + for(int i = 0; i < numSpheres; ++i) { auto rad = minRadius + (i * radiusDelta); - sphereRadii.push_back( rad * rad ); + sphereRadii.push_back(rad * rad); } // Initialize all material volume fractions to 0 - std::vector > materialVolumeFractionsData; - for (int i = 0; i < numMaterials; ++i) + std::vector> materialVolumeFractionsData; + for(int i = 0; i < numMaterials; ++i) { std::vector tempVec; tempVec.resize(cellData.m_numElems, 0); @@ -840,46 +897,65 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) } // Use the uniform sampling method to generate volume fractions for each material - for (int eID = 0; eID < cellData.m_numElems; ++eID) + for(int eID = 0; eID < cellData.m_numElems; ++eID) { - mir::Point2 v0 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 0]]; - mir::Point2 v1 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 1]]; - mir::Point2 v2 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 2]]; - mir::Point2 v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; - - mir::Point2 v4 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; - mir::Point2 v5 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 5]]; - mir::Point2 v6 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; - mir::Point2 v7 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; + mir::Point2 v0 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 0]]; + mir::Point2 v1 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 1]]; + mir::Point2 v2 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 2]]; + mir::Point2 v3 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; + + mir::Point2 v4 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; + mir::Point2 v5 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 5]]; + mir::Point2 v6 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; + mir::Point2 v7 = + cellData.m_mapData + .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; // Run the uniform sampling to determine how much of the current cell is composed of each material - int materialCount[numMaterials]; - for (int i = 0; i < numMaterials; ++i) - materialCount[i] = 0; + int materialCount[numMaterials]; + for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - axom::float64 delta_x = axom::utilities::abs(v2[0] - v1[0]) / (double) (gridSize - 1); - axom::float64 delta_y = axom::utilities::abs(v0[1] - v1[1]) / (double) (gridSize - 1); - axom::float64 delta_z = axom::utilities::abs(v5[2] - v1[2]) / (double) (gridSize - 1); + axom::float64 delta_x = + axom::utilities::abs(v2[0] - v1[0]) / (double)(gridSize - 1); + axom::float64 delta_y = + axom::utilities::abs(v0[1] - v1[1]) / (double)(gridSize - 1); + axom::float64 delta_z = + axom::utilities::abs(v5[2] - v1[2]) / (double)(gridSize - 1); - for (int z = 0; z < gridSize; ++z) + for(int z = 0; z < gridSize; ++z) { - for (int y = 0; y < gridSize; ++y) + for(int y = 0; y < gridSize; ++y) { - for (int x = 0; x < gridSize; ++x) + for(int x = 0; x < gridSize; ++x) { - mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], - delta_y * y + v1[1], + mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], + delta_y * y + v1[1], delta_z * z + v1[2]); bool isPointSampled = false; - for (int cID = 0; cID < numSpheres && !isPointSampled; ++cID) + for(int cID = 0; cID < numSpheres && !isPointSampled; ++cID) { - if (primal::squared_distance(samplePoint, sphereCenter) < sphereRadii[cID]) + if(primal::squared_distance(samplePoint, sphereCenter) < + sphereRadii[cID]) { materialCount[cID]++; isPointSampled = true; } } - if (!isPointSampled) + if(!isPointSampled) { // The point was not within any of the circles, so increment the count for the default material materialCount[defaultMaterialID]++; @@ -889,16 +965,17 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) } // Assign the element volume fractions based on the count of the samples in each circle - for (int matID = 0; matID < numMaterials; ++matID) + for(int matID = 0; matID < numMaterials; ++matID) { - materialVolumeFractionsData[matID][eID] = materialCount[matID] / (double) (gridSize * gridSize * gridSize); + materialVolumeFractionsData[matID][eID] = + materialCount[matID] / (double)(gridSize * gridSize * gridSize); } } - std::vector elementParents; // For the base mesh, the parents are always themselves + std::vector elementParents; // For the base mesh, the parents are always themselves std::vector elementDominantMaterials; std::vector elementShapeTypes; - for (int i = 0; i < cellData.m_numElems; ++i) + for(int i = 0; i < cellData.m_numElems; ++i) { elementParents.push_back(i); elementDominantMaterials.push_back(NULL_MAT); @@ -919,14 +996,19 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) // Build the mesh mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topology, mapData, materialVolumeFractionsData); + testMesh.initializeMesh(verts, + elems, + numMaterials, + topology, + mapData, + materialVolumeFractionsData); return testMesh; } //-------------------------------------------------------------------------------- -mir::CellData MeshTester::generateGrid3D(int gridSize) +mir::CellData MeshTester::generateGrid3D(int gridSize) { // Generate the topology for a uniform quad mesh with n x n elements automatically int numElements = gridSize * gridSize * gridSize; @@ -934,53 +1016,63 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) // Generate the evInds std::vector evInds; - for (int eID = 0; eID < numElements; ++eID) + for(int eID = 0; eID < numElements; ++eID) { int vertsPerLayer = (gridSize + 1) * (gridSize + 1); int elemsPerLayer = gridSize * gridSize; - int layer = eID / (gridSize * gridSize); // note the integer division + int layer = eID / (gridSize * gridSize); // note the integer division int vertsPerRow = gridSize + 1; int elemsPerRow = gridSize; int row = (eID % elemsPerLayer) / gridSize; // note the integer division // front face of the grid cube - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 0 + (vertsPerLayer * layer) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * layer) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * layer) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 1 + (vertsPerLayer * layer) ); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + + 0 + (vertsPerLayer * layer)); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + + (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * layer)); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + + (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * layer)); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + + 1 + (vertsPerLayer * layer)); // back face of the grid cube - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 0 + (vertsPerLayer * (layer + 1)) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * (layer + 1)) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * (layer + 1)) ); - evInds.push_back( (eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + 1 + (vertsPerLayer * (layer + 1)) ); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + + 0 + (vertsPerLayer * (layer + 1))); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + + (vertsPerRow * (row + 1)) + 0 + + (vertsPerLayer * (layer + 1))); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + + (vertsPerRow * (row + 1)) + 1 + + (vertsPerLayer * (layer + 1))); + evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + + 1 + (vertsPerLayer * (layer + 1))); } // Generate the evBegins std::vector evBegins; evBegins.push_back(0); - for (int i = 0; i < numElements; ++i) + for(int i = 0; i < numElements; ++i) { evBegins.push_back((i + 1) * 8); } // Generate the veInds - std::map > veInds_data; + std::map> veInds_data; std::vector veInds; - for (int evInd_itr = 0; evInd_itr < numElements * 8; ++evInd_itr) + for(int evInd_itr = 0; evInd_itr < numElements * 8; ++evInd_itr) { - int currentElementID = evInd_itr / 8; // note the integer division + int currentElementID = evInd_itr / 8; // note the integer division veInds_data[evInds[evInd_itr]].push_back(currentElementID); } - - for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + + for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) { // Sort the vector std::sort(itr->second.begin(), itr->second.end()); // Add the elements associated with the current vertex to veInds - for (unsigned long i = 0; i < itr->second.size(); ++i) + for(unsigned long i = 0; i < itr->second.size(); ++i) veInds.push_back(itr->second[i]); } @@ -988,7 +1080,7 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) std::vector veBegins; veBegins.push_back(0); int currentIndexCount = 0; - for (auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) + for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) { currentIndexCount += itr->second.size(); veBegins.push_back(currentIndexCount); @@ -996,11 +1088,11 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) // Generate the vertex positions std::vector points; - for (int z = 0; z < gridSize + 1; ++z) + for(int z = 0; z < gridSize + 1; ++z) { - for (int y = gridSize; y > -1; --y) + for(int y = gridSize; y > -1; --y) { - for (int x = 0; x < gridSize + 1; ++x) + for(int x = 0; x < gridSize + 1; ++x) { points.push_back(mir::Point2::make_point(x, y, z)); } @@ -1021,5 +1113,5 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) //-------------------------------------------------------------------------------- -} -} +} // namespace mir +} // namespace axom diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 25d5336279..1d111b410b 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -26,64 +26,63 @@ namespace axom { namespace mir { - /** +/** * \class MeshTester * * \brief A class used to generate MIRMeshs with specific properties so * that the reconstruction output can be validated visually. * */ - class MeshTester - { +class MeshTester +{ public: - template - using Vec = std::vector; + template + using Vec = std::vector; - using IndexVec = Vec; - using VolFracVec = Vec; - using VolumeFractions = Vec; + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; - - public: - /** +public: + /** * \brief Default constructor. */ - MeshTester() = default; + MeshTester() = default; - /** + /** * \brief Default destructor. */ - ~MeshTester() = default; + ~MeshTester() = default; - public: - /** +public: + /** * \brief Initializes an MIRMesh based on the example from Meredith 2004 paper. * * \note The mesh is a 3x3 uniform grid of quads with 2 materials. * * \return The generated mesh. */ - mir::MIRMesh initTestCaseOne(); + mir::MIRMesh initTestCaseOne(); - /** + /** * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. * * \note The mesh is a 3x3 uniform grid of quads with 3 materials. * * \return The generated mesh. */ - mir::MIRMesh initTestCaseTwo(); + mir::MIRMesh initTestCaseTwo(); - /** + /** * \brief Initializes an MIRMesh used for testing triangle clipping cases. * * \note The mesh is a set of four triangles with 2 materials * * \return The generated mesh. */ - mir::MIRMesh initTestCaseThree(); + mir::MIRMesh initTestCaseThree(); - /** + /** * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. * * \note The mesh is a 3x3 uniform grid with 2 materials and has a single circle in the center composed @@ -91,9 +90,9 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh initTestCaseFour(); + mir::MIRMesh initTestCaseFour(); - /** + /** * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform 2D grid. * * \param gridSize The number of elements in the width and the height of the uniform grid. @@ -103,9 +102,9 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); + mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); - /** + /** * \brief Initializes a mesh to be used for testing a set of concentric spheres centered in a uniform 3D grid. * * \param gridSize The number of elements in the width and the height of the uniform grid. @@ -115,9 +114,9 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh initTestCaseSix(int gridSize, int numSpheres); + mir::MIRMesh initTestCaseSix(int gridSize, int numSpheres); - /** + /** * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. * * \param gridSize The number of elements in the width and height of the uniform grid. @@ -126,11 +125,11 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius); - - /** + mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius); + + /** * \brief Initializes a mesh to be used for validating the results of quad clipping. * * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such @@ -138,24 +137,24 @@ namespace mir * * \return The generated mesh. */ - mir::MIRMesh initQuadClippingTestMesh(); + mir::MIRMesh initQuadClippingTestMesh(); - private: - /** +private: + /** * \brief Generates a 2D uniform grid of n x n elements. * * \param gridSize The number of elements in the width and height of the uniform grid. */ - mir::CellData generateGrid(int gridSize); + mir::CellData generateGrid(int gridSize); - /** + /** * \brief Generates a 3D uniform grid of n x n x n elements. * * \param gridSize The number of elements in the width, height, and depth of the uniform grid. */ - mir::CellData generateGrid3D(int gridSize); + mir::CellData generateGrid3D(int gridSize); - /** + /** * \brief Calculates the percent overlap between the given circle and quad. * * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. @@ -168,16 +167,15 @@ namespace mir * * /return The percent value overlap of the circle and the quad between [0, 1]. */ - axom::float64 calculatePercentOverlapMonteCarlo( - int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3); - - /** + axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); + + /** * \brief Calculates the number of corners of the quad that are within the circle. * * \param circleCenter The center point of the circle. @@ -189,15 +187,14 @@ namespace mir * * \return The number of corners of the quad that are within the circle. */ - int circleQuadCornersOverlaps(const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3); - - }; -} -} + int circleQuadCornersOverlaps(const mir::Point2& circleCenter, + axom::float64 circleRadius, + const mir::Point2& quadP0, + const mir::Point2& quadP1, + const mir::Point2& quadP2, + const mir::Point2& quadP3); +}; +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 40ef179aaa..b1b0ad067b 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -27,7 +27,6 @@ namespace mir { namespace utilities { - /** * \brief Build an o2m relation that lets us look up the zones for a node. */ @@ -35,7 +34,6 @@ template class NodeToZoneRelationBuilder { public: - /** * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. * @@ -55,14 +53,16 @@ class NodeToZoneRelationBuilder * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. */ template - void buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const; + void buildRelation(const ViewType &nodes_view, + ViewType &zones_view, + ViewType &offsets_view) const; }; - template template -void -NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, ViewType &zones_view, ViewType &offsets_view) const +void NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, + ViewType &zones_view, + ViewType &offsets_view) const { assert(nodes_view.size() == zones_view.size()); @@ -73,10 +73,9 @@ NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, const auto n = nodes_view.size(); axom::Array keys(n, n, allocatorID); auto keys_view = keys.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - keys_view[i] = nodes_view[i]; - }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { keys_view[i] = nodes_view[i]; }); // Sort the keys, zones in place. This sorts the zones_view which we want for output. RAJA::sort_pairs(RAJA::make_span(keys_view, n), @@ -85,37 +84,39 @@ NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, // Make a mask array for where differences occur. axom::Array mask(n, n, allocatorID); auto mask_view = mask.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - const axom::IndexType different = (keys_view[i] != keys_view[i - 1]) ? 1 : 0; - const axom::IndexType m = (i >= 1) ? different : 1; - mask_view[i] = m; - }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + const axom::IndexType different = + (keys_view[i] != keys_view[i - 1]) ? 1 : 0; + const axom::IndexType m = (i >= 1) ? different : 1; + mask_view[i] = m; + }); // Do a scan on the mask array to build an offset array. axom::Array dest_offsets(n, n, allocatorID); auto dest_offsets_view = dest_offsets.view(); RAJA::exclusive_scan(RAJA::make_span(mask_view, n), RAJA::make_span(dest_offsets_view, n), - RAJA::operators::plus{}); + RAJA::operators::plus {}); // Build the offsets to each node's zone ids. - axom::for_all(offsets_view.size(), AXOM_LAMBDA(axom::IndexType i) - { - offsets_view[i] = 0; - }); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - if(mask_view[i]) - { - offsets_view[dest_offsets_view[i]] = i; - } - }); + axom::for_all( + offsets_view.size(), + AXOM_LAMBDA(axom::IndexType i) { offsets_view[i] = 0; }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + if(mask_view[i]) + { + offsets_view[dest_offsets_view[i]] = i; + } + }); } template -void -NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) +void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, + conduit::Node &relation) { using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -129,7 +130,8 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit n_sizes.set_allocator(allocatorID); n_offsets.set_allocator(allocatorID); - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); if(type == "unstructured") { @@ -139,32 +141,33 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit const auto connSize = n_connectivity.dtype().number_of_elements(); // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. - const auto nnodes = conduit::blueprint::mesh::utils::coordset::length(*coordset); + const auto nnodes = + conduit::blueprint::mesh::utils::coordset::length(*coordset); if(shape.is_polyhedral()) { - views::dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) - { + views::dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) { const auto nzones = topoView.numberOfZones(); axom::Array sizes(nzones, nzones, allocatorID); auto sizes_view = sizes.view(); // Run through the topology once to do a count of each zone's unique node ids. RAJA::ReduceSum count(0); - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); const auto connSize = count.get(); // Do a scan on the size array to build an offset array. axom::Array offsets(nzones, nzones, allocatorID); auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(sizes_view, nzones), - RAJA::make_span(offsets_view, nzones), - RAJA::operators::plus{}); + RAJA::exclusive_scan( + RAJA::make_span(sizes_view, nzones), + RAJA::make_span(offsets_view, nzones), + RAJA::operators::plus {}); sizes.clear(); // Allocate Conduit arrays on the device in a data type that matches the connectivity. @@ -176,30 +179,37 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit n_sizes.set(conduit::DataType(intTypeId, nnodes)); n_offsets.set(conduit::DataType(intTypeId, nnodes)); - views::IndexNode_to_ArrayView_same(n_conn, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) - { - // Run through the data one more time to build the nodes and zones arrays. - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } + views::IndexNode_to_ArrayView_same( + n_conn, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { + // Run through the data one more time to build the nodes and zones arrays. + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); + + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); }); - - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) - { - sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); - }); - }); }); } else if(shape.is_polygonal()) @@ -215,28 +225,40 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit n_offsets.set(conduit::DataType(intTypeId, nnodes)); // Make zones for each node - views::IndexNode_to_ArrayView_same(n_zones, n_topo_sizes, n_topo_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) - { - using DataType = typename decltype(zonesView)::value_type; - axom::for_all(0, nzones, AXOM_LAMBDA(axom::IndexType zoneIndex) - { - for(DataType i = 0; i < sizesView[zoneIndex]; i++) - zonesView[offsetsView[zoneIndex] + i] = zoneIndex; + views::IndexNode_to_ArrayView_same( + n_zones, + n_topo_sizes, + n_topo_offsets, + [&](auto zonesView, auto sizesView, auto offsetsView) { + using DataType = typename decltype(zonesView)::value_type; + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + for(DataType i = 0; i < sizesView[zoneIndex]; i++) + zonesView[offsetsView[zoneIndex] + i] = zoneIndex; + }); }); - }); - views::IndexNode_to_ArrayView_same(n_connectivity, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) - { - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) - { - sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); + views::IndexNode_to_ArrayView_same( + n_connectivity, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); }); - }); } else { @@ -248,23 +270,32 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit n_sizes.set(conduit::DataType(intTypeId, nnodes)); n_offsets.set(conduit::DataType(intTypeId, nnodes)); - views::IndexNode_to_ArrayView_same(n_connectivity, n_zones, n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) - { - // Make zones for each node - axom::for_all(0, connSize, AXOM_LAMBDA(axom::IndexType index) - { - zonesView[index] = index / nodesPerShape; - }); - // Make the relation, outputting into the zonesView and offsetsView. -// using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all(offsetsView.size(), AXOM_LAMBDA(auto i) - { - sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (connSize - offsetsView[i]); + views::IndexNode_to_ArrayView_same( + n_connectivity, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { + // Make zones for each node + axom::for_all( + 0, + connSize, + AXOM_LAMBDA(axom::IndexType index) { + zonesView[index] = index / nodesPerShape; + }); + // Make the relation, outputting into the zonesView and offsetsView. + // using ViewType = decltype(connectivityView); + buildRelation(connectivityView, zonesView, offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); }); - }); } } else @@ -272,15 +303,18 @@ NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit // These are all structured topos of some sort. Make an unstructured representation and recurse. conduit::Node mesh; - axom::mir::utilities::blueprint::to_unstructured(topo, *coordset, "newtopo", mesh); - + axom::mir::utilities::blueprint::to_unstructured(topo, + *coordset, + "newtopo", + mesh); + // Recurse using the unstructured mesh. execute(mesh["newtopo"], relation); } } -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 393042eca6..cad7cdfddc 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -16,7 +16,6 @@ namespace axom { namespace mir { - /** * \brief This struct contains the type that should be used to accumulate values of type T. */ @@ -39,7 +38,6 @@ template class RecenterField { public: - /** * \brief Convert the input field to a different association type using the o2mrelation and store the new field in the output field. * @@ -47,15 +45,20 @@ class RecenterField * \param relation The node that contains an o2mrelation with nodes to zones. * \param outField[out] The node that will contain the new field. */ - static void execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField); + static void execute(const conduit::Node &field, + const conduit::Node &relation, + conduit::Node &outField); }; template -void -RecenterField::execute(const conduit::Node &field, const conduit::Node &relation, conduit::Node &outField) +void RecenterField::execute(const conduit::Node &field, + const conduit::Node &relation, + conduit::Node &outField) { - auto handleComponent = [](const conduit::Node &relation, const conduit::Node &n_comp, conduit::Node &n_out, int allocatorID) - { + auto handleComponent = [](const conduit::Node &relation, + const conduit::Node &n_comp, + conduit::Node &n_out, + int allocatorID) { // Get the data field for the o2m relation. const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); @@ -63,34 +66,40 @@ RecenterField::execute(const conduit::Node &field, const conduit::Nod const conduit::Node &n_relvalues = relation[data_paths[0]]; const conduit::Node &n_sizes = relation["sizes"]; const conduit::Node &n_offsets = relation["offsets"]; - views::IndexNode_to_ArrayView_same(n_relvalues, n_sizes, n_offsets, [&](auto relView, auto sizesView, auto offsetsView) - { - const auto relSize = sizesView.size(); - - // Allocate data for n_out (same type as n_comp). - n_out.set_allocator(allocatorID); - n_out.set(n_comp.dtype().id(), relSize); - - views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) - { - using Precision = typename decltype(compView)::value_type; - using AccumType = typename accumulate_traits::value_type; - axom::for_all(relSize, AXOM_LAMBDA(int relIndex) - { - const auto n = sizesView[relIndex]; - const auto offset = offsetsView[relIndex]; - - AccumType sum = 0; - for(int i = 0; i < n; i++) - { - const auto id = relView[offset + i]; - sum += static_cast(compView[id]); - } - - outView[relIndex] = static_cast(sum / n); - }); + views::IndexNode_to_ArrayView_same( + n_relvalues, + n_sizes, + n_offsets, + [&](auto relView, auto sizesView, auto offsetsView) { + const auto relSize = sizesView.size(); + + // Allocate data for n_out (same type as n_comp). + n_out.set_allocator(allocatorID); + n_out.set(n_comp.dtype().id(), relSize); + + views::Node_to_ArrayView_same( + n_comp, + n_out, + [&](auto compView, auto outView) { + using Precision = typename decltype(compView)::value_type; + using AccumType = typename accumulate_traits::value_type; + axom::for_all( + relSize, + AXOM_LAMBDA(int relIndex) { + const auto n = sizesView[relIndex]; + const auto offset = offsetsView[relIndex]; + + AccumType sum = 0; + for(int i = 0; i < n; i++) + { + const auto id = relView[offset + i]; + sum += static_cast(compView[id]); + } + + outView[relIndex] = static_cast(sum / n); + }); + }); }); - }); }; const std::string association = field.fetch_existing("association").as_string(); @@ -107,7 +116,10 @@ RecenterField::execute(const conduit::Node &field, const conduit::Nod for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { const conduit::Node &n_comp = n_values[c]; - handleComponent(relation, n_comp, outField["values"][n_comp.name()], allocatorID); + handleComponent(relation, + n_comp, + outField["values"][n_comp.name()], + allocatorID); } } else @@ -116,7 +128,7 @@ RecenterField::execute(const conduit::Node &field, const conduit::Nod } } -} // end namespace mir -} // end namespace axom +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/ZooClippingTables.cpp index 21a0c6ca61..e1cd5c3c3d 100644 --- a/src/axom/mir/ZooClippingTables.cpp +++ b/src/axom/mir/ZooClippingTables.cpp @@ -9,485 +9,783 @@ namespace axom { namespace mir { +// Quad Vertex Local Indices +// +// 0 7 3 +// @---------@---------@ +// | | +// | | +// | | +// 4 @ @ 6 +// | | +// | | +// | | +// @---------@---------@ +// 1 5 2 +// +const int quadClipTable[16][19] = { + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {3, 6, 5, 2, 4, 3, 1, 5, 6, 3, 0, 1, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 5, 7, 4, 7, 5, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 3, 0, 2, 3, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 4, 0, 2, 6, 7, 3, 7, 6, 3, -1}, + {4, 0, 4, 6, 3, 4, 4, 1, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 4, 7, 4, 4, 1, 3, 7, 3, 1, 2, 3, -1, -1, -1, -1, -1, -1}, + {3, 0, 4, 7, 4, 4, 1, 3, 7, 3, 1, 2, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 4, 6, 3, 4, 4, 1, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 4, 0, 2, 6, 7, 3, 7, 6, 3, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 3, 0, 2, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 5, 7, 4, 7, 5, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 5, 2, 4, 3, 1, 5, 6, 3, 0, 1, 3, -1, -1, -1, -1, -1, -1}, + {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; - // Quad Vertex Local Indices - // - // 0 7 3 - // @---------@---------@ - // | | - // | | - // | | - // 4 @ @ 6 - // | | - // | | - // | | - // @---------@---------@ - // 1 5 2 - // - const int quadClipTable[16][19] = - { - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, - {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, - {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, - {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, - {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, - {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, - {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} - }; - - const std::vector > quadClipTableVec = - { - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, - {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, - {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, - {3,0,4,7,4,4,1,3,7,3,1,2,3,-1,-1,-1,-1,-1,-1}, - {4,0,4,6,3,4,4,1,2,6,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,4,1,5,4,0,4,5,2,4,0,2,6,7,3,7,6,3,-1}, - {3,4,1,5,4,0,4,5,2,3,0,2,3,-1,-1,-1,-1,-1,-1}, - {4,0,1,5,7,4,7,5,2,3,-1,-1,-1,-1,-1,-1,-1,-1}, - {3,6,5,2,4,3,1,5,6,3,0,1,3,-1,-1,-1,-1,-1,-1}, - {3,3,7,6,4,7,0,2,6,3,0,1,2,-1,-1,-1,-1,-1,-1}, - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1} - }; - - +const std::vector> quadClipTableVec = { + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {3, 6, 5, 2, 4, 3, 1, 5, 6, 3, 0, 1, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 5, 7, 4, 7, 5, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 3, 0, 2, 3, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 4, 0, 2, 6, 7, 3, 7, 6, 3, -1}, + {4, 0, 4, 6, 3, 4, 4, 1, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 0, 4, 7, 4, 4, 1, 3, 7, 3, 1, 2, 3, -1, -1, -1, -1, -1, -1}, + {3, 0, 4, 7, 4, 4, 1, 3, 7, 3, 1, 2, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 4, 6, 3, 4, 4, 1, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 4, 0, 2, 6, 7, 3, 7, 6, 3, -1}, + {3, 4, 1, 5, 4, 0, 4, 5, 2, 3, 0, 2, 3, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 5, 7, 4, 7, 5, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1}, + {3, 6, 5, 2, 4, 3, 1, 5, 6, 3, 0, 1, 3, -1, -1, -1, -1, -1, -1}, + {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; - // Triangle Vertex Local Indices - // 0 - // @ - // / \ +// Triangle Vertex Local Indices +// 0 +// @ +// / \ // / \ // 3 @ @ 5 - // / \ +// / \ // / \ // 1 @-----@-----@ 2 - // 4 - const int triangleClipTable[8][10] = - { - {3,0,1,2,-1,-1,-1,-1,-1,-1}, - {4,0,1,4,5,3,5,4,2,-1}, - {4,0,3,4,2,3,3,1,4,-1}, - {4,3,1,2,5,3,0,3,5,-1}, - {4,3,1,2,5,3,0,3,5,-1}, - {4,0,3,4,2,3,3,1,4,-1}, - {4,0,1,4,5,3,5,4,2,-1}, - {3,0,1,2,-1,-1,-1,-1,-1,-1} - }; +// 4 +const int triangleClipTable[8][10] = {{3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 4, 5, 3, 5, 4, 2, -1}, + {4, 0, 3, 4, 2, 3, 3, 1, 4, -1}, + {4, 3, 1, 2, 5, 3, 0, 3, 5, -1}, + {4, 3, 1, 2, 5, 3, 0, 3, 5, -1}, + {4, 0, 3, 4, 2, 3, 3, 1, 4, -1}, + {4, 0, 1, 4, 5, 3, 5, 4, 2, -1}, + {3, 0, 1, 2, -1, -1, -1, -1, -1, -1}}; + +const std::vector> triangleClipTableVec = { + {3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, + {4, 0, 1, 4, 5, 3, 5, 4, 2, -1}, + {4, 0, 3, 4, 2, 3, 3, 1, 4, -1}, + {4, 3, 1, 2, 5, 3, 0, 3, 5, -1}, + {4, 3, 1, 2, 5, 3, 0, 3, 5, -1}, + {4, 0, 3, 4, 2, 3, 3, 1, 4, -1}, + {4, 0, 1, 4, 5, 3, 5, 4, 2, -1}, + {3, 0, 1, 2, -1, -1, -1, -1, -1, -1}}; - const std::vector > triangleClipTableVec = - { - {3,0,1,2,-1,-1,-1,-1,-1,-1}, - {4,0,1,4,5,3,5,4,2,-1}, - {4,0,3,4,2,3,3,1,4,-1}, - {4,3,1,2,5,3,0,3,5,-1}, - {4,3,1,2,5,3,0,3,5,-1}, - {4,0,3,4,2,3,3,1,4,-1}, - {4,0,1,4,5,3,5,4,2,-1}, - {3,0,1,2,-1,-1,-1,-1,-1,-1} - }; +const std::vector> tetrahedronClipTableVec = { + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {4, 3, 6, 9, 8, 6, 0, 1, 2, 6, 9, 8, -1}, + {4, 2, 5, 7, 8, 6, 0, 1, 3, 5, 7, 8, -1}, + {6, 2, 5, 7, 3, 6, 9, 6, 0, 5, 6, 1, 7, 9, -1}, + {4, 1, 4, 7, 9, 6, 0, 2, 3, 4, 7, 9, -1}, + {6, 1, 4, 7, 3, 6, 8, 6, 0, 4, 6, 2, 7, 8, -1}, + {6, 1, 4, 9, 2, 5, 8, 6, 0, 4, 5, 3, 9, 8, -1}, + {4, 0, 4, 5, 6, 6, 1, 2, 3, 4, 5, 6, -1}, - const std::vector > tetrahedronClipTableVec = - { - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - {4,3,6,9,8,6,0,1,2,6,9,8,-1}, - {4,2,5,7,8,6,0,1,3,5,7,8,-1}, - {6,2,5,7,3,6,9,6,0,5,6,1,7,9,-1}, - {4,1,4,7,9,6,0,2,3,4,7,9,-1}, - {6,1,4,7,3,6,8,6,0,4,6,2,7,8,-1}, - {6,1,4,9,2,5,8,6,0,4,5,3,9,8,-1}, - {4,0,4,5,6,6,1,2,3,4,5,6,-1}, + {4, 0, 4, 5, 6, 6, 1, 2, 3, 4, 5, 6, -1}, + {6, 1, 4, 9, 2, 5, 8, 6, 0, 4, 5, 3, 9, 8, -1}, + {6, 1, 4, 7, 3, 6, 8, 6, 0, 4, 6, 2, 7, 8, -1}, + {4, 1, 4, 7, 9, 6, 0, 2, 3, 4, 7, 9, -1}, + {6, 2, 5, 7, 3, 6, 9, 6, 0, 5, 6, 1, 7, 9, -1}, + {4, 2, 5, 7, 8, 6, 0, 1, 3, 5, 7, 8, -1}, + {4, 3, 6, 9, 8, 6, 0, 1, 2, 6, 9, 8, -1}, + {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, +}; - {4,0,4,5,6,6,1,2,3,4,5,6,-1}, - {6,1,4,9,2,5,8,6,0,4,5,3,9,8,-1}, - {6,1,4,7,3,6,8,6,0,4,6,2,7,8,-1}, - {4,1,4,7,9,6,0,2,3,4,7,9,-1}, - {6,2,5,7,3,6,9,6,0,5,6,1,7,9,-1}, - {4,2,5,7,8,6,0,1,3,5,7,8,-1}, - {4,3,6,9,8,6,0,1,2,6,9,8,-1}, - {4,0,1,2,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}, - }; +// {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,-1}, // correct decomp +// {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1} // incorect decomp (currently used) +const std::vector> pyramidClipTableVec = { + {5, 0, 1, 2, 3, 4, -1}, + {5, 9, 10, 11, 12, 4, 8, 9, 10, 11, 12, 0, 1, 2, 3, -1}, + {4, 3, 8, 7, 12, 6, 8, 7, 12, 0, 2, 4, 4, 0, 1, 2, 4, -1}, + {6, 3, 8, 7, 4, 9, 11, 4, 4, 9, 10, 11, 6, 9, + 8, 0, 11, 7, 2, 6, 9, 10, 11, 0, 1, 2, -1}, + {4, 2, 6, 7, 11, 6, 6, 7, 11, 1, 3, 4, 4, 0, 1, 3, 4, -1}, + {6, 2, 6, 7, 4, 10, 12, 4, 4, 9, 10, 12, 6, 10, + 6, 1, 12, 7, 3, 6, 9, 10, 12, 0, 1, 3, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 1, 5, 6, 10, 6, 5, 6, 10, 0, 2, 4, 4, 0, 2, 3, 4, -1}, + {6, 1, 5, 6, 4, 9, 11, 4, 4, 9, 11, 12, 6, 9, + 5, 0, 11, 6, 2, 6, 9, 11, 12, 0, 2, 3, -1}, + {4, 1, 10, 5, 6, 6, 10, 5, 6, 4, 0, 2, 6, + 4, 0, 2, 12, 8, 7, 4, 3, 12, 8, 7, -1}, + {4, 0, 9, 5, 8, 6, 9, 5, 8, 4, 1, 3, 6, + 4, 1, 3, 11, 6, 7, 4, 2, 11, 6, 7, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {6, 0, 5, 8, 4, 10, 12, 4, 4, 10, 11, 12, 6, 10, + 5, 1, 12, 8, 3, 6, 10, 11, 12, 1, 2, 3, -1}, + {4, 0, 5, 8, 9, 6, 5, 8, 9, 1, 3, 4, 4, 1, 2, 3, 4, -1}, + {4, 0, 5, 8, 9, 6, 5, 8, 9, 1, 3, 4, 4, 1, 2, 3, 4, -1}, + {6, 0, 5, 8, 4, 10, 12, 4, 4, 10, 11, 12, 6, 10, + 5, 1, 12, 8, 3, 6, 10, 11, 12, 1, 2, 3, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 0, 9, 5, 8, 6, 9, 5, 8, 4, 1, 3, 6, + 4, 1, 3, 11, 6, 7, 4, 2, 11, 6, 7, -1}, + {4, 1, 10, 5, 6, 6, 10, 5, 6, 4, 0, 2, 6, + 4, 0, 2, 12, 8, 7, 4, 3, 12, 8, 7, -1}, + {6, 1, 5, 6, 4, 9, 11, 4, 4, 9, 11, 12, 6, 9, + 5, 0, 11, 6, 2, 6, 9, 11, 12, 0, 2, 3, -1}, + {4, 1, 5, 6, 10, 6, 5, 6, 10, 0, 2, 4, 4, 0, 2, 3, 4, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {4, 13, 0, 1, 4, 4, 13, 1, 2, 4, 4, 13, 2, 3, 4, 4, + 13, 3, 0, 4, 4, 13, 0, 3, 1, 4, 13, 1, 3, 2, -1}, + {6, 2, 6, 7, 4, 10, 12, 4, 4, 9, 10, 12, 6, 10, + 6, 1, 12, 7, 3, 6, 9, 10, 12, 0, 1, 3, -1}, + {4, 2, 6, 7, 11, 6, 6, 7, 11, 1, 3, 4, 4, 0, 1, 3, 4, -1}, + {6, 3, 8, 7, 4, 9, 11, 4, 4, 9, 10, 11, 6, 9, + 8, 0, 11, 7, 2, 6, 9, 10, 11, 0, 1, 2, -1}, + {4, 3, 8, 7, 12, 6, 8, 7, 12, 0, 2, 4, 4, 0, 1, 2, 4, -1}, + {5, 9, 10, 11, 12, 4, 8, 9, 10, 11, 12, 0, 1, 2, 3, -1}, + {5, 0, 1, 2, 3, 4, -1}, +}; - // {5,0,1,2,3,13,4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,-1}, // correct decomp - // {4,0,1,4,13,4,1,2,4,13,4,2,3,4,13,4,3,0,4,13,4,0,1,3,13,4,1,2,3,13,-1} // incorect decomp (currently used) - const std::vector > pyramidClipTableVec = - { - {5,0,1,2,3,4,-1}, - {5,9,10,11,12,4,8,9,10,11,12,0,1,2,3,-1}, - {4,3,8,7,12,6,8,7,12,0,2,4,4,0,1,2,4,-1}, - {6,3,8,7,4,9,11,4,4,9,10,11,6,9,8,0,11,7,2,6,9,10,11,0,1,2,-1}, - {4,2,6,7,11,6,6,7,11,1,3,4,4,0,1,3,4,-1}, - {6,2,6,7,4,10,12,4,4,9,10,12,6,10,6,1,12,7,3,6,9,10,12,0,1,3,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,1,5,6,10,6,5,6,10,0,2,4,4,0,2,3,4,-1}, - {6,1,5,6,4,9,11,4,4,9,11,12,6,9,5,0,11,6,2,6,9,11,12,0,2,3,-1}, - {4,1,10,5,6,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,3,12,8,7,-1}, - {4,0,9,5,8,6,9,5,8,4,1,3,6,4,1,3,11,6,7,4,2,11,6,7,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {6,0,5,8,4,10,12,4,4,10,11,12,6,10,5,1,12,8,3,6,10,11,12,1,2,3,-1}, - {4,0,5,8,9,6,5,8,9,1,3,4,4,1,2,3,4,-1}, - {4,0,5,8,9,6,5,8,9,1,3,4,4,1,2,3,4,-1}, - {6,0,5,8,4,10,12,4,4,10,11,12,6,10,5,1,12,8,3,6,10,11,12,1,2,3,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,0,9,5,8,6,9,5,8,4,1,3,6,4,1,3,11,6,7,4,2,11,6,7,-1}, - {4,1,10,5,6,6,10,5,6,4,0,2,6,4,0,2,12,8,7,4,3,12,8,7,-1}, - {6,1,5,6,4,9,11,4,4,9,11,12,6,9,5,0,11,6,2,6,9,11,12,0,2,3,-1}, - {4,1,5,6,10,6,5,6,10,0,2,4,4,0,2,3,4,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {4,13,0,1,4,4,13,1,2,4,4,13,2,3,4,4,13,3,0,4,4,13,0,3,1,4,13,1,3,2,-1}, - {6,2,6,7,4,10,12,4,4,9,10,12,6,10,6,1,12,7,3,6,9,10,12,0,1,3,-1}, - {4,2,6,7,11,6,6,7,11,1,3,4,4,0,1,3,4,-1}, - {6,3,8,7,4,9,11,4,4,9,10,11,6,9,8,0,11,7,2,6,9,10,11,0,1,2,-1}, - {4,3,8,7,12,6,8,7,12,0,2,4,4,0,1,2,4,-1}, - {5,9,10,11,12,4,8,9,10,11,12,0,1,2,3,-1}, - {5,0,1,2,3,4,-1}, - }; +// currently set to decompose as if every clipping case were considered "tough" (2**6 = 64 cases total) +const std::vector> triangularPrismClipTableVec = { + {6, 0, 1, 2, 3, 4, 5, -1}, + {4, 5, 14, 13, 11, 6, 14, 13, 11, 3, 4, 2, 5, 4, 1, 0, 3, 2, -1}, + {4, 4, 12, 13, 10, 6, 12, 13, 10, 3, 5, 1, 5, 5, 2, 0, 3, 1, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 3, 12, 14, 9, 6, 12, 14, 9, 4, 5, 0, 5, 5, 2, 1, 4, 0, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 0, 1, 2, 9, 10, 11, 6, 9, 10, 11, 3, 4, 5, -1}, + {4, 2, 8, 7, 11, 6, 8, 7, 11, 0, 1, 5, 5, 1, 4, 3, 0, 5, -1}, + {6, 2, 8, 7, 5, 14, 13, 8, 0, 1, 4, 3, 8, 7, 13, 14, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 1, 6, 7, 10, 6, 6, 7, 10, 0, 2, 4, 5, 2, 5, 3, 0, 4, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 1, 6, 7, 4, 12, 13, 8, 0, 2, 5, 3, 6, 7, 13, 12, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 0, 6, 8, 3, 12, 14, 8, 1, 2, 5, 4, 6, 8, 14, 12, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 6, 8, 9, 6, 6, 8, 9, 1, 2, 3, 5, 2, 5, 4, 1, 3, -1}, + {4, 0, 6, 8, 9, 6, 6, 8, 9, 1, 2, 3, 5, 2, 5, 4, 1, 3, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 0, 6, 8, 3, 12, 14, 8, 1, 2, 5, 4, 6, 8, 14, 12, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 1, 6, 7, 4, 12, 13, 8, 0, 2, 5, 3, 6, 7, 13, 12, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 1, 6, 7, 10, 6, 6, 7, 10, 0, 2, 4, 5, 2, 5, 3, 0, 4, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {6, 2, 8, 7, 5, 14, 13, 8, 0, 1, 4, 3, 8, 7, 13, 14, -1}, + {4, 2, 8, 7, 11, 6, 8, 7, 11, 0, 1, 5, 5, 1, 4, 3, 0, 5, -1}, + {6, 3, 4, 5, 9, 10, 11, 6, 9, 10, 11, 0, 1, 2, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 3, 12, 14, 9, 6, 12, 14, 9, 4, 5, 0, 5, 5, 2, 1, 4, 0, -1}, + {4, 0, 1, 2, 15, 4, 3, 5, 4, 15, 5, 0, 2, 5, 3, + 15, 5, 1, 4, 5, 2, 15, 5, 0, 3, 4, 1, 15, -1}, + {4, 4, 12, 13, 10, 6, 12, 13, 10, 3, 5, 1, 5, 5, 2, 0, 3, 1, -1}, + {4, 5, 14, 13, 11, 6, 14, 13, 11, 3, 4, 2, 5, 4, 1, 0, 3, 2, -1}, + {6, 0, 1, 2, 3, 4, 5, -1}}; - // currently set to decompose as if every clipping case were considered "tough" (2**6 = 64 cases total) - const std::vector > triangularPrismClipTableVec = - { - {6,0,1,2,3,4,5,-1}, - {4,5,14,13,11,6,14,13,11,3,4,2,5,4,1,0,3,2,-1}, - {4,4,12,13,10,6,12,13,10,3,5,1,5,5,2,0,3,1,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,3,12,14,9,6,12,14,9,4,5,0,5,5,2,1,4,0,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,0,1,2,9,10,11,6,9,10,11,3,4,5,-1}, - {4,2,8,7,11,6,8,7,11,0,1,5,5,1,4,3,0,5,-1}, - {6,2,8,7,5,14,13,8,0,1,4,3,8,7,13,14,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,1,6,7,10,6,6,7,10,0,2,4,5,2,5,3,0,4,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,1,6,7,4,12,13,8,0,2,5,3,6,7,13,12,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,0,6,8,3,12,14,8,1,2,5,4,6,8,14,12,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,6,8,9,6,6,8,9,1,2,3,5,2,5,4,1,3,-1}, - {4,0,6,8,9,6,6,8,9,1,2,3,5,2,5,4,1,3,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,0,6,8,3,12,14,8,1,2,5,4,6,8,14,12,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,1,6,7,4,12,13,8,0,2,5,3,6,7,13,12,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,1,6,7,10,6,6,7,10,0,2,4,5,2,5,3,0,4,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {6,2,8,7,5,14,13,8,0,1,4,3,8,7,13,14,-1}, - {4,2,8,7,11,6,8,7,11,0,1,5,5,1,4,3,0,5,-1}, - {6,3,4,5,9,10,11,6,9,10,11,0,1,2,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,3,12,14,9,6,12,14,9,4,5,0,5,5,2,1,4,0,-1}, - {4,0,1,2,15,4,3,5,4,15,5,0,2,5,3,15,5,1,4,5,2,15,5,0,3,4,1,15,-1}, - {4,4,12,13,10,6,12,13,10,3,5,1,5,5,2,0,3,1,-1}, - {4,5,14,13,11,6,14,13,11,3,4,2,5,4,1,0,3,2,-1}, - {6,0,1,2,3,4,5,-1} - }; +// currently set to decompose as if every clipping case were considered "tough" (2**8 = 256 cases total) +const std::vector> hexahedronClipTableVec = { + {8, 0, 1, 2, 3, 4, 5, 6, 7, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {5, 0, 1, 2, 3, 20, 5, 4, 5, 6, 7, 20, 5, 1, 2, 6, 5, 20, 5, + 0, 4, 7, 3, 20, 5, 0, 1, 5, 4, 20, 5, 2, 6, 7, 3, 20, -1}, + {8, 0, 1, 2, 3, 4, 5, 6, 7, -1}}; - // currently set to decompose as if every clipping case were considered "tough" (2**8 = 256 cases total) - const std::vector > hexahedronClipTableVec = - { - {8,0,1,2,3,4,5,6,7,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {5,0,1,2,3,20,5,4,5,6,7,20,5,1,2,6,5,20,5,0,4,7,3,20,5,0,1,5,4,20,5,2,6,7,3,20,-1}, - {8,0,1,2,3,4,5,6,7,-1} - }; - -} -} \ No newline at end of file +} // namespace mir +} // namespace axom \ No newline at end of file diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/ZooClippingTables.hpp index 0b7629548e..3a555d96d8 100644 --- a/src/axom/mir/ZooClippingTables.hpp +++ b/src/axom/mir/ZooClippingTables.hpp @@ -19,15 +19,15 @@ namespace axom { namespace mir { - extern const int quadClipTable[16][19]; - extern const int triangleClipTable[8][10]; - - extern const std::vector > triangleClipTableVec; - extern const std::vector > quadClipTableVec; - extern const std::vector > tetrahedronClipTableVec; - extern const std::vector > pyramidClipTableVec; - extern const std::vector > triangularPrismClipTableVec; - extern const std::vector > hexahedronClipTableVec; -} -} +extern const int quadClipTable[16][19]; +extern const int triangleClipTable[8][10]; + +extern const std::vector> triangleClipTableVec; +extern const std::vector> quadClipTableVec; +extern const std::vector> tetrahedronClipTableVec; +extern const std::vector> pyramidClipTableVec; +extern const std::vector> triangularPrismClipTableVec; +extern const std::vector> hexahedronClipTableVec; +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index e020da75af..65a5a33688 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -53,7 +53,7 @@ void ConduitAllocateThroughAxom::internal_free(void *ptr) } #endif -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 251150cb9c..8f40d80b48 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -31,44 +31,86 @@ namespace utilities { namespace blueprint { - //------------------------------------------------------------------------------ /** * \brief This class provides a couple of type traits that let us map C++ types * to types / values useful in Conduit. */ template -struct cpp2conduit { static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; }; +struct cpp2conduit +{ + static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; +}; template <> -struct cpp2conduit { using type = conduit::int8; static constexpr conduit::index_t id = conduit::DataType::INT8_ID; }; +struct cpp2conduit +{ + using type = conduit::int8; + static constexpr conduit::index_t id = conduit::DataType::INT8_ID; +}; template <> -struct cpp2conduit { using type = conduit::int16; static constexpr conduit::index_t id = conduit::DataType::INT16_ID; }; +struct cpp2conduit +{ + using type = conduit::int16; + static constexpr conduit::index_t id = conduit::DataType::INT16_ID; +}; template <> -struct cpp2conduit { using type = conduit::int32; static constexpr conduit::index_t id = conduit::DataType::INT32_ID; }; +struct cpp2conduit +{ + using type = conduit::int32; + static constexpr conduit::index_t id = conduit::DataType::INT32_ID; +}; template <> -struct cpp2conduit { using type = conduit::int64; static constexpr conduit::index_t id = conduit::DataType::INT64_ID; }; +struct cpp2conduit +{ + using type = conduit::int64; + static constexpr conduit::index_t id = conduit::DataType::INT64_ID; +}; template <> -struct cpp2conduit { using type = conduit::uint8; static constexpr conduit::index_t id = conduit::DataType::UINT8_ID; }; +struct cpp2conduit +{ + using type = conduit::uint8; + static constexpr conduit::index_t id = conduit::DataType::UINT8_ID; +}; template <> -struct cpp2conduit { using type = conduit::uint16; static constexpr conduit::index_t id = conduit::DataType::UINT16_ID; }; +struct cpp2conduit +{ + using type = conduit::uint16; + static constexpr conduit::index_t id = conduit::DataType::UINT16_ID; +}; template <> -struct cpp2conduit { using type = conduit::uint32; static constexpr conduit::index_t id = conduit::DataType::UINT32_ID; }; +struct cpp2conduit +{ + using type = conduit::uint32; + static constexpr conduit::index_t id = conduit::DataType::UINT32_ID; +}; template <> -struct cpp2conduit { using type = conduit::uint64; static constexpr conduit::index_t id = conduit::DataType::UINT64_ID; }; +struct cpp2conduit +{ + using type = conduit::uint64; + static constexpr conduit::index_t id = conduit::DataType::UINT64_ID; +}; template <> -struct cpp2conduit { using type = conduit::float32; static constexpr conduit::index_t id = conduit::DataType::FLOAT32_ID; }; +struct cpp2conduit +{ + using type = conduit::float32; + static constexpr conduit::index_t id = conduit::DataType::FLOAT32_ID; +}; template <> -struct cpp2conduit { using type = conduit::float64; static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; }; +struct cpp2conduit +{ + using type = conduit::float64; + static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; +}; //------------------------------------------------------------------------------ /** @@ -86,7 +128,8 @@ class ConduitAllocateThroughAxom static conduit::index_t conduitAllocatorID = -1; if(conduitAllocatorID == -1) { - conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); + conduitAllocatorID = + conduit::utils::register_allocator(internal_allocate, internal_free); } return conduitAllocatorID; } @@ -95,7 +138,8 @@ class ConduitAllocateThroughAxom static void *internal_allocate(size_t items, size_t item_size) { const auto axomAllocatorID = axom::execution_space::allocatorID(); - void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); + void *ptr = static_cast( + axom::allocate(items * item_size, axomAllocatorID)); //std::cout << axom::execution_space::name() << ": Allocated for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; return ptr; } @@ -122,8 +166,10 @@ class ConduitAllocateThroughAxom * \note There are blueprint methods for this sort of thing but this one is accelerated. */ template -void -to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const std::string &topoName, conduit::Node &mesh) +void to_unstructured(const conduit::Node &topo, + const conduit::Node &coordset, + const std::string &topoName, + conduit::Node &mesh) { const std::string type = topo.fetch_existing("type").as_string(); ConduitAllocateThroughAxom c2a; @@ -146,39 +192,48 @@ to_unstructured(const conduit::Node &topo, const conduit::Node &coordset, const n_newsizes.set_allocator(c2a.getConduitAllocatorID()); n_newoffsets.set_allocator(c2a.getConduitAllocatorID()); - views::dispatch_structured_topologies(topo, coordset, [&](const std::string &shape, auto &topoView) - { - int ptsPerZone = 2; - if(shape == "quad") - ptsPerZone = 4; - else if(shape == "hex") - ptsPerZone = 8; - - newtopo["elements/shape"] = shape; - - // Allocate new mesh data. - const auto nzones = topoView.numberOfZones(); - const auto connSize = nzones * ptsPerZone; - n_newconn.set(conduit::DataType::index_t(connSize)); - n_newsizes.set(conduit::DataType::index_t(nzones)); - n_newoffsets.set(conduit::DataType::index_t(nzones)); - - // Make views for the mesh data. - axom::ArrayView connView(static_cast(n_newconn.data_ptr()), connSize); - axom::ArrayView sizesView(static_cast(n_newsizes.data_ptr()), nzones); - axom::ArrayView offsetsView(static_cast(n_newoffsets.data_ptr()), nzones); - - // Fill in the new connectivity. - topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < ptsPerZone; i++) - connView[start + i] = static_cast(zone.getIds()[i]); - - sizesView[zoneIndex] = ptsPerZone; - offsetsView[zoneIndex] = start; + views::dispatch_structured_topologies( + topo, + coordset, + [&](const std::string &shape, auto &topoView) { + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + newtopo["elements/shape"] = shape; + + // Allocate new mesh data. + const auto nzones = topoView.numberOfZones(); + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + n_newsizes.set(conduit::DataType::index_t(nzones)); + n_newoffsets.set(conduit::DataType::index_t(nzones)); + + // Make views for the mesh data. + axom::ArrayView connView( + static_cast(n_newconn.data_ptr()), + connSize); + axom::ArrayView sizesView( + static_cast(n_newsizes.data_ptr()), + nzones); + axom::ArrayView offsetsView( + static_cast(n_newoffsets.data_ptr()), + nzones); + + // Fill in the new connectivity. + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < ptsPerZone; i++) + connView[start + i] = + static_cast(zone.getIds()[i]); + + sizesView[zoneIndex] = ptsPerZone; + offsetsView[zoneIndex] = start; + }); }); - }); } } @@ -206,7 +261,8 @@ void copy(conduit::Node &dest, const conduit::Node &src) // Allocate the node's memory in the right place. dest.reset(); dest.set_allocator(c2a.getConduitAllocatorID()); - dest.set(conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); + dest.set( + conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); // Copy the data to the destination node. Axom uses Umpire to manage that. if(src.is_compact()) @@ -237,31 +293,34 @@ void copy(conduit::Node &dest, const conduit::Node &src) template std::pair minmax(const conduit::Node &n) { - std::pair retval{0., 0.}; + std::pair retval {0., 0.}; - axom::mir::views::Node_to_ArrayView(n, [&](auto nview) - { + axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { using value_type = typename decltype(nview)::value_type; - using reduce_policy = typename axom::execution_space::reduce_policy; - - RAJA::ReduceMin vmin(std::numeric_limits::max()); - RAJA::ReduceMax vmax(std::numeric_limits::min()); - - axom::for_all(nview.size(), AXOM_LAMBDA(auto index) - { - vmin.min(nview[index]); - vmax.max(nview[index]); - }); + using reduce_policy = + typename axom::execution_space::reduce_policy; + + RAJA::ReduceMin vmin( + std::numeric_limits::max()); + RAJA::ReduceMax vmax( + std::numeric_limits::min()); + + axom::for_all( + nview.size(), + AXOM_LAMBDA(auto index) { + vmin.min(nview[index]); + vmax.max(nview[index]); + }); - retval = std::pair{vmin.get(), vmax.get()}; + retval = std::pair {vmin.get(), vmax.get()}; }); return retval; } -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 393723bd96..569aa866f5 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -15,7 +15,6 @@ namespace mir { namespace clipping { - /** * \accelerated * \brief This class contains a view of table data and it provides an @@ -28,7 +27,7 @@ class TableView using TableData = unsigned char; using IndexView = axom::ArrayView; using TableDataView = axom::ArrayView; - + /** * \brief An iterator for shapes within a table case. */ @@ -83,12 +82,11 @@ class TableView * \return true if the iterators are equal; false otherwise. */ AXOM_HOST_DEVICE - inline bool operator == (const iterator &it) const + inline bool operator==(const iterator &it) const { // Do not worry about m_offset return m_shapeStart == it.m_shapeStart && - m_currentShape == it.m_currentShape && - m_numShapes == it.m_numShapes; + m_currentShape == it.m_currentShape && m_numShapes == it.m_numShapes; } /** @@ -97,12 +95,11 @@ class TableView * \return true if the iterators are different; false otherwise. */ AXOM_HOST_DEVICE - inline bool operator != (const iterator &it) const + inline bool operator!=(const iterator &it) const { // Do not worry about m_offset return m_shapeStart != it.m_shapeStart || - m_currentShape != it.m_currentShape || - m_numShapes != it.m_numShapes; + m_currentShape != it.m_currentShape || m_numShapes != it.m_numShapes; } /** @@ -122,26 +119,46 @@ class TableView { switch(shape) { - case ST_PNT: os << "ST_PNT"; break; - case ST_TRI: os << "ST_TRI"; break; - case ST_QUA: os << "ST_QUA"; break; - case ST_TET: os << "ST_TET"; break; - case ST_PYR: os << "ST_PYR"; break; - case ST_WDG: os << "ST_WDG"; break; - case ST_HEX: os << "ST_HEX"; break; + case ST_PNT: + os << "ST_PNT"; + break; + case ST_TRI: + os << "ST_TRI"; + break; + case ST_QUA: + os << "ST_QUA"; + break; + case ST_TET: + os << "ST_TET"; + break; + case ST_PYR: + os << "ST_PYR"; + break; + case ST_WDG: + os << "ST_WDG"; + break; + case ST_HEX: + os << "ST_HEX"; + break; } } void printColor(std::ostream &os, TableData color) const { switch(color) { - case COLOR0: os << "COLOR0"; break; - case COLOR1: os << "COLOR1"; break; - case NOCOLOR: os << "NOCOLOR"; break; + case COLOR0: + os << "COLOR0"; + break; + case COLOR1: + os << "COLOR1"; + break; + case NOCOLOR: + os << "NOCOLOR"; + break; } } void printIds(std::ostream &os, const TableData *ids, int n) const - { + { for(int i = 0; i < n; i++) { if(ids[i] >= P0 && ids[i] <= P7) @@ -158,6 +175,7 @@ class TableView os << " "; } } + public: void print(std::ostream &os) const { @@ -167,13 +185,13 @@ class TableView int offset = 2; if(ptr[0] == ST_PNT) { - os << static_cast(ptr[1]); // point number. + os << static_cast(ptr[1]); // point number. os << " "; printColor(os, ptr[2]); os << " "; - os << static_cast(ptr[3]); // npts + os << static_cast(ptr[3]); // npts os << " "; offset = 4; } @@ -204,30 +222,42 @@ class TableView const auto shape = caseData[0]; switch(shape) { - case ST_PNT: retval = 4 + caseData[3]; break; - case ST_TRI: retval = 2 + 3; break; - case ST_QUA: retval = 2 + 4; break; - case ST_TET: retval = 2 + 4; break; - case ST_PYR: retval = 2 + 5; break; - case ST_WDG: retval = 2 + 6; break; - case ST_HEX: retval = 2 + 8; break; + case ST_PNT: + retval = 4 + caseData[3]; + break; + case ST_TRI: + retval = 2 + 3; + break; + case ST_QUA: + retval = 2 + 4; + break; + case ST_TET: + retval = 2 + 4; + break; + case ST_PYR: + retval = 2 + 5; + break; + case ST_WDG: + retval = 2 + 6; + break; + case ST_HEX: + retval = 2 + 8; + break; } return retval; } - TableData *m_shapeStart{nullptr}; - int m_offset{0}; - int m_currentShape{0}; - int m_numShapes{0}; + TableData *m_shapeStart {nullptr}; + int m_offset {0}; + int m_currentShape {0}; + int m_numShapes {0}; }; /** * \brief Constructor */ AXOM_HOST_DEVICE - TableView() : m_shapes(), m_offsets(), m_table() - { - } + TableView() : m_shapes(), m_offsets(), m_table() { } /** * \brief Constructor @@ -237,9 +267,13 @@ class TableView * \param table The table data that contains all cases. */ AXOM_HOST_DEVICE - TableView(const IndexView &shapes, const IndexView &offsets, const TableDataView &table) : m_shapes(shapes), m_offsets(offsets), m_table(table) - { - } + TableView(const IndexView &shapes, + const IndexView &offsets, + const TableDataView &table) + : m_shapes(shapes) + , m_offsets(offsets) + , m_table(table) + { } /** * \brief Return the number of cases for the clipping table. @@ -279,16 +313,16 @@ class TableView assert(static_cast(caseId) < m_shapes.size()); iterator it; it.m_shapeStart = const_cast(m_table.data() + m_offsets[caseId]); - it.m_offset = 0; // not checked in iterator::operator== + it.m_offset = 0; // not checked in iterator::operator== it.m_currentShape = m_shapes[caseId]; it.m_numShapes = m_shapes[caseId]; return it; } private: - IndexView m_shapes; // The number of shapes in each case. - IndexView m_offsets; // The offset to the case in the table. - TableDataView m_table; // The table data that contains the shapes. + IndexView m_shapes; // The number of shapes in each case. + IndexView m_offsets; // The offset to the case in the table. + TableDataView m_table; // The table data that contains the shapes. }; /** @@ -307,10 +341,7 @@ class Table * \brief Returns whether the table data have been loaded. * \return True if the data have been loaded; false otherwise. */ - bool isLoaded() const - { - return m_shapes.size() > 0; - } + bool isLoaded() const { return m_shapes.size() > 0; } /** * \brief Load table data into the arrays, moving data as needed. @@ -321,7 +352,11 @@ class Table * \param table The clipping table data. * \param tableLen The size of the clipping table data. */ - void load(size_t n, const IndexData *shapes, const IndexData *offsets, const TableData *table, size_t tableLen) + void load(size_t n, + const IndexData *shapes, + const IndexData *offsets, + const TableData *table, + size_t tableLen) { const int allocatorID = execution_space::allocatorID(); @@ -383,8 +418,7 @@ class ClipTableManager */ void load(int dim) { - for(const auto shape : shapes(dim)) - loadShape(shape); + for(const auto shape : shapes(dim)) loadShape(shape); } /** @@ -399,16 +433,17 @@ class ClipTableManager std::vector s; if(dim == -1 || dim == 2) { - for(const auto value : std::vector{ST_TRI, ST_QUA}) + for(const auto value : std::vector {ST_TRI, ST_QUA}) s.push_back(value); } if(dim == -1 || dim == 3) { - for(const auto value : std::vector{ST_TET, ST_PYR, ST_WDG, ST_HEX}) + for(const auto value : std::vector {ST_TET, ST_PYR, ST_WDG, ST_HEX}) s.push_back(value); } return s; } + private: /** * \brief Turn a shape into an table index. @@ -417,10 +452,7 @@ class ClipTableManager * * \return An index into the m_tables array. */ - constexpr static size_t shapeToIndex(size_t shape) - { - return shape - ST_MIN; - } + constexpr static size_t shapeToIndex(size_t shape) { return shape - ST_MIN; } /** * \brief Load the clipping table for a shape. @@ -483,11 +515,11 @@ class ClipTableManager } } - axom::StackArray, NumberOfTables> m_tables{}; + axom::StackArray, NumberOfTables> m_tables {}; }; -} // end namespace clipping -} // end namespace mir -} // end namespace axom +} // end namespace clipping +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index dd5f1393ab..6a93311cba 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -16,17 +16,17 @@ namespace mir = axom::mir; std::string usageString() { - return "Args are "; + return "Args are "; } -int main( int argc, char** argv ) +int main(int argc, char **argv) { - axom::slic::SimpleLogger logger; // create & initialize test logger - axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - - if (argc != 4) + axom::slic::SimpleLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel(axom::slic::message::Info); + + if(argc != 4) { - SLIC_WARNING("Incorrect number of args. " << usageString() ); + SLIC_WARNING("Incorrect number of args. " << usageString()); return 1; } @@ -48,24 +48,24 @@ int main( int argc, char** argv ) timer.start(); mir::InterfaceReconstructor reconstructor; mir::MIRMesh processedMesh; - reconstructor.computeReconstructedInterface(testMesh, processedMesh); + reconstructor.computeReconstructedInterface(testMesh, processedMesh); timer.stop(); SLIC_INFO("Material interface reconstruction time: " - << timer.elapsedTimeInMilliSec() << " ms."); + << timer.elapsedTimeInMilliSec() << " ms."); // Output results processedMesh.writeMeshToFile(outputFilePath, "outputConcentricCircles.vtk"); - + return 0; } - catch (std::invalid_argument const &e) + catch(std::invalid_argument const &e) { - SLIC_WARNING("Bad input. " << usageString() ); + SLIC_WARNING("Bad input. " << usageString()); return 1; } - catch (std::out_of_range const &e) + catch(std::out_of_range const &e) { - SLIC_WARNING("Integer overflow. " << usageString() ); + SLIC_WARNING("Integer overflow. " << usageString()); return 1; } } diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index f41c44d053..e7dc7b7e9e 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -27,134 +27,132 @@ enum InputStatus struct Input { - int m_test_case; // valid values 1,2,3,4,5 - bool m_should_iterate; - int m_iter_count; - double m_iter_percent; - bool m_verbose; - std::string m_output_dir; - InputStatus m_status; - - Input() - : m_test_case(2) - , m_should_iterate(false) - , m_iter_count(100) - , m_iter_percent(.3) - , m_verbose(false) - , m_status(SUCCESS) - { - m_output_dir = fs::joinPath(AXOM_BIN_DIR, "mir_examples"); - } - - Input(int argc, char** argv) : Input() - { - for (int i = 1 ; i < argc ; /* increment i in loop */) + int m_test_case; // valid values 1,2,3,4,5 + bool m_should_iterate; + int m_iter_count; + double m_iter_percent; + bool m_verbose; + std::string m_output_dir; + InputStatus m_status; + + Input() + : m_test_case(2) + , m_should_iterate(false) + , m_iter_count(100) + , m_iter_percent(.3) + , m_verbose(false) + , m_status(SUCCESS) + { + m_output_dir = fs::joinPath(AXOM_BIN_DIR, "mir_examples"); + } + + Input(int argc, char** argv) : Input() + { + for(int i = 1; i < argc; /* increment i in loop */) + { + std::string arg = argv[i]; + if(arg == "--test-case") { - std::string arg = argv[i]; - if (arg == "--test-case") - { - m_test_case = std::stoi(argv[++i]); - } - else if (arg == "--output-dir") - { - m_output_dir = argv[++i]; - } - else if (arg == "--iter-count") - { - m_iter_count = std::stoi(argv[++i]); - m_should_iterate = true; - } - else if (arg == "--iter-percent") - { - m_iter_percent = std::stod(argv[++i]); - m_should_iterate = true; - } - else if (arg == "--verbose") + m_test_case = std::stoi(argv[++i]); + } + else if(arg == "--output-dir") + { + m_output_dir = argv[++i]; + } + else if(arg == "--iter-count") + { + m_iter_count = std::stoi(argv[++i]); + m_should_iterate = true; + } + else if(arg == "--iter-percent") + { + m_iter_percent = std::stod(argv[++i]); + m_should_iterate = true; + } + else if(arg == "--verbose") + { + m_verbose = true; + } + else // help or unknown parameter + { + if(arg != "--help" && arg != "-h") { - m_verbose = true; + SLIC_WARNING("Unrecognized parameter: " << arg); + m_status = INVALID; } - else // help or unknown parameter + else { - if(arg != "--help" && arg != "-h") - { - SLIC_WARNING("Unrecognized parameter: " << arg); - m_status = INVALID; - } - else - { - m_status = SHOWHELP; - } - return; + m_status = SHOWHELP; } - ++i; + return; } + ++i; + } + + checkTestCase(); + checkOutputDir(); + checkIterationParams(); + } - checkTestCase(); - checkOutputDir(); - checkIterationParams(); - } - - - bool shouldIterate() const { return m_should_iterate; } - int numIterations() const { return m_iter_count; } - int iterPercentage() const { return m_iter_percent; } - - - void showhelp() - { - std::cout - << "Argument usage:" - "\n --help Show this help message." - "\n --test-case N Mesh test case. Default N = 2." - "\n Valid values {1,2,3,4,5,6}" - "\n --output-dir dir Directory for output mesh" - "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" - "\n --iter-count N Number of iterations for iterative algorithm" - "\n Defaults to 100." - "\n Setting a value triggers iterative algorithm" - "\n --iter-percent D Volume diff percentage for iterative algorithm" - "\n Must be between 0 and 1. Defaults to 0.3" - "\n Setting a value triggers iterative algorithm" - "\n --verbose Increases verbosity of output" - << std::endl << std::endl; - }; + bool shouldIterate() const { return m_should_iterate; } + int numIterations() const { return m_iter_count; } + int iterPercentage() const { return m_iter_percent; } + + void showhelp() + { + std::cout + << "Argument usage:" + "\n --help Show this help message." + "\n --test-case N Mesh test case. Default N = 2." + "\n Valid values {1,2,3,4,5,6}" + "\n --output-dir dir Directory for output mesh" + "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" + "\n --iter-count N Number of iterations for iterative algorithm" + "\n Defaults to 100." + "\n Setting a value triggers iterative algorithm" + "\n --iter-percent D Volume diff percentage for iterative algorithm" + "\n Must be between 0 and 1. Defaults to 0.3" + "\n Setting a value triggers iterative algorithm" + "\n --verbose Increases verbosity of output" + << std::endl + << std::endl; + }; private: - void checkTestCase() - { - if(m_test_case < 1 || m_test_case > 6) - { - m_status = INVALID; - SLIC_WARNING("Invalid test case " << m_test_case); - } - } + void checkTestCase() + { + if(m_test_case < 1 || m_test_case > 6) + { + m_status = INVALID; + SLIC_WARNING("Invalid test case " << m_test_case); + } + } - void checkOutputDir() - { - if(! fs::pathExists(m_output_dir)) + void checkOutputDir() + { + if(!fs::pathExists(m_output_dir)) + { + fs::makeDirsForPath(m_output_dir); + } + } + + void checkIterationParams() + { + if(m_should_iterate) + { + if(m_iter_count < 1) { - fs::makeDirsForPath(m_output_dir); + m_status = INVALID; + SLIC_WARNING("Invalid iteration count " << m_iter_count); } - } - void checkIterationParams() - { - if(m_should_iterate) + if(m_iter_percent <= 0. || m_iter_percent > 1.) { - if(m_iter_count < 1) - { - m_status = INVALID; - SLIC_WARNING("Invalid iteration count " << m_iter_count); - } - - if(m_iter_percent <= 0. || m_iter_percent > 1.) - { - m_status = INVALID; - SLIC_WARNING("Invalid iteration percentage " << m_iter_percent); - } + m_status = INVALID; + SLIC_WARNING("Invalid iteration percentage " << m_iter_percent); } - } - + } + } }; /*! @@ -163,19 +161,19 @@ struct Input int main(int argc, char** argv) { axom::slic::SimpleLogger logger; // create & initialize test logger - axom::slic::setLoggingMsgLevel( axom::slic::message::Info ); - + axom::slic::setLoggingMsgLevel(axom::slic::message::Info); + // Parse arguments Input params(argc, argv); - if (params.m_status != SUCCESS) + if(params.m_status != SUCCESS) { - if (params.m_status == SHOWHELP) + if(params.m_status == SHOWHELP) { params.showhelp(); return 0; } - else if (params.m_status == INVALID) + else if(params.m_status == INVALID) { params.showhelp(); return 1; @@ -189,23 +187,36 @@ int main(int argc, char** argv) switch(params.m_test_case) { - case 1: testMesh = tester.initTestCaseOne(); break; - case 2: testMesh = tester.initTestCaseTwo(); break; - case 3: testMesh = tester.initTestCaseThree(); break; - case 4: testMesh = tester.initTestCaseFour(); break; - case 5: testMesh = tester.initTestCaseFive(25, 12); break; - case 6: testMesh = tester.initTestCaseSix(15, 3); break; + case 1: + testMesh = tester.initTestCaseOne(); + break; + case 2: + testMesh = tester.initTestCaseTwo(); + break; + case 3: + testMesh = tester.initTestCaseThree(); + break; + case 4: + testMesh = tester.initTestCaseFour(); + break; + case 5: + testMesh = tester.initTestCaseFive(25, 12); + break; + case 6: + testMesh = tester.initTestCaseSix(15, 3); + break; } timer.stop(); - SLIC_INFO( "Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); - SLIC_INFO("Test mesh is " << (testMesh.isValid(true) ? "" : " NOT") << " valid."); + SLIC_INFO("Test mesh is " << (testMesh.isValid(true) ? "" : " NOT") + << " valid."); if(params.m_verbose) { - SLIC_INFO("Initial mesh:"); - testMesh.print(); + SLIC_INFO("Initial mesh:"); + testMesh.print(); } // Begin material interface reconstruction @@ -213,35 +224,40 @@ int main(int argc, char** argv) mir::MIRMesh processedMesh; mir::InterfaceReconstructor reconstructor; - - if(! params.shouldIterate()) // Process once, with original Meredith algorithm + + if(!params.shouldIterate()) // Process once, with original Meredith algorithm { - reconstructor.computeReconstructedInterface(testMesh, processedMesh); + reconstructor.computeReconstructedInterface(testMesh, processedMesh); } - else // use iterative algorithm + else // use iterative algorithm { - int n = params.numIterations(); - double p = params.iterPercentage(); + int n = params.numIterations(); + double p = params.iterPercentage(); - reconstructor.computeReconstructedInterfaceIterative(testMesh, n, p, processedMesh); + reconstructor.computeReconstructedInterfaceIterative(testMesh, + n, + p, + processedMesh); } timer.stop(); - SLIC_INFO( "Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); + SLIC_INFO("Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results processedMesh.writeMeshToFile(params.m_output_dir, "processedMesh.vtk"); - using VolFracs = std::vector >; + using VolFracs = std::vector>; timer.start(); - VolFracs materialVolumeFractionsElement = processedMesh.computeOriginalElementVolumeFractions(); + VolFracs materialVolumeFractionsElement = + processedMesh.computeOriginalElementVolumeFractions(); timer.stop(); - SLIC_INFO( "Computing volumes took: " << timer.elapsedTimeInMilliSec() << " ms."); + SLIC_INFO("Computing volumes took: " << timer.elapsedTimeInMilliSec() + << " ms."); if(params.m_verbose) { - SLIC_INFO("Final mesh:"); - processedMesh.print(); + SLIC_INFO("Final mesh:"); + processedMesh.print(); } return 0; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 19a37b4b59..4a33af54e3 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -42,7 +42,6 @@ #endif // clang-format on - template void test_conduit_allocate() { @@ -56,17 +55,16 @@ void test_conduit_allocate() // Make sure we can store some values into the data that were allocated. int *ptr = static_cast(n.data_ptr()); - axom::for_all(nValues, AXOM_LAMBDA(auto index) - { - ptr[index] = index; - }); + axom::for_all( + nValues, + AXOM_LAMBDA(auto index) { ptr[index] = index; }); EXPECT_EQ(n.dtype().number_of_elements(), nValues); } TEST(mir_blueprint_utilities, allocate) { - test_conduit_allocate(); + test_conduit_allocate(); #if defined(AXOM_USE_OPENMP) test_conduit_allocate(); #endif @@ -89,22 +87,26 @@ void test_copy_braid(const conduit::Node &mesh) constexpr double eps = 1.e-7; - auto x = axom::mir::utilities::blueprint::minmax(mesh["coordsets/coords/values/x"]); + auto x = axom::mir::utilities::blueprint::minmax( + mesh["coordsets/coords/values/x"]); //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; EXPECT_NEAR(x.first, -10., eps); EXPECT_NEAR(x.second, 10., eps); - auto y = axom::mir::utilities::blueprint::minmax(mesh["coordsets/coords/values/y"]); + auto y = axom::mir::utilities::blueprint::minmax( + mesh["coordsets/coords/values/y"]); //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; EXPECT_NEAR(y.first, -10., eps); EXPECT_NEAR(y.second, 10., eps); - auto c = axom::mir::utilities::blueprint::minmax(mesh["topologies/mesh/elements/connectivity"]); + auto c = axom::mir::utilities::blueprint::minmax( + mesh["topologies/mesh/elements/connectivity"]); //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; EXPECT_NEAR(c.first, 0., eps); EXPECT_NEAR(c.second, 999., eps); - auto r = axom::mir::utilities::blueprint::minmax(mesh["fields/radial/values"]); + auto r = axom::mir::utilities::blueprint::minmax( + mesh["fields/radial/values"]); //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; EXPECT_NEAR(r.first, 19.2450089729875, eps); EXPECT_NEAR(r.second, 173.205080756888, eps); @@ -130,12 +132,12 @@ TEST(mir_blueprint_utilities, copy) TEST(mir_blueprint_utilities, to_unstructured) { - // TODO: to_unstructured + // TODO: to_unstructured } //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp index 7e39346973..c95be0dd83 100644 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ b/src/axom/mir/tests/mir_cell_clipper.cpp @@ -23,30 +23,32 @@ TEST(mir_clipping_case, all_triangle_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (auto actualClippingCase = 0; actualClippingCase < numCases; ++actualClippingCase) + for(auto actualClippingCase = 0; actualClippingCase < numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -58,30 +60,32 @@ TEST(mir_clipping_case, all_quad_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (auto actualClippingCase = 0; actualClippingCase < numCases; ++actualClippingCase) + for(auto actualClippingCase = 0; actualClippingCase < numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -93,30 +97,33 @@ TEST(mir_clipping_case, all_tetrahedron_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < (unsigned int)numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -128,30 +135,33 @@ TEST(mir_clipping_case, all_pyramid_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < (unsigned int)numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -163,30 +173,33 @@ TEST(mir_clipping_case, all_triangular_prism_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < (unsigned int)numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -198,30 +211,33 @@ TEST(mir_clipping_case, all_hexahedron_cases) int numVerts = mir::utilities::numVerts(shape); int numCases = pow(2, numVerts); - for (unsigned int actualClippingCase = 0; actualClippingCase < (unsigned int) numCases; ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < (unsigned int)numCases; + ++actualClippingCase) { std::vector matOneVF; std::vector matTwoVF; - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue = 0.0; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } mir::CellClipper clipper; - unsigned int computedClippingCase = clipper.determineClippingCase(shape, matOneVF, matTwoVF); + unsigned int computedClippingCase = + clipper.determineClippingCase(shape, matOneVF, matTwoVF); - EXPECT_EQ ( computedClippingCase, actualClippingCase ); + EXPECT_EQ(computedClippingCase, actualClippingCase); } } @@ -236,9 +252,12 @@ TEST(mir_clipping_place, clip_edge_when_mat_one_dominates) axom::float64 vfMatTwoVertexTwo = 1.0; mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, + vfMatTwoVertexOne, + vfMatOneVertexTwo, + vfMatTwoVertexTwo); - EXPECT_DOUBLE_EQ( tValue, 0.0 ); + EXPECT_DOUBLE_EQ(tValue, 0.0); } //---------------------------------------------------------------------- @@ -252,14 +271,16 @@ TEST(mir_clipping_place, clip_edge_when_mat_two_dominates) axom::float64 vfMatTwoVertexTwo = 0.0; mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, + vfMatTwoVertexOne, + vfMatOneVertexTwo, + vfMatTwoVertexTwo); - EXPECT_DOUBLE_EQ( tValue, 0.0 ); + EXPECT_DOUBLE_EQ(tValue, 0.0); } // //---------------------------------------------------------------------- - TEST(mir_clipping_place, clip_edge_in_middle) { axom::float64 vfMatOneVertexOne = 1.0; @@ -269,9 +290,12 @@ TEST(mir_clipping_place, clip_edge_in_middle) axom::float64 vfMatTwoVertexTwo = 1.0; mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, + vfMatTwoVertexOne, + vfMatOneVertexTwo, + vfMatTwoVertexTwo); - EXPECT_DOUBLE_EQ( tValue, 0.5 ); + EXPECT_DOUBLE_EQ(tValue, 0.5); } // //---------------------------------------------------------------------- @@ -285,9 +309,12 @@ TEST(mir_clipping_place, clip_edge_with_one_null_material) axom::float64 vfMatTwoVertexTwo = 0.0; mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, + vfMatTwoVertexOne, + vfMatOneVertexTwo, + vfMatTwoVertexTwo); - EXPECT_DOUBLE_EQ( tValue, 0.0 ); + EXPECT_DOUBLE_EQ(tValue, 0.0); } // //---------------------------------------------------------------------- @@ -301,9 +328,12 @@ TEST(mir_clipping_place, clip_edge_with_two_null_material) axom::float64 vfMatTwoVertexTwo = -1.0; mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, vfMatTwoVertexOne, vfMatOneVertexTwo, vfMatTwoVertexTwo); + axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, + vfMatTwoVertexOne, + vfMatOneVertexTwo, + vfMatTwoVertexTwo); - EXPECT_DOUBLE_EQ( tValue, 0.0 ); + EXPECT_DOUBLE_EQ(tValue, 0.0); } //---------------------------------------------------------------------- @@ -314,33 +344,33 @@ TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_zero) std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; - std::vector > vertexVF; + std::vector> vertexVF; vertexVF.push_back(matOneVF); vertexVF.push_back(matTwoVF); - std::map > newElements; - std::map > newVertices; - axom::float64 tValues[8] = { 0 }; + std::map> newElements; + std::map> newVertices; + axom::float64 tValues[8] = {0}; mir::CellClipper clipper; clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ( 1, newElements.size() ); - EXPECT_EQ( 4, newVertices.size() ); - EXPECT_EQ( 0, newElements[0][0] ); - EXPECT_EQ( 1, newElements[0][1] ); - EXPECT_EQ( 2, newElements[0][2] ); - EXPECT_EQ( 3, newElements[0][3] ); + EXPECT_EQ(1, newElements.size()); + EXPECT_EQ(4, newVertices.size()); + + EXPECT_EQ(0, newElements[0][0]); + EXPECT_EQ(1, newElements[0][1]); + EXPECT_EQ(2, newElements[0][2]); + EXPECT_EQ(3, newElements[0][3]); - EXPECT_EQ( 0, newVertices[0][0] ); - EXPECT_EQ( 0, newVertices[1][0] ); - EXPECT_EQ( 0, newVertices[2][0] ); - EXPECT_EQ( 0, newVertices[3][0] ); + EXPECT_EQ(0, newVertices[0][0]); + EXPECT_EQ(0, newVertices[1][0]); + EXPECT_EQ(0, newVertices[2][0]); + EXPECT_EQ(0, newVertices[3][0]); - for (int i = 0; i < 8; ++i) + for(int i = 0; i < 8; ++i) { - EXPECT_DOUBLE_EQ( 0, tValues[i] ); + EXPECT_DOUBLE_EQ(0, tValues[i]); } } @@ -352,52 +382,52 @@ TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_one) std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; - std::vector > vertexVF; + std::vector> vertexVF; vertexVF.push_back(matOneVF); vertexVF.push_back(matTwoVF); - std::map > newElements; - std::map > newVertices; - axom::float64 tValues[8] = { 0 }; + std::map> newElements; + std::map> newVertices; + axom::float64 tValues[8] = {0}; mir::CellClipper clipper; clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ( 3, newElements.size() ); - EXPECT_EQ( 6, newVertices.size() ); + + EXPECT_EQ(3, newElements.size()); + EXPECT_EQ(6, newVertices.size()); // Check the first element - EXPECT_EQ( 3, newElements[0][0] ); - EXPECT_EQ( 7, newElements[0][1] ); - EXPECT_EQ( 6, newElements[0][2] ); + EXPECT_EQ(3, newElements[0][0]); + EXPECT_EQ(7, newElements[0][1]); + EXPECT_EQ(6, newElements[0][2]); // Check the second element - EXPECT_EQ( 7, newElements[1][0] ); - EXPECT_EQ( 0, newElements[1][1] ); - EXPECT_EQ( 2, newElements[1][2] ); - EXPECT_EQ( 6, newElements[1][3] ); + EXPECT_EQ(7, newElements[1][0]); + EXPECT_EQ(0, newElements[1][1]); + EXPECT_EQ(2, newElements[1][2]); + EXPECT_EQ(6, newElements[1][3]); // Check the third element - EXPECT_EQ( 0, newElements[2][0] ); - EXPECT_EQ( 1, newElements[2][1] ); - EXPECT_EQ( 2, newElements[2][2] ); + EXPECT_EQ(0, newElements[2][0]); + EXPECT_EQ(1, newElements[2][1]); + EXPECT_EQ(2, newElements[2][2]); // Check each vertex's associated elements - EXPECT_EQ( 1, newVertices[0][0] ); - EXPECT_EQ( 2, newVertices[0][1] ); + EXPECT_EQ(1, newVertices[0][0]); + EXPECT_EQ(2, newVertices[0][1]); - EXPECT_EQ( 2, newVertices[1][0] ); - - EXPECT_EQ( 1, newVertices[2][0] ); - EXPECT_EQ( 2, newVertices[2][1] ); + EXPECT_EQ(2, newVertices[1][0]); - EXPECT_EQ( 0, newVertices[3][0] ); - - EXPECT_EQ( 0, newVertices[6][0] ); - EXPECT_EQ( 1, newVertices[6][1] ); + EXPECT_EQ(1, newVertices[2][0]); + EXPECT_EQ(2, newVertices[2][1]); - EXPECT_EQ( 0, newVertices[7][0] ); - EXPECT_EQ( 1, newVertices[7][1] ); + EXPECT_EQ(0, newVertices[3][0]); + + EXPECT_EQ(0, newVertices[6][0]); + EXPECT_EQ(1, newVertices[6][1]); + + EXPECT_EQ(0, newVertices[7][0]); + EXPECT_EQ(1, newVertices[7][1]); } //---------------------------------------------------------------------- @@ -408,39 +438,38 @@ TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_three) std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; - std::vector > vertexVF; + std::vector> vertexVF; vertexVF.push_back(matOneVF); vertexVF.push_back(matTwoVF); - std::map > newElements; - std::map > newVertices; - axom::float64 tValues[8] = { 0 }; + std::map> newElements; + std::map> newVertices; + axom::float64 tValues[8] = {0}; mir::CellClipper clipper; clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ( 2, newElements.size() ); - EXPECT_EQ( 6, newVertices.size() ); - - EXPECT_EQ( 0, newElements[0][0] ); - EXPECT_EQ( 1, newElements[0][1] ); - EXPECT_EQ( 5, newElements[0][2] ); - EXPECT_EQ( 7, newElements[0][3] ); - - EXPECT_EQ( 7, newElements[1][0] ); - EXPECT_EQ( 5, newElements[1][1] ); - EXPECT_EQ( 2, newElements[1][2] ); - EXPECT_EQ( 3, newElements[1][3] ); - - EXPECT_EQ( 0, newVertices[0][0] ); - EXPECT_EQ( 0, newVertices[1][0] ); - EXPECT_EQ( 1, newVertices[2][0] ); - EXPECT_EQ( 1, newVertices[3][0] ); - EXPECT_EQ( 0, newVertices[5][0] ); - EXPECT_EQ( 1, newVertices[5][1] ); - EXPECT_EQ( 0, newVertices[7][0] ); - EXPECT_EQ( 1, newVertices[7][1] ); + EXPECT_EQ(2, newElements.size()); + EXPECT_EQ(6, newVertices.size()); + + EXPECT_EQ(0, newElements[0][0]); + EXPECT_EQ(1, newElements[0][1]); + EXPECT_EQ(5, newElements[0][2]); + EXPECT_EQ(7, newElements[0][3]); + + EXPECT_EQ(7, newElements[1][0]); + EXPECT_EQ(5, newElements[1][1]); + EXPECT_EQ(2, newElements[1][2]); + EXPECT_EQ(3, newElements[1][3]); + + EXPECT_EQ(0, newVertices[0][0]); + EXPECT_EQ(0, newVertices[1][0]); + EXPECT_EQ(1, newVertices[2][0]); + EXPECT_EQ(1, newVertices[3][0]); + EXPECT_EQ(0, newVertices[5][0]); + EXPECT_EQ(1, newVertices[5][1]); + EXPECT_EQ(0, newVertices[7][0]); + EXPECT_EQ(1, newVertices[7][1]); } //---------------------------------------------------------------------- @@ -451,53 +480,52 @@ TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_five) std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; - std::vector > vertexVF; + std::vector> vertexVF; vertexVF.push_back(matOneVF); vertexVF.push_back(matTwoVF); - std::map > newElements; - std::map > newVertices; - axom::float64 tValues[8] = { 0 }; + std::map> newElements; + std::map> newVertices; + axom::float64 tValues[8] = {0}; mir::CellClipper clipper; clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ( 4, newElements.size() ); - EXPECT_EQ( 8, newVertices.size() ); - - EXPECT_EQ( 4, newElements[0][0] ); - EXPECT_EQ( 1, newElements[0][1] ); - EXPECT_EQ( 5, newElements[0][2] ); - - EXPECT_EQ( 0, newElements[1][0] ); - EXPECT_EQ( 4, newElements[1][1] ); - EXPECT_EQ( 5, newElements[1][2] ); - EXPECT_EQ( 2, newElements[1][3] ); - - EXPECT_EQ( 0, newElements[2][0] ); - EXPECT_EQ( 2, newElements[2][1] ); - EXPECT_EQ( 6, newElements[2][2] ); - EXPECT_EQ( 7, newElements[2][3] ); - - EXPECT_EQ( 7, newElements[3][0] ); - EXPECT_EQ( 6, newElements[3][1] ); - EXPECT_EQ( 3, newElements[3][2] ); - - EXPECT_EQ( 1, newVertices[0][0] ); - EXPECT_EQ( 2, newVertices[0][1] ); - EXPECT_EQ( 0, newVertices[1][0] ); - EXPECT_EQ( 1, newVertices[2][0] ); - EXPECT_EQ( 2, newVertices[2][1] ); - EXPECT_EQ( 3, newVertices[3][0] ); - EXPECT_EQ( 0, newVertices[4][0] ); - EXPECT_EQ( 1, newVertices[4][1] ); - EXPECT_EQ( 0, newVertices[5][0] ); - EXPECT_EQ( 1, newVertices[5][1] ); - EXPECT_EQ( 2, newVertices[6][0] ); - EXPECT_EQ( 3, newVertices[6][1] ); - EXPECT_EQ( 2, newVertices[7][0] ); - EXPECT_EQ( 3, newVertices[7][1] ); + EXPECT_EQ(4, newElements.size()); + EXPECT_EQ(8, newVertices.size()); + + EXPECT_EQ(4, newElements[0][0]); + EXPECT_EQ(1, newElements[0][1]); + EXPECT_EQ(5, newElements[0][2]); + + EXPECT_EQ(0, newElements[1][0]); + EXPECT_EQ(4, newElements[1][1]); + EXPECT_EQ(5, newElements[1][2]); + EXPECT_EQ(2, newElements[1][3]); + + EXPECT_EQ(0, newElements[2][0]); + EXPECT_EQ(2, newElements[2][1]); + EXPECT_EQ(6, newElements[2][2]); + EXPECT_EQ(7, newElements[2][3]); + + EXPECT_EQ(7, newElements[3][0]); + EXPECT_EQ(6, newElements[3][1]); + EXPECT_EQ(3, newElements[3][2]); + + EXPECT_EQ(1, newVertices[0][0]); + EXPECT_EQ(2, newVertices[0][1]); + EXPECT_EQ(0, newVertices[1][0]); + EXPECT_EQ(1, newVertices[2][0]); + EXPECT_EQ(2, newVertices[2][1]); + EXPECT_EQ(3, newVertices[3][0]); + EXPECT_EQ(0, newVertices[4][0]); + EXPECT_EQ(1, newVertices[4][1]); + EXPECT_EQ(0, newVertices[5][0]); + EXPECT_EQ(1, newVertices[5][1]); + EXPECT_EQ(2, newVertices[6][0]); + EXPECT_EQ(3, newVertices[6][1]); + EXPECT_EQ(2, newVertices[7][0]); + EXPECT_EQ(3, newVertices[7][1]); } //---------------------------------------------------------------------- @@ -507,7 +535,9 @@ TEST(clipping_table_mesh_generation, triangle_meshes) mir::Shape shape = mir::Shape::Triangle; int numVerts = mir::utilities::numVerts(shape); - for (auto actualClippingCase = 0u; actualClippingCase < mir::triangleClipTableVec.size(); ++actualClippingCase) + for(auto actualClippingCase = 0u; + actualClippingCase < mir::triangleClipTableVec.size(); + ++actualClippingCase) { // Initialize the mesh int numElements = 1; @@ -515,25 +545,25 @@ TEST(clipping_table_mesh_generation, triangle_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2 }; - topology.m_evBegins = { 0,3 }; - topology.m_veInds = { 0,0,0 }; - topology.m_veBegins = { 0,1,2,3 }; + topology.m_evInds = {0, 1, 2}; + topology.m_evBegins = {0, 3}; + topology.m_veInds = {0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -544,33 +574,23 @@ TEST(clipping_table_mesh_generation, triangle_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.5, 0.717 ), - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ) - }; + std::vector points = {mir::Point2::make_point(0.5, 0.717), + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Triangle }; + mapData.m_shapeTypes = {mir::Shape::Triangle}; // Build the mesh mir::MIRMesh testMesh; @@ -584,9 +604,10 @@ TEST(clipping_table_mesh_generation, triangle_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_triangle_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_triangle_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } //---------------------------------------------------------------------- @@ -596,7 +617,9 @@ TEST(clipping_table_mesh_generation, quad_meshes) mir::Shape shape = mir::Shape::Quad; int numVerts = mir::utilities::numVerts(shape); - for (unsigned int actualClippingCase = 0; actualClippingCase < mir::quadClipTableVec.size(); ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < mir::quadClipTableVec.size(); + ++actualClippingCase) { // Initialize the mesh int numElements = 1; @@ -604,25 +627,25 @@ TEST(clipping_table_mesh_generation, quad_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2,3 }; - topology.m_evBegins = { 0,4 }; - topology.m_veInds = { 0,0,0,0 }; - topology.m_veBegins = { 0,1,2,3,4 }; + topology.m_evInds = {0, 1, 2, 3}; + topology.m_evBegins = {0, 4}; + topology.m_veInds = {0, 0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3, 4}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -633,34 +656,24 @@ TEST(clipping_table_mesh_generation, quad_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.0, 1.0 ), - mir::Point2::make_point( 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0 ), - mir::Point2::make_point( 1.0, 1.0 ) - }; + std::vector points = {mir::Point2::make_point(0.0, 1.0), + mir::Point2::make_point(0.0, 0.0), + mir::Point2::make_point(1.0, 0.0), + mir::Point2::make_point(1.0, 1.0)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Quad }; + mapData.m_shapeTypes = {mir::Shape::Quad}; // Build the mesh mir::MIRMesh testMesh; @@ -674,9 +687,10 @@ TEST(clipping_table_mesh_generation, quad_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_quad_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_quad_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } //---------------------------------------------------------------------- @@ -686,7 +700,9 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) mir::Shape shape = mir::Shape::Tetrahedron; int numVerts = mir::utilities::numVerts(shape); - for (unsigned int actualClippingCase = 0; actualClippingCase < mir::tetrahedronClipTableVec.size(); ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < mir::tetrahedronClipTableVec.size(); + ++actualClippingCase) { // Initialize the mesh int numElements = 1; @@ -694,25 +710,25 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2,3 }; - topology.m_evBegins = { 0,4 }; - topology.m_veInds = { 0,0,0,0 }; - topology.m_veBegins = { 0,1,2,3,4 }; + topology.m_evInds = {0, 1, 2, 3}; + topology.m_evBegins = {0, 4}; + topology.m_veInds = {0, 0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3, 4}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) + for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -723,34 +739,25 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.5, 0.717, 0.0 ), - mir::Point2::make_point( 0.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0, 0.0 ), - mir::Point2::make_point( 0.5, 0.3585, 0.717 ) - }; + std::vector points = { + mir::Point2::make_point(0.5, 0.717, 0.0), + mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(0.5, 0.3585, 0.717)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Tetrahedron }; + mapData.m_shapeTypes = {mir::Shape::Tetrahedron}; // Build the mesh mir::MIRMesh testMesh; @@ -764,9 +771,10 @@ TEST(clipping_table_mesh_generation, tetrahedron_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_tetrahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_tetrahedron_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } //---------------------------------------------------------------------- @@ -776,7 +784,9 @@ TEST(clipping_table_mesh_generation, pyramid_meshes) mir::Shape shape = mir::Shape::Pyramid; int numVerts = mir::utilities::numVerts(shape); - for (unsigned int actualClippingCase = 0; actualClippingCase < mir::pyramidClipTableVec.size(); ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < mir::pyramidClipTableVec.size(); + ++actualClippingCase) { { // Initialize the mesh @@ -785,25 +795,25 @@ TEST(clipping_table_mesh_generation, pyramid_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2,3,4 }; - topology.m_evBegins = { 0,5 }; - topology.m_veInds = { 0,0,0,0,0 }; - topology.m_veBegins = { 0,1,2,3,4,5 }; + topology.m_evInds = {0, 1, 2, 3, 4}; + topology.m_evBegins = {0, 5}; + topology.m_veInds = {0, 0, 0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3, 4, 5}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -814,35 +824,26 @@ TEST(clipping_table_mesh_generation, pyramid_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 1.0, 0.0 ), - mir::Point2::make_point( 0.0, 1.0, 0.0 ), - mir::Point2::make_point( 0.5, 0.5, 0.717) - }; + std::vector points = { + mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 1.0, 0.0), + mir::Point2::make_point(0.0, 1.0, 0.0), + mir::Point2::make_point(0.5, 0.5, 0.717)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Pyramid }; + mapData.m_shapeTypes = {mir::Shape::Pyramid}; // Build the mesh mir::MIRMesh testMesh; @@ -856,11 +857,11 @@ TEST(clipping_table_mesh_generation, pyramid_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_pyramid_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_pyramid_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } - } //---------------------------------------------------------------------- @@ -870,7 +871,9 @@ TEST(clipping_table_mesh_generation, triangular_prism_meshes) mir::Shape shape = mir::Shape::Triangular_Prism; int numVerts = mir::utilities::numVerts(shape); - for (unsigned int actualClippingCase = 0; actualClippingCase < mir::triangularPrismClipTableVec.size(); ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < mir::triangularPrismClipTableVec.size(); + ++actualClippingCase) { // Initialize the mesh int numElements = 1; @@ -878,25 +881,25 @@ TEST(clipping_table_mesh_generation, triangular_prism_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2,3,4,5 }; - topology.m_evBegins = { 0,6 }; - topology.m_veInds = { 0,0,0,0,0,0 }; - topology.m_veBegins = { 0,1,2,3,4,5,6 }; + topology.m_evInds = {0, 1, 2, 3, 4, 5}; + topology.m_evBegins = {0, 6}; + topology.m_veInds = {0, 0, 0, 0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3, 4, 5, 6}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -907,36 +910,26 @@ TEST(clipping_table_mesh_generation, triangular_prism_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.5, 0.717, 0.0 ), - mir::Point2::make_point( 0.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0, 0.0 ), - mir::Point2::make_point( 0.5, 0.717, 2.0 ), - mir::Point2::make_point( 0.0, 0.0, 2.0 ), - mir::Point2::make_point( 1.0, 0.0, 2.0 ) - }; + std::vector points = {mir::Point2::make_point(0.5, 0.717, 0.0), + mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(0.5, 0.717, 2.0), + mir::Point2::make_point(0.0, 0.0, 2.0), + mir::Point2::make_point(1.0, 0.0, 2.0)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Triangular_Prism }; + mapData.m_shapeTypes = {mir::Shape::Triangular_Prism}; // Build the mesh mir::MIRMesh testMesh; @@ -950,9 +943,10 @@ TEST(clipping_table_mesh_generation, triangular_prism_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_triangular_prism_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_triangular_prism_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } //---------------------------------------------------------------------- @@ -962,7 +956,9 @@ TEST(clipping_table_mesh_generation, hexahedron_meshes) mir::Shape shape = mir::Shape::Hexahedron; int numVerts = mir::utilities::numVerts(shape); - for (unsigned int actualClippingCase = 0; actualClippingCase < mir::hexahedronClipTableVec.size(); ++actualClippingCase) + for(unsigned int actualClippingCase = 0; + actualClippingCase < mir::hexahedronClipTableVec.size(); + ++actualClippingCase) { // Initialize the mesh int numElements = 1; @@ -970,25 +966,25 @@ TEST(clipping_table_mesh_generation, hexahedron_meshes) // Create the mesh connectivity information mir::CellTopologyData topology; - topology.m_evInds = { 0,1,2,3,4,5,6,7 }; - topology.m_evBegins = { 0,8 }; - topology.m_veInds = { 0,0,0,0,0,0,0,0 }; - topology.m_veBegins = { 0,1,2,3,4,5,6,7,8 }; + topology.m_evInds = {0, 1, 2, 3, 4, 5, 6, 7}; + topology.m_evBegins = {0, 8}; + topology.m_veInds = {0, 0, 0, 0, 0, 0, 0, 0}; + topology.m_veBegins = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); + mir::VertSet verts = mir::VertSet(numVertices); + mir::ElemSet elems = mir::ElemSet(numElements); // Calculate the vertex volume fractions needed to clip with the current case std::vector matOneVF; std::vector matTwoVF; std::string bitString(""); - for (unsigned int bitIndex = 0; bitIndex < (unsigned int) numVerts; ++bitIndex) + for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) { unsigned int shiftedBit = 1; shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); axom::float64 matOneValue; - if (actualClippingCase & shiftedBit) + if(actualClippingCase & shiftedBit) { matOneValue = 1.0; bitString += "1"; @@ -999,38 +995,28 @@ TEST(clipping_table_mesh_generation, hexahedron_meshes) bitString += "0"; } - matOneVF.push_back( matOneValue ); - matTwoVF.push_back( 1.0 - matOneValue ); + matOneVF.push_back(matOneValue); + matTwoVF.push_back(1.0 - matOneValue); } - std::vector > vertexVF = { - matOneVF, - matTwoVF - }; - - std::vector > elementVF = { - { 0.5 }, - { 0.5 } - }; + std::vector> vertexVF = {matOneVF, matTwoVF}; + std::vector> elementVF = {{0.5}, {0.5}}; - std::vector points = - { - mir::Point2::make_point( 0.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 0.0, 0.0 ), - mir::Point2::make_point( 1.0, 1.0, 0.0 ), - mir::Point2::make_point( 0.0, 1.0, 0.0 ), - mir::Point2::make_point( 0.0, 0.0, 1.0 ), - mir::Point2::make_point( 1.0, 0.0, 1.0 ), - mir::Point2::make_point( 1.0, 1.0, 1.0 ), - mir::Point2::make_point( 0.0, 1.0, 1.0 ) - }; + std::vector points = {mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 1.0, 0.0), + mir::Point2::make_point(0.0, 1.0, 0.0), + mir::Point2::make_point(0.0, 0.0, 1.0), + mir::Point2::make_point(1.0, 0.0, 1.0), + mir::Point2::make_point(1.0, 1.0, 1.0), + mir::Point2::make_point(0.0, 1.0, 1.0)}; mir::CellMapData mapData; - mapData.m_elementDominantMaterials = { mir::NULL_MAT }; - mapData.m_elementParents = { 0 }; + mapData.m_elementDominantMaterials = {mir::NULL_MAT}; + mapData.m_elementParents = {0}; mapData.m_vertexPositions = points; - mapData.m_shapeTypes = { mir::Shape::Hexahedron }; + mapData.m_shapeTypes = {mir::Shape::Hexahedron}; // Build the mesh mir::MIRMesh testMesh; @@ -1044,9 +1030,10 @@ TEST(clipping_table_mesh_generation, hexahedron_meshes) // Write out the processed mesh std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_hexahedron_" + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; + std::string fileName = "mir_clippingcase_hexahedron_" + + std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; outputMesh.writeMeshToFile(dirName, fileName, "/"); - } + } } //---------------------------------------------------------------------- @@ -1062,5 +1049,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_CELL_CLIPPER_TEST_H_ +#endif // MIR_CELL_CLIPPER_TEST_H_ diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp index afb8c4c97a..ec80922a70 100644 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ b/src/axom/mir/tests/mir_cell_generator.cpp @@ -19,69 +19,69 @@ TEST(mir_cell_generator, generate_quad_topology) { EXPECT_EQ(true, 1); // this function generates the evInds, evBegins, ... etc given the map of elements -> verts, and verts -> elements - std::map > elementMap; - elementMap[0] = { 4, 1, 5 }; - elementMap[1] = { 0, 4, 5, 2 }; - elementMap[2] = { 0, 2, 6, 7 }; - elementMap[3] = { 7, 6, 3 }; - - std::map > vertexMap; - vertexMap[0] = { 1, 2 }; - vertexMap[1] = { 0 }; - vertexMap[2] = { 1, 2 }; - vertexMap[3] = { 3 }; - vertexMap[4] = { 0, 1 }; - vertexMap[5] = { 0, 1 }; - vertexMap[6] = { 2, 3 }; - vertexMap[7] = { 2, 3 }; + std::map> elementMap; + elementMap[0] = {4, 1, 5}; + elementMap[1] = {0, 4, 5, 2}; + elementMap[2] = {0, 2, 6, 7}; + elementMap[3] = {7, 6, 3}; + + std::map> vertexMap; + vertexMap[0] = {1, 2}; + vertexMap[1] = {0}; + vertexMap[2] = {1, 2}; + vertexMap[3] = {3}; + vertexMap[4] = {0, 1}; + vertexMap[5] = {0, 1}; + vertexMap[6] = {2, 3}; + vertexMap[7] = {2, 3}; mir::CellData cellData; mir::CellGenerator generator; generator.generateTopologyData(elementMap, vertexMap, cellData); - EXPECT_EQ( 4, cellData.m_topology.m_evInds[0] ); - EXPECT_EQ( 1, cellData.m_topology.m_evInds[1] ); - EXPECT_EQ( 5, cellData.m_topology.m_evInds[2] ); - EXPECT_EQ( 0, cellData.m_topology.m_evInds[3] ); - EXPECT_EQ( 4, cellData.m_topology.m_evInds[4] ); - EXPECT_EQ( 5, cellData.m_topology.m_evInds[5] ); - EXPECT_EQ( 2, cellData.m_topology.m_evInds[6] ); - EXPECT_EQ( 0, cellData.m_topology.m_evInds[7] ); - EXPECT_EQ( 2, cellData.m_topology.m_evInds[8] ); - EXPECT_EQ( 6, cellData.m_topology.m_evInds[9] ); - EXPECT_EQ( 7, cellData.m_topology.m_evInds[10] ); - EXPECT_EQ( 7, cellData.m_topology.m_evInds[11] ); - EXPECT_EQ( 6, cellData.m_topology.m_evInds[12] ); - EXPECT_EQ( 3, cellData.m_topology.m_evInds[13] ); - - EXPECT_EQ( 1, cellData.m_topology.m_veInds[0] ); - EXPECT_EQ( 2, cellData.m_topology.m_veInds[1] ); - EXPECT_EQ( 0, cellData.m_topology.m_veInds[2] ); - EXPECT_EQ( 1, cellData.m_topology.m_veInds[3] ); - EXPECT_EQ( 2, cellData.m_topology.m_veInds[4] ); - EXPECT_EQ( 3, cellData.m_topology.m_veInds[5] ); - EXPECT_EQ( 0, cellData.m_topology.m_veInds[6] ); - EXPECT_EQ( 1, cellData.m_topology.m_veInds[7] ); - EXPECT_EQ( 0, cellData.m_topology.m_veInds[8] ); - EXPECT_EQ( 1, cellData.m_topology.m_veInds[9] ); - EXPECT_EQ( 2, cellData.m_topology.m_veInds[10] ); - EXPECT_EQ( 3, cellData.m_topology.m_veInds[11] ); - EXPECT_EQ( 2, cellData.m_topology.m_veInds[12] ); - EXPECT_EQ( 3, cellData.m_topology.m_veInds[13] ); - - EXPECT_EQ( 0, cellData.m_topology.m_evBegins[0] ); - EXPECT_EQ( 3, cellData.m_topology.m_evBegins[1] ); - EXPECT_EQ( 7, cellData.m_topology.m_evBegins[2] ); - EXPECT_EQ( 11, cellData.m_topology.m_evBegins[3] ); - - EXPECT_EQ( 0, cellData.m_topology.m_veBegins[0] ); - EXPECT_EQ( 2, cellData.m_topology.m_veBegins[1] ); - EXPECT_EQ( 3, cellData.m_topology.m_veBegins[2] ); - EXPECT_EQ( 5, cellData.m_topology.m_veBegins[3] ); - EXPECT_EQ( 6, cellData.m_topology.m_veBegins[4] ); - EXPECT_EQ( 8, cellData.m_topology.m_veBegins[5] ); - EXPECT_EQ( 10, cellData.m_topology.m_veBegins[6] ); - EXPECT_EQ( 12, cellData.m_topology.m_veBegins[7] ); + EXPECT_EQ(4, cellData.m_topology.m_evInds[0]); + EXPECT_EQ(1, cellData.m_topology.m_evInds[1]); + EXPECT_EQ(5, cellData.m_topology.m_evInds[2]); + EXPECT_EQ(0, cellData.m_topology.m_evInds[3]); + EXPECT_EQ(4, cellData.m_topology.m_evInds[4]); + EXPECT_EQ(5, cellData.m_topology.m_evInds[5]); + EXPECT_EQ(2, cellData.m_topology.m_evInds[6]); + EXPECT_EQ(0, cellData.m_topology.m_evInds[7]); + EXPECT_EQ(2, cellData.m_topology.m_evInds[8]); + EXPECT_EQ(6, cellData.m_topology.m_evInds[9]); + EXPECT_EQ(7, cellData.m_topology.m_evInds[10]); + EXPECT_EQ(7, cellData.m_topology.m_evInds[11]); + EXPECT_EQ(6, cellData.m_topology.m_evInds[12]); + EXPECT_EQ(3, cellData.m_topology.m_evInds[13]); + + EXPECT_EQ(1, cellData.m_topology.m_veInds[0]); + EXPECT_EQ(2, cellData.m_topology.m_veInds[1]); + EXPECT_EQ(0, cellData.m_topology.m_veInds[2]); + EXPECT_EQ(1, cellData.m_topology.m_veInds[3]); + EXPECT_EQ(2, cellData.m_topology.m_veInds[4]); + EXPECT_EQ(3, cellData.m_topology.m_veInds[5]); + EXPECT_EQ(0, cellData.m_topology.m_veInds[6]); + EXPECT_EQ(1, cellData.m_topology.m_veInds[7]); + EXPECT_EQ(0, cellData.m_topology.m_veInds[8]); + EXPECT_EQ(1, cellData.m_topology.m_veInds[9]); + EXPECT_EQ(2, cellData.m_topology.m_veInds[10]); + EXPECT_EQ(3, cellData.m_topology.m_veInds[11]); + EXPECT_EQ(2, cellData.m_topology.m_veInds[12]); + EXPECT_EQ(3, cellData.m_topology.m_veInds[13]); + + EXPECT_EQ(0, cellData.m_topology.m_evBegins[0]); + EXPECT_EQ(3, cellData.m_topology.m_evBegins[1]); + EXPECT_EQ(7, cellData.m_topology.m_evBegins[2]); + EXPECT_EQ(11, cellData.m_topology.m_evBegins[3]); + + EXPECT_EQ(0, cellData.m_topology.m_veBegins[0]); + EXPECT_EQ(2, cellData.m_topology.m_veBegins[1]); + EXPECT_EQ(3, cellData.m_topology.m_veBegins[2]); + EXPECT_EQ(5, cellData.m_topology.m_veBegins[3]); + EXPECT_EQ(6, cellData.m_topology.m_veBegins[4]); + EXPECT_EQ(8, cellData.m_topology.m_veBegins[5]); + EXPECT_EQ(10, cellData.m_topology.m_veBegins[6]); + EXPECT_EQ(12, cellData.m_topology.m_veBegins[7]); } //---------------------------------------------------------------------- @@ -90,52 +90,56 @@ TEST(mir_cell_generator, generate_vertex_positions) { mir::Shape shapeType = mir::Shape::Quad; - std::map > vertexMap; - vertexMap[0] = { 1, 2 }; - vertexMap[1] = { 0 }; - vertexMap[2] = { 1, 2 }; - vertexMap[3] = { 3 }; - vertexMap[4] = { 0, 1 }; - vertexMap[5] = { 0, 1 }; - vertexMap[6] = { 2, 3 }; - vertexMap[7] = { 2, 3 }; + std::map> vertexMap; + vertexMap[0] = {1, 2}; + vertexMap[1] = {0}; + vertexMap[2] = {1, 2}; + vertexMap[3] = {3}; + vertexMap[4] = {0, 1}; + vertexMap[5] = {0, 1}; + vertexMap[6] = {2, 3}; + vertexMap[7] = {2, 3}; std::vector originalVertexPositions; - originalVertexPositions.push_back( mir::Point2::make_point(0.0, 1.0) ); - originalVertexPositions.push_back( mir::Point2::make_point(0.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2::make_point(1.0, 0.0) ); - originalVertexPositions.push_back( mir::Point2::make_point(1.0, 1.0) ); + originalVertexPositions.push_back(mir::Point2::make_point(0.0, 1.0)); + originalVertexPositions.push_back(mir::Point2::make_point(0.0, 0.0)); + originalVertexPositions.push_back(mir::Point2::make_point(1.0, 0.0)); + originalVertexPositions.push_back(mir::Point2::make_point(1.0, 1.0)); - axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + axom::float64 tValues[8] = {0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5}; mir::CellData cellData; mir::CellGenerator cellGenerator; - cellGenerator.generateVertexPositions(shapeType, vertexMap, originalVertexPositions, tValues, cellData); + cellGenerator.generateVertexPositions(shapeType, + vertexMap, + originalVertexPositions, + tValues, + cellData); const auto& positions = cellData.m_mapData.m_vertexPositions; - EXPECT_NEAR( positions[0][0], 0.0, 0.00001 ); - EXPECT_NEAR( positions[0][1], 1.0, 0.00001 ); + EXPECT_NEAR(positions[0][0], 0.0, 0.00001); + EXPECT_NEAR(positions[0][1], 1.0, 0.00001); - EXPECT_NEAR( positions[1][0], 0.0, 0.00001 ); - EXPECT_NEAR( positions[1][1], 0.0, 0.00001 ); + EXPECT_NEAR(positions[1][0], 0.0, 0.00001); + EXPECT_NEAR(positions[1][1], 0.0, 0.00001); - EXPECT_NEAR( positions[2][0], 1.0, 0.00001 ); - EXPECT_NEAR( positions[2][1], 0.0, 0.00001 ); + EXPECT_NEAR(positions[2][0], 1.0, 0.00001); + EXPECT_NEAR(positions[2][1], 0.0, 0.00001); - EXPECT_NEAR( positions[3][0], 1.0, 0.00001 ); - EXPECT_NEAR( positions[3][1], 1.0, 0.00001 ); + EXPECT_NEAR(positions[3][0], 1.0, 0.00001); + EXPECT_NEAR(positions[3][1], 1.0, 0.00001); - EXPECT_NEAR( positions[4][0], 0.0, 0.00001 ); - EXPECT_NEAR( positions[4][1], 0.5, 0.00001 ); + EXPECT_NEAR(positions[4][0], 0.0, 0.00001); + EXPECT_NEAR(positions[4][1], 0.5, 0.00001); - EXPECT_NEAR( positions[5][0], 0.5, 0.00001 ); - EXPECT_NEAR( positions[5][1], 0.0, 0.00001 ); + EXPECT_NEAR(positions[5][0], 0.5, 0.00001); + EXPECT_NEAR(positions[5][1], 0.0, 0.00001); - EXPECT_NEAR( positions[6][0], 1.0, 0.00001 ); - EXPECT_NEAR( positions[6][1], 0.5, 0.00001 ); + EXPECT_NEAR(positions[6][0], 1.0, 0.00001); + EXPECT_NEAR(positions[6][1], 0.5, 0.00001); - EXPECT_NEAR( positions[7][0], 0.5, 0.00001 ); - EXPECT_NEAR( positions[7][1], 1.0, 0.00001 ); + EXPECT_NEAR(positions[7][0], 0.5, 0.00001); + EXPECT_NEAR(positions[7][1], 1.0, 0.00001); } //---------------------------------------------------------------------- @@ -144,51 +148,53 @@ TEST(mir_cell_generator, generate_vertex_volume_fractions) { mir::Shape shapeType = mir::Shape::Quad; - std::map > vertexMap; - vertexMap[0] = { 1, 2 }; - vertexMap[1] = { 0 }; - vertexMap[2] = { 1, 2 }; - vertexMap[3] = { 3 }; - vertexMap[4] = { 0, 1 }; - vertexMap[5] = { 0, 1 }; - vertexMap[6] = { 2, 3 }; - vertexMap[7] = { 2, 3 }; + std::map> vertexMap; + vertexMap[0] = {1, 2}; + vertexMap[1] = {0}; + vertexMap[2] = {1, 2}; + vertexMap[3] = {3}; + vertexMap[4] = {0, 1}; + vertexMap[5] = {0, 1}; + vertexMap[6] = {2, 3}; + vertexMap[7] = {2, 3}; - std::vector > originalVertexVF(2); - originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; - originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + std::vector> originalVertexVF(2); + originalVertexVF[0] = {0.0, 0.33, 0.67, 1.0}; + originalVertexVF[1] = {1.0, 0.67, 0.33, 0.0}; - axom::float64 tValues[8] = { 0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5 }; + axom::float64 tValues[8] = {0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5}; mir::CellData cellData; mir::CellGenerator cellGenerator; - cellGenerator.generateVertexVolumeFractions(shapeType, vertexMap, originalVertexVF, tValues, cellData); + cellGenerator.generateVertexVolumeFractions(shapeType, + vertexMap, + originalVertexVF, + tValues, + cellData); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][0], 0.0, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][0], 1.0, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][0], 0.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][0], 1.0, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][1], 0.33, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][1], 0.67, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][1], 0.33, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][1], 0.67, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][2], 0.67, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][2], 0.33, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][2], 0.67, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][2], 0.33, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][3], 1.0, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][3], 0.0, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][3], 1.0, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][3], 0.0, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][4], 0.165, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][4], 0.835, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][5], 0.5, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][5], 0.5, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][4], 0.165, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][4], 0.835, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][6], 0.835, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][6], 0.165, 0.00001); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][5], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][5], 0.5, 0.00001 ); - - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][6], 0.835, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][6], 0.165, 0.00001 ); - - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[0][7], 0.5, 0.00001 ); - EXPECT_NEAR( cellData.m_mapData.m_vertexVolumeFractions[1][7], 0.5, 0.00001 ); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][7], 0.5, 0.00001); + EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][7], 0.5, 0.00001); } //---------------------------------------------------------------------- @@ -197,21 +203,26 @@ TEST(mir_cell_generator, determine_clean_cell_material) { mir::Shape shapeType = mir::Shape::Quad; - std::vector vertexIDs = { 0, 1, 5, 7 }; + std::vector vertexIDs = {0, 1, 5, 7}; int matOne = 0; int matTwo = 1; - std::vector > originalVertexVF(2); - originalVertexVF[0] = { 0.0, 0.33, 0.67, 1.0 }; - originalVertexVF[1] = { 1.0, 0.67, 0.33, 0.0 }; + std::vector> originalVertexVF(2); + originalVertexVF[0] = {0.0, 0.33, 0.67, 1.0}; + originalVertexVF[1] = {1.0, 0.67, 0.33, 0.0}; mir::CellData cellData; mir::CellGenerator cellGenerator; - int dominantMaterial = cellGenerator.determineCleanCellMaterial(shapeType, vertexIDs, matOne, matTwo, originalVertexVF); - - EXPECT_EQ( dominantMaterial, 1 ); + int dominantMaterial = + cellGenerator.determineCleanCellMaterial(shapeType, + vertexIDs, + matOne, + matTwo, + originalVertexVF); + + EXPECT_EQ(dominantMaterial, 1); } //---------------------------------------------------------------------- @@ -227,5 +238,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_CELL_GENERATOR_TEST_H_ +#endif // MIR_CELL_GENERATOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 43b69ca11c..dcd5aee870 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -42,15 +42,13 @@ template void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) { int d[3] = {0, 0, 0}; - for(int i = 0; i < dims.size(); i++) - d[i] = dims[i]; + for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); // Make a new distance field. const float dist = 6.5f; const conduit::Node &n_coordset = mesh["coordsets"][0]; - axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) - { + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { mesh["fields/distance/topology"] = "mesh"; mesh["fields/distance/association"] = "vertex"; conduit::Node &n_values = mesh["fields/distance/values"]; @@ -61,8 +59,7 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) { const auto pt = coordsetView[index]; float norm2 = 0.f; - for(int i = 0; i < pt.DIMENSION; i++) - norm2 += pt[i] * pt[i]; + for(int i = 0; i < pt.DIMENSION; i++) norm2 += pt[i] * pt[i]; valuesPtr[index] = sqrt(norm2) - dist; } }); @@ -72,79 +69,84 @@ int conduit_to_vtk_cell(int shape_value) { int vtktype = 0; if(shape_value == axom::mir::views::Tri_ShapeID) - vtktype = 5; // VTK_TRIANGLE + vtktype = 5; // VTK_TRIANGLE else if(shape_value == axom::mir::views::Quad_ShapeID) - vtktype = 9; // VTK_QUAD + vtktype = 9; // VTK_QUAD else if(shape_value == axom::mir::views::Polygon_ShapeID) - vtktype = 7; // VTK_POLYGON + vtktype = 7; // VTK_POLYGON else if(shape_value == axom::mir::views::Tet_ShapeID) - vtktype = 10; // VTK_TETRA + vtktype = 10; // VTK_TETRA else if(shape_value == axom::mir::views::Pyramid_ShapeID) - vtktype = 14; // VTK_PYRAMID + vtktype = 14; // VTK_PYRAMID else if(shape_value == axom::mir::views::Wedge_ShapeID) - vtktype = 13; // VTK_WEDGE + vtktype = 13; // VTK_WEDGE else if(shape_value == axom::mir::views::Hex_ShapeID) - vtktype = 12; // VTK_HEXAHEDRON + vtktype = 12; // VTK_HEXAHEDRON return vtktype; } -void -conduit_save_vtk(const conduit::Node &node, const std::string &path) +void conduit_save_vtk(const conduit::Node &node, const std::string &path) { - FILE *file = fopen(path.c_str(), "wt"); - - // Write the VTK file header - fprintf(file, "# vtk DataFile Version 3.0\n"); - fprintf(file, "Unstructured Grid Example\n"); - fprintf(file, "ASCII\n"); - fprintf(file, "DATASET UNSTRUCTURED_GRID\n"); - - // Write the points - const conduit::Node &points = node["coordsets/coords/values"]; - const conduit::Node &x = points["x"]; - const conduit::Node &y = points["y"]; - const conduit::Node &z = points["z"]; - size_t num_points = x.dtype().number_of_elements(); - - fprintf(file, "POINTS %zu float\n", num_points); - for (size_t i = 0; i < num_points; ++i) { - fprintf(file, "%f %f %f\n", x.as_float64_array()[i], y.as_float64_array()[i], z.as_float64_array()[i]); - } + FILE *file = fopen(path.c_str(), "wt"); + + // Write the VTK file header + fprintf(file, "# vtk DataFile Version 3.0\n"); + fprintf(file, "Unstructured Grid Example\n"); + fprintf(file, "ASCII\n"); + fprintf(file, "DATASET UNSTRUCTURED_GRID\n"); + + // Write the points + const conduit::Node &points = node["coordsets/coords/values"]; + const conduit::Node &x = points["x"]; + const conduit::Node &y = points["y"]; + const conduit::Node &z = points["z"]; + size_t num_points = x.dtype().number_of_elements(); + + fprintf(file, "POINTS %zu float\n", num_points); + for(size_t i = 0; i < num_points; ++i) + { + fprintf(file, + "%f %f %f\n", + x.as_float64_array()[i], + y.as_float64_array()[i], + z.as_float64_array()[i]); + } - // Write the cells - const conduit::Node &topologies = node["topologies"]; - const conduit::Node &topo = topologies[0]; - const conduit::Node &elements = topo["elements"]; - const conduit::Node &connectivity = elements["connectivity"]; - size_t num_cells = elements["sizes"].dtype().number_of_elements(); - size_t total_num_indices = connectivity.dtype().number_of_elements(); - - fprintf(file, "CELLS %zu %zu\n", num_cells, total_num_indices + num_cells); - size_t index = 0; - for (size_t i = 0; i < num_cells; ++i) { - size_t cell_size = elements["sizes"].as_int32_array()[i]; - fprintf(file, "%zu", cell_size); - for (size_t j = 0; j < cell_size; ++j) { - fprintf(file, " %d", connectivity.as_int32_array()[index++]); - } - fprintf(file, "\n"); + // Write the cells + const conduit::Node &topologies = node["topologies"]; + const conduit::Node &topo = topologies[0]; + const conduit::Node &elements = topo["elements"]; + const conduit::Node &connectivity = elements["connectivity"]; + size_t num_cells = elements["sizes"].dtype().number_of_elements(); + size_t total_num_indices = connectivity.dtype().number_of_elements(); + + fprintf(file, "CELLS %zu %zu\n", num_cells, total_num_indices + num_cells); + size_t index = 0; + for(size_t i = 0; i < num_cells; ++i) + { + size_t cell_size = elements["sizes"].as_int32_array()[i]; + fprintf(file, "%zu", cell_size); + for(size_t j = 0; j < cell_size; ++j) + { + fprintf(file, " %d", connectivity.as_int32_array()[index++]); } + fprintf(file, "\n"); + } - // Write the cell types - const conduit::Node &shapes = elements["shapes"]; - fprintf(file, "CELL_TYPES %zu\n", num_cells); - for (size_t i = 0; i < num_cells; ++i) { - const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); - fprintf(file, "%d\n", type); - } + // Write the cell types + const conduit::Node &shapes = elements["shapes"]; + fprintf(file, "CELL_TYPES %zu\n", num_cells); + for(size_t i = 0; i < num_cells; ++i) + { + const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); + fprintf(file, "%d\n", type); + } - // Close the file - fclose(file); + // Close the file + fclose(file); } - - TEST(mir_clipfield, options) { int nzones = 6; @@ -187,7 +189,7 @@ TEST(mir_clipfield, options) conduit::Node n_fields; n_fields["distance/topology"] = "topo"; n_fields["distance/association"] = "vertex"; - n_fields["distance/values"].set(std::vector{0.,1.,2.,3.}); + n_fields["distance/values"].set(std::vector {0., 1., 2., 3.}); // There are currently no fields in the options. fields should just return the clip field. auto fields = opts.fields(n_fields); @@ -236,9 +238,9 @@ TEST(mir_clipfield, options) EXPECT_EQ(selectedZonesView[4], 4); EXPECT_EQ(selectedZonesView[5], 5); - // Put some "selectedZones" in the options. + // Put some "selectedZones" in the options. opts.invalidateSelectedZones(); - options["selectedZones"].set(std::vector{5,4,3}); + options["selectedZones"].set(std::vector {5, 4, 3}); selectedZonesView = opts.selectedZonesView(); EXPECT_EQ(selectedZonesView.size(), 3); EXPECT_EQ(selectedZonesView[0], 3); @@ -264,53 +266,75 @@ TEST(mir_clipfield, blend_group_builder) 0 1 2 */ - axom::Array blendGroups{{8, 5}}; - axom::Array blendGroupsLen{{/*zone 0*/ 4+1+1+1+1+2+2+2, /*zone 1*/ 1+1+1+1+2}}; - axom::Array blendGroupOffsets{{0, 8}}; - axom::Array blendOffsets{{0, blendGroupsLen[0]}}; - - axom::Array blendNames{{/*zone 0*/ 6,0,1,3,4,7,8,9, /*zone 1*/ 1,2,4,5,9}}; - axom::Array blendGroupSizes{{/*zone 0*/4,1,1,1,1,2,2,2, /*zone 1*/1,1,1,1,2}}; - axom::Array blendGroupStart{{/*zone 0*/0,4,5,6,7,8,10,12, /*zone 1*/ 13, 14, 15, 16, 18}}; - axom::Array blendIds{{/*zone 0*/ - 0,1,2,3, // 6 (bgname) // 0 (bgindex) - 0, // 0 // 1 - 1, // 1 // 2 - 3, // 3 // 3 - 4, // 4 // 4 - 0,1, // 7 // 5 - 3,4, // 8 // 6 - 1,4, // 9 // 7 - /*zone 1*/ - 1, // 1 // 8 - 2, // 2 // 9 - 4, // 4 // 10 - 5, // 5 // 11 - 1,4 // 9 // 12 - }}; - axom::Array blendCoeff{{/*zone 0*/ - 0.25, 0.25, 0.25, 0.25, - 1., - 1., - 1., - 1., - 0.5, 0.5, - 0.5, 0.5, - 0.5, 0.5, - /*zone 1*/ - 1., - 1., - 1., - 1., - 0.5, 0.5 - }}; - axom::Array blendUniqueNames{{0,1,2,3,4,5,6,7,8,9}}; - axom::Array blendUniqueIndices{{1,2,9,3,4,11,0,5,6,7}}; + axom::Array blendGroups {{8, 5}}; + axom::Array blendGroupsLen { + {/*zone 0*/ 4 + 1 + 1 + 1 + 1 + 2 + 2 + 2, /*zone 1*/ 1 + 1 + 1 + 1 + 2}}; + axom::Array blendGroupOffsets {{0, 8}}; + axom::Array blendOffsets {{0, blendGroupsLen[0]}}; + + axom::Array blendNames { + {/*zone 0*/ 6, 0, 1, 3, 4, 7, 8, 9, /*zone 1*/ 1, 2, 4, 5, 9}}; + axom::Array blendGroupSizes { + {/*zone 0*/ 4, 1, 1, 1, 1, 2, 2, 2, /*zone 1*/ 1, 1, 1, 1, 2}}; + axom::Array blendGroupStart { + {/*zone 0*/ 0, 4, 5, 6, 7, 8, 10, 12, /*zone 1*/ 13, 14, 15, 16, 18}}; + axom::Array blendIds {{ + /*zone 0*/ + 0, + 1, + 2, + 3, // 6 (bgname) // 0 (bgindex) + 0, // 0 // 1 + 1, // 1 // 2 + 3, // 3 // 3 + 4, // 4 // 4 + 0, + 1, // 7 // 5 + 3, + 4, // 8 // 6 + 1, + 4, // 9 // 7 + /*zone 1*/ + 1, // 1 // 8 + 2, // 2 // 9 + 4, // 4 // 10 + 5, // 5 // 11 + 1, + 4 // 9 // 12 + }}; + axom::Array blendCoeff {{/*zone 0*/ + 0.25, + 0.25, + 0.25, + 0.25, + 1., + 1., + 1., + 1., + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + /*zone 1*/ + 1., + 1., + 1., + 1., + 0.5, + 0.5}}; + axom::Array blendUniqueNames {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}; + axom::Array blendUniqueIndices {{1, 2, 9, 3, 4, 11, 0, 5, 6, 7}}; axom::mir::clipping::BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); builder.setBlendGroupOffsets(blendOffsets.view(), blendGroupOffsets.view()); - builder.setBlendViews(blendNames.view(), blendGroupSizes.view(), blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + builder.setBlendViews(blendNames.view(), + blendGroupSizes.view(), + blendGroupStart.view(), + blendIds.view(), + blendCoeff.view()); //std::cout << "-------- zone 0 --------" << std::endl; auto z0 = builder.blendGroupsForZone(0); @@ -340,8 +364,7 @@ template bool increasing(const ArrayType &arr) { bool retval = true; - for(size_t i = 1; i < arr.size(); i++) - retval &= (arr[i] >= arr[i - 1]); + for(size_t i = 1; i < arr.size(); i++) retval &= (arr[i] >= arr[i - 1]); return retval; } @@ -359,12 +382,10 @@ std::vector permute(const std::vector &input) { order[i] = drand48(); } - std::sort(indices.begin(), indices.end(), [&](int a, int b) - { + std::sort(indices.begin(), indices.end(), [&](int a, int b) { return order[a] < order[b]; }); - for(size_t i = 0; i < input.size(); i++) - values[i] = input[indices[i]]; + for(size_t i = 0; i < input.size(); i++) values[i] = input[indices[i]]; return values; } @@ -404,7 +425,7 @@ TEST(mir_clipfield, sort_values) //------------------------------------------------------------------------------ TEST(mir_clipfield, unique) { -/* + /* 8---9---10--11 | | | | 4---5---6---7 @@ -412,7 +433,8 @@ TEST(mir_clipfield, unique) 0---1---2---3 */ - axom::Array ids{{0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10}}; + axom::Array ids {{0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, + 4, 5, 9, 8, 5, 6, 10, 9, 6, 7, 11, 10}}; axom::Array uIds, uIndices; axom::mir::utilities::unique(ids.view(), uIds, uIndices); @@ -422,7 +444,7 @@ TEST(mir_clipfield, unique) { EXPECT_EQ(uIds[i], i); EXPECT_EQ(uIds[i], ids[uIndices[i]]); - } + } } //------------------------------------------------------------------------------ @@ -463,75 +485,92 @@ TEST(mir_clipfield, make_name) void make_one_hex(conduit::Node &hostMesh) { hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 1., 1., 0., 0., 1., 1., 0.}}); - hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 1., 1., 0., 0., 1., 1.}}); - hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 0., 0., 0., 1., 1., 1., 1.}}); + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 1., 1., 0., 0., 1., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 1., 1., 0., 0., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 0., 0., 0., 1., 1., 1., 1.}}); hostMesh["topologies/topo/type"] = "unstructured"; hostMesh["topologies/topo/coordset"] = "coords"; hostMesh["topologies/topo/elements/shape"] = "hex"; - hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4,5,6,7}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector{8}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4, 5, 6, 7}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {8}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); hostMesh["fields/distance/topology"] = "topo"; hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set(std::vector{{ 1., -1., -1., -1., -1., -1., -1., -1.}}); + hostMesh["fields/distance/values"].set( + std::vector {{1., -1., -1., -1., -1., -1., -1., -1.}}); } void make_one_tet(conduit::Node &hostMesh) { hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 0.}}); - hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1.}}); - hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 0., 0.}}); + hostMesh["coordsets/coords/values/x"].set(std::vector {{0., 0., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set(std::vector {{0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector {{0., 1., 0., 0.}}); hostMesh["topologies/topo/type"] = "unstructured"; hostMesh["topologies/topo/coordset"] = "coords"; hostMesh["topologies/topo/elements/shape"] = "tet"; - hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector{4}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {4}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); hostMesh["fields/distance/topology"] = "topo"; hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set(std::vector{{ -1., -1., -1., 1.}}); + hostMesh["fields/distance/values"].set(std::vector {{-1., -1., -1., 1.}}); } void make_one_pyr(conduit::Node &hostMesh) { hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 1., 0.5}}); - hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 0., 1.}}); - hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 1., 0., 0.5}}); + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 0., 1., 1., 0.5}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 1., 1., 0., 0.5}}); hostMesh["topologies/topo/type"] = "unstructured"; hostMesh["topologies/topo/coordset"] = "coords"; hostMesh["topologies/topo/elements/shape"] = "pyramid"; - hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector{5}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {5}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); hostMesh["fields/distance/topology"] = "topo"; hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set(std::vector{{ 1., 1., -1., -1., -1.}}); + hostMesh["fields/distance/values"].set( + std::vector {{1., 1., -1., -1., -1.}}); } void make_one_wdg(conduit::Node &hostMesh) { hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 0., 1., 0., 0., 1}}); - hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1., 1., 1.}}); - hostMesh["coordsets/coords/values/z"].set(std::vector{{0., 1., 0., 0., 1., 0.}}); + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 0., 1., 0., 0., 1}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 1., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 1., 0., 0., 1., 0.}}); hostMesh["topologies/topo/type"] = "unstructured"; hostMesh["topologies/topo/coordset"] = "coords"; hostMesh["topologies/topo/elements/shape"] = "wedge"; - hostMesh["topologies/topo/elements/connectivity"].set(std::vector{{0,1,2,3,4,5}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector{6}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector{0}); + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4, 5}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {6}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); hostMesh["fields/distance/topology"] = "topo"; hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set(std::vector{{ 1., 1., -1., -1., -1., -1.}}); + hostMesh["fields/distance/values"].set( + std::vector {{1., 1., -1., -1., -1., -1.}}); } template void test_one_shape(const conduit::Node &hostMesh, const std::string &name) { - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; + using TopoView = + axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; // Copy mesh to device @@ -542,18 +581,25 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); - axom::ArrayView xView(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); - axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); - axom::ArrayView zView(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); + axom::ArrayView xView(static_cast(n_x.data_ptr()), + n_x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(n_y.data_ptr()), + n_y.dtype().number_of_elements()); + axom::ArrayView zView(static_cast(n_z.data_ptr()), + n_z.dtype().number_of_elements()); CoordsetView coordsetView(xView, yView, zView); - conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/topo/elements/connectivity"); - axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); + conduit::Node &n_conn = + deviceMesh.fetch_existing("topologies/topo/elements/connectivity"); + axom::ArrayView connView(static_cast(n_conn.data_ptr()), + n_conn.dtype().number_of_elements()); TopoView topoView(connView); // Clip the data - conduit::Node deviceClipMesh, options; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); + conduit::Node deviceClipMesh, options; + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); options["clipField"] = "distance"; options["clipValue"] = 0.; options["inside"] = 1; @@ -587,33 +633,32 @@ void test_one_shape_exec(const conduit::Node &hostMesh, const std::string &name) #endif } - TEST(mir_clipfield, onetet) { conduit::Node hostMesh; make_one_tet(hostMesh); - test_one_shape_exec >(hostMesh, "one_tet"); + test_one_shape_exec>(hostMesh, "one_tet"); } TEST(mir_clipfield, onepyr) { conduit::Node hostMesh; make_one_pyr(hostMesh); - test_one_shape_exec >(hostMesh, "one_pyr"); + test_one_shape_exec>(hostMesh, "one_pyr"); } TEST(mir_clipfield, onewdg) { conduit::Node hostMesh; make_one_wdg(hostMesh); - test_one_shape_exec >(hostMesh, "one_wdg"); + test_one_shape_exec>(hostMesh, "one_wdg"); } TEST(mir_clipfield, onehex) { conduit::Node hostMesh; make_one_hex(hostMesh); - test_one_shape_exec >(hostMesh, "one_hex"); + test_one_shape_exec>(hostMesh, "one_hex"); } //------------------------------------------------------------------------------ @@ -624,8 +669,8 @@ void braid2d_clip_test(const std::string &type, const std::string &name) using TopoView = axom::mir::views::StructuredTopologyView; using CoordsetView = axom::mir::views::UniformCoordsetView; - axom::StackArray dims{10, 10}; - axom::StackArray zoneDims{dims[0] - 1, dims[1] - 1}; + axom::StackArray dims {10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; // Create the data conduit::Node hostMesh, deviceMesh; @@ -634,9 +679,9 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); // Create views - axom::StackArray origin{0., 0.}, spacing{1., 1.}; + axom::StackArray origin {0., 0.}, spacing {1., 1.}; CoordsetView coordsetView(dims, origin, spacing); - TopoView topoView(Indexing{zoneDims}); + TopoView topoView(Indexing {zoneDims}); // Create options to control the clipping. const std::string clipTopoName("cliptopo"); @@ -662,7 +707,9 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Clip the data conduit::Node deviceClipMesh; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); clipper.execute(deviceMesh, options, deviceClipMesh); // Copy device->host @@ -674,28 +721,47 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); // Now, take the clipped mesh and clip it again using a mixed topology view. - using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; + using MixedTopoView = + axom::mir::views::UnstructuredTopologyMixedShapeView; using ExpCoordsetView = axom::mir::views::ExplicitCoordsetView; - conduit::Node &n_x = deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x"); - conduit::Node &n_y = deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y"); - axom::ArrayView xView(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); - axom::ArrayView yView(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); + conduit::Node &n_x = + deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x"); + conduit::Node &n_y = + deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y"); + axom::ArrayView xView(static_cast(n_x.data_ptr()), + n_x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(n_y.data_ptr()), + n_y.dtype().number_of_elements()); ExpCoordsetView mixedCoordsetView(xView, yView); - conduit::Node &n_device_topo = deviceClipMesh.fetch_existing("topologies/" + clipTopoName); + conduit::Node &n_device_topo = + deviceClipMesh.fetch_existing("topologies/" + clipTopoName); conduit::Node &n_conn = n_device_topo.fetch_existing("elements/connectivity"); conduit::Node &n_shapes = n_device_topo.fetch_existing("elements/shapes"); conduit::Node &n_sizes = n_device_topo.fetch_existing("elements/sizes"); conduit::Node &n_offsets = n_device_topo.fetch_existing("elements/offsets"); - axom::ArrayView connView(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); - axom::ArrayView shapesView(static_cast(n_shapes.data_ptr()), n_shapes.dtype().number_of_elements()); - axom::ArrayView sizesView(static_cast(n_sizes.data_ptr()), n_sizes.dtype().number_of_elements()); - axom::ArrayView offsetsView(static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); - MixedTopoView mixedTopoView(n_device_topo, connView, shapesView, sizesView, offsetsView); + axom::ArrayView connView( + static_cast(n_conn.data_ptr()), + n_conn.dtype().number_of_elements()); + axom::ArrayView shapesView( + static_cast(n_shapes.data_ptr()), + n_shapes.dtype().number_of_elements()); + axom::ArrayView sizesView( + static_cast(n_sizes.data_ptr()), + n_sizes.dtype().number_of_elements()); + axom::ArrayView offsetsView( + static_cast(n_offsets.data_ptr()), + n_offsets.dtype().number_of_elements()); + MixedTopoView mixedTopoView(n_device_topo, + connView, + shapesView, + sizesView, + offsetsView); // Clip the data - conduit::Node deviceClipMixedMesh; - axom::mir::clipping::ClipField mixedClipper(mixedTopoView, mixedCoordsetView); + conduit::Node deviceClipMixedMesh; + axom::mir::clipping::ClipField + mixedClipper(mixedTopoView, mixedCoordsetView); options["clipField"] = "new_braid"; options["clipValue"] = 1.; options["fields"].reset(); @@ -707,11 +773,16 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Copy device->host conduit::Node hostClipMixedMesh; - axom::mir::utilities::blueprint::copy(hostClipMixedMesh, deviceClipMixedMesh); + axom::mir::utilities::blueprint::copy(hostClipMixedMesh, + deviceClipMixedMesh); // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, name + "_mixed", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, name + "_mixed_yaml", "yaml"); + conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, + name + "_mixed", + "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, + name + "_mixed_yaml", + "yaml"); // Load a clipped baseline file & compare. } @@ -719,28 +790,36 @@ void braid2d_clip_test(const std::string &type, const std::string &name) template void braid3d_clip_test(const std::string &type, const std::string &name) { - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView; + using TopoView = + axom::mir::views::UnstructuredTopologySingleShapeView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; // Create the data - const axom::StackArray dims{10, 10, 10}; + const axom::StackArray dims {10, 10, 10}; conduit::Node hostMesh, deviceMesh; braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); + conduit::relay::io::blueprint::save_mesh(hostMesh, + name + "_orig_yaml", + "yaml"); // Create views conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); - const axom::ArrayView x(static_cast(n_x.data_ptr()), n_x.dtype().number_of_elements()); - const axom::ArrayView y(static_cast(n_y.data_ptr()), n_y.dtype().number_of_elements()); - const axom::ArrayView z(static_cast(n_z.data_ptr()), n_z.dtype().number_of_elements()); + const axom::ArrayView x(static_cast(n_x.data_ptr()), + n_x.dtype().number_of_elements()); + const axom::ArrayView y(static_cast(n_y.data_ptr()), + n_y.dtype().number_of_elements()); + const axom::ArrayView z(static_cast(n_z.data_ptr()), + n_z.dtype().number_of_elements()); CoordsetView coordsetView(x, y, z); - conduit::Node &n_conn = deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"); - const axom::ArrayView conn(static_cast(n_conn.data_ptr()), n_conn.dtype().number_of_elements()); + conduit::Node &n_conn = + deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"); + const axom::ArrayView conn(static_cast(n_conn.data_ptr()), + n_conn.dtype().number_of_elements()); TopoView topoView(conn); // Create options to control the clipping. @@ -751,7 +830,9 @@ void braid3d_clip_test(const std::string &type, const std::string &name) // Clip the data conduit::Node deviceClipMesh; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); clipper.execute(deviceMesh, options, deviceClipMesh); // Copy device->host @@ -787,9 +868,9 @@ TEST(mir_clipfield, uniform2d) { braid2d_clip_test("uniform", "uniform2d"); -//#if defined(AXOM_USE_OPENMP) -// braid2d_clip_test("uniform", "uniform2d_omp"); -//#endif + //#if defined(AXOM_USE_OPENMP) + // braid2d_clip_test("uniform", "uniform2d_omp"); + //#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) braid2d_clip_test("uniform", "uniform2d_cuda"); @@ -822,7 +903,7 @@ TEST(mir_clipfield, hex) //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp index 56057d060e..98f990ae90 100644 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ b/src/axom/mir/tests/mir_interface_reconstructor.cpp @@ -26,5 +26,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ +#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp index 61e8a48a14..04da3f65e9 100644 --- a/src/axom/mir/tests/mir_mesh.cpp +++ b/src/axom/mir/tests/mir_mesh.cpp @@ -11,122 +11,123 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" - namespace mir = axom::mir; class MirMeshTest : public ::testing::Test { public: + template + using Vec = std::vector; - template - using Vec = std::vector; - - using IndexVec = Vec; - using VolFracVec = Vec; - using VolumeFractions = Vec; + using IndexVec = Vec; + using VolFracVec = Vec; + using VolumeFractions = Vec; - enum { GREEN = 0, BLUE = 1 }; + enum + { + GREEN = 0, + BLUE = 1 + }; public: - - mir::MIRMesh& getMesh() { return m_mesh; } - const mir::MIRMesh& getMesh() const { return m_mesh; } + mir::MIRMesh& getMesh() { return m_mesh; } + const mir::MIRMesh& getMesh() const { return m_mesh; } protected: void SetUp() override { - m_verts= mir::VertSet(16); - m_elems= mir::ElemSet(9); - m_nMats = 2; - - // Create the mesh connectivity information - setupTopoData(); - setupMapData(); - setupVolumeFractions(); - - m_mesh.initializeMesh(this->m_verts, this->m_elems, this->m_nMats, - this->m_topoData, this->m_mapData, this->m_volFracs); + m_verts = mir::VertSet(16); + m_elems = mir::ElemSet(9); + m_nMats = 2; + + // Create the mesh connectivity information + setupTopoData(); + setupMapData(); + setupVolumeFractions(); + + m_mesh.initializeMesh(this->m_verts, + this->m_elems, + this->m_nMats, + this->m_topoData, + this->m_mapData, + this->m_volFracs); } // Set up the mesh's topological connectivity void setupTopoData() { - m_topoData.m_evInds = { - 0,4,5,1, // elem 0, card 4, start 0 - 1,5,6,2, // elem 1, card 4, start 4 - 2,6,7,3, // elem 2, card 4, start 8 - 4,8,9,5, // elem 3, card 4, start 12 - 5,9,10,6, // elem 4, card 4, start 16 - 6,10,11,7, // elem 5, card 4, start 20 - 8,12,13,9, // elem 6, card 4, start 24 - 9,13,14,10, // elem 7, card 4, start 28 - 10,14,15,11 // elem 8, card 4, start 32, end 36 - }; - - m_topoData.m_evBegins = { - 0,4,8,12,16,20,24,28,32,36 - }; - - m_topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0,1, // vert 1, card 2, start 1 - 1,2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0,3, // vert 4, card 2, start 6 - 0,1,3,4, // vert 5, card 4, start 8 - 1,2,4,5, // vert 6, card 4, start 12 - 2,5, // vert 7, card 2, start 16 - 3,6, // vert 8, card 2, start 18 - 3,4,6,7, // vert 9, card 4, start 20 - 4,5,7,8, // vert 10, card 4, start 24 - 5,8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6,7, // vert 13, card 2, start 31 - 7,8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - - m_topoData.m_veBegins = { - 0,1,3,5,6,8,12,16,18,20,24,28,30,31,33,35,36 - }; + m_topoData.m_evInds = { + 0, 4, 5, 1, // elem 0, card 4, start 0 + 1, 5, 6, 2, // elem 1, card 4, start 4 + 2, 6, 7, 3, // elem 2, card 4, start 8 + 4, 8, 9, 5, // elem 3, card 4, start 12 + 5, 9, 10, 6, // elem 4, card 4, start 16 + 6, 10, 11, 7, // elem 5, card 4, start 20 + 8, 12, 13, 9, // elem 6, card 4, start 24 + 9, 13, 14, 10, // elem 7, card 4, start 28 + 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 + }; + + m_topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; + + m_topoData.m_veInds = { + 0, // vert 0, card 1, start 0 + 0, 1, // vert 1, card 2, start 1 + 1, 2, // vert 2, card 2, start 3 + 2, // vert 3, card 1, start 5 + 0, 3, // vert 4, card 2, start 6 + 0, 1, 3, 4, // vert 5, card 4, start 8 + 1, 2, 4, 5, // vert 6, card 4, start 12 + 2, 5, // vert 7, card 2, start 16 + 3, 6, // vert 8, card 2, start 18 + 3, 4, 6, 7, // vert 9, card 4, start 20 + 4, 5, 7, 8, // vert 10, card 4, start 24 + 5, 8, // vert 11, card 2, start 28 + 6, // vert 12, card 1, start 30 + 6, 7, // vert 13, card 2, start 31 + 7, 8, // vert 14, card 2, start 33 + 8, // vert 15, card 1, start 35, end 36 + }; + + m_topoData.m_veBegins = + {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; } // Set up the mesh's map data void setupMapData() { - m_mapData.m_vertexPositions = { - mir::Point2( 0.0, 3.0 ), - mir::Point2( 1.0, 3.0 ), - mir::Point2( 2.0, 3.0 ), - mir::Point2( 3.0, 3.0 ), - - mir::Point2( 0.0, 2.0 ), - mir::Point2( 1.0, 2.0 ), - mir::Point2( 2.0, 2.0 ), - mir::Point2( 3.0, 2.0 ), - - mir::Point2( 0.0, 1.0 ), - mir::Point2( 1.0, 1.0 ), - mir::Point2( 2.0, 1.0 ), - mir::Point2( 3.0, 1.0 ), - - mir::Point2( 0.0, 0.0 ), - mir::Point2( 1.0, 0.0 ), - mir::Point2( 2.0, 0.0 ), - mir::Point2( 3.0, 0.0 ) - }; - - m_mapData.m_elementDominantMaterials = Vec(m_elems.size(), mir::NULL_MAT); - m_mapData.m_elementParents = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - m_mapData.m_shapeTypes = Vec(m_elems.size(), mir::Shape::Quad); + m_mapData.m_vertexPositions = {mir::Point2(0.0, 3.0), + mir::Point2(1.0, 3.0), + mir::Point2(2.0, 3.0), + mir::Point2(3.0, 3.0), + + mir::Point2(0.0, 2.0), + mir::Point2(1.0, 2.0), + mir::Point2(2.0, 2.0), + mir::Point2(3.0, 2.0), + + mir::Point2(0.0, 1.0), + mir::Point2(1.0, 1.0), + mir::Point2(2.0, 1.0), + mir::Point2(3.0, 1.0), + + mir::Point2(0.0, 0.0), + mir::Point2(1.0, 0.0), + mir::Point2(2.0, 0.0), + mir::Point2(3.0, 0.0)}; + + m_mapData.m_elementDominantMaterials = + Vec(m_elems.size(), mir::NULL_MAT); + m_mapData.m_elementParents = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + m_mapData.m_shapeTypes = Vec(m_elems.size(), mir::Shape::Quad); } // Set up the mesh's volume fraction data void setupVolumeFractions() { - m_volFracs.resize(m_nMats); - m_volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - m_volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; + m_volFracs.resize(m_nMats); + m_volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; + m_volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; } protected: @@ -142,75 +143,71 @@ class MirMeshTest : public ::testing::Test mir::MIRMesh m_mesh; }; - -TEST_F(MirMeshTest,default_ctor) +TEST_F(MirMeshTest, default_ctor) { - mir::MIRMesh mesh; - EXPECT_TRUE(mesh.isValid(true)); + mir::MIRMesh mesh; + EXPECT_TRUE(mesh.isValid(true)); } - -TEST_F(MirMeshTest,initialize) +TEST_F(MirMeshTest, initialize) { - mir::MIRMesh mesh; - mesh.initializeMesh( - this->m_verts, - this->m_elems, - this->m_nMats, - this->m_topoData, - this->m_mapData, - this->m_volFracs); - - EXPECT_TRUE(mesh.isValid(true)); - - mesh.print(); + mir::MIRMesh mesh; + mesh.initializeMesh(this->m_verts, + this->m_elems, + this->m_nMats, + this->m_topoData, + this->m_mapData, + this->m_volFracs); + + EXPECT_TRUE(mesh.isValid(true)); + + mesh.print(); } -TEST_F(MirMeshTest,copy_ctor) +TEST_F(MirMeshTest, copy_ctor) { - // test copy constructor - { - mir::MIRMesh& mesh = this->getMesh(); - EXPECT_TRUE(mesh.isValid(true)); - - mir::MIRMesh mirCopy(mesh); - EXPECT_TRUE( mirCopy.isValid(true) ); - } - - // test const copy constructor - { - const mir::MIRMesh& cmesh = this->getMesh(); - EXPECT_TRUE(cmesh.isValid(true)); - - // test copy constructor - const mir::MIRMesh mirCopy(cmesh); - EXPECT_TRUE( mirCopy.isValid(true) ); - } + // test copy constructor + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + + mir::MIRMesh mirCopy(mesh); + EXPECT_TRUE(mirCopy.isValid(true)); + } + + // test const copy constructor + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + // test copy constructor + const mir::MIRMesh mirCopy(cmesh); + EXPECT_TRUE(mirCopy.isValid(true)); + } } -TEST_F(MirMeshTest,copy_assign) +TEST_F(MirMeshTest, copy_assign) { - // test copy assignment from a non-const MIRMesh - { - mir::MIRMesh& mesh = this->getMesh(); - EXPECT_TRUE(mesh.isValid(true)); - - mir::MIRMesh mirCopy; - mirCopy = mesh; - EXPECT_TRUE( mirCopy.isValid(true) ); - } - - // test copy assignment from a const MIRMesh - { - const mir::MIRMesh& cmesh = this->getMesh(); - EXPECT_TRUE(cmesh.isValid(true)); - - mir::MIRMesh mirCopy; - mirCopy = cmesh; - EXPECT_TRUE( mirCopy.isValid(true) ); - } -} + // test copy assignment from a non-const MIRMesh + { + mir::MIRMesh& mesh = this->getMesh(); + EXPECT_TRUE(mesh.isValid(true)); + mir::MIRMesh mirCopy; + mirCopy = mesh; + EXPECT_TRUE(mirCopy.isValid(true)); + } + + // test copy assignment from a const MIRMesh + { + const mir::MIRMesh& cmesh = this->getMesh(); + EXPECT_TRUE(cmesh.isValid(true)); + + mir::MIRMesh mirCopy; + mirCopy = cmesh; + EXPECT_TRUE(mirCopy.isValid(true)); + } +} //------------------------------------------------------------------------------ @@ -225,5 +222,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_MESH_TEST_H_ +#endif // MIR_MESH_TEST_H_ diff --git a/src/axom/mir/tests/mir_smoke.cpp b/src/axom/mir/tests/mir_smoke.cpp index 06a8e09486..4f18a91f28 100644 --- a/src/axom/mir/tests/mir_smoke.cpp +++ b/src/axom/mir/tests/mir_smoke.cpp @@ -11,15 +11,13 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" - -TEST(mir,smoke) +TEST(mir, smoke) { SLIC_INFO("Running smoke test for MIR component"); - - EXPECT_TRUE( true ); - EXPECT_EQ( 0, 0 ); -} + EXPECT_TRUE(true); + EXPECT_EQ(0, 0); +} //---------------------------------------------------------------------- @@ -34,5 +32,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_SMOKE_H_ +#endif // MIR_SMOKE_H_ diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 98df90fb0d..e7df190a48 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -17,83 +17,87 @@ namespace mir = axom::mir; TEST(mir_shape_tests, shape_dimesionality) { - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Triangle), false ); - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Quad), false ); - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Tetrahedron), true ); - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Pyramid), true ); - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Triangular_Prism), true ); - EXPECT_EQ( mir::utilities::isShapeThreeDimensional(mir::Shape::Hexahedron), true ); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Triangle), false); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Quad), false); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Tetrahedron), + true); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Pyramid), true); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Triangular_Prism), + true); + EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Hexahedron), + true); } //---------------------------------------------------------------------- TEST(mir_shape_tests, check_shape_central_vertex) { - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangle, 6), false); - - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Quad, 8), false); - - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 9), false); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 10), true); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 11), false); - - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 12), false); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 13), true); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Pyramid, 14), false); - - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 14), false); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 15), true); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 16), false); - - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 19), false); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 20), true); - EXPECT_EQ( mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 21), false); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangle, 6), false); + + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Quad, 8), false); + + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 9), false); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 10), true); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 11), false); + + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 12), false); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 13), true); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 14), false); + + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 14), + false); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 15), + true); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 16), + false); + + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 19), false); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 20), true); + EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 21), false); } //---------------------------------------------------------------------- TEST(mir_shape_tests, determine_central_vertex) { - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Triangle), -1 ); - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Quad), -1 ); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Triangle), -1); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Quad), -1); - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Tetrahedron), 10 ); - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Pyramid), 13 ); - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Triangular_Prism), 15 ); - EXPECT_EQ( mir::utilities::getCenterVertex(mir::Shape::Hexahedron), 20 ); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Tetrahedron), 10); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Pyramid), 13); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Triangular_Prism), 15); + EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Hexahedron), 20); } //---------------------------------------------------------------------- TEST(mir_compute_averages, float_value) { - std::vector values = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; + std::vector values = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; - axom::float64 average = mir::utilities::computeAverageFloat( values ); + axom::float64 average = mir::utilities::computeAverageFloat(values); - EXPECT_DOUBLE_EQ( average, 2.5 ); + EXPECT_DOUBLE_EQ(average, 2.5); } //---------------------------------------------------------------------- TEST(mir_compute_averages, point_value) { - std::vector points = { - mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(0.0, 1.0, 0.0), - mir::Point2::make_point(0.0, 0.0, 1.0), - mir::Point2::make_point(1.0, 1.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 1.0), - mir::Point2::make_point(0.0, 1.0, 1.0), - mir::Point2::make_point(1.0, 1.0, 1.0) - }; - - mir::Point2 centroid = mir::utilities::computeAveragePoint( points ); - - EXPECT_DOUBLE_EQ( centroid[0], 0.5); - EXPECT_DOUBLE_EQ( centroid[1], 0.5); - EXPECT_DOUBLE_EQ( centroid[2], 0.5); + std::vector points = {mir::Point2::make_point(0.0, 0.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 0.0), + mir::Point2::make_point(0.0, 1.0, 0.0), + mir::Point2::make_point(0.0, 0.0, 1.0), + mir::Point2::make_point(1.0, 1.0, 0.0), + mir::Point2::make_point(1.0, 0.0, 1.0), + mir::Point2::make_point(0.0, 1.0, 1.0), + mir::Point2::make_point(1.0, 1.0, 1.0)}; + + mir::Point2 centroid = mir::utilities::computeAveragePoint(points); + + EXPECT_DOUBLE_EQ(centroid[0], 0.5); + EXPECT_DOUBLE_EQ(centroid[1], 0.5); + EXPECT_DOUBLE_EQ(centroid[2], 0.5); } //---------------------------------------------------------------------- @@ -109,5 +113,4 @@ int main(int argc, char* argv[]) return result; } - -#endif // MIR_UTILITIES_TEST_H_ +#endif // MIR_UTILITIES_TEST_H_ diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 2ea5950b2c..347eafb39f 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -8,7 +8,6 @@ #include "axom/core.hpp" #include "axom/mir.hpp" - TEST(mir_views, shape2conduitName) { EXPECT_EQ(axom::mir::views::LineShape::name(), "line"); @@ -35,9 +34,9 @@ TEST(mir_views, shape2conduitName) TEST(mir_views, explicit_coordsetview) { - axom::Array x{{0., 1., 2., 3., 4., 5.}}; - axom::Array y{{10., 11., 12., 13., 14., 15.}}; - axom::Array z{{20., 21., 22., 23., 24., 25.}}; + axom::Array x {{0., 1., 2., 3., 4., 5.}}; + axom::Array y {{10., 11., 12., 13., 14., 15.}}; + axom::Array z {{20., 21., 22., 23., 24., 25.}}; axom::mir::views::ExplicitCoordsetView view2d(x.view(), y.view()); EXPECT_EQ(view2d.size(), 6); @@ -48,7 +47,9 @@ TEST(mir_views, explicit_coordsetview) EXPECT_EQ(view2d[i], P); } - axom::mir::views::ExplicitCoordsetView view3d(x.view(), y.view(), z.view()); + axom::mir::views::ExplicitCoordsetView view3d(x.view(), + y.view(), + z.view()); EXPECT_EQ(view3d.size(), 6); for(axom::IndexType i = 0; i < view3d.size(); i++) { diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index 6955e12ab9..9d2eebc628 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -13,20 +13,21 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) { using Indexing2D = axom::mir::views::StridedStructuredIndexing; - using LogicalIndex = typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; - LogicalIndex dims{4, 3}; // window size in 4*3 elements in 6*5 overall - LogicalIndex origin{2, 2}; - LogicalIndex stride{1, 6}; + using LogicalIndex = + typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; + LogicalIndex dims {4, 3}; // window size in 4*3 elements in 6*5 overall + LogicalIndex origin {2, 2}; + LogicalIndex stride {1, 6}; Indexing2D indexing(dims, origin, stride); EXPECT_EQ(indexing.dimensions(), 2); EXPECT_EQ(indexing.size(), dims[0] * dims[1]); - const LogicalIndex logical0_0{0, 0}; + const LogicalIndex logical0_0 {0, 0}; const auto index0_0 = indexing.LogicalIndexToIndex(logical0_0); EXPECT_EQ(index0_0, 14); - const LogicalIndex logical2_2{2, 2}; + const LogicalIndex logical2_2 {2, 2}; const auto index2_2 = indexing.LogicalIndexToIndex(logical2_2); EXPECT_EQ(index2_2, 28); @@ -35,39 +36,40 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) for(int j = 0; j < dims[1]; j++) { - for(int i = 0; i < dims[0]; i++) - { - LogicalIndex logical{i, j}; - const auto flat = indexing.LogicalIndexToIndex(logical); - const auto logical2 = indexing.IndexToLogicalIndex(flat); - EXPECT_EQ(logical, logical2); - } + for(int i = 0; i < dims[0]; i++) + { + LogicalIndex logical {i, j}; + const auto flat = indexing.LogicalIndexToIndex(logical); + const auto logical2 = indexing.IndexToLogicalIndex(flat); + EXPECT_EQ(logical, logical2); + } } EXPECT_TRUE(indexing.contains(logical0_0)); - EXPECT_TRUE(indexing.contains(LogicalIndex{dims[0] - 1, dims[1] - 1})); - EXPECT_FALSE(indexing.contains(LogicalIndex{4, 0})); - EXPECT_FALSE(indexing.contains(LogicalIndex{4, 3})); + EXPECT_TRUE(indexing.contains(LogicalIndex {dims[0] - 1, dims[1] - 1})); + EXPECT_FALSE(indexing.contains(LogicalIndex {4, 0})); + EXPECT_FALSE(indexing.contains(LogicalIndex {4, 3})); } //---------------------------------------------------------------------- TEST(mir_views_indexing, strided_structured_indexing_3d) { using Indexing3D = axom::mir::views::StridedStructuredIndexing; - using LogicalIndex = typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; - LogicalIndex dims{4, 3, 3}; // window size in 4*3*3 elements in 6*5*5 overall - LogicalIndex origin{2, 2, 2}; - LogicalIndex stride{1, 6, 30}; + using LogicalIndex = + typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; + LogicalIndex dims {4, 3, 3}; // window size in 4*3*3 elements in 6*5*5 overall + LogicalIndex origin {2, 2, 2}; + LogicalIndex stride {1, 6, 30}; Indexing3D indexing(dims, origin, stride); EXPECT_EQ(indexing.dimensions(), 3); EXPECT_EQ(indexing.size(), dims[0] * dims[1] * dims[2]); - const LogicalIndex logical0_0_0{0, 0, 0}; + const LogicalIndex logical0_0_0 {0, 0, 0}; const auto index0_0_0 = indexing.LogicalIndexToIndex(logical0_0_0); EXPECT_EQ(index0_0_0, 2 * 30 + 14); - const LogicalIndex logical2_2_2{2, 2, 2}; + const LogicalIndex logical2_2_2 {2, 2, 2}; const auto index2_2_2 = indexing.LogicalIndexToIndex(logical2_2_2); EXPECT_EQ(index2_2_2, (2 + 2) * 30 + 28); @@ -80,7 +82,7 @@ TEST(mir_views_indexing, strided_structured_indexing_3d) { for(int i = 0; i < dims[0]; i++) { - LogicalIndex logical{i, j, k}; + LogicalIndex logical {i, j, k}; const auto flat = indexing.LogicalIndexToIndex(logical); const auto logical2 = indexing.IndexToLogicalIndex(flat); EXPECT_EQ(logical, logical2); @@ -89,9 +91,10 @@ TEST(mir_views_indexing, strided_structured_indexing_3d) } EXPECT_TRUE(indexing.contains(logical0_0_0)); - EXPECT_TRUE(indexing.contains(LogicalIndex{dims[0] - 1, dims[1] - 1, dims[2] - 1})); - EXPECT_FALSE(indexing.contains(LogicalIndex{4, 0, 0})); - EXPECT_FALSE(indexing.contains(LogicalIndex{4, 3, 0})); + EXPECT_TRUE( + indexing.contains(LogicalIndex {dims[0] - 1, dims[1] - 1, dims[2] - 1})); + EXPECT_FALSE(indexing.contains(LogicalIndex {4, 0, 0})); + EXPECT_FALSE(indexing.contains(LogicalIndex {4, 3, 0})); } //---------------------------------------------------------------------- diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp index cb84c3bc98..76bf002546 100644 --- a/src/axom/mir/utilities.cpp +++ b/src/axom/mir/utilities.cpp @@ -10,8 +10,6 @@ namespace axom namespace mir { namespace utilities -{ - -} // end namespace utilities -} // end namespace mir -} // end namespace axom +{ } // end namespace utilities +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index c930d3d944..6b43322c3e 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -28,7 +28,6 @@ namespace mir { namespace utilities { - //------------------------------------------------------------------------------ /** * \brief This class and its specializations provide a type trait that lets us @@ -38,16 +37,28 @@ namespace utilities * \note this belongs in algorithm utilities, maybe core. */ template -struct accumulation_traits { using type = float; }; +struct accumulation_traits +{ + using type = float; +}; template <> -struct accumulation_traits { using type = double; }; +struct accumulation_traits +{ + using type = double; +}; template <> -struct accumulation_traits { using type = double; }; +struct accumulation_traits +{ + using type = double; +}; template <> -struct accumulation_traits { using type = double; }; +struct accumulation_traits +{ + using type = double; +}; //------------------------------------------------------------------------------ /** @@ -60,8 +71,7 @@ struct accumulation_traits { using type = double; }; * \return The index where value was located in view or -1 if not found. */ template -AXOM_HOST_DEVICE -std::int32_t bsearch(T value, const axom::ArrayView &view) +AXOM_HOST_DEVICE std::int32_t bsearch(T value, const axom::ArrayView &view) { std::int32_t index = -1; std::int32_t left = 0; @@ -143,17 +153,16 @@ inline std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) * \param[in] n The number of values to be sorted. */ template -AXOM_HOST_DEVICE -void sort_values(ValueType *v, IndexType n) +AXOM_HOST_DEVICE void sort_values(ValueType *v, IndexType n) { for(IndexType i = 0; i < n - 1; i++) { const IndexType m = n - i - 1; for(IndexType j = 0; j < m; j++) { - if(v[j] > v[j+1]) + if(v[j] > v[j + 1]) { - axom::utilities::swap(v[j], v[j+1]); + axom::utilities::swap(v[j], v[j + 1]); } } } @@ -167,8 +176,7 @@ void sort_values(ValueType *v, IndexType n) * \return A hashed name for the id. */ template -AXOM_HOST_DEVICE -std::uint64_t make_name_1(ValueType id) +AXOM_HOST_DEVICE std::uint64_t make_name_1(ValueType id) { return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); }; @@ -182,8 +190,7 @@ std::uint64_t make_name_1(ValueType id) * \return A hashed name for the ids. */ template -AXOM_HOST_DEVICE -std::uint64_t make_name_2(ValueType id0, ValueType id1) +AXOM_HOST_DEVICE std::uint64_t make_name_2(ValueType id0, ValueType id1) { ValueType data[2] = {id0, id1}; if(id1 < id0) @@ -191,7 +198,8 @@ std::uint64_t make_name_2(ValueType id0, ValueType id1) data[0] = id1; data[1] = id0; } - return hash_bytes(reinterpret_cast(data), 2 * sizeof(ValueType)); + return hash_bytes(reinterpret_cast(data), + 2 * sizeof(ValueType)); }; //------------------------------------------------------------------------------ @@ -207,12 +215,11 @@ std::uint64_t make_name_2(ValueType id0, ValueType id1) * \return A hashed name for the ids. */ template -AXOM_HOST_DEVICE -std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) +AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, + std::uint32_t n) { assert(n <= MaxValues); - if(n == 2) - return make_name_2(values[0], values[1]); + if(n == 2) return make_name_2(values[0], values[1]); // Make sure the values are sorted before hashing. ValueType sorted[MaxValues]; @@ -222,7 +229,8 @@ std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) } sort_values(sorted, n); - return hash_bytes(reinterpret_cast(sorted), n * sizeof(ValueType)); + return hash_bytes(reinterpret_cast(sorted), + n * sizeof(ValueType)); } //------------------------------------------------------------------------------ @@ -238,7 +246,9 @@ std::uint64_t make_name_n(const ValueType *values, std::uint32_t n) * */ template -void unique(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) +void unique(const axom::ArrayView &keys_orig_view, + axom::Array &skeys, + axom::Array &sindices) { using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -250,11 +260,12 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array axom::Array indices(n, n, allocatorID); auto keys_view = keys.view(); auto indices_view = indices.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - keys_view[i] = keys_orig_view[i]; - indices_view[i] = i; - }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + keys_view[i] = keys_orig_view[i]; + indices_view[i] = i; + }); // Sort the keys, indices in place. RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), @@ -264,19 +275,21 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array axom::Array mask(n, n, allocatorID); auto mask_view = mask.view(); RAJA::ReduceSum mask_sum(0); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - const axom::IndexType m = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; - mask_view[i] = m; - mask_sum += m; - }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + const axom::IndexType m = + (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; + mask_view[i] = m; + mask_sum += m; + }); // Do a scan on the mask array to build an offset array. axom::Array offsets(n, n, allocatorID); auto offsets_view = offsets.view(); RAJA::exclusive_scan(RAJA::make_span(mask_view.data(), n), RAJA::make_span(offsets_view.data(), n), - RAJA::operators::plus{}); + RAJA::operators::plus {}); // Allocate the output arrays. const axom::IndexType newsize = mask_sum.get(); @@ -287,18 +300,19 @@ void unique(const axom::ArrayView &keys_orig_view, axom::Array // offset in the new array. auto skeys_view = skeys.view(); auto sindices_view = sindices.view(); - axom::for_all(n, AXOM_LAMBDA(axom::IndexType i) - { - if(mask_view[i]) - { - skeys_view[offsets_view[i]] = keys_view[i]; - sindices_view[offsets_view[i]] = indices_view[i]; - } - }); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + if(mask_view[i]) + { + skeys_view[offsets_view[i]] = keys_view[i]; + sindices_view[offsets_view[i]] = indices_view[i]; + } + }); } -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 43a433685d..ad372df42d 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -17,14 +17,12 @@ namespace mir { namespace views { - /** * \brief This class provides a view for Conduit/Blueprint explicit coordsets. */ template class ExplicitCoordsetView -{ -}; +{ }; /** * \brief This class provides a view for Conduit/Blueprint 2d explicit coordsets. @@ -45,7 +43,8 @@ class ExplicitCoordsetView */ AXOM_HOST_DEVICE ExplicitCoordsetView(const axom::ArrayView &x, - const axom::ArrayView &y) : m_coordinates{x,y} + const axom::ArrayView &y) + : m_coordinates {x, y} { SLIC_ASSERT(x.size() == y.size()); } @@ -66,12 +65,12 @@ class ExplicitCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - getPoint(IndexType vertex_index) const + PointType getPoint(IndexType vertex_index) const { SLIC_ASSERT(vertex_index < size()); - return PointType(std::initializer_list{m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index]}); + return PointType( + std::initializer_list {m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index]}); } /** @@ -82,8 +81,7 @@ class ExplicitCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](IndexType vertex_index) const + PointType operator[](IndexType vertex_index) const { return getPoint(vertex_index); } @@ -113,7 +111,8 @@ class ExplicitCoordsetView AXOM_HOST_DEVICE ExplicitCoordsetView(const axom::ArrayView &x, const axom::ArrayView &y, - const axom::ArrayView &z) : m_coordinates{x,y,z} + const axom::ArrayView &z) + : m_coordinates {x, y, z} { SLIC_ASSERT(x.size() == y.size() && x.size() == z.size()); } @@ -134,13 +133,13 @@ class ExplicitCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - getPoint(IndexType vertex_index) const + PointType getPoint(IndexType vertex_index) const { SLIC_ASSERT(vertex_index < size()); - return PointType(std::initializer_list{m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index], - m_coordinates[2][vertex_index]}); + return PointType( + std::initializer_list {m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index], + m_coordinates[2][vertex_index]}); } /** @@ -151,8 +150,7 @@ class ExplicitCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](IndexType vertex_index) const + PointType operator[](IndexType vertex_index) const { return getPoint(vertex_index); } @@ -161,9 +159,8 @@ class ExplicitCoordsetView axom::ArrayView m_coordinates[3]; }; - -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/MaterialView.cpp b/src/axom/mir/views/MaterialView.cpp index 26883f093d..85a984e34e 100644 --- a/src/axom/mir/views/MaterialView.cpp +++ b/src/axom/mir/views/MaterialView.cpp @@ -11,7 +11,6 @@ namespace mir { namespace views { - MaterialInformation materials(const conduit::Node &matset) { MaterialInformation info; @@ -20,12 +19,12 @@ MaterialInformation materials(const conduit::Node &matset) const conduit::Node &mm = matset["material_map"]; for(conduit::index_t i = 0; i < mm.number_of_children(); i++) { - info.push_back(Material{static_cast(i), mm[i].name()}); + info.push_back(Material {static_cast(i), mm[i].name()}); } } return info; } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 5e8a3ba4f3..29b5636e54 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -21,7 +21,6 @@ namespace mir { namespace views { - /** * \brief This object contains information about the materials as provided by a Conduit node. * @@ -30,7 +29,7 @@ namespace views struct Material { int number; - std::string name; + std::string name; }; using MaterialInformation = std::vector; @@ -86,10 +85,7 @@ class UnibufferMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfZones() const - { - return m_sizes.size(); - } + size_t getNumberOfZones() const { return m_sizes.size(); } AXOM_HOST_DEVICE size_t getNumberOfMaterials(ZoneIndex zi) const @@ -127,8 +123,7 @@ class UnibufferMaterialView { const auto idx = m_indices[offset + i]; - if(m_material_ids[idx] == mat) - return true; + if(m_material_ids[idx] == mat) return true; } return false; } @@ -161,7 +156,6 @@ class UnibufferMaterialView axom::ArrayView m_indices; }; - /** matsets: @@ -192,7 +186,8 @@ class MultiBufferMaterialView constexpr static size_t MaxMaterials = MAXMATERIALS; - void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) + void add(const axom::ArrayView &ids, + const axom::ArrayView &vfs) { assert(m_size + 1 < MaxMaterials); @@ -206,7 +201,7 @@ class MultiBufferMaterialView { size_t nzones = 0; for(int i = 0; i < m_size; i++) - nzones = axom::utilities::max(nzones, m_indices[i].size()); + nzones = axom::utilities::max(nzones, m_indices[i].size()); return nzones; } @@ -219,8 +214,7 @@ class MultiBufferMaterialView if(zi < m_indices[i].size()) { const auto idx = m_indices[zi]; - if(m_values[i][idx] > 0.) - nmats++; + if(m_values[i][idx] > 0.) nmats++; } } @@ -252,7 +246,7 @@ class MultiBufferMaterialView { assert(mat < m_size); assert(zi < m_indices[mat].size()); - + const auto idx = m_indices[mat][zi]; return m_values[mat][zi] > 0.; } @@ -262,15 +256,16 @@ class MultiBufferMaterialView { assert(mat < m_size); assert(zi < m_indices[mat].size()); - + const auto idx = m_indices[mat][zi]; vf = m_values[mat][zi]; return vf > 0.; } + private: - axom::StackArray, MAXMATERIALS> m_values{}; - axom::StackArray, MAXMATERIALS> m_indices{}; - size_t m_size{0}; + axom::StackArray, MAXMATERIALS> m_values {}; + axom::StackArray, MAXMATERIALS> m_indices {}; + size_t m_size {0}; }; /** @@ -366,8 +361,9 @@ class ElementDominantMaterialView } return contains; } + private: - axom::StaticArray, MAXMATERIALS> m_volume_fractions{}; + axom::StaticArray, MAXMATERIALS> m_volume_fractions {}; }; /** @@ -400,7 +396,8 @@ class MaterialDominantMaterialView constexpr static size_t MaxMaterials = MAXMATERIALS; - void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) + void add(const axom::ArrayView &ids, + const axom::ArrayView &vfs) { assert(m_size + 1 < MaxMaterials); @@ -540,10 +537,10 @@ class MaterialDominantMaterialView } private: - StackArray, MAXMATERIALS> m_element_ids{}; - StackArray, MAXMATERIALS> m_volume_fractions{}; - size_t m_size{0}; - size_t m_nzones{0}; + StackArray, MAXMATERIALS> m_element_ids {}; + StackArray, MAXMATERIALS> m_volume_fractions {}; + size_t m_size {0}; + size_t m_nzones {0}; }; #if 0 @@ -706,8 +703,8 @@ axom::Array selectMixedZones(const UnibufferMaterialView &view) } #endif -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 6134fa268e..346af37f45 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -35,42 +35,40 @@ using DataType = ::conduit::DataType; namespace detail { - -struct Delimiter {}; +struct Delimiter +{ }; /// Used to separate arguments. constexpr Delimiter ArgumentDelimiter; -template +template constexpr int encode_types(Args... args) { return (... | args); } -template +template constexpr int select_types(Args... args) { return encode_types((1 << args)...); } -constexpr bool type_selected(int flag, int bit) -{ - return flag & (1 << bit); -} +constexpr bool type_selected(int flag, int bit) { return flag & (1 << bit); } -constexpr int select_all_types() -{ - return -1; -} +constexpr int select_all_types() { return -1; } constexpr int select_index_types() { - return select_types(conduit::DataType::INT32_ID, conduit::DataType::INT64_ID, conduit::DataType::UINT32_ID, conduit::DataType::UINT64_ID); + return select_types(conduit::DataType::INT32_ID, + conduit::DataType::INT64_ID, + conduit::DataType::UINT32_ID, + conduit::DataType::UINT64_ID); } constexpr int select_float_types() { - return select_types(conduit::DataType::FLOAT32_ID, conduit::DataType::FLOAT64_ID); + return select_types(conduit::DataType::FLOAT32_ID, + conduit::DataType::FLOAT64_ID); } //------------------------------------------------------------------------------ @@ -81,282 +79,371 @@ constexpr int select_float_types() /// /// TODO: Handle strided data from the Conduit node. -template std::enable_if_t -Node_to_ArrayView_single_int8(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int8(const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_int8_ptr()), size); + axom::ArrayView view( + const_cast(n.as_int8_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int8(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int8( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int8 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int8(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int8(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_int8_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int8(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int8( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int8 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int16(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int16( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_int16_ptr()), size); + axom::ArrayView view( + const_cast(n.as_int16_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int16(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int16( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int16 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int16(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int16(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_int16_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int16(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int16( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int16 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int32(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int32( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_int32_ptr()), size); + axom::ArrayView view( + const_cast(n.as_int32_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int32(const conduit::Node & AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int32( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int32(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int32(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_int32_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int32( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int64(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int64( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_int64_ptr()), size); + axom::ArrayView view( + const_cast(n.as_int64_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int64( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int64 node."); } -template std::enable_if_t -Node_to_ArrayView_single_int64(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_int64(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_int64_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_int64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_int64( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported int64 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint8(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint8( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_uint8_ptr()), size); + axom::ArrayView view( + const_cast(n.as_uint8_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint8(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint8( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint8 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint8(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_uint8_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint8(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint8( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint8 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint16(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint16( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_uint16_ptr()), size); + axom::ArrayView view( + const_cast(n.as_uint16_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint16(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint16( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint16 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint16(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint16(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_uint16_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint16(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint16( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint16 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint32(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint32( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_uint32_ptr()), size); + axom::ArrayView view( + const_cast(n.as_uint32_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint32(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint32( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint32(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint32(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_uint32_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint32( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint64(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint64( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_uint64_ptr()), size); + axom::ArrayView view( + const_cast(n.as_uint64_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint64( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint64 node."); } -template std::enable_if_t -Node_to_ArrayView_single_uint64(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_uint64(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_uint64_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_uint64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_uint64( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported uint64 node."); } -template std::enable_if_t -Node_to_ArrayView_single_float32(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_float32( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_float32_ptr()), size); + axom::ArrayView view( + const_cast(n.as_float32_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_float32(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_float32( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported float32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_float32(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_float32(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_float32_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_float32(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_float32( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported float32 node."); } -template std::enable_if_t -Node_to_ArrayView_single_float64(const conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_float64( + const conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(const_cast(n.as_float64_ptr()), size); + axom::ArrayView view( + const_cast(n.as_float64_ptr()), + size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_float64(const conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_float64( + const conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported float64 node."); } -template std::enable_if_t -Node_to_ArrayView_single_float64(conduit::Node &n, FuncType &&func) +template +std::enable_if_t Node_to_ArrayView_single_float64(conduit::Node &n, + FuncType &&func) { const auto size = n.dtype().number_of_elements(); axom::ArrayView view(n.as_float64_ptr(), size); func(view); } -template std::enable_if_t -Node_to_ArrayView_single_float64(conduit::Node &AXOM_UNUSED_PARAM(n), FuncType && AXOM_UNUSED_PARAM(func)) +template +std::enable_if_t Node_to_ArrayView_single_float64( + conduit::Node &AXOM_UNUSED_PARAM(n), + FuncType &&AXOM_UNUSED_PARAM(func)) { SLIC_WARNING("Unsupported float64 node."); } @@ -377,47 +464,66 @@ void Node_to_ArrayView_single(const conduit::Node &n, FuncType &&func) if(n.dtype().is_int8()) { - Node_to_ArrayView_single_int8(n, func); + Node_to_ArrayView_single_int8( + n, + func); } else if(n.dtype().is_int16()) { - Node_to_ArrayView_single_int16(n, func); + Node_to_ArrayView_single_int16( + n, + func); } else if(n.dtype().is_int32()) { - Node_to_ArrayView_single_int32(n, func); + Node_to_ArrayView_single_int32( + n, + func); } else if(n.dtype().is_int64()) { - Node_to_ArrayView_single_int64(n, func); + Node_to_ArrayView_single_int64( + n, + func); } else if(n.dtype().is_uint8()) { - Node_to_ArrayView_single_uint8(n, func); + Node_to_ArrayView_single_uint8( + n, + func); } else if(n.dtype().is_uint16()) { - Node_to_ArrayView_single_uint16(n, func); + Node_to_ArrayView_single_uint16( + n, + func); } else if(n.dtype().is_uint32()) { - Node_to_ArrayView_single_uint32(n, func); + Node_to_ArrayView_single_uint32( + n, + func); } else if(n.dtype().is_uint64()) { - Node_to_ArrayView_single_uint64(n, func); + Node_to_ArrayView_single_uint64( + n, + func); } else if(n.dtype().is_float32()) { - Node_to_ArrayView_single_float32(n, func); + Node_to_ArrayView_single_float32< + type_selected(Types, conduit::DataType::FLOAT32_ID)>(n, func); } else if(n.dtype().is_float64()) { - Node_to_ArrayView_single_float64(n, func); + Node_to_ArrayView_single_float64< + type_selected(Types, conduit::DataType::FLOAT64_ID)>(n, func); } else { - SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " << n.path()); + SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " + << n.path()); } } @@ -426,70 +532,87 @@ void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) { if(n.dtype().is_int8()) { - Node_to_ArrayView_single_int8(n, func); + Node_to_ArrayView_single_int8( + n, + func); } else if(n.dtype().is_int16()) { - Node_to_ArrayView_single_int16(n, func); + Node_to_ArrayView_single_int16( + n, + func); } else if(n.dtype().is_int32()) { - Node_to_ArrayView_single_int32(n, func); + Node_to_ArrayView_single_int32( + n, + func); } else if(n.dtype().is_int64()) { - Node_to_ArrayView_single_int64(n, func); + Node_to_ArrayView_single_int64( + n, + func); } else if(n.dtype().is_uint8()) { - Node_to_ArrayView_single_uint8(n, func); + Node_to_ArrayView_single_uint8( + n, + func); } else if(n.dtype().is_uint16()) { - Node_to_ArrayView_single_uint16(n, func); + Node_to_ArrayView_single_uint16( + n, + func); } else if(n.dtype().is_uint32()) { - Node_to_ArrayView_single_uint32(n, func); + Node_to_ArrayView_single_uint32( + n, + func); } else if(n.dtype().is_uint64()) { - Node_to_ArrayView_single_uint64(n, func); + Node_to_ArrayView_single_uint64( + n, + func); } else if(n.dtype().is_float32()) { - Node_to_ArrayView_single_float32(n, func); + Node_to_ArrayView_single_float32< + type_selected(Types, conduit::DataType::FLOAT32_ID)>(n, func); } else if(n.dtype().is_float64()) { - Node_to_ArrayView_single_float64(n, func); + Node_to_ArrayView_single_float64< + type_selected(Types, conduit::DataType::FLOAT64_ID)>(n, func); } else { - SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " << n.path()); + SLIC_ERROR("Unsupported data type " << n.dtype().name() << " on node " + << n.path()); } } -template -void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View&... views) +template +void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &... views) { func(views...); } -template -void Node_to_ArrayView_internal(const conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) { - Node_to_ArrayView_single(first, [&](auto view) - { + Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); }); } -template -void Node_to_ArrayView_internal(conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) { - Node_to_ArrayView_single(first, [&](auto view) - { + Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); }); } @@ -497,228 +620,306 @@ void Node_to_ArrayView_internal(conduit::Node &first, Args&&... args) //------------------------------------------------------------------------------ /// NOTE: handle const conduit::Node& better. For now, const_cast. -template std::enable_if_t -Node_to_ArrayView_same_internal_int8(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_int8( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_int8_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_int8_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_int8(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_int8( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_int16(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_int16( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_int16_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_int16_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_int16(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_int16( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_int32(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_int32( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_int32_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_int32_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_int32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_int32( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_int64(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_int64( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_int64_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_int64_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_int64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_int64( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint8(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_uint8( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_uint8_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_uint8_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint8(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_uint8( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint16(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_uint16( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_uint16_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_uint16_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint16(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_uint16( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint32(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_uint32( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_uint32_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_uint32_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_uint32( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint64(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_uint64( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_uint64_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_uint64_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_uint64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_uint64( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_float32(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_float32( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_float32_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_float32_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_float32(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_float32( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } -template std::enable_if_t -Node_to_ArrayView_same_internal_float64(FuncType &&func, Args&&... args) +template +std::enable_if_t Node_to_ArrayView_same_internal_float64( + FuncType &&func, + Args &&... args) { - func(axom::ArrayView(const_cast(args.as_float64_ptr()), args.dtype().number_of_elements())...); + func(axom::ArrayView( + const_cast(args.as_float64_ptr()), + args.dtype().number_of_elements())...); } -template std::enable_if_t -Node_to_ArrayView_same_internal_float64(FuncType && AXOM_UNUSED_PARAM(func), Args&&... AXOM_UNUSED_PARAM(args)) -{ -} +template +std::enable_if_t Node_to_ArrayView_same_internal_float64( + FuncType &&AXOM_UNUSED_PARAM(func), + Args &&... AXOM_UNUSED_PARAM(args)) +{ } template -void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit::Node &first, Args&&... args) +void Node_to_ArrayView_same_internal(FuncType &&func, + Delimiter, + const conduit::Node &first, + Args &&... args) { if(first.dtype().is_int8()) { - Node_to_ArrayView_same_internal_int8(func, first, args...); + Node_to_ArrayView_same_internal_int8< + type_selected(Types, conduit::DataType::INT8_ID)>(func, first, args...); } else if(first.dtype().is_int16()) { - Node_to_ArrayView_same_internal_int16(func, first, args...); + Node_to_ArrayView_same_internal_int16< + type_selected(Types, conduit::DataType::INT16_ID)>(func, first, args...); } else if(first.dtype().is_int32()) { - Node_to_ArrayView_same_internal_int32(func, first, args...); + Node_to_ArrayView_same_internal_int32< + type_selected(Types, conduit::DataType::INT32_ID)>(func, first, args...); } else if(first.dtype().is_int64()) { - Node_to_ArrayView_same_internal_int64(func, first, args...); + Node_to_ArrayView_same_internal_int64< + type_selected(Types, conduit::DataType::INT64_ID)>(func, first, args...); } else if(first.dtype().is_uint8()) { - Node_to_ArrayView_same_internal_uint8(func, first, args...); + Node_to_ArrayView_same_internal_uint8< + type_selected(Types, conduit::DataType::UINT8_ID)>(func, first, args...); } else if(first.dtype().is_uint16()) { - Node_to_ArrayView_same_internal_uint16(func, first, args...); + Node_to_ArrayView_same_internal_uint16< + type_selected(Types, conduit::DataType::UINT16_ID)>(func, first, args...); } else if(first.dtype().is_uint32()) { - Node_to_ArrayView_same_internal_uint32(func, first, args...); + Node_to_ArrayView_same_internal_uint32< + type_selected(Types, conduit::DataType::UINT32_ID)>(func, first, args...); } else if(first.dtype().is_uint64()) { - Node_to_ArrayView_same_internal_uint64(func, first, args...); + Node_to_ArrayView_same_internal_uint64< + type_selected(Types, conduit::DataType::UINT64_ID)>(func, first, args...); } else if(first.dtype().is_float32()) { - Node_to_ArrayView_same_internal_float32(func, first, args...); + Node_to_ArrayView_same_internal_float32< + type_selected(Types, conduit::DataType::FLOAT32_ID)>(func, first, args...); } else if(first.dtype().is_float64()) { - Node_to_ArrayView_same_internal_float64(func, first, args...); + Node_to_ArrayView_same_internal_float64< + type_selected(Types, conduit::DataType::FLOAT64_ID)>(func, first, args...); } else { - SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " << first.path()); + SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " + << first.path()); } } template -void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node &first, Args&&... args) +void Node_to_ArrayView_same_internal(FuncType &&func, + Delimiter, + conduit::Node &first, + Args &&... args) { if(first.dtype().is_int8()) { - Node_to_ArrayView_same_internal_int8(func, first, args...); + Node_to_ArrayView_same_internal_int8< + type_selected(Types, conduit::DataType::INT8_ID)>(func, first, args...); } else if(first.dtype().is_int16()) { - Node_to_ArrayView_same_internal_int16(func, first, args...); + Node_to_ArrayView_same_internal_int16< + type_selected(Types, conduit::DataType::INT16_ID)>(func, first, args...); } else if(first.dtype().is_int32()) { - Node_to_ArrayView_same_internal_int32(func, first, args...); + Node_to_ArrayView_same_internal_int32< + type_selected(Types, conduit::DataType::INT32_ID)>(func, first, args...); } else if(first.dtype().is_int64()) { - Node_to_ArrayView_same_internal_int64(func, first, args...); + Node_to_ArrayView_same_internal_int64< + type_selected(Types, conduit::DataType::INT64_ID)>(func, first, args...); } else if(first.dtype().is_uint8()) { - Node_to_ArrayView_same_internal_uint8(func, first, args...); + Node_to_ArrayView_same_internal_uint8< + type_selected(Types, conduit::DataType::UINT8_ID)>(func, first, args...); } else if(first.dtype().is_uint16()) { - Node_to_ArrayView_same_internal_uint16(func, first, args...); + Node_to_ArrayView_same_internal_uint16< + type_selected(Types, conduit::DataType::UINT16_ID)>(func, first, args...); } else if(first.dtype().is_uint32()) { - Node_to_ArrayView_same_internal_uint32(func, first, args...); + Node_to_ArrayView_same_internal_uint32< + type_selected(Types, conduit::DataType::UINT32_ID)>(func, first, args...); } else if(first.dtype().is_uint64()) { - Node_to_ArrayView_same_internal_uint64(func, first, args...); + Node_to_ArrayView_same_internal_uint64< + type_selected(Types, conduit::DataType::UINT64_ID)>(func, first, args...); } else if(first.dtype().is_float32()) { - Node_to_ArrayView_same_internal_float32(func, first, args...); + Node_to_ArrayView_same_internal_float32< + type_selected(Types, conduit::DataType::FLOAT32_ID)>(func, first, args...); } else if(first.dtype().is_float64()) { - Node_to_ArrayView_same_internal_float64(func, first, args...); + Node_to_ArrayView_same_internal_float64< + type_selected(Types, conduit::DataType::FLOAT64_ID)>(func, first, args...); } else { - SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " << first.path()); + SLIC_ERROR("Unsupported data type " << first.dtype().name() << " on node " + << first.path()); } } /// Reorder args -template -void Node_to_ArrayView_same_internal(const conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&... args) { Node_to_ArrayView_same_internal(args..., first); } -template -void Node_to_ArrayView_same_internal(conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) { Node_to_ArrayView_same_internal(args..., first); } -} // namespace detail +} // namespace detail //------------------------------------------------------------------------------ // Node to ArrayView. Handle all types. @@ -738,14 +939,14 @@ void Node_to_ArrayView_same_internal(conduit::Node &first, Args&&... args) * Node_to_ArrayView(node1, node2, [](auto &view1, auto &view2) { }); * */ -template -void Node_to_ArrayView(const conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } -template -void Node_to_ArrayView(conduit::Node &first, Args&&... args) +template +void Node_to_ArrayView(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } @@ -766,74 +967,102 @@ void Node_to_ArrayView(conduit::Node &first, Args&&... args) * */ template -void Node_to_ArrayView_same(const conduit::Node &first, Args&&... args) +void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal(first, + args..., + detail::ArgumentDelimiter); } template -void Node_to_ArrayView_same(conduit::Node &first, Args&&... args) +void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal(first, + args..., + detail::ArgumentDelimiter); } //------------------------------------------------------------------------------ // Index Node to ArrayView. Handle types used for indexing. //------------------------------------------------------------------------------ -template -void IndexNode_to_ArrayView(const conduit::Node &first, Args&&... args) +template +void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_internal( + first, + args..., + detail::ArgumentDelimiter); } -template -void IndexNode_to_ArrayView(conduit::Node &first, Args&&... args) +template +void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_internal( + first, + args..., + detail::ArgumentDelimiter); } template -void IndexNode_to_ArrayView_same(const conduit::Node &first, Args&&... args) +void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal( + first, + args..., + detail::ArgumentDelimiter); } template -void IndexNode_to_ArrayView_same(conduit::Node &first, Args&&... args) +void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal( + first, + args..., + detail::ArgumentDelimiter); } //------------------------------------------------------------------------------ // Float Node to ArrayView. Handle float types. //------------------------------------------------------------------------------ -template -void FloatNode_to_ArrayView(const conduit::Node &first, Args&&... args) +template +void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_internal( + first, + args..., + detail::ArgumentDelimiter); } -template -void FloatNode_to_ArrayView(conduit::Node &first, Args&&... args) +template +void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_internal( + first, + args..., + detail::ArgumentDelimiter); } template -void FloatNode_to_ArrayView_same(const conduit::Node &first, Args&&... args) +void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal( + first, + args..., + detail::ArgumentDelimiter); } template -void FloatNode_to_ArrayView_same(conduit::Node &first, Args&&... args) +void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&... args) { - detail::Node_to_ArrayView_same_internal(first, args..., detail::ArgumentDelimiter); + detail::Node_to_ArrayView_same_internal( + first, + args..., + detail::ArgumentDelimiter); } -} // namespace views -} // namespace mir -} // namespace axom +} // namespace views +} // namespace mir +} // namespace axom #endif diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index d106ff03ac..ddaaf36993 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -19,7 +19,6 @@ namespace mir { namespace views { - /// NOTE: The rectilinear coordset views could be combined into a single RectilinearCoordset /// view that is templated on NDIMS but the resulting SFINAE would just overcomplicate it. @@ -43,9 +42,10 @@ class RectilinearCoordsetView2 */ AXOM_HOST_DEVICE RectilinearCoordsetView2(const axom::ArrayView &x, - const axom::ArrayView &y) : m_coordinates{x, y}, m_indexing(LogicalIndex{{x.size(), y.size()}}) - { - } + const axom::ArrayView &y) + : m_coordinates {x, y} + , m_indexing(LogicalIndex {{x.size(), y.size()}}) + { } /** * \brief Return the number of points in the coordset. @@ -53,10 +53,7 @@ class RectilinearCoordsetView2 * \return The number of points in the coordset. */ AXOM_HOST_DEVICE - IndexType size() const - { - return m_indexing.size(); - } + IndexType size() const { return m_indexing.size(); } /** * \brief Return the requested point from the coordset. @@ -68,8 +65,9 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType(std::initializer_list{m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]]}); + return PointType( + std::initializer_list {m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]]}); } /** @@ -93,8 +91,7 @@ class RectilinearCoordsetView2 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](LogicalIndex vertex_index) const + PointType operator[](LogicalIndex vertex_index) const { return getPoint(vertex_index); } @@ -107,14 +104,13 @@ class RectilinearCoordsetView2 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](IndexType vertex_index) const + PointType operator[](IndexType vertex_index) const { return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } private: - axom::ArrayView m_coordinates[2]; + axom::ArrayView m_coordinates[2]; StructuredIndexing m_indexing; }; @@ -140,9 +136,10 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE RectilinearCoordsetView3(const axom::ArrayView &x, const axom::ArrayView &y, - const axom::ArrayView &z) : m_coordinates{x, y, z}, m_indexing(LogicalIndex{{x.size(), y.size(), z.size()}}) - { - } + const axom::ArrayView &z) + : m_coordinates {x, y, z} + , m_indexing(LogicalIndex {{x.size(), y.size(), z.size()}}) + { } /** * \brief Return the number of nodes in the coordset. @@ -152,16 +149,10 @@ class RectilinearCoordsetView3 /// @{ AXOM_HOST_DEVICE - IndexType size() const - { - return m_indexing.size(); - } + IndexType size() const { return m_indexing.size(); } AXOM_HOST_DEVICE - IndexType numberOfNodes() const - { - return m_indexing.size(); - } + IndexType numberOfNodes() const { return m_indexing.size(); } /// @} /** @@ -174,9 +165,10 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType(std::initializer_list{m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]], - m_coordinates[2][vertex_index[2]]}); + return PointType( + std::initializer_list {m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]], + m_coordinates[2][vertex_index[2]]}); } /** @@ -200,8 +192,7 @@ class RectilinearCoordsetView3 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](LogicalIndex vertex_index) const + PointType operator[](LogicalIndex vertex_index) const { return getPoint(vertex_index); } @@ -214,19 +205,18 @@ class RectilinearCoordsetView3 * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](IndexType vertex_index) const + PointType operator[](IndexType vertex_index) const { return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } private: - axom::ArrayView m_coordinates[3]; + axom::ArrayView m_coordinates[3]; StructuredIndexing m_indexing; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 896db3a1fb..63d100f454 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -19,29 +19,27 @@ namespace mir { namespace views { - // Shape ids. These are used to identify shapes. These are used as indices // in bit fields in some algorithms. enum { - Point_ShapeID = 0, - Line_ShapeID = 1, - Tri_ShapeID = 2, - Quad_ShapeID = 3, - Polygon_ShapeID = 4, - Tet_ShapeID = 5, - Pyramid_ShapeID = 6, - Wedge_ShapeID = 7, - Hex_ShapeID = 8, - Polyhedron_ShapeID = 9, - Mixed_ShapeID = 10, - - Invalid_ShapeID = 20 + Point_ShapeID = 0, + Line_ShapeID = 1, + Tri_ShapeID = 2, + Quad_ShapeID = 3, + Polygon_ShapeID = 4, + Tet_ShapeID = 5, + Pyramid_ShapeID = 6, + Wedge_ShapeID = 7, + Hex_ShapeID = 8, + Polyhedron_ShapeID = 9, + Mixed_ShapeID = 10, + + Invalid_ShapeID = 20 }; // TODO: PointTraits - /* 0*-----------* 1 @@ -49,25 +47,32 @@ enum */ struct LineTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Line_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Line_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 2; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 2; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + { + return 2; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 1; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } constexpr static IndexType faces[][2] = {{0, 1}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int /*edgeIndex*/) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int /*edgeIndex*/) { - return axom::StackArray{0,1}; + return axom::StackArray {0, 1}; } AXOM_HOST_DEVICE constexpr static const char *name() { return "line"; } @@ -85,25 +90,32 @@ struct LineTraits */ struct TriTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Tri_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Tri_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 3; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + { + return 3; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 3; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } constexpr static IndexType faces[][3] = {{0, 1, 2}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}}; + const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 0}}; return edges[edgeIndex]; } @@ -122,25 +134,32 @@ struct TriTraits */ struct QuadTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Quad_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Quad_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + { + return 4; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } constexpr static IndexType faces[][4] = {{0, 1, 2, 3}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}}; + const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}}; return edges[edgeIndex]; } @@ -165,25 +184,36 @@ struct QuadTraits */ struct TetTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Tet_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Tet_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + { + return 3; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 6; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } - constexpr static IndexType faces[][3] = {{0,1,3}, {1,2,3}, {2,0,3}, {0,2,1}}; + constexpr static IndexType faces[][3] = {{0, 1, 3}, + {1, 2, 3}, + {2, 0, 3}, + {0, 2, 1}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 0},{0, 3}, {1, 3}, {2, 3}}; + const axom::StackArray edges[] = + {{0, 1}, {1, 2}, {2, 0}, {0, 3}, {1, 3}, {2, 3}}; return edges[edgeIndex]; } @@ -217,18 +247,30 @@ struct PyramidTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 5; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) { return faceIndex == 0 ? 4 : 3; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) + { + return faceIndex == 0 ? 4 : 3; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 5; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 8; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } - constexpr static int faces[][4] = {{3,2,1,0}, {0,1,4,-1}, {1,2,4,-1}, {2,3,4,-1}, {3,0,4,-1}}; + constexpr static int faces[][4] = {{3, 2, 1, 0}, + {0, 1, 4, -1}, + {1, 2, 4, -1}, + {2, 3, 4, -1}, + {3, 0, 4, -1}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0,1}, {1,2}, {2,3}, {3,0}, {0,4}, {1,4}, {2,4}, {3,4}}; + const axom::StackArray edges[] = + {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {0, 4}, {1, 4}, {2, 4}, {3, 4}}; return edges[edgeIndex]; } @@ -256,7 +298,7 @@ struct PyramidTraits */ struct WedgeTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Wedge_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Wedge_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -264,18 +306,30 @@ struct WedgeTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 6; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) { return (faceIndex == 0 || faceIndex == 1) ? 3 : 4; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int faceIndex) + { + return (faceIndex == 0 || faceIndex == 1) ? 3 : 4; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 5; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 9; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } - constexpr static int faces[][4] = {{0,2,1,-1}, {3,4,5,-1}, {0,1,4,3}, {1,2,5,4}, {2,0,3,5}}; + constexpr static int faces[][4] = {{0, 2, 1, -1}, + {3, 4, 5, -1}, + {0, 1, 4, 3}, + {1, 2, 5, 4}, + {2, 0, 3, 5}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0,1}, {1,2}, {2,0}, {3,4}, {4,5}, {5,3}, {0,3}, {1,4}, {2,5}}; + const axom::StackArray edges[] = + {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5}, {5, 3}, {0, 3}, {1, 4}, {2, 5}}; return edges[edgeIndex]; } @@ -300,7 +354,7 @@ struct WedgeTraits */ struct HexTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Hex_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Hex_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } @@ -308,18 +362,41 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 8; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) { return 4; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + { + return 4; + } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 4; } AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 6; } AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 12; } - AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) { return numberOfNodes() * zoneIndex; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return numberOfNodes() * zoneIndex; + } - constexpr static IndexType faces[][4] = {{3, 0, 4, 7}, {1, 2, 6, 5}, {0, 1, 5, 4}, {3, 7, 6, 2}, {0, 3, 2, 1}, {4, 5, 6, 7}}; + constexpr static IndexType faces[][4] = {{3, 0, 4, 7}, + {1, 2, 6, 5}, + {0, 1, 5, 4}, + {3, 7, 6, 2}, + {0, 3, 2, 1}, + {4, 5, 6, 7}}; - AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge(int edgeIndex) + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int edgeIndex) { - const axom::StackArray edges[] = {{0, 1}, {1, 2}, {2, 3}, {3, 0}, {4, 5}, {5, 6}, {6, 7}, {7, 4}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; + const axom::StackArray edges[] = {{0, 1}, + {1, 2}, + {2, 3}, + {3, 0}, + {4, 5}, + {5, 6}, + {6, 7}, + {7, 4}, + {0, 4}, + {1, 5}, + {3, 7}, + {2, 6}}; return edges[edgeIndex]; } @@ -336,7 +413,7 @@ n-1 *-... * 2 */ struct PolygonTraits { - AXOM_HOST_DEVICE constexpr static int id() { return Polygon_ShapeID; } + AXOM_HOST_DEVICE constexpr static int id() { return Polygon_ShapeID; } AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return true; } @@ -344,7 +421,6 @@ struct PolygonTraits AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 20; } AXOM_HOST_DEVICE constexpr static const char *name() { return "polygon"; } - }; template @@ -356,9 +432,8 @@ struct PolygonShape : public PolygonTraits /** * \brief Construct a shape. */ - AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) : m_idsView(ids) - { - } + AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) + : m_idsView(ids) { } /** * \brief Get the ids that make up this shape. @@ -383,7 +458,7 @@ struct PolygonShape : public PolygonTraits { const auto p0 = edgeIndex % m_idsView.size(); const auto p1 = (edgeIndex + 1) % m_idsView.size(); - return axom::StackArray{p0, p1}; + return axom::StackArray {p0, p1}; } private: @@ -402,7 +477,9 @@ struct Shape : public ShapeTraits /** * \brief Construct a shape. */ - AXOM_HOST_DEVICE Shape(const ConnectivityView &ids) : m_idsView(ids), m_faceIds() + AXOM_HOST_DEVICE Shape(const ConnectivityView &ids) + : m_idsView(ids) + , m_faceIds() { SLIC_ASSERT(m_idsView.size() == ShapeTraits::numberOfNodes()); } @@ -412,7 +489,10 @@ struct Shape : public ShapeTraits * * \return The i'th id that makes up this shape. */ - AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const { return m_idsView[index]; } + AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const + { + return m_idsView[index]; + } /** * \brief Get the ids that make up this shape. @@ -436,19 +516,19 @@ struct Shape : public ShapeTraits * \return An array view (wrapping m_faceIds) that contains the ids for the face. */ AXOM_HOST_DEVICE - ConnectivityView - getFace(int faceIndex) const + ConnectivityView getFace(int faceIndex) const { if constexpr(ShapeTraits::dimension() == 2) return m_idsView; else - { + { const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); for(IndexType i = 0; i < nnodes; i++) m_faceIds[i] = m_idsView[ShapeTraits::faces[faceIndex][i]]; return ConnectivityView(m_faceIds.m_data, nnodes); } } + private: ConnectivityView m_idsView; mutable axom::StackArray m_faceIds; @@ -476,7 +556,6 @@ using WedgeShape = Shape; template using HexShape = Shape; - /** * \brief This is a shape that can act as any of the other shapes. * @@ -495,7 +574,9 @@ struct VariableShape * \param ids The ids that describe the shape. */ AXOM_HOST_DEVICE - VariableShape(int shapeId, const ConnectivityView &ids) : m_shapeId(shapeId), m_idsView(ids) + VariableShape(int shapeId, const ConnectivityView &ids) + : m_shapeId(shapeId) + , m_idsView(ids) { //SLIC_ASSERT(shapeId >= Point_ShapeID && shapeID <= Hex_ShapeID); } @@ -514,36 +595,65 @@ struct VariableShape IndexType dim = 2; switch(m_shapeId) { - case Line_ShapeID: dim = LineTraits::dimension(); break; - case Tri_ShapeID: dim = TriTraits::dimension(); break; - case Quad_ShapeID: dim = QuadTraits::dimension(); break; - case Polygon_ShapeID: dim = 2; break; - case Tet_ShapeID: dim = TetTraits::dimension(); break; - case Pyramid_ShapeID: dim = PyramidTraits::dimension(); break; - case Wedge_ShapeID: dim = WedgeTraits::dimension(); break; - case Hex_ShapeID: dim = HexTraits::dimension(); break; + case Line_ShapeID: + dim = LineTraits::dimension(); + break; + case Tri_ShapeID: + dim = TriTraits::dimension(); + break; + case Quad_ShapeID: + dim = QuadTraits::dimension(); + break; + case Polygon_ShapeID: + dim = 2; + break; + case Tet_ShapeID: + dim = TetTraits::dimension(); + break; + case Pyramid_ShapeID: + dim = PyramidTraits::dimension(); + break; + case Wedge_ShapeID: + dim = WedgeTraits::dimension(); + break; + case Hex_ShapeID: + dim = HexTraits::dimension(); + break; } return dim; } - AXOM_HOST_DEVICE IndexType numberOfNodes() const - { - return m_idsView.size(); - } + AXOM_HOST_DEVICE IndexType numberOfNodes() const { return m_idsView.size(); } AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const { IndexType nnodes = 0; switch(m_shapeId) { - case Line_ShapeID: nnodes = LineTraits::numberOfNodesInFace(faceIndex); break; - case Tri_ShapeID: nnodes = TriTraits::numberOfNodesInFace(faceIndex); break; - case Quad_ShapeID: nnodes = QuadTraits::numberOfNodesInFace(faceIndex); break; - case Polygon_ShapeID: nnodes = (faceIndex == 0) ? m_idsView.size() : 0; break; - case Tet_ShapeID: nnodes = TetTraits::numberOfNodesInFace(faceIndex); break; - case Pyramid_ShapeID: nnodes = PyramidTraits::numberOfNodesInFace(faceIndex); break; - case Wedge_ShapeID: nnodes = WedgeTraits::numberOfNodesInFace(faceIndex); break; - case Hex_ShapeID: nnodes = HexTraits::numberOfNodesInFace(faceIndex); break; + case Line_ShapeID: + nnodes = LineTraits::numberOfNodesInFace(faceIndex); + break; + case Tri_ShapeID: + nnodes = TriTraits::numberOfNodesInFace(faceIndex); + break; + case Quad_ShapeID: + nnodes = QuadTraits::numberOfNodesInFace(faceIndex); + break; + case Polygon_ShapeID: + nnodes = (faceIndex == 0) ? m_idsView.size() : 0; + break; + case Tet_ShapeID: + nnodes = TetTraits::numberOfNodesInFace(faceIndex); + break; + case Pyramid_ShapeID: + nnodes = PyramidTraits::numberOfNodesInFace(faceIndex); + break; + case Wedge_ShapeID: + nnodes = WedgeTraits::numberOfNodesInFace(faceIndex); + break; + case Hex_ShapeID: + nnodes = HexTraits::numberOfNodesInFace(faceIndex); + break; } return nnodes; } @@ -553,14 +663,30 @@ struct VariableShape IndexType nnodes = 0; switch(m_shapeId) { - case Line_ShapeID: nnodes = LineTraits::maxNodesInFace(); break; - case Tri_ShapeID: nnodes = TriTraits::maxNodesInFace(); break; - case Quad_ShapeID: nnodes = QuadTraits::maxNodesInFace(); break; - case Polygon_ShapeID: nnodes = PolygonShape::maxNodesInFace(); break; - case Tet_ShapeID: nnodes = TetTraits::maxNodesInFace(); break; - case Pyramid_ShapeID: nnodes = PyramidTraits::maxNodesInFace(); break; - case Wedge_ShapeID: nnodes = WedgeTraits::maxNodesInFace(); break; - case Hex_ShapeID: nnodes = HexTraits::maxNodesInFace(); break; + case Line_ShapeID: + nnodes = LineTraits::maxNodesInFace(); + break; + case Tri_ShapeID: + nnodes = TriTraits::maxNodesInFace(); + break; + case Quad_ShapeID: + nnodes = QuadTraits::maxNodesInFace(); + break; + case Polygon_ShapeID: + nnodes = PolygonShape::maxNodesInFace(); + break; + case Tet_ShapeID: + nnodes = TetTraits::maxNodesInFace(); + break; + case Pyramid_ShapeID: + nnodes = PyramidTraits::maxNodesInFace(); + break; + case Wedge_ShapeID: + nnodes = WedgeTraits::maxNodesInFace(); + break; + case Hex_ShapeID: + nnodes = HexTraits::maxNodesInFace(); + break; } return nnodes; } @@ -570,14 +696,30 @@ struct VariableShape IndexType nfaces = 0; switch(m_shapeId) { - case Line_ShapeID: nfaces = LineTraits::numberOfFaces(); break; - case Tri_ShapeID: nfaces = TriTraits::numberOfFaces(); break; - case Quad_ShapeID: nfaces = QuadTraits::numberOfFaces(); break; - case Polygon_ShapeID: nfaces = 1; break; - case Tet_ShapeID: nfaces = TetTraits::numberOfFaces(); break; - case Pyramid_ShapeID: nfaces = PyramidTraits::numberOfFaces(); break; - case Wedge_ShapeID: nfaces = WedgeTraits::numberOfFaces(); break; - case Hex_ShapeID: nfaces = HexTraits::numberOfFaces(); break; + case Line_ShapeID: + nfaces = LineTraits::numberOfFaces(); + break; + case Tri_ShapeID: + nfaces = TriTraits::numberOfFaces(); + break; + case Quad_ShapeID: + nfaces = QuadTraits::numberOfFaces(); + break; + case Polygon_ShapeID: + nfaces = 1; + break; + case Tet_ShapeID: + nfaces = TetTraits::numberOfFaces(); + break; + case Pyramid_ShapeID: + nfaces = PyramidTraits::numberOfFaces(); + break; + case Wedge_ShapeID: + nfaces = WedgeTraits::numberOfFaces(); + break; + case Hex_ShapeID: + nfaces = HexTraits::numberOfFaces(); + break; } return nfaces; } @@ -587,14 +729,30 @@ struct VariableShape IndexType nedges = 0; switch(m_shapeId) { - case Line_ShapeID: nedges = LineTraits::numberOfEdges(); break; - case Tri_ShapeID: nedges = TriTraits::numberOfEdges(); break; - case Quad_ShapeID: nedges = QuadTraits::numberOfEdges(); break; - case Polygon_ShapeID: nedges = m_idsView.size(); break; - case Tet_ShapeID: nedges = TetTraits::numberOfEdges(); break; - case Pyramid_ShapeID: nedges = PyramidTraits::numberOfEdges(); break; - case Wedge_ShapeID: nedges = WedgeTraits::numberOfEdges(); break; - case Hex_ShapeID: nedges = HexTraits::numberOfEdges(); break; + case Line_ShapeID: + nedges = LineTraits::numberOfEdges(); + break; + case Tri_ShapeID: + nedges = TriTraits::numberOfEdges(); + break; + case Quad_ShapeID: + nedges = QuadTraits::numberOfEdges(); + break; + case Polygon_ShapeID: + nedges = m_idsView.size(); + break; + case Tet_ShapeID: + nedges = TetTraits::numberOfEdges(); + break; + case Pyramid_ShapeID: + nedges = PyramidTraits::numberOfEdges(); + break; + case Wedge_ShapeID: + nedges = WedgeTraits::numberOfEdges(); + break; + case Hex_ShapeID: + nedges = HexTraits::numberOfEdges(); + break; } return nedges; } @@ -604,9 +762,15 @@ struct VariableShape axom::StackArray edge; switch(m_shapeId) { - case Line_ShapeID: edge = LineTraits::getEdge(edgeIndex); break; - case Tri_ShapeID: edge = TriTraits::getEdge(edgeIndex); break; - case Quad_ShapeID: edge = QuadTraits::getEdge(edgeIndex); break; + case Line_ShapeID: + edge = LineTraits::getEdge(edgeIndex); + break; + case Tri_ShapeID: + edge = TriTraits::getEdge(edgeIndex); + break; + case Quad_ShapeID: + edge = QuadTraits::getEdge(edgeIndex); + break; case Polygon_ShapeID: { const auto n = m_idsView.size(); @@ -614,10 +778,18 @@ struct VariableShape edge[1] = (edgeIndex + 1) % n; break; } - case Tet_ShapeID: edge = TetTraits::getEdge(edgeIndex); break; - case Pyramid_ShapeID: edge = PyramidTraits::getEdge(edgeIndex); break; - case Wedge_ShapeID: edge = WedgeTraits::getEdge(edgeIndex); break; - case Hex_ShapeID: edge = HexTraits::getEdge(edgeIndex); break; + case Tet_ShapeID: + edge = TetTraits::getEdge(edgeIndex); + break; + case Pyramid_ShapeID: + edge = PyramidTraits::getEdge(edgeIndex); + break; + case Wedge_ShapeID: + edge = WedgeTraits::getEdge(edgeIndex); + break; + case Hex_ShapeID: + edge = HexTraits::getEdge(edgeIndex); + break; } return edge; } @@ -627,7 +799,10 @@ struct VariableShape * * \return The i'th id that makes up this shape. */ - AXOM_HOST_DEVICE ConnectivityType getId(IndexType index) const { return m_idsView[index]; } + AXOM_HOST_DEVICE ConnectivityType getId(IndexType index) const + { + return m_idsView[index]; + } /** * \brief Get the ids that make up this shape. @@ -637,6 +812,7 @@ struct VariableShape AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } AXOM_HOST_DEVICE constexpr static const char *name() { return "mixed"; } + private: int m_shapeId; ConnectivityView m_idsView; @@ -671,9 +847,8 @@ inline int shapeNameToID(const std::string &name) return id; } - -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index 103c3284e4..a91fbe804e 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -15,7 +15,6 @@ namespace mir { namespace views { - /** * \accelerated * \class StridedStructuredIndexing @@ -55,9 +54,13 @@ class StridedStructuredIndexing * \param strides The amount to stride when moving to the next element for each logical dimension. */ AXOM_HOST_DEVICE - StridedStructuredIndexing(const LogicalIndex &dims, const LogicalIndex &offsets, const LogicalIndex &strides) : m_dimensions(dims), m_offsets(offsets), m_strides(strides) - { - } + StridedStructuredIndexing(const LogicalIndex &dims, + const LogicalIndex &offsets, + const LogicalIndex &strides) + : m_dimensions(dims) + , m_offsets(offsets) + , m_strides(strides) + { } /** * \brief Return the number of values in the index space. @@ -67,10 +70,9 @@ class StridedStructuredIndexing AXOM_HOST_DEVICE IndexType size() const { - IndexType sz = 1; - for(int i = 0; i < NDIMS; i++) - sz *= m_dimensions[i]; - return sz; + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) sz *= m_dimensions[i]; + return sz; } /** @@ -87,9 +89,7 @@ class StridedStructuredIndexing * \return The j stride to move up a row. */ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims >= 2, IndexType>::type - jStride() const + AXOM_HOST_DEVICE typename std::enable_if<_ndims >= 2, IndexType>::type jStride() const { return m_strides[1]; } @@ -100,9 +100,7 @@ class StridedStructuredIndexing * \return The k stride to move forward a "page". */ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, IndexType>::type - kStride() const + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type kStride() const { return m_strides[2]; } @@ -117,8 +115,7 @@ class StridedStructuredIndexing /// @{ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 1, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -127,8 +124,7 @@ class StridedStructuredIndexing } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -138,8 +134,7 @@ class StridedStructuredIndexing } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -160,30 +155,27 @@ class StridedStructuredIndexing */ /// @{ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 1, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return ((m_offsets[0] + logical[0]) * m_strides[0]); } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return ((m_offsets[0] + logical[0]) * m_strides[0]) + - ((m_offsets[1] + logical[1]) * m_strides[1]); + ((m_offsets[1] + logical[1]) * m_strides[1]); } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return ((m_offsets[0] + logical[0]) * m_strides[0]) + - ((m_offsets[1] + logical[1]) * m_strides[1]) + - ((m_offsets[2] + logical[2]) * m_strides[2]); + ((m_offsets[1] + logical[1]) * m_strides[1]) + + ((m_offsets[2] + logical[2]) * m_strides[2]); } /// @} @@ -228,9 +220,8 @@ class StridedStructuredIndexing StridedStructuredIndexing expand() const { StridedStructuredIndexing retval(*this); - for(int i = 0; i < dimensions(); i++) - retval.m_dimensions[i]++; - + for(int i = 0; i < dimensions(); i++) retval.m_dimensions[i]++; + return retval; } @@ -242,8 +233,8 @@ class StridedStructuredIndexing /// @{ template AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 1, StridedStructuredIndexing>::type - expand() const + typename std::enable_if<_ndims == 1, StridedStructuredIndexing>::type + expand() const { StridedStructuredIndexing retval(*this); retval.m_dimensions[0]++; @@ -252,8 +243,8 @@ class StridedStructuredIndexing template AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, StridedStructuredIndexing>::type - expand() const + typename std::enable_if<_ndims == 2, StridedStructuredIndexing>::type + expand() const { StridedStructuredIndexing retval(*this); retval.m_dimensions[0]++; @@ -264,8 +255,8 @@ class StridedStructuredIndexing template AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, StridedStructuredIndexing>::type - expand() const + typename std::enable_if<_ndims == 3, StridedStructuredIndexing>::type + expand() const { StridedStructuredIndexing retval(*this); retval.m_dimensions[0]++; @@ -281,13 +272,13 @@ class StridedStructuredIndexing /// @} private: - LogicalIndex m_dimensions{}; - LogicalIndex m_offsets{}; - LogicalIndex m_strides{}; + LogicalIndex m_dimensions {}; + LogicalIndex m_offsets {}; + LogicalIndex m_strides {}; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index a6bc0b6e24..fdedab4318 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -16,7 +16,6 @@ namespace mir { namespace views { - /** * \brief This class encapsulates a structured mesh size and contains methods to * help with indexing into it. @@ -38,14 +37,10 @@ class StructuredIndexing * \param dims The dimensions we're indexing. */ AXOM_HOST_DEVICE - StructuredIndexing() : m_dimensions() - { - } + StructuredIndexing() : m_dimensions() { } AXOM_HOST_DEVICE - StructuredIndexing(const LogicalIndex &dims) : m_dimensions(dims) - { - } + StructuredIndexing(const LogicalIndex &dims) : m_dimensions(dims) { } /** * \brief Return the number of points in the coordset. @@ -55,10 +50,9 @@ class StructuredIndexing AXOM_HOST_DEVICE IndexType size() const { - IndexType sz = 1; - for(int i = 0; i < NDIMS; i++) - sz *= m_dimensions[i]; - return sz; + IndexType sz = 1; + for(int i = 0; i < NDIMS; i++) sz *= m_dimensions[i]; + return sz; } /** @@ -75,9 +69,7 @@ class StructuredIndexing * \return The j stride to move up a row. */ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims >= 2, IndexType>::type - jStride() const + AXOM_HOST_DEVICE typename std::enable_if<_ndims >= 2, IndexType>::type jStride() const { return m_dimensions[0]; } @@ -88,9 +80,7 @@ class StructuredIndexing * \return The k stride to move forward a "page". */ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, IndexType>::type - kStride() const + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type kStride() const { return m_dimensions[0] * m_dimensions[1]; } @@ -105,8 +95,7 @@ class StructuredIndexing /// @{ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 1, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -115,8 +104,7 @@ class StructuredIndexing } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -127,8 +115,7 @@ class StructuredIndexing } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, LogicalIndex>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; @@ -151,27 +138,25 @@ class StructuredIndexing */ /// @{ template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 1, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return logical[0]; } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 2, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return logical[1] * m_dimensions[0] + logical[0]; } template - AXOM_HOST_DEVICE - typename std::enable_if<_ndims == 3, IndexType>::type + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { - return (logical[2] * m_dimensions[1] * m_dimensions[0]) + (logical[1] * m_dimensions[0]) + logical[0]; + return (logical[2] * m_dimensions[1] * m_dimensions[0]) + + (logical[1] * m_dimensions[0]) + logical[0]; } /// @} @@ -216,16 +201,16 @@ class StructuredIndexing StructuredIndexing expand() const { StructuredIndexing retval(*this); - for(int i = 0; i < dimensions(); i++) - retval.m_dimensions[i]++; + for(int i = 0; i < dimensions(); i++) retval.m_dimensions[i]++; return retval; } + private: - LogicalIndex m_dimensions{}; + LogicalIndex m_dimensions {}; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index c26f201d8e..e07788ac0d 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -14,7 +14,6 @@ namespace mir { namespace views { - /** * \brief This class provides a view for Conduit/Blueprint structured grid types. * @@ -44,8 +43,7 @@ class StructuredTopologyView */ AXOM_HOST_DEVICE StructuredTopologyView(const IndexingPolicy &indexing) : m_indexing(indexing) - { - } + { } /** * \brief Return the number of zones. @@ -53,10 +51,7 @@ class StructuredTopologyView * \return The number of zones. */ AXOM_HOST_DEVICE - IndexType size() const - { - return m_indexing.size(); - } + IndexType size() const { return m_indexing.size(); } /** * \brief Return the number of zones. @@ -70,7 +65,10 @@ class StructuredTopologyView * * \return The mesh logical dimensions. */ - const LogicalIndex &logicalDimensions() const { return m_indexing.logicalDimensions(); } + const LogicalIndex &logicalDimensions() const + { + return m_indexing.logicalDimensions(); + } /** * \brief Execute a function for each zone in the mesh using axom::for_all. @@ -81,78 +79,83 @@ class StructuredTopologyView * \param func The function/lambda that will be executed for each zone in the mesh. */ template - AXOM_HOST - void for_all_zones(FuncType &&func) const + AXOM_HOST void for_all_zones(FuncType &&func) const { const auto nzones = numberOfZones(); // Q: Should we make a for_all() that iterates over multiple ranges? // Q: Should the logical index be passed to the lambda? - if constexpr (IndexingPolicy::dimensions() == 3) + if constexpr(IndexingPolicy::dimensions() == 3) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - using ShapeType = HexShape; - - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); - IndexType data[8]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - data[4] = data[0] + kp; - data[5] = data[1] + kp; - data[6] = data[2] + kp; - data[7] = data[3] + kp; - - const ShapeType shape(axom::ArrayView(data, 8)); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + using ShapeType = HexShape; + + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); + IndexType data[8]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + data[4] = data[0] + kp; + data[5] = data[1] + kp; + data[6] = data[2] + kp; + data[7] = data[3] + kp; + + const ShapeType shape(axom::ArrayView(data, 8)); + func(zoneIndex, shape); + }); } - else if constexpr (IndexingPolicy::dimensions() == 2) + else if constexpr(IndexingPolicy::dimensions() == 2) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - using ShapeType = QuadShape; - - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - IndexType data[4]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - - const ShapeType shape(axom::ArrayView(data, 4)); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + using ShapeType = QuadShape; + + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + IndexType data[4]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + + const ShapeType shape(axom::ArrayView(data, 4)); + func(zoneIndex, shape); + }); } - else if constexpr (IndexingPolicy::dimensions() == 1) + else if constexpr(IndexingPolicy::dimensions() == 1) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - using ShapeType = LineShape; + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + using ShapeType = LineShape; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - IndexType data[2]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + IndexType data[2]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; - const ShapeType shape(axom::ArrayView(data, 2)); - func(zoneIndex, shape); - }); + const ShapeType shape(axom::ArrayView(data, 2)); + func(zoneIndex, shape); + }); } } @@ -166,7 +169,8 @@ class StructuredTopologyView * \param func The function/lambda that will be executed for each zone in the mesh. */ template - void for_selected_zones(const ViewType &selectedIdsView, const FuncType &&func) const + void for_selected_zones(const ViewType &selectedIdsView, + const FuncType &&func) const { const auto nSelectedZones = selectedIdsView.size(); ViewType idsView(selectedIdsView); @@ -174,73 +178,79 @@ class StructuredTopologyView // Q: Should we make a for_all() that iterates over multiple ranges? // Q: Should the logical index be passed to the lambda? - if constexpr (IndexingPolicy::dimensions() == 3) + if constexpr(IndexingPolicy::dimensions() == 3) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) - { - using ShapeType = HexShape; - - const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); - IndexType data[8]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - data[4] = data[0] + kp; - data[5] = data[1] + kp; - data[6] = data[2] + kp; - data[7] = data[3] + kp; - - const ShapeType shape(axom::ArrayView(data, 8)); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(auto selectIndex) { + using ShapeType = HexShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + const auto kp = nodeIndexing.kStride(); + IndexType data[8]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + data[4] = data[0] + kp; + data[5] = data[1] + kp; + data[6] = data[2] + kp; + data[7] = data[3] + kp; + + const ShapeType shape(axom::ArrayView(data, 8)); + func(zoneIndex, shape); + }); } - else if constexpr (IndexingPolicy::dimensions() == 2) + else if constexpr(IndexingPolicy::dimensions() == 2) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) - { - using ShapeType = QuadShape; - - const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - IndexType data[4]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - - const ShapeType shape(axom::ArrayView(data, 4)); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(auto selectIndex) { + using ShapeType = QuadShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = nodeIndexing.jStride(); + IndexType data[4]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + + const ShapeType shape(axom::ArrayView(data, 4)); + func(zoneIndex, shape); + }); } - else if constexpr (IndexingPolicy::dimensions() == 1) + else if constexpr(IndexingPolicy::dimensions() == 1) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) - { - using ShapeType = LineShape; - - const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - IndexType data[2]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); - data[1] = data[0] + 1; - - const ShapeType shape(axom::ArrayView(data, 2)); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(auto selectIndex) { + using ShapeType = LineShape; + + const auto zoneIndex = idsView[selectIndex]; + const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + IndexType data[2]; + data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[1] = data[0] + 1; + + const ShapeType shape(axom::ArrayView(data, 2)); + func(zoneIndex, shape); + }); } } @@ -248,8 +258,8 @@ class StructuredTopologyView IndexingPolicy m_indexing; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index 4fba388a51..0e5a4fd2be 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -17,7 +17,6 @@ namespace mir { namespace views { - /** * \class This class provides a view for Conduit/Blueprint uniform coordsets. * @@ -30,7 +29,8 @@ class UniformCoordsetView { public: using LogicalIndex = axom::StackArray; - using ExtentsType = axom::StackArray;; + using ExtentsType = axom::StackArray; + ; using IndexType = axom::IndexType; using value_type = DataType; using PointType = axom::primal::Point; @@ -45,9 +45,11 @@ class UniformCoordsetView AXOM_HOST_DEVICE UniformCoordsetView(const LogicalIndex &dims, const ExtentsType &origin, - const ExtentsType &spacing) : m_indexing(dims), m_origin(origin), m_spacing(spacing) - { - } + const ExtentsType &spacing) + : m_indexing(dims) + , m_origin(origin) + , m_spacing(spacing) + { } /** * \brief Return the number of points in the coordset. @@ -56,16 +58,10 @@ class UniformCoordsetView */ /// @{ AXOM_HOST_DEVICE - IndexType size() const - { - return m_indexing.size(); - } + IndexType size() const { return m_indexing.size(); } AXOM_HOST_DEVICE - IndexType numberOfNodes() const - { - return m_indexing.size(); - } + IndexType numberOfNodes() const { return m_indexing.size(); } /// @} /** @@ -92,8 +88,7 @@ class UniformCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](const LogicalIndex &vertex_index) const + PointType operator[](const LogicalIndex &vertex_index) const { return getPoint(vertex_index); } @@ -106,19 +101,18 @@ class UniformCoordsetView * \return A point that corresponds to \a vertex_index. */ AXOM_HOST_DEVICE - PointType - operator[](IndexType vertex_index) const + PointType operator[](IndexType vertex_index) const { return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } StructuredIndexing m_indexing; - ExtentsType m_origin; - ExtentsType m_spacing; + ExtentsType m_origin; + ExtentsType m_spacing; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index a2f5f2bc6f..f1ab561ba9 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -15,7 +15,6 @@ namespace mir { namespace views { - /** * \brief Given a shape value, we can get the Shape::id() that is used internally. */ @@ -28,9 +27,7 @@ class ShapeMap /** * \brief Constructor */ - AXOM_HOST_DEVICE ShapeMap() : m_shape_values(), m_shape_ids() - { - } + AXOM_HOST_DEVICE ShapeMap() : m_shape_values(), m_shape_ids() { } /** * \brief Constructor @@ -38,27 +35,23 @@ class ShapeMap * \param shape_values A view of sorted values used in the Conduit data. * \param shape_ids A view of shape ids that correspond to the values from Conduit. */ - AXOM_HOST_DEVICE ShapeMap(const axom::ArrayView &shape_values, const axom::ArrayView &shape_ids) : m_shape_values(shape_values), m_shape_ids(shape_ids) - { - } + AXOM_HOST_DEVICE ShapeMap(const axom::ArrayView &shape_values, + const axom::ArrayView &shape_ids) + : m_shape_values(shape_values) + , m_shape_ids(shape_ids) + { } /** * \brief Return the size of the shape map. * \return The number of entries in the shape map. */ - AXOM_HOST_DEVICE IndexType size() const - { - return m_shape_values.size(); - } + AXOM_HOST_DEVICE IndexType size() const { return m_shape_values.size(); } /** * \brief Return whether the shape map is empty. * \return True if the map is empty; False otherwise. */ - AXOM_HOST_DEVICE bool empty() const - { - return m_shape_values.empty(); - } + AXOM_HOST_DEVICE bool empty() const { return m_shape_values.empty(); } /** * \brief Given a shape value (as in the Conduit shapes array), return the shape id. @@ -78,7 +71,6 @@ class ShapeMap axom::ArrayView m_shape_ids; }; - /** * \brief This class provides a view for Conduit/Blueprint mixed shape unstructured grids. * @@ -106,13 +98,18 @@ class UnstructuredTopologyMixedShapeView const ConnectivityView &conn, const ConnectivityView &shapes, const ConnectivityView &sizes, - const ConnectivityView &offsets) : - m_topo(topo), m_connectivity(conn), m_shapes(shapes), m_sizes(sizes), m_offsets(offsets) + const ConnectivityView &offsets) + : m_topo(topo) + , m_connectivity(conn) + , m_shapes(shapes) + , m_sizes(sizes) + , m_offsets(offsets) { SLIC_ASSERT(m_shapes.size() != 0); SLIC_ASSERT(m_sizes.size() != 0); SLIC_ASSERT(m_offsets.size() != 0); - SLIC_ASSERT(m_offsets.size() == m_sizes.size() && m_offsets.size() == m_shapes.size()); + SLIC_ASSERT(m_offsets.size() == m_sizes.size() && + m_offsets.size() == m_shapes.size()); } /** @@ -127,10 +124,7 @@ class UnstructuredTopologyMixedShapeView * * \return The number of zones. */ - IndexType numberOfZones() const - { - return m_sizes.size(); - } + IndexType numberOfZones() const { return m_sizes.size(); } /** * \brief Execute a function for each zone in the mesh. @@ -155,14 +149,18 @@ class UnstructuredTopologyMixedShapeView const ConnectivityView shapes(m_shapes); const ConnectivityView sizes(m_sizes); const ConnectivityView offsets(m_offsets); - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - const ConnectivityView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); - const auto shapeID = shapeMap[shapes[zoneIndex]]; - // TODO: SLIC_ASSERT(shapeID > 0); - const ShapeType shape(shapeID, shapeData); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const ConnectivityView shapeData( + connectivityView.data() + offsets[zoneIndex], + sizes[zoneIndex]); + const auto shapeID = shapeMap[shapes[zoneIndex]]; + // TODO: SLIC_ASSERT(shapeID > 0); + const ShapeType shape(shapeID, shapeData); + func(zoneIndex, shape); + }); } /** @@ -190,15 +188,19 @@ class UnstructuredTopologyMixedShapeView const ConnectivityView shapes(m_shapes); const ConnectivityView sizes(m_sizes); const ConnectivityView offsets(m_offsets); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) - { - const auto zoneIndex = selectedIdsView[selectIndex]; - const ConnectivityView shapeData(connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); - const auto shapeID = shapeMap[shapes[zoneIndex]]; - // TODO: SLIC_ASSERT(shapeID > 0); - const ShapeType shape(shapeID, shapeData); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(int selectIndex) { + const auto zoneIndex = selectedIdsView[selectIndex]; + const ConnectivityView shapeData( + connectivityView.data() + offsets[zoneIndex], + sizes[zoneIndex]); + const auto shapeID = shapeMap[shapes[zoneIndex]]; + // TODO: SLIC_ASSERT(shapeID > 0); + const ShapeType shape(shapeID, shapeData); + func(zoneIndex, shape); + }); } private: @@ -209,11 +211,14 @@ class UnstructuredTopologyMixedShapeView * \param[out] ids The Shape ids that correspond to the shape values. * \param allocatorID The allocator to use when creating the arrays. */ - void buildShapeMap(axom::Array &values, axom::Array &ids, int allocatorID) const + void buildShapeMap(axom::Array &values, + axom::Array &ids, + int allocatorID) const { // Make the map from the Conduit shape_map. Use std::map to sort the key values. std::map sm; - const conduit::Node &m_shape_map = m_topo.fetch_existing("elements/shape_map"); + const conduit::Node &m_shape_map = + m_topo.fetch_existing("elements/shape_map"); for(conduit::index_t i = 0; i < m_shape_map.number_of_children(); i++) { const auto value = static_cast(m_shape_map[i].to_int()); @@ -238,15 +243,15 @@ class UnstructuredTopologyMixedShapeView axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); } - const conduit::Node & m_topo; + const conduit::Node &m_topo; axom::ArrayView m_connectivity; axom::ArrayView m_shapes; axom::ArrayView m_sizes; axom::ArrayView m_offsets; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 44be4b6cf3..a13465f5ba 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -14,7 +14,6 @@ namespace mir { namespace views { - /** * \brief This class implements a view for Blueprint polyhedral topologies. */ @@ -33,18 +32,24 @@ class UnstructuredTopologyPolyhedralView const ConnectivityView &subelement_offsets, const ConnectivityView &element_conn, const ConnectivityView &element_sizes, - const ConnectivityView &element_offsets) : - m_subelement_conn(subelement_conn), m_subelement_sizes(subelement_sizes), m_subelement_offsets(subelement_offsets), - m_element_conn(element_conn), m_element_sizes(element_sizes), m_element_offsets(element_offsets) - { - } + const ConnectivityView &element_offsets) + : m_subelement_conn(subelement_conn) + , m_subelement_sizes(subelement_sizes) + , m_subelement_offsets(subelement_offsets) + , m_element_conn(element_conn) + , m_element_sizes(element_sizes) + , m_element_offsets(element_offsets) + { } AXOM_HOST_DEVICE - PolyhedronData(const PolyhedronData &obj) : - m_subelement_conn(obj.m_subelement_conn), m_subelement_sizes(obj.m_subelement_sizes), m_subelement_offsets(obj.m_subelement_offsets), - m_element_conn(obj.m_element_conn), m_element_sizes(obj.m_element_sizes), m_element_offsets(obj.m_element_offsets) - { - } + PolyhedronData(const PolyhedronData &obj) + : m_subelement_conn(obj.m_subelement_conn) + , m_subelement_sizes(obj.m_subelement_sizes) + , m_subelement_offsets(obj.m_subelement_offsets) + , m_element_conn(obj.m_element_conn) + , m_element_sizes(obj.m_element_sizes) + , m_element_offsets(obj.m_element_offsets) + { } ConnectivityView m_subelement_conn; ConnectivityView m_subelement_sizes; @@ -62,17 +67,18 @@ class UnstructuredTopologyPolyhedralView AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return true; } AXOM_HOST_DEVICE constexpr static int id() { return Polyhedron_ShapeID; } - AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj), m_zoneIndex(zi), m_ids() - { - } + AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) + : m_data(obj) + , m_zoneIndex(zi) + , m_ids() + { } /// This implementation does not return unique number of nodes. AXOM_HOST_DEVICE IndexType numberOfNodes() const { axom::IndexType nnodes = 0; const auto nFaces = numberOfFaces(); - for(axom::IndexType f = 0; f < nFaces; f++) - nnodes += getFace(f).size(); + for(axom::IndexType f = 0; f < nFaces; f++) nnodes += getFace(f).size(); } AXOM_HOST_DEVICE IndexType numberOfFaces() const @@ -132,18 +138,23 @@ class UnstructuredTopologyPolyhedralView AXOM_HOST_DEVICE ConnectivityView getFace(int faceIndex) const { - const ConnectivityView element_face_ids(m_data.m_element_conn.data() + m_data.m_element_offsets[m_zoneIndex], m_data.m_element_sizes[m_zoneIndex]); + const ConnectivityView element_face_ids( + m_data.m_element_conn.data() + m_data.m_element_offsets[m_zoneIndex], + m_data.m_element_sizes[m_zoneIndex]); const auto faceId = element_face_ids[faceIndex]; - return ConnectivityView(m_data.m_subelement_conn.data() + m_data.m_subelement_offsets[faceId], m_data.m_subelement_sizes[faceId]); + return ConnectivityView( + m_data.m_subelement_conn.data() + m_data.m_subelement_offsets[faceId], + m_data.m_subelement_sizes[faceId]); } private: - AXOM_HOST_DEVICE bool find(const ConnectivityView *arr, axom::IndexType n, ConnectivityView value) const + AXOM_HOST_DEVICE bool find(const ConnectivityView *arr, + axom::IndexType n, + ConnectivityView value) const { bool found = false; - for(axom::IndexType i = 0; i < n && !found; i++) - found = arr[i] == value; + for(axom::IndexType i = 0; i < n && !found; i++) found = arr[i] == value; return found; } @@ -155,21 +166,22 @@ class UnstructuredTopologyPolyhedralView using ShapeType = PolyhedronShape; - UnstructuredTopologyPolyhedralView(const axom::ArrayView &subelement_conn, - const axom::ArrayView &subelement_sizes, - const axom::ArrayView &subelement_offsets, - const axom::ArrayView &element_conn, - const axom::ArrayView &element_sizes, - const axom::ArrayView &element_offsets) : - m_data(subelement_conn, subelement_sizes, subelement_offsets, - element_conn, element_sizes, element_offsets) - { - } - - IndexType numberOfZones() const - { - return m_data.m_element_sizes.size(); - } + UnstructuredTopologyPolyhedralView( + const axom::ArrayView &subelement_conn, + const axom::ArrayView &subelement_sizes, + const axom::ArrayView &subelement_offsets, + const axom::ArrayView &element_conn, + const axom::ArrayView &element_sizes, + const axom::ArrayView &element_offsets) + : m_data(subelement_conn, + subelement_sizes, + subelement_offsets, + element_conn, + element_sizes, + element_offsets) + { } + + IndexType numberOfZones() const { return m_data.m_element_sizes.size(); } /** * \brief Return the dimension of the shape. @@ -184,11 +196,13 @@ class UnstructuredTopologyPolyhedralView const auto nzones = numberOfZones(); const PolyhedronData sd(m_data); - axom::for_all(0, nzones, AXOM_LAMBDA(int zoneIndex) - { - const PolyhedronShape shape(sd, zoneIndex); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(int zoneIndex) { + const PolyhedronShape shape(sd, zoneIndex); + func(zoneIndex, shape); + }); } template @@ -198,20 +212,22 @@ class UnstructuredTopologyPolyhedralView ViewType idsView(selectedIdsView); const PolyhedronData sd(m_data); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(int selectIndex) - { - const auto zoneIndex = idsView[selectIndex]; - const PolyhedronShape shape(sd, zoneIndex); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(int selectIndex) { + const auto zoneIndex = idsView[selectIndex]; + const PolyhedronShape shape(sd, zoneIndex); + func(zoneIndex, shape); + }); } private: PolyhedronData m_data; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index cf8d26cdc0..a6d587f75a 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -14,7 +14,6 @@ namespace mir { namespace views { - /** * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. * @@ -34,9 +33,11 @@ class UnstructuredTopologySingleShapeView * * \param conn The mesh connectivity. */ - UnstructuredTopologySingleShapeView(const ConnectivityView &conn) : m_connectivityView(conn), m_sizesView(), m_offsetsView() - { - } + UnstructuredTopologySingleShapeView(const ConnectivityView &conn) + : m_connectivityView(conn) + , m_sizesView() + , m_offsetsView() + { } /** * \brief Constructor @@ -47,7 +48,10 @@ class UnstructuredTopologySingleShapeView */ UnstructuredTopologySingleShapeView(const ConnectivityView &conn, const ConnectivityView &sizes, - const ConnectivityView &offsets) : m_connectivityView(conn), m_sizesView(sizes), m_offsetsView(offsets) + const ConnectivityView &offsets) + : m_connectivityView(conn) + , m_sizesView(sizes) + , m_offsetsView(offsets) { SLIC_ASSERT(m_sizesView.size() != 0); SLIC_ASSERT(m_offsetsView.size() != 0); @@ -68,7 +72,9 @@ class UnstructuredTopologySingleShapeView */ IndexType numberOfZones() const { - return (m_sizesView.size() != 0) ? m_sizesView.size() : (m_connectivityView.size() / ShapeType::numberOfNodes()); + return (m_sizesView.size() != 0) + ? m_sizesView.size() + : (m_connectivityView.size() / ShapeType::numberOfNodes()); } /** @@ -85,25 +91,33 @@ class UnstructuredTopologySingleShapeView const auto nzones = numberOfZones(); ConnectivityView connectivityView(m_connectivityView); - if constexpr (ShapeType::is_variable_size()) + if constexpr(ShapeType::is_variable_size()) { ConnectivityView sizesView(m_sizesView); ConnectivityView offsetsView(m_offsetsView); - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - const ConnectivityView shapeDataView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); - const ShapeType shape(shapeDataView); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const ConnectivityView shapeDataView( + connectivityView.data() + offsetsView[zoneIndex], + sizesView[zoneIndex]); + const ShapeType shape(shapeDataView); + func(zoneIndex, shape); + }); } else { - axom::for_all(0, nzones, AXOM_LAMBDA(auto zoneIndex) - { - const ConnectivityView shapeDataView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); - const ShapeType shape(shapeDataView); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const ConnectivityView shapeDataView( + connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); + const ShapeType shape(shapeDataView); + func(zoneIndex, shape); + }); } } @@ -123,27 +137,35 @@ class UnstructuredTopologySingleShapeView ConnectivityView connectivityView(m_connectivityView); const ViewType localSelectedIdsView(selectedIdsView); - if constexpr (ShapeType::is_variable_size()) + if constexpr(ShapeType::is_variable_size()) { ConnectivityView sizesView(m_sizesView); ConnectivityView offsetsView(m_offsetsView); - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) - { - const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeIdsView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); - const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(auto selectIndex) { + const auto zoneIndex = localSelectedIdsView[selectIndex]; + const ConnectivityView shapeIdsView( + connectivityView.data() + offsetsView[zoneIndex], + sizesView[zoneIndex]); + const ShapeType shape(shapeIdsView); + func(zoneIndex, shape); + }); } else { - axom::for_all(0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) - { - const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeIdsView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); - const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); - }); + axom::for_all( + 0, + nSelectedZones, + AXOM_LAMBDA(auto selectIndex) { + const auto zoneIndex = localSelectedIdsView[selectIndex]; + const ConnectivityView shapeIdsView( + connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); + const ShapeType shape(shapeIdsView); + func(zoneIndex, shape); + }); } } @@ -153,8 +175,8 @@ class UnstructuredTopologySingleShapeView ConnectivityView m_offsetsView; }; -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 1671522bc2..19a12fd99a 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -17,7 +17,6 @@ namespace mir { namespace views { - /** * \brief Given a Conduit/Blueprint coordset, create an appropriate view and * call the supplied function, passing the coordset view to it. @@ -34,13 +33,13 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) const std::string cstype = coordset["type"].as_string(); if(cstype == "uniform") { - const std::string keys[] = {"i", "j", "k"}; + const std::string keys[] = {"i", "j", "k"}; const conduit::Node &n_dims = coordset["dims"]; const conduit::index_t ndims = n_dims.number_of_children(); if(ndims == 2) { axom::StackArray dims; - axom::StackArray origin{0., 0.}, spacing{1., 1.}; + axom::StackArray origin {0., 0.}, spacing {1., 1.}; for(int i = 0; i < ndims; i++) { dims[i] = n_dims.fetch_existing(keys[i]).to_int(); @@ -56,7 +55,7 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) else if(ndims == 3) { axom::StackArray dims; - axom::StackArray origin{0., 0., 0.}, spacing{1., 1., 1.}; + axom::StackArray origin {0., 0., 0.}, spacing {1., 1., 1.}; for(int i = 0; i < ndims; i++) { dims[i] = n_dims.fetch_existing(keys[i]).to_int(); @@ -68,26 +67,36 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) UniformCoordsetView coordView(dims, origin, spacing); func(coordView); - } + } } else if(cstype == "rectilinear") { const conduit::Node &values = coordset["values"]; if(values.number_of_children() == 2) { - axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], [&](auto xView, auto yView) - { - RectilinearCoordsetView2 coordView(xView, yView); - func(coordView); - }); + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + [&](auto xView, auto yView) { + RectilinearCoordsetView2 coordView( + xView, + yView); + func(coordView); + }); } else if(values.number_of_children() == 3) { - axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], values[2], [&](auto xView, auto yView, auto zView) - { - RectilinearCoordsetView3 coordView(xView, yView, zView); - func(coordView); - }); + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + values[2], + [&](auto xView, auto yView, auto zView) { + RectilinearCoordsetView3 coordView( + xView, + yView, + zView); + func(coordView); + }); } } else if(cstype == "explicit") @@ -97,25 +106,35 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) const conduit::Node &values = coordset["values"]; if(values.number_of_children() == 2) { - axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], [&](auto xView, auto yView) - { - ExplicitCoordsetView coordView(xView, yView); - func(coordView); - }); + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + [&](auto xView, auto yView) { + ExplicitCoordsetView coordView( + xView, + yView); + func(coordView); + }); } else if(values.number_of_children() == 3) { - axom::mir::views::FloatNode_to_ArrayView_same(values[0], values[1], values[2], [&](auto xView, auto yView, auto zView) - { - ExplicitCoordsetView coordView(xView, yView, zView); - func(coordView); - }); + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + values[2], + [&](auto xView, auto yView, auto zView) { + ExplicitCoordsetView coordView( + xView, + yView, + zView); + func(coordView); + }); } } } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp index f445ee4bc0..36d7bbbc1a 100644 --- a/src/axom/mir/views/dispatch_material.hpp +++ b/src/axom/mir/views/dispatch_material.hpp @@ -17,7 +17,6 @@ namespace mir { namespace views { - /** * \brief Dispatch a Conduit node containing a matset to a function as the appropriate type of matset view. * @@ -27,26 +26,27 @@ namespace views * \param func The function/lambda that will operate on the matset view. */ template -void -dispatch_material(const conduit::Node &matset, FuncType &&func) +void dispatch_material(const conduit::Node &matset, FuncType &&func) { constexpr static size_t MaxMaterials = 20; if(conduit::blueprint::mesh::matset::is_uni_buffer(matset)) { - IndexNode_to_ArrayView_same(matset["material_ids"],matset["sizes"],matset["offsets"],matset["indices"], - [&](auto material_ids, auto sizes, auto offsets, auto indices) - { - FloatNode_to_ArrayView(matset["volume_fractions"], [&](auto volume_fractions) - { - using IndexType = typename decltype(material_ids)::value_type; - using FloatType = typename decltype(volume_fractions)::value_type; - - UnibufferMaterialView matsetView; - matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); - func(matsetView); - }); - }); + IndexNode_to_ArrayView_same( + matset["material_ids"], + matset["sizes"], + matset["offsets"], + matset["indices"], + [&](auto material_ids, auto sizes, auto offsets, auto indices) { + FloatNode_to_ArrayView(matset["volume_fractions"], [&](auto volume_fractions) { + using IndexType = typename decltype(material_ids)::value_type; + using FloatType = typename decltype(volume_fractions)::value_type; + + UnibufferMaterialView matsetView; + matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); + func(matsetView); + }); + }); } #if 0 else if(conduit::blueprint::mesh::matset::is_multi_buffer(matset)) @@ -142,8 +142,8 @@ dispatch_material(const conduit::Node &matset, FuncType &&func) #endif } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index d3deea1f0b..d2734cc3fd 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -18,7 +18,6 @@ namespace mir { namespace views { - /** * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. * @@ -29,41 +28,52 @@ namespace views * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. */ -template -void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) +template +void dispatch_rectilinear_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), + const conduit::Node &coordset, + FuncType &&func) { const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); switch(axes.size()) { case 3: - if constexpr (dimension_selected(SelectedDimensions, 3)) + if constexpr(dimension_selected(SelectedDimensions, 3)) { axom::StackArray zoneDims; - zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; - zoneDims[2] = coordset.fetch_existing(axes[2]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> topoView(zoneDims); + zoneDims[0] = + coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = + coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + zoneDims[2] = + coordset.fetch_existing(axes[2]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("hex"); func(shape, topoView); } break; case 2: - if constexpr (dimension_selected(SelectedDimensions, 2)) + if constexpr(dimension_selected(SelectedDimensions, 2)) { axom::StackArray zoneDims; - zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> topoView(zoneDims); + zoneDims[0] = + coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = + coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("quad"); func(shape, topoView); } break; case 1: - if constexpr (dimension_selected(SelectedDimensions, 1)) + if constexpr(dimension_selected(SelectedDimensions, 1)) { axom::StackArray zoneDims; - zoneDims[0] = coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> topoView(zoneDims); + zoneDims[0] = + coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("line"); func(shape, topoView); } @@ -73,8 +83,8 @@ void dispatch_rectilinear_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo) } } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index fc1adc844f..6afb5b6353 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -21,7 +21,6 @@ namespace mir { namespace views { - /** * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. * @@ -33,20 +32,20 @@ namespace views * \param fillValue the value to use if the array is not found. */ template -bool -fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr, int fillValue) +bool fillFromNode(const conduit::Node &n, + const std::string &key, + ArrayType &arr, + int fillValue) { bool found = false; if((found = n.has_path(key)) == true) { const auto acc = n.fetch_existing(key).as_int_accessor(); - for(int i = 0; i < arr.size(); i++) - arr[i] = acc[i]; + for(int i = 0; i < arr.size(); i++) arr[i] = acc[i]; } else { - for(int i = 0; i < arr.size(); i++) - arr[i] = fillValue; + for(int i = 0; i < arr.size(); i++) arr[i] = fillValue; } return found; } @@ -61,7 +60,7 @@ fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr, int * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) { // TODO: add strided structured variant @@ -76,7 +75,7 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) switch(ndims) { case 3: - if constexpr (dimension_selected(SelectedDimensions, 3)) + if constexpr(dimension_selected(SelectedDimensions, 3)) { const std::string shape("hex"); axom::StackArray zoneDims; @@ -93,21 +92,27 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) strides[1] = zoneDims[0]; strides[2] = zoneDims[0] * zoneDims[1]; } - - views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); - views::StructuredTopologyView> topoView(zoneIndexing); + + views::StridedStructuredIndexing zoneIndexing( + zoneDims, + offsets, + strides); + views::StructuredTopologyView< + views::StridedStructuredIndexing> + topoView(zoneIndexing); func(shape, topoView); } else { views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> topoView(zoneIndexing); + views::StructuredTopologyView> + topoView(zoneIndexing); func(shape, topoView); } } break; case 2: - if constexpr (dimension_selected(SelectedDimensions, 2)) + if constexpr(dimension_selected(SelectedDimensions, 2)) { const std::string shape("quad"); axom::StackArray zoneDims; @@ -122,21 +127,27 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) { strides[1] = zoneDims[0]; } - - views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); - views::StructuredTopologyView> topoView(zoneIndexing); + + views::StridedStructuredIndexing zoneIndexing( + zoneDims, + offsets, + strides); + views::StructuredTopologyView< + views::StridedStructuredIndexing> + topoView(zoneIndexing); func(shape, topoView); } else { views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> topoView(zoneIndexing); + views::StructuredTopologyView> + topoView(zoneIndexing); func(shape, topoView); } } break; case 1: - if constexpr (dimension_selected(SelectedDimensions, 1)) + if constexpr(dimension_selected(SelectedDimensions, 1)) { const std::string shape("line"); axom::StackArray zoneDims; @@ -148,14 +159,20 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) fillFromNode(topo, offsetsKey, offsets, 0); fillFromNode(topo, stridesKey, strides, 1); - views::StridedStructuredIndexing zoneIndexing(zoneDims, offsets, strides); - views::StructuredTopologyView> topoView(zoneIndexing); + views::StridedStructuredIndexing zoneIndexing( + zoneDims, + offsets, + strides); + views::StructuredTopologyView< + views::StridedStructuredIndexing> + topoView(zoneIndexing); func(shape, topoView); } else { views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> topoView(zoneIndexing); + views::StructuredTopologyView> + topoView(zoneIndexing); func(shape, topoView); } } @@ -173,8 +190,10 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template -void dispatch_structured_topologies(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +template +void dispatch_structured_topologies(const conduit::Node &topo, + const conduit::Node &coordset, + FuncType &&func) { const std::string type = topo["type"].as_string(); if(type == "uniform") @@ -185,8 +204,8 @@ void dispatch_structured_topologies(const conduit::Node &topo, const conduit::No dispatch_structured_topology(topo, func); } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index 7b0b99cbdc..04e9ea646b 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -20,7 +20,6 @@ namespace mir { namespace views { - /** * \brief Creates a topology view and passes that view to the supplied function. * @@ -31,8 +30,10 @@ namespace views * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template -void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) +template +void dispatch_topology(const conduit::Node &topo, + const conduit::Node &coordset, + FuncType &&func) { const auto type = topo.fetch_existing("type").as_string(); @@ -46,8 +47,8 @@ void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, dispatch_unstructured_topology(topo, func); } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index c34eea2302..a74b0bc7a8 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -16,7 +16,6 @@ namespace mir { namespace views { - /** * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. * @@ -26,42 +25,47 @@ namespace views * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. */ -template -void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), const conduit::Node &coordset, FuncType &&func) +template +void dispatch_uniform_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), + const conduit::Node &coordset, + FuncType &&func) { const conduit::Node &n_dims = coordset["dims"]; switch(n_dims.dtype().number_of_elements()) { default: case 3: - if constexpr (dimension_selected(SelectedDimensions, 3)) + if constexpr(dimension_selected(SelectedDimensions, 3)) { axom::StackArray zoneDims; zoneDims[0] = n_dims.as_int_accessor()[0] - 1; zoneDims[1] = n_dims.as_int_accessor()[1] - 1; zoneDims[2] = n_dims.as_int_accessor()[2] - 1; - views::StructuredTopologyView> topoView(zoneDims); + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("hex"); func(shape, topoView); } break; case 2: - if constexpr (dimension_selected(SelectedDimensions, 2)) + if constexpr(dimension_selected(SelectedDimensions, 2)) { axom::StackArray zoneDims; zoneDims[0] = n_dims.as_int_accessor()[0] - 1; zoneDims[1] = n_dims.as_int_accessor()[1] - 1; - views::StructuredTopologyView> topoView(zoneDims); + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("quad"); func(shape, topoView); } break; case 1: - if constexpr (dimension_selected(SelectedDimensions, 3)) + if constexpr(dimension_selected(SelectedDimensions, 3)) { axom::StackArray zoneDims; zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - views::StructuredTopologyView> topoView(zoneDims); + views::StructuredTopologyView> + topoView(zoneDims); const std::string shape("line"); func(shape, topoView); } @@ -69,8 +73,8 @@ void dispatch_uniform_topology(const conduit::Node & AXOM_UNUSED_PARAM(topo), co } } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 095f895880..d3dd0f09d2 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -21,7 +21,6 @@ namespace mir { namespace views { - // Turn on all bits so all shapes will be enabled. constexpr int AnyShape = -1; @@ -34,20 +33,34 @@ constexpr int AnyShape = -1; * \param func The function/lambda to call with the topology view. */ template -void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncType &&func) +void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, + FuncType &&func) { const std::string shape = topo["elements/shape"].as_string(); if(shape == "polyhedral") { IndexNode_to_ArrayView_same( - topo["subelements/connectivity"], topo["subelements/sizes"], topo["subelements/offsets"], - topo["elements/connectivity"], topo["elements/sizes"], topo["elements/offsets"], - [&](auto seConnView, auto seSizesView, auto seOffsetsView, auto connView, auto sizesView, auto offsetsView) - { - using ConnType = typename decltype(seConnView)::value_type; - UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, seOffsetsView, connView, sizesView, offsetsView); - func(shape, ugView); - }); + topo["subelements/connectivity"], + topo["subelements/sizes"], + topo["subelements/offsets"], + topo["elements/connectivity"], + topo["elements/sizes"], + topo["elements/offsets"], + [&](auto seConnView, + auto seSizesView, + auto seOffsetsView, + auto connView, + auto sizesView, + auto offsetsView) { + using ConnType = typename decltype(seConnView)::value_type; + UnstructuredTopologyPolyhedralView ugView(seConnView, + seSizesView, + seOffsetsView, + connView, + sizesView, + offsetsView); + func(shape, ugView); + }); } } @@ -64,20 +77,27 @@ void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncTy * later in the for_all_zones method. */ template -void dispatch_unstructured_mixed_topology(const conduit::Node &topo, FuncType &&func) +void dispatch_unstructured_mixed_topology(const conduit::Node &topo, + FuncType &&func) { const std::string shape = topo["elements/shape"].as_string(); if(shape == "mixed") { IndexNode_to_ArrayView_same( - topo["elements/connectivity"], topo["elements/shapes"], topo["elements/sizes"], topo["elements/offsets"], - [&](auto connView, auto shapesView, auto sizesView, auto offsetsView) - { - using ConnType = typename decltype(connView)::value_type; + topo["elements/connectivity"], + topo["elements/shapes"], + topo["elements/sizes"], + topo["elements/offsets"], + [&](auto connView, auto shapesView, auto sizesView, auto offsetsView) { + using ConnType = typename decltype(connView)::value_type; - UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, offsetsView); - func(shape, ugView); - }); + UnstructuredTopologyMixedShapeView ugView(topo, + connView, + shapesView, + sizesView, + offsetsView); + func(shape, ugView); + }); } } @@ -97,18 +117,18 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) const std::string type = topo["type"].as_string(); if(type == "unstructured") { - const std::string shape = topo["elements/shape"].as_string(); - bool eligible = true; + const std::string shape = topo["elements/shape"].as_string(); + bool eligible = true; - // Conditionally add polyhedron support. - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Polyhedron_ShapeID)) + // Conditionally add polyhedron support. + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Polyhedron_ShapeID)) + { + if(shape == "polyhedral") { - if(shape == "polyhedral") - { - dispatch_unstructured_polyhedral_topology(topo, func); - eligible = false; - } + dispatch_unstructured_polyhedral_topology(topo, func); + eligible = false; } + } #if 0 // TODO: Can't use polygon with single shape view because its sizes are not known at compile time. @@ -128,80 +148,81 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) } } #endif - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Mixed_ShapeID)) + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Mixed_ShapeID)) + { + if(eligible && shape == "mixed") { - if(eligible && shape == "mixed") - { - dispatch_unstructured_mixed_topology(topo, func); - eligible = false; - } + dispatch_unstructured_mixed_topology(topo, func); + eligible = false; } + } - IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) + IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) { + using ConnType = typename decltype(connView)::value_type; + // TODO: points, lines + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) { - using ConnType = typename decltype(connView)::value_type; - // TODO: points, lines - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) + if(eligible && shape == "tet") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) + { + if(eligible && shape == "tet") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) + { + if(eligible && shape == "tet") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) + { + if(eligible && shape == "pyramid") { - if(eligible && shape == "pyramid") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) + { + if(eligible && shape == "wedge") { - if(eligible && shape == "wedge") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) + { + if(eligible && shape == "hex") { - if(eligible && shape == "hex") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - - }); + } + }); } } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/views/dispatch_utilities.hpp b/src/axom/mir/views/dispatch_utilities.hpp index e5d0498b7c..1baeb3a6fa 100644 --- a/src/axom/mir/views/dispatch_utilities.hpp +++ b/src/axom/mir/views/dispatch_utilities.hpp @@ -12,14 +12,13 @@ namespace mir { namespace views { - -template +template constexpr int encode_dimensions(Dimensions... dims) { return (... | dims); } -template +template constexpr int select_dimensions(Dimensions... dims) { return encode_dimensions((1 << dims)...); @@ -30,8 +29,8 @@ constexpr bool dimension_selected(int encoded_dims, int dim) return encoded_dims & (1 << dim); } -} // end namespace views -} // end namespace mir -} // end namespace axom +} // end namespace views +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/slam/Map.hpp b/src/axom/slam/Map.hpp index 0901b1a198..536db5ec81 100644 --- a/src/axom/slam/Map.hpp +++ b/src/axom/slam/Map.hpp @@ -846,11 +846,9 @@ bool Map::isValid(bool verboseOutput) const std::stringstream sstr; sstr << "\n*** Detailed results of isValid on the map.\n"; - sstr << "Map was NOT valid.\n" - << sstr.str() - << std::endl; + sstr << "Map was NOT valid.\n" << sstr.str() << std::endl; - SLIC_DEBUG( sstr.str() ); + SLIC_DEBUG(sstr.str()); } return bValid; @@ -865,7 +863,7 @@ void Map::print() const { std::stringstream sstr; - if (!m_set.get()) + if(!m_set.get()) { sstr << "** map is empty."; } @@ -886,13 +884,12 @@ void Map::print() const } } - SLIC_INFO( sstr.str() ); + SLIC_INFO(sstr.str()); } else { - SLIC_INFO("Map was not valid."); + SLIC_INFO("Map was not valid."); } - } } // end namespace slam From e5dd66d3f7ce3d8d708fb96265f20e61ac46227d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 10:38:14 -0700 Subject: [PATCH 128/290] Move some name generation to NamingPolicy. --- src/axom/mir/ClipField.hpp | 110 ++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 20eaec804a..43d61e03a1 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -26,10 +26,10 @@ // so we can write serial versions for when RAJA is not enabled. namespace axom { -template +template struct scans { - inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) + inline void exclusive_scan(const ContiguousMemoryContainer &input, ContiguousMemoryContainer &output) { using loop_policy = typename axom::execution_space::loop_policy; assert(input.size() == output.size()); @@ -39,10 +39,10 @@ struct scans } }; -template -inline void exclusive_scan(const ArrayViewType &input, ArrayViewType &output) +template +inline void exclusive_scan(const ContiguousMemoryContainer &input, ContiguousMemoryContainer &output) { - scans s; + scans s; s.exclusive_scan(input, output); } @@ -89,11 +89,11 @@ std::map shapeMap_ValueName(const conduit::Node &n_shape_map) } /** - * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate Shape::id() value. + * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate ShapeID value. * - * \param st_index The value we want to translate into a Shape::id() value. + * \param st_index The value we want to translate into a ShapeID value. * - * \return The Shape::id() value that matches the st_index, or 0 if there is no match. + * \return The ShapeID value that matches the st_index, or 0 if there is no match. */ template AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) @@ -129,9 +129,9 @@ AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) /** * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. * - * \param shapes This is a bitwise-or of various Shape::id() values. + * \param shapes This is a bitwise-or of various (1 << ShapeID) values. * - * \return A map of Conduit shape name to Shape::id() value. + * \return A map of Conduit shape name to ShapeID value. */ std::map shapeMap_FromFlags(std::uint64_t shapes) { @@ -485,37 +485,82 @@ class ClipOptions axom::Array m_selectedZones; // Storage for a list of selected zone ids. }; +//------------------------------------------------------------------------------ +/** + * \brief This class implements a naming policy that uses some hashing functions + * to produce a "name" for an array of ids. + */ +class HashNaming +{ +public: + using KeyType = std::uint64_t; + + /** + * \brief Make a name from an array of ids. + * + * \param ids The array of ids. + * \param numIds The number of ids in the array. + * + * \return The name that describes the array of ids. + * + * \note Different make_name_* functions are used because we can skip most + * sorting for 1,2 element arrays. + */ + AXOM_HOST_DEVICE + inline static KeyType makeName(const IndexType *ids, IndexType numIds) + { + KeyType name{}; + if(numIds == 1) + { + name = axom::mir::utilities::make_name_1(ids[0]); + } + else if(numIds == 2) + { + name = axom::mir::utilities::make_name_2(ids[0], ids[1]); + } + else + { + name = axom::mir::utilities::make_name_n(ids, numIds); + } + return name; + } +}; + //------------------------------------------------------------------------------ /** * \brief This class encapsulates most of the logic for building blend groups. * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam NamingPolicy The naming policy used to create names from node ids. + * * \note The class only contains views to data so it can be copied into lambdas. */ -template +template class BlendGroupBuilder { public: - using KeyType = std::uint64_t; + using KeyType = typename NamingPolicy::KeyType; /** * \brief This struct holds the views that represent data for blend groups. */ struct State { - IndexType m_nzones; // clang-format off + IndexType m_nzones; + axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. - axom::ArrayView m_blendNamesView; // Blend group names + axom::ArrayView m_blendNamesView; // Blend group names axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. axom::ArrayView m_blendIdsView; // blend group ids. - axom::ArrayView m_blendCoeffView; // blend group weights. + axom::ArrayView m_blendCoeffView; // blend group weights. - axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. + axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. // clang-format on }; @@ -692,24 +737,10 @@ class BlendGroupBuilder m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; // Store "name" of blend group. - KeyType blendName; - if(numIds == 1) - { - blendName = axom::mir::utilities::make_name_1( - m_state->m_blendIdsView[m_startOffset]); - } - else if(numIds == 2) - { - blendName = axom::mir::utilities::make_name_2( - m_state->m_blendIdsView[m_startOffset], - m_state->m_blendIdsView[m_startOffset + 1]); - } - else - { - blendName = axom::mir::utilities::make_name_n( + KeyType blendName = NamingPolicy::makeName( m_state->m_blendIdsView.data() + m_startOffset, numIds); - } + m_state->m_blendNamesView[m_blendGroupId] = blendName; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) const float w = weightSum(); @@ -894,6 +925,7 @@ class BlendGroupBuilder private: State m_state; }; + //------------------------------------------------------------------------------ /** @@ -903,8 +935,9 @@ class BlendGroupBuilder * \tparam ExecSpace The execution space where the compute-heavy kernels run. * \tparam TopologyView The topology view that can operate on the Blueprint topology. * \tparam CoordsetView The coordset view that can operate on the Blueprint coordset. + * \tparam NamingPolicy The policy for making names from arrays of ids. */ -template +template class ClipField { public: @@ -912,11 +945,12 @@ class ClipField using SliceData = axom::mir::utilities::blueprint::SliceData; using ClipTableViews = axom::StackArray; - using KeyType = std::uint64_t; using BitSet = std::uint64_t; + using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; using ConnectivityType = typename TopologyView::ConnectivityType; + using BlendGroupBuilderType = BlendGroupBuilder; /** * \brief Constructor @@ -1048,7 +1082,7 @@ class ClipField allocatorID); // Start of zone's blend group offsets in definitions. // Make an object to help manage building the blend groups. - BlendGroupBuilder builder; + BlendGroupBuilderType builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets @@ -1208,7 +1242,7 @@ class ClipField * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void computeSizes(ClipTableViews clipTableViews, - BlendGroupBuilder builder, + BlendGroupBuilderType builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, @@ -1382,7 +1416,7 @@ class ClipField * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeBlendGroups(ClipTableViews clipTableViews, - BlendGroupBuilder builder, + BlendGroupBuilderType builder, ZoneData zoneData, ClipOptions &opts, const conduit::Node &n_clip_field_values) const @@ -1505,7 +1539,7 @@ class ClipField * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeConnectivity(ClipTableViews clipTableViews, - BlendGroupBuilder builder, + BlendGroupBuilderType builder, ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, From 7ece514aa80618c33480b2dde83e14a9d626b457 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 10:39:23 -0700 Subject: [PATCH 129/290] make style --- src/axom/mir/ClipField.hpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 43d61e03a1..319a9d93bd 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -29,7 +29,8 @@ namespace axom template struct scans { - inline void exclusive_scan(const ContiguousMemoryContainer &input, ContiguousMemoryContainer &output) + inline void exclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) { using loop_policy = typename axom::execution_space::loop_policy; assert(input.size() == output.size()); @@ -40,7 +41,8 @@ struct scans }; template -inline void exclusive_scan(const ContiguousMemoryContainer &input, ContiguousMemoryContainer &output) +inline void exclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) { scans s; s.exclusive_scan(input, output); @@ -509,7 +511,7 @@ class HashNaming AXOM_HOST_DEVICE inline static KeyType makeName(const IndexType *ids, IndexType numIds) { - KeyType name{}; + KeyType name {}; if(numIds == 1) { name = axom::mir::utilities::make_name_1(ids[0]); @@ -737,9 +739,9 @@ class BlendGroupBuilder m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; // Store "name" of blend group. - KeyType blendName = NamingPolicy::makeName( - m_state->m_blendIdsView.data() + m_startOffset, - numIds); + KeyType blendName = + NamingPolicy::makeName(m_state->m_blendIdsView.data() + m_startOffset, + numIds); m_state->m_blendNamesView[m_blendGroupId] = blendName; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) @@ -937,7 +939,10 @@ class BlendGroupBuilder * \tparam CoordsetView The coordset view that can operate on the Blueprint coordset. * \tparam NamingPolicy The policy for making names from arrays of ids. */ -template +template class ClipField { public: From d85270f339bc467aab935fb78990c2e695e311b8 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 12:27:49 -0700 Subject: [PATCH 130/290] A test for a mixed mesh --- src/axom/mir/tests/mir_clipfield.cpp | 231 ++++++++++++++++-- .../UnstructuredTopologyMixedShapeView.hpp | 8 +- .../UnstructuredTopologyPolyhedralView.hpp | 6 +- 3 files changed, 223 insertions(+), 22 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index dcd5aee870..3266a3162a 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -38,15 +38,9 @@ #endif // clang-format on -template -void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) +void add_distance(conduit::Node &mesh, float dist = 6.5f) { - int d[3] = {0, 0, 0}; - for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; - conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); - // Make a new distance field. - const float dist = 6.5f; const conduit::Node &n_coordset = mesh["coordsets"][0]; axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { mesh["fields/distance/topology"] = "mesh"; @@ -65,6 +59,89 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) }); } + +template +void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) +{ + int d[3] = {0, 0, 0}; + for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; + conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); + + add_distance(mesh); +} + +void mixed3d(conduit::Node &mesh) +{ + // clang-format off + const std::vector conn{{ + // tets + 0,6,1,3, + 3,6,1,9, + 6,7,1,9, + 3,9,1,4, + 9,7,1,4, + 9,7,4,10, + // pyramids + 1,7,8,2,4, + 11,8,7,10,4, + 2,8,11,5,4, + // wedges + 6,7,9,12,13,15, + 9,7,10,15,13,16, + // hex + 7,13,14,8,10,16,17,11 + }}; + const std::vector shapes{{ + 0,0,0,0,0,0, + 1,1,1, + 2,2, + 3 + }}; + const std::vector sizes{{ + 4,4,4,4,4,4, + 5,5,5, + 6,6, + 8 + }}; + const std::vector offsets{{ + 0,4,8,12,16,20, + 24,29,34, + 39,45, + 51 + }}; + constexpr float LOW = -10.f; + constexpr float MID = 0.f; + constexpr float HIGH = 10.f; + const std::vector x{{ + LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH + }}; + const std::vector y{{ + LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID + }}; + const std::vector z{{ + LOW, LOW, LOW, LOW, LOW, LOW, MID, MID, MID, MID, MID, MID, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH + }}; + // clang-format off + + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(x); + mesh["coordsets/coords/values/y"].set(y); + mesh["coordsets/coords/values/z"].set(z); + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "mixed"; + mesh["topologies/mesh/elements/connectivity"].set(conn); + mesh["topologies/mesh/elements/shapes"].set(shapes); + mesh["topologies/mesh/elements/sizes"].set(sizes); + mesh["topologies/mesh/elements/offsets"].set(offsets); + mesh["topologies/mesh/elements/shape_map/tet"] = 0; + mesh["topologies/mesh/elements/shape_map/pyramid"] = 1; + mesh["topologies/mesh/elements/shape_map/wedge"] = 2; + mesh["topologies/mesh/elements/shape_map/hex"] = 3; + + add_distance(mesh, 0.f); +} + int conduit_to_vtk_cell(int shape_value) { int vtktype = 0; @@ -104,14 +181,17 @@ void conduit_save_vtk(const conduit::Node &node, const std::string &path) size_t num_points = x.dtype().number_of_elements(); fprintf(file, "POINTS %zu float\n", num_points); - for(size_t i = 0; i < num_points; ++i) + axom::mir::views::FloatNode_to_ArrayView(x, y, z, [&](auto xView, auto yView, auto zView) { - fprintf(file, - "%f %f %f\n", - x.as_float64_array()[i], - y.as_float64_array()[i], - z.as_float64_array()[i]); - } + for(size_t i = 0; i < num_points; ++i) + { + fprintf(file, + "%f %f %f\n", + static_cast(xView[i]), + static_cast(yView[i]), + static_cast(zView[i])); + } + }); // Write the cells const conduit::Node &topologies = node["topologies"]; @@ -135,12 +215,23 @@ void conduit_save_vtk(const conduit::Node &node, const std::string &path) } // Write the cell types - const conduit::Node &shapes = elements["shapes"]; fprintf(file, "CELL_TYPES %zu\n", num_cells); - for(size_t i = 0; i < num_cells; ++i) + if(elements.has_child("shapes")) { - const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); - fprintf(file, "%d\n", type); + const conduit::Node &shapes = elements["shapes"]; + for(size_t i = 0; i < num_cells; ++i) + { + const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); + fprintf(file, "%d\n", type); + } + } + else + { + const auto type = conduit_to_vtk_cell(axom::mir::views::shapeNameToID(elements["shape"].as_string())); + for(size_t i = 0; i < num_cells; ++i) + { + fprintf(file, "%d\n", type); + } } // Close the file @@ -901,15 +992,117 @@ TEST(mir_clipfield, hex) braid3d_clip_test_exec>("hexs", "hex"); } +template +void braid3d_mixed_clip_test(const std::string &name) +{ + using CoordType = float; + using ConnType = int; + using TopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + // Create the data + conduit::Node hostMesh, deviceMesh; + mixed3d(hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostMesh, + name + "_orig_yaml", + "yaml"); + + // Create views + conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); + conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); + conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); + const axom::ArrayView x(static_cast(n_x.data_ptr()), + n_x.dtype().number_of_elements()); + const axom::ArrayView y(static_cast(n_y.data_ptr()), + n_y.dtype().number_of_elements()); + const axom::ArrayView z(static_cast(n_z.data_ptr()), + n_z.dtype().number_of_elements()); + CoordsetView coordsetView(x, y, z); + + conduit::Node &n_device_topo = deviceMesh.fetch_existing("topologies/mesh"); + conduit::Node &n_conn = n_device_topo.fetch_existing("elements/connectivity"); + conduit::Node &n_shapes = n_device_topo.fetch_existing("elements/shapes"); + conduit::Node &n_sizes = n_device_topo.fetch_existing("elements/sizes"); + conduit::Node &n_offsets = n_device_topo.fetch_existing("elements/offsets"); + axom::ArrayView connView( + static_cast(n_conn.data_ptr()), + n_conn.dtype().number_of_elements()); + axom::ArrayView shapesView( + static_cast(n_shapes.data_ptr()), + n_shapes.dtype().number_of_elements()); + axom::ArrayView sizesView( + static_cast(n_sizes.data_ptr()), + n_sizes.dtype().number_of_elements()); + axom::ArrayView offsetsView( + static_cast(n_offsets.data_ptr()), + n_offsets.dtype().number_of_elements()); + TopoView topoView(n_device_topo, + connView, + shapesView, + sizesView, + offsetsView); + + // Create options to control the clipping. + conduit::Node options; + options["clipField"] = "distance"; + options["clipValue"] = 12.f; + options["inside"] = 1; + options["outside"] = 0; + + // Clip the data + conduit::Node deviceClipMesh; + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Save data. + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); + conduit_save_vtk(hostClipMesh, name + ".vtk"); +} + +TEST(mir_clipfield, mixed) +{ + const std::string name("mixed"); + braid3d_mixed_clip_test(name); + +#if defined(AXOM_USE_OPENMP) + braid3d_mixed_clip_test(name + "_omp"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid3d_mixed_clip_test(name + "_cuda"); +#endif + +#if defined(AXOM_USE_HIP) + braid3d_mixed_clip_test(name + "_hip"); +#endif +} + //------------------------------------------------------------------------------ +void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) +{ + std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; + // This is on purpose. + while (1) + ; +} +//------------------------------------------------------------------------------ int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); axom::slic::SimpleLogger logger; // create & initialize test logger, - + conduit::utils::set_error_handler(conduit_debug_err_handler); result = RUN_ALL_TESTS(); return result; } diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index f1ab561ba9..c99b20844a 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -76,6 +76,8 @@ class ShapeMap * * \tparam IndexT The index type that will be used for connectivity, etc. * \tparam ShapeT The shape type. + * + * \note This view does not support topologies that also contain polyhedral elements. */ template class UnstructuredTopologyMixedShapeView @@ -83,7 +85,7 @@ class UnstructuredTopologyMixedShapeView public: using ConnectivityType = ConnT; using ConnectivityView = axom::ArrayView; - using ShapeType = VariableShape; + using ShapeType = VariableShape; /** * \brief Constructor @@ -184,15 +186,17 @@ class UnstructuredTopologyMixedShapeView buildShapeMap(values, ids, allocatorID); const ShapeMap shapeMap(values.view(), ids.view()); + // Make views that can be captured. const ConnectivityView connectivityView(m_connectivity); const ConnectivityView shapes(m_shapes); const ConnectivityView sizes(m_sizes); const ConnectivityView offsets(m_offsets); + const ViewType deviceSelectedIdsView(selectedIdsView); axom::for_all( 0, nSelectedZones, AXOM_LAMBDA(int selectIndex) { - const auto zoneIndex = selectedIdsView[selectIndex]; + const auto zoneIndex = deviceSelectedIdsView[selectIndex]; const ConnectivityView shapeData( connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index a13465f5ba..b1cb5efc46 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -78,7 +78,11 @@ class UnstructuredTopologyPolyhedralView { axom::IndexType nnodes = 0; const auto nFaces = numberOfFaces(); - for(axom::IndexType f = 0; f < nFaces; f++) nnodes += getFace(f).size(); + for(axom::IndexType f = 0; f < nFaces; f++) + { + nnodes += getFace(f).size(); + } + return nnodes; } AXOM_HOST_DEVICE IndexType numberOfFaces() const From b3ce4e0f25b105a2f9057ab5f7806472bfa608c9 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 12:28:32 -0700 Subject: [PATCH 131/290] make style --- src/axom/mir/tests/mir_clipfield.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 3266a3162a..552987deb4 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -59,7 +59,6 @@ void add_distance(conduit::Node &mesh, float dist = 6.5f) }); } - template void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) { From d9150f36da6cf2db0efd1e26d7b2cf7df51ae3c2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 15:39:06 -0700 Subject: [PATCH 132/290] Moved methods for better linking --- src/axom/mir/ClipField.hpp | 121 +++++++++++++++---------------------- 1 file changed, 48 insertions(+), 73 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 319a9d93bd..558ddf7422 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -58,37 +58,6 @@ namespace clipping { namespace details { -/** - * \brief Turns a Conduit "shape_map" node into an easier STL-representation. - * - * \param n_shape_map The node that contains the shape_map. - * \return An STL-representation of the shape_map. - */ -std::map shapeMap_NameValue(const conduit::Node &n_shape_map) -{ - std::map sm; - for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) - { - sm[n_shape_map[i].name()] = n_shape_map[i].to_int(); - } - return sm; -} - -/** - * \brief Turns a Conduit "shape_map" node into an easier STL-representation. - * - * \param n_shape_map The node that contains the shape_map. - * \return An STL-representation of the shape_map. - */ -std::map shapeMap_ValueName(const conduit::Node &n_shape_map) -{ - std::map sm; - for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) - { - sm[n_shape_map[i].to_int()] = n_shape_map[i].name(); - } - return sm; -} /** * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate ShapeID value. @@ -98,7 +67,7 @@ std::map shapeMap_ValueName(const conduit::Node &n_shape_map) * \return The ShapeID value that matches the st_index, or 0 if there is no match. */ template -AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) +inline AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) { int shapeID = 0; switch(st_index) @@ -128,48 +97,13 @@ AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) return shapeID; } -/** - * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. - * - * \param shapes This is a bitwise-or of various (1 << ShapeID) values. - * - * \return A map of Conduit shape name to ShapeID value. - */ -std::map shapeMap_FromFlags(std::uint64_t shapes) -{ - std::map sm; - - if(axom::utilities::bitIsSet(shapes, views::Line_ShapeID)) - sm["line"] = views::Line_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Tri_ShapeID)) - sm["tri"] = views::Tri_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Quad_ShapeID)) - sm["quad"] = views::Quad_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Tet_ShapeID)) - sm["tet"] = views::Tet_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Pyramid_ShapeID)) - sm["pyramid"] = views::Pyramid_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Wedge_ShapeID)) - sm["wedge"] = views::Wedge_ShapeID; - - if(axom::utilities::bitIsSet(shapes, views::Hex_ShapeID)) - sm["hex"] = views::Hex_ShapeID; - - return sm; -} - /** * \brief Returns a clip table index for the input shapeId. * \param shapeId A shapeID (e.g. Tet_ShapeID) * \return The clip table index for the shape. */ AXOM_HOST_DEVICE -int getClipTableIndex(int shapeId) +inline int getClipTableIndex(int shapeId) { int index = 0; switch(shapeId) @@ -197,26 +131,26 @@ int getClipTableIndex(int shapeId) } AXOM_HOST_DEVICE -bool color0Selected(int selection) +inline bool color0Selected(int selection) { return axom::utilities::bitIsSet(selection, 0); } AXOM_HOST_DEVICE -bool color1Selected(int selection) +inline bool color1Selected(int selection) { return axom::utilities::bitIsSet(selection, 1); } AXOM_HOST_DEVICE -bool generatedPointIsSelected(unsigned char color, int selection) +inline bool generatedPointIsSelected(unsigned char color, int selection) { return color == NOCOLOR || (color0Selected(selection) && color == COLOR0) || (color1Selected(selection) && color == COLOR1); } AXOM_HOST_DEVICE -bool shapeIsSelected(unsigned char color, int selection) +inline bool shapeIsSelected(unsigned char color, int selection) { return (color0Selected(selection) && color == COLOR0) || (color1Selected(selection) && color == COLOR1); @@ -1703,7 +1637,7 @@ class ClipField // Add shape information to the connectivity. const auto shapesUsed = shapesUsed_reduce.get(); - const auto shapeMap = details::shapeMap_FromFlags(shapesUsed); + const auto shapeMap = shapeMap_FromFlags(shapesUsed); if(axom::utilities::countBits(shapesUsed) > 1) { n_newTopo["elements/shape"] = "mixed"; @@ -1853,6 +1787,47 @@ class ClipField } } + /** + * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. + * + * \param shapes This is a bitwise-or of various (1 << ShapeID) values. + * + * \return A map of Conduit shape name to ShapeID value. + */ + std::map shapeMap_FromFlags(std::uint64_t shapes) const + { + std::map sm; + + if(axom::utilities::bitIsSet(shapes, views::Line_ShapeID)) + sm["line"] = views::Line_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Tri_ShapeID)) + sm["tri"] = views::Tri_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Quad_ShapeID)) + sm["quad"] = views::Quad_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Polygon_ShapeID)) + sm["polygon"] = views::Polygon_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Tet_ShapeID)) + sm["tet"] = views::Tet_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Pyramid_ShapeID)) + sm["pyramid"] = views::Pyramid_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Wedge_ShapeID)) + sm["wedge"] = views::Wedge_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Hex_ShapeID)) + sm["hex"] = views::Hex_ShapeID; + + if(axom::utilities::bitIsSet(shapes, views::Polyhedron_ShapeID)) + sm["polyhedron"] = views::Polyhedron_ShapeID; + + return sm; + } + private: TopologyView m_topologyView; CoordsetView m_coordsetView; From 7b981ce63e208554e17238ed6dfcfde07c2b109d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 15:39:18 -0700 Subject: [PATCH 133/290] Better VTK file saving --- src/axom/mir/blueprint_utilities.cpp | 167 ++++++++++++++++++++++----- src/axom/mir/blueprint_utilities.hpp | 11 ++ 2 files changed, 150 insertions(+), 28 deletions(-) diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index 65a5a33688..17a4e244e6 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -3,7 +3,10 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include +#include namespace axom { @@ -13,45 +16,153 @@ namespace utilities { namespace blueprint { -#if 0 -conduit::index_t ConduitAllocateThroughAxom::conduitAllocatorID = -1; -int ConduitAllocateThroughAxom::axomAllocatorID = 0; -ConduitAllocateThroughAxom::ConduitAllocateThroughAxom(int _allocatorID) +/** + * \brief Turns a ShapeID to a VTK cell type value. + * + * \param shape_value A ShapeID. + * \return A corresponding VTK cell type. + */ +static int ShapeID_to_vtk_cell(int shape_value) { - // Stash the Axom allocatorID so we don't just use the default later. - axomAllocatorID = _allocatorID; + int vtktype = 0; + if(shape_value == axom::mir::views::Tri_ShapeID) + vtktype = 5; // VTK_TRIANGLE + else if(shape_value == axom::mir::views::Quad_ShapeID) + vtktype = 9; // VTK_QUAD + else if(shape_value == axom::mir::views::Polygon_ShapeID) + vtktype = 7; // VTK_POLYGON + else if(shape_value == axom::mir::views::Tet_ShapeID) + vtktype = 10; // VTK_TETRA + else if(shape_value == axom::mir::views::Pyramid_ShapeID) + vtktype = 14; // VTK_PYRAMID + else if(shape_value == axom::mir::views::Wedge_ShapeID) + vtktype = 13; // VTK_WEDGE + else if(shape_value == axom::mir::views::Hex_ShapeID) + vtktype = 12; // VTK_HEXAHEDRON + else if(shape_value == axom::mir::views::Polyhedron_ShapeID) + vtktype = 42; // VTK_POLYHEDRON - // Register internal functions here as Conduit allocator functions. This returns - // the conduitAllocatorID, which is the value that we must pass to Node::set_allocator. - if(conduitAllocatorID == -1) - { - conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); - } + return vtktype; } -ConduitAllocateThroughAxom::~ConduitAllocateThroughAxom() +static void save_unstructured_vtk(const conduit::Node &mesh, const std::string &path) { -} + FILE *file = fopen(path.c_str(), "wt"); + if(file == nullptr) + { + SLIC_ERROR(fmt::format("The file {} could not be created.", path)); + return; + } -conduit::index_t ConduitAllocateThroughAxom::getConduitAllocatorID() const -{ - return conduitAllocatorID; -} + // Write the VTK file header + fprintf(file, "# vtk DataFile Version 3.0\n"); + fprintf(file, "Unstructured Grid Example\n"); + fprintf(file, "ASCII\n"); + fprintf(file, "DATASET UNSTRUCTURED_GRID\n"); -void *ConduitAllocateThroughAxom::internal_allocate(size_t items, size_t item_size) -{ - void *ptr = static_cast(axom::allocate(items * item_size, axomAllocatorID)); - //std::cout << "Allocating for Conduit via axom: items=" << items << ", item_size=" << item_size << ", ptr=" << ptr << std::endl; - return ptr; + // Write the points + const conduit::Node &coordset = mesh["coordsets"][0]; + const conduit::Node &points = coordset["values"]; + const auto x = points["x"].as_double_accessor(); + const auto y = points["y"].as_double_accessor(); + size_t num_points = 0; + views::dispatch_coordset(coordset, [&](auto coordsetView) + { + num_points = coordsetView.size(); + fprintf(file, "POINTS %zu float\n", num_points); + + if(coordsetView.dimension() == 3) + { + const auto z = points["z"].as_double_accessor(); + for(size_t i = 0; i < num_points; ++i) + { + const auto p = coordsetView[i]; + fprintf(file, + "%f %f %f\n", + static_cast(p[0]), + static_cast(p[1]), + static_cast(p[2])); + } + } + else + { + for(size_t i = 0; i < num_points; ++i) + { + const auto p = coordsetView[i]; + fprintf(file, + "%f %f 0\n", + static_cast(p[0]), + static_cast(p[1])); + } + } + }); + + // Write the cells + const conduit::Node &topologies = mesh["topologies"]; + const conduit::Node &topo = topologies[0]; + const conduit::Node &elements = topo["elements"]; + const conduit::Node &connectivity = elements["connectivity"]; + size_t num_cells = elements["sizes"].dtype().number_of_elements(); + size_t total_num_indices = connectivity.dtype().number_of_elements(); + + fprintf(file, "CELLS %zu %zu\n", num_cells, total_num_indices + num_cells); + size_t index = 0; + for(size_t i = 0; i < num_cells; ++i) + { + size_t cell_size = elements["sizes"].as_int32_array()[i]; + fprintf(file, "%zu", cell_size); + for(size_t j = 0; j < cell_size; ++j) + { + fprintf(file, " %d", connectivity.as_int32_array()[index++]); + } + fprintf(file, "\n"); + } + + // Write the cell types + fprintf(file, "CELL_TYPES %zu\n", num_cells); + if(elements.has_child("shapes")) + { + const conduit::Node &shapes = elements["shapes"]; + for(size_t i = 0; i < num_cells; ++i) + { + const auto type = ShapeID_to_vtk_cell(shapes.as_int32_array()[i]); + fprintf(file, "%d\n", type); + } + } + else + { + const auto type = ShapeID_to_vtk_cell(axom::mir::views::shapeNameToID(elements["shape"].as_string())); + for(size_t i = 0; i < num_cells; ++i) + { + fprintf(file, "%d\n", type); + } + } + + // TODO: write fields. + + // Close the file + fclose(file); } -void ConduitAllocateThroughAxom::internal_free(void *ptr) +void save_vtk(const conduit::Node &mesh, const std::string &path) { - //std::cout << "Dellocating for Conduit via axom: ptr=" << ptr << std::endl; - axom::deallocate(ptr); + const conduit::Node &n_topologies = mesh.fetch_existing("topologies"); + if(n_topologies.number_of_children() != 1) + { + SLIC_ERROR("The mesh must have a single topology."); + return; + } + + // For now. + if(n_topologies[0].fetch_existing("type").as_string() != "unstructured") + { + SLIC_ERROR("The mesh must have a single unstructured topology."); + return; + } + + save_unstructured_vtk(mesh, path); } -#endif } // end namespace blueprint } // end namespace utilities diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 8f40d80b48..af94fcd5e6 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace axom { @@ -318,6 +319,16 @@ std::pair minmax(const conduit::Node &n) return retval; } +/** + * \brief Save a Blueprint mesh to a legacy ASCII VTK file. + * + * \param node The node that contains the mesh data. + * \param path The file path to save. + * + * \note This function currently handles only unstructured topos with explicit coordsets. + */ +void save_vtk(const conduit::Node &node, const std::string &path); + } // end namespace blueprint } // end namespace utilities } // end namespace mir From c7f50f3717327bf6623e79dd4f5646c157ca7d36 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 15:39:42 -0700 Subject: [PATCH 134/290] save/load/compare baselines --- src/axom/mir/tests/mir_clipfield.cpp | 139 ++++++++----- src/axom/mir/tests/mir_testing_helpers.hpp | 187 ++++++++++++++++++ src/axom/mir/views/ExplicitCoordsetView.hpp | 4 + .../mir/views/RectilinearCoordsetView.hpp | 4 + src/axom/mir/views/UniformCoordsetView.hpp | 4 +- 5 files changed, 289 insertions(+), 49 deletions(-) create mode 100644 src/axom/mir/tests/mir_testing_helpers.hpp diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 552987deb4..90ca2628d0 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -38,6 +38,24 @@ #endif // clang-format on +//------------------------------------------------------------------------------ + +// Uncomment to generate baselines +//#define AXOM_TESTING_GENERATE_BASELINES + +// Uncomment to save visualization files for debugging (when making baselines) +//#define AXOM_TESTING_SAVE_VISUALIZATION + +// Include after seq_exec is defined. +#include "axom/mir/tests/mir_testing_helpers.hpp" + +std::string baselineDirectory() +{ + return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), + "mir_clipfield"); +} +//------------------------------------------------------------------------------ + void add_distance(conduit::Node &mesh, float dist = 6.5f) { // Make a new distance field. @@ -700,9 +718,14 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); - // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "yaml"); + // Handle baseline comparison. + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif } template @@ -711,15 +734,15 @@ void test_one_shape_exec(const conduit::Node &hostMesh, const std::string &name) test_one_shape(hostMesh, name); #if defined(AXOM_USE_OPENMP) - test_one_shape(hostMesh, name + "_omp"); + test_one_shape(hostMesh, name); #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_one_shape(hostMesh, name + "_cuda"); + test_one_shape(hostMesh, name); #endif #if defined(AXOM_USE_HIP) - test_one_shape(hostMesh, name + "_hip"); + test_one_shape(hostMesh, name); #endif } @@ -806,9 +829,16 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); - // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif + } // Now, take the clipped mesh and clip it again using a mixed topology view. using MixedTopoView = @@ -866,15 +896,33 @@ void braid2d_clip_test(const std::string &type, const std::string &name) axom::mir::utilities::blueprint::copy(hostClipMixedMesh, deviceClipMixedMesh); - // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, - name + "_mixed", - "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMixedMesh, - name + "_mixed_yaml", - "yaml"); + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name + "_mixed")); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMixedMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMixedMesh)); +#endif + } +} + +TEST(mir_clipfield, uniform2d) +{ + braid2d_clip_test("uniform", "uniform2d"); + +#if defined(AXOM_USE_OPENMP) + braid2d_clip_test("uniform", "uniform2d"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid2d_clip_test("uniform", "uniform2d"); +#endif - // Load a clipped baseline file & compare. +#if defined(AXOM_USE_HIP) + braid2d_clip_test("uniform", "uniform2d"); +#endif } template @@ -929,10 +977,14 @@ void braid3d_clip_test(const std::string &type, const std::string &name) conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); - // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); - conduit_save_vtk(hostClipMesh, name + ".vtk"); + // Handle baseline comparison. + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif } /// Execute the braid3d test for a single shape on multiple ExecSpaces @@ -942,32 +994,15 @@ void braid3d_clip_test_exec(const std::string &type, const std::string &name) braid3d_clip_test(type, name); #if defined(AXOM_USE_OPENMP) - braid3d_clip_test(type, name + "_omp"); + braid3d_clip_test(type, name); #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - braid3d_clip_test(type, name + "_cuda"); + braid3d_clip_test(type, name); #endif #if defined(AXOM_USE_HIP) - braid3d_clip_test(type, name + "_hip"); -#endif -} - -TEST(mir_clipfield, uniform2d) -{ - braid2d_clip_test("uniform", "uniform2d"); - - //#if defined(AXOM_USE_OPENMP) - // braid2d_clip_test("uniform", "uniform2d_omp"); - //#endif - -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - braid2d_clip_test("uniform", "uniform2d_cuda"); -#endif - -#if defined(AXOM_USE_HIP) - braid2d_clip_test("uniform", "uniform2d_hip"); + braid3d_clip_test(type, name); #endif } @@ -1061,10 +1096,14 @@ void braid3d_mixed_clip_test(const std::string &name) conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); - // Save data. - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name, "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostClipMesh, name + "_yaml", "yaml"); - conduit_save_vtk(hostClipMesh, name + ".vtk"); + // Handle baseline comparison. + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif } TEST(mir_clipfield, mixed) @@ -1073,19 +1112,20 @@ TEST(mir_clipfield, mixed) braid3d_mixed_clip_test(name); #if defined(AXOM_USE_OPENMP) - braid3d_mixed_clip_test(name + "_omp"); + braid3d_mixed_clip_test(name); #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - braid3d_mixed_clip_test(name + "_cuda"); + braid3d_mixed_clip_test(name); #endif #if defined(AXOM_USE_HIP) - braid3d_mixed_clip_test(name + "_hip"); + braid3d_mixed_clip_test(name); #endif } //------------------------------------------------------------------------------ +#if defined(DEBUGGING_TEST_CASES) void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; @@ -1093,6 +1133,7 @@ void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int while (1) ; } +#endif //------------------------------------------------------------------------------ int main(int argc, char *argv[]) @@ -1101,7 +1142,9 @@ int main(int argc, char *argv[]) ::testing::InitGoogleTest(&argc, argv); axom::slic::SimpleLogger logger; // create & initialize test logger, +#if defined(DEBUGGING_TEST_CASES) conduit::utils::set_error_handler(conduit_debug_err_handler); +#endif result = RUN_ALL_TESTS(); return result; } diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp new file mode 100644 index 0000000000..ccbd116447 --- /dev/null +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -0,0 +1,187 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_TESTING_HELPERS_HPP_ +#define AXOM_MIR_TESTING_HELPERS_HPP_ + +#include "axom/config.hpp" +#include "axom/core.hpp" +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Define better names for the execution spaces. This header needs to be included +// after the ExecSpace types are defined. +template +struct execution_name { static std::string name() { return "seq"; } }; + +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + #if defined(AXOM_USE_OPENMP) + template <> + struct execution_name { static std::string name() { return "omp"; } }; + #endif + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + template <> + struct execution_name { static std::string name() { return "cuda"; } }; + #endif + #if defined(AXOM_USE_HIP) + template <> + struct execution_name { static std::string name() { return "hip"; } }; + #endif +#endif + +//------------------------------------------------------------------------------ +std::string pjoin(const std::string &path, const std::string &filename) +{ + return axom::utilities::filesystem::joinPath(path, filename); +} + +void psplit(const std::string &filepath, std::string &path, std::string &filename) +{ + axom::Path p(filepath); + path = p.dirName(); + filename = p.baseName(); +} + +std::string dataDirectory() { return AXOM_DATA_DIR; } + +std::string testData(const std::string &filename) +{ + return pjoin(dataDirectory(), filename); +} + +std::string baselineDirectory(); + +std::string yamlRoot(const std::string &filepath) +{ + std::string retval, path, filename; + psplit(filepath, path, filename); + auto idx = filename.rfind("."); + if(idx != std::string::npos) + { + retval = filename.substr(0, idx); + } + else + { + retval = filename; + } + return retval; +} + +bool compareConduit(const conduit::Node &n1, + const conduit::Node &n2, + double tolerance, + conduit::Node &info) +{ + bool same = true; + if(n1.dtype().id() == n2.dtype().id() && n1.dtype().is_floating_point()) + { + const auto a1 = n1.as_double_accessor(); + const auto a2 = n2.as_double_accessor(); + double maxdiff = 0.; + for(int i = 0; i < a1.number_of_elements() && same; i++) + { + double diff = fabs(a1[i] - a2[i]); + maxdiff = std::max(diff, maxdiff); + same &= diff <= tolerance; + if(!same) + { + info.append().set( + axom::fmt::format("\"{}\" fields differ at index {}.", n1.name(), i)); + } + } + info["maxdiff"][n1.name()] = maxdiff; + } + else + { + for(int i = 0; i < n1.number_of_children() && same; i++) + { + const auto &n1c = n1.child(i); + const auto &n2c = n2.fetch_existing(n1c.name()); + same &= compareConduit(n1c, n2c, tolerance, info); + } + } + return same; +} + +void saveBaseline(const std::string &filename, const conduit::Node &n) +{ + std::string file_with_ext(filename + ".yaml"); + SLIC_INFO(axom::fmt::format("Save baseline {}", file_with_ext)); + conduit::relay::io::save(n, file_with_ext, "yaml"); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + SLIC_INFO(axom::fmt::format("Save visualization files...")); + conduit::relay::io::blueprint::save_mesh(n, filename + "_hdf5", "hdf5"); + axom::mir::utilities::blueprint::save_vtk(n, filename + "_vtk.vtk"); +#endif +} + +void saveBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node &n) +{ + for(const auto &path : baselinePaths) + { + axom::utilities::filesystem::makeDirsForPath(path); + std::string filename(pjoin(path, baselineName)); + saveBaseline(filename, n); + } +} + +bool loadBaseline(const std::string &filename, conduit::Node &n) +{ + bool loaded = false; + std::string file_with_ext(filename + ".yaml"); + //SLIC_INFO(axom::fmt::format("Load baseline {}", file_with_ext)); + if(axom::utilities::filesystem::pathExists(file_with_ext)) + { + conduit::relay::io::load(file_with_ext, "yaml", n); + loaded = true; + } + return loaded; +} + +template +std::vector baselinePaths() +{ + std::vector paths; + paths.push_back(pjoin(baselineDirectory(), execution_name::name())); + paths.push_back(baselineDirectory()); + return paths; +} + +bool compareBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node ¤t, double tolerance = 1.e-10) +{ + bool success = false; + for(const auto &path : baselinePaths) + { + try + { + // Load the baseline file. + conduit::Node info, baselineNode; + std::string filename(pjoin(path, baselineName)); + if(loadBaseline(filename, baselineNode)) + { + // Compare the baseline to the current DC. + SLIC_INFO(axom::fmt::format("Comparing to baseline {}", filename)); + success = compareConduit(baselineNode, current, tolerance, info); + if(!success) + { + info.print(); + } + // We found a baseline so we can exit + break; + } + } + catch(...) + { + SLIC_INFO( + axom::fmt::format("Could not load {} from {}!", baselineName, path)); + } + } + return success; +} + +#endif diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index ad372df42d..7a8e310bc2 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -35,6 +35,8 @@ class ExplicitCoordsetView using value_type = DataType; using PointType = axom::primal::Point; + constexpr static int dimension() { return 2; } + /** * \brief Constructor * @@ -101,6 +103,8 @@ class ExplicitCoordsetView using value_type = DataType; using PointType = axom::primal::Point; + constexpr static int dimension() { return 3; } + /** * \brief Constructor * diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index ddaaf36993..b61461bb99 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -34,6 +34,8 @@ class RectilinearCoordsetView2 using value_type = DataType; using PointType = axom::primal::Point; + constexpr static int dimension() { return 2; } + /** * \brief Constructor * @@ -126,6 +128,8 @@ class RectilinearCoordsetView3 using value_type = DataType; using PointType = axom::primal::Point; + constexpr static int dimension() { return 3; } + /** * \brief Constructor * diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index 0e5a4fd2be..67f9916e58 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -30,11 +30,13 @@ class UniformCoordsetView public: using LogicalIndex = axom::StackArray; using ExtentsType = axom::StackArray; - ; + using IndexType = axom::IndexType; using value_type = DataType; using PointType = axom::primal::Point; + constexpr static int dimension() { return NDIMS; } + /** * \brief Constructor * From 1e73a44b5d10b7f14200c7eeb0a4e50e9c6835d8 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 15:40:22 -0700 Subject: [PATCH 135/290] make style --- src/axom/mir/ClipField.hpp | 1 - src/axom/mir/blueprint_utilities.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 558ddf7422..76d2c83086 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -58,7 +58,6 @@ namespace clipping { namespace details { - /** * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate ShapeID value. * diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index 17a4e244e6..a914838224 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -16,7 +16,6 @@ namespace utilities { namespace blueprint { - /** * \brief Turns a ShapeID to a VTK cell type value. * @@ -46,7 +45,8 @@ static int ShapeID_to_vtk_cell(int shape_value) return vtktype; } -static void save_unstructured_vtk(const conduit::Node &mesh, const std::string &path) +static void save_unstructured_vtk(const conduit::Node &mesh, + const std::string &path) { FILE *file = fopen(path.c_str(), "wt"); if(file == nullptr) @@ -67,8 +67,7 @@ static void save_unstructured_vtk(const conduit::Node &mesh, const std::string & const auto x = points["x"].as_double_accessor(); const auto y = points["y"].as_double_accessor(); size_t num_points = 0; - views::dispatch_coordset(coordset, [&](auto coordsetView) - { + views::dispatch_coordset(coordset, [&](auto coordsetView) { num_points = coordsetView.size(); fprintf(file, "POINTS %zu float\n", num_points); @@ -132,7 +131,8 @@ static void save_unstructured_vtk(const conduit::Node &mesh, const std::string & } else { - const auto type = ShapeID_to_vtk_cell(axom::mir::views::shapeNameToID(elements["shape"].as_string())); + const auto type = ShapeID_to_vtk_cell( + axom::mir::views::shapeNameToID(elements["shape"].as_string())); for(size_t i = 0; i < num_cells; ++i) { fprintf(file, "%d\n", type); From 1a5a120a8ae9a8975a2589d8ed2ada952f19a7d8 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 16:28:04 -0700 Subject: [PATCH 136/290] Added tests for rectilinear clipping --- src/axom/mir/tests/mir_clipfield.cpp | 229 ++++++++++++--------- src/axom/mir/tests/mir_testing_helpers.hpp | 6 + 2 files changed, 133 insertions(+), 102 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 90ca2628d0..f4c0876ccf 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -159,102 +159,6 @@ void mixed3d(conduit::Node &mesh) add_distance(mesh, 0.f); } -int conduit_to_vtk_cell(int shape_value) -{ - int vtktype = 0; - if(shape_value == axom::mir::views::Tri_ShapeID) - vtktype = 5; // VTK_TRIANGLE - else if(shape_value == axom::mir::views::Quad_ShapeID) - vtktype = 9; // VTK_QUAD - else if(shape_value == axom::mir::views::Polygon_ShapeID) - vtktype = 7; // VTK_POLYGON - else if(shape_value == axom::mir::views::Tet_ShapeID) - vtktype = 10; // VTK_TETRA - else if(shape_value == axom::mir::views::Pyramid_ShapeID) - vtktype = 14; // VTK_PYRAMID - else if(shape_value == axom::mir::views::Wedge_ShapeID) - vtktype = 13; // VTK_WEDGE - else if(shape_value == axom::mir::views::Hex_ShapeID) - vtktype = 12; // VTK_HEXAHEDRON - - return vtktype; -} - -void conduit_save_vtk(const conduit::Node &node, const std::string &path) -{ - FILE *file = fopen(path.c_str(), "wt"); - - // Write the VTK file header - fprintf(file, "# vtk DataFile Version 3.0\n"); - fprintf(file, "Unstructured Grid Example\n"); - fprintf(file, "ASCII\n"); - fprintf(file, "DATASET UNSTRUCTURED_GRID\n"); - - // Write the points - const conduit::Node &points = node["coordsets/coords/values"]; - const conduit::Node &x = points["x"]; - const conduit::Node &y = points["y"]; - const conduit::Node &z = points["z"]; - size_t num_points = x.dtype().number_of_elements(); - - fprintf(file, "POINTS %zu float\n", num_points); - axom::mir::views::FloatNode_to_ArrayView(x, y, z, [&](auto xView, auto yView, auto zView) - { - for(size_t i = 0; i < num_points; ++i) - { - fprintf(file, - "%f %f %f\n", - static_cast(xView[i]), - static_cast(yView[i]), - static_cast(zView[i])); - } - }); - - // Write the cells - const conduit::Node &topologies = node["topologies"]; - const conduit::Node &topo = topologies[0]; - const conduit::Node &elements = topo["elements"]; - const conduit::Node &connectivity = elements["connectivity"]; - size_t num_cells = elements["sizes"].dtype().number_of_elements(); - size_t total_num_indices = connectivity.dtype().number_of_elements(); - - fprintf(file, "CELLS %zu %zu\n", num_cells, total_num_indices + num_cells); - size_t index = 0; - for(size_t i = 0; i < num_cells; ++i) - { - size_t cell_size = elements["sizes"].as_int32_array()[i]; - fprintf(file, "%zu", cell_size); - for(size_t j = 0; j < cell_size; ++j) - { - fprintf(file, " %d", connectivity.as_int32_array()[index++]); - } - fprintf(file, "\n"); - } - - // Write the cell types - fprintf(file, "CELL_TYPES %zu\n", num_cells); - if(elements.has_child("shapes")) - { - const conduit::Node &shapes = elements["shapes"]; - for(size_t i = 0; i < num_cells; ++i) - { - const auto type = conduit_to_vtk_cell(shapes.as_int32_array()[i]); - fprintf(file, "%d\n", type); - } - } - else - { - const auto type = conduit_to_vtk_cell(axom::mir::views::shapeNameToID(elements["shape"].as_string())); - for(size_t i = 0; i < num_cells; ++i) - { - fprintf(file, "%d\n", type); - } - } - - // Close the file - fclose(file); -} - TEST(mir_clipfield, options) { int nzones = 6; @@ -789,7 +693,9 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::Node hostMesh, deviceMesh; braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif // Create views axom::StackArray origin {0., 0.}, spacing {1., 1.}; @@ -925,6 +831,127 @@ TEST(mir_clipfield, uniform2d) #endif } +//------------------------------------------------------------------------------ +template +struct make_rectilinear_coordset {}; + +template <> +struct make_rectilinear_coordset<2> +{ + static axom::mir::views::RectilinearCoordsetView2 create(conduit::Node &coordset) + { + conduit::Node &x = coordset["values/x"]; + conduit::Node &y = coordset["values/y"]; + axom::ArrayView xView(static_cast(x.data_ptr()), x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(y.data_ptr()), y.dtype().number_of_elements()); + return axom::mir::views::RectilinearCoordsetView2(xView, yView); + } +}; +template <> +struct make_rectilinear_coordset<3> +{ + static axom::mir::views::RectilinearCoordsetView3 create(conduit::Node &coordset) + { + conduit::Node &x = coordset["values/x"]; + conduit::Node &y = coordset["values/y"]; + conduit::Node &z = coordset["values/z"]; + axom::ArrayView xView(static_cast(x.data_ptr()), x.dtype().number_of_elements()); + axom::ArrayView yView(static_cast(y.data_ptr()), y.dtype().number_of_elements()); + axom::ArrayView zView(static_cast(z.data_ptr()), z.dtype().number_of_elements()); + return axom::mir::views::RectilinearCoordsetView3(xView, yView, zView); + } +}; + +template +void braid_rectilinear_clip_test(const std::string &name) +{ + using Indexing = axom::mir::views::StructuredIndexing; + using TopoView = axom::mir::views::StructuredTopologyView; + + axom::StackArray dims, zoneDims; + for(int i = 0; i < NDIMS; i++) + { + dims[i] = 10; + zoneDims[i] = dims[i] - 1; + } + + // Create the data + conduit::Node hostMesh, deviceMesh; + braid("rectilinear", dims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + + // Create views + auto coordsetView = make_rectilinear_coordset::create(hostMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + TopoView topoView(Indexing {zoneDims}); + + // Create options to control the clipping. + conduit::Node options; + options["clipField"] = "distance"; + options["inside"] = 1; + options["outside"] = 1; + + // Clip the data + conduit::Node deviceClipMesh; + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif + } +} + + +TEST(mir_clipfield, rectilinear2d) +{ + braid_rectilinear_clip_test("rectilinear2d"); + +#if defined(AXOM_USE_OPENMP) + braid_rectilinear_clip_test("rectilinear2d"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid_rectilinear_clip_test("rectilinear2d"); +#endif + +#if defined(AXOM_USE_HIP) + braid_rectilinear_clip_test("rectilinear2d"); +#endif +} + +TEST(mir_clipfield, rectilinear3d) +{ + braid_rectilinear_clip_test("rectilinear3d"); + +#if defined(AXOM_USE_OPENMP) + braid_rectilinear_clip_test("rectilinear3d"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid_rectilinear_clip_test("rectilinear3d"); +#endif + +#if defined(AXOM_USE_HIP) + braid_rectilinear_clip_test("rectilinear3d"); +#endif +} + template void braid3d_clip_test(const std::string &type, const std::string &name) { @@ -937,10 +964,9 @@ void braid3d_clip_test(const std::string &type, const std::string &name) conduit::Node hostMesh, deviceMesh; braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostMesh, - name + "_orig_yaml", - "yaml"); +#endif // Create views conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); @@ -1038,10 +1064,9 @@ void braid3d_mixed_clip_test(const std::string &name) conduit::Node hostMesh, deviceMesh; mixed3d(hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostMesh, - name + "_orig_yaml", - "yaml"); +#endif // Create views conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index ccbd116447..d01e7c5f98 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -155,6 +155,7 @@ std::vector baselinePaths() bool compareBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node ¤t, double tolerance = 1.e-10) { bool success = false; + int count = 0; for(const auto &path : baselinePaths) { try @@ -167,6 +168,7 @@ bool compareBaseline(const std::vector &baselinePaths, const std::s // Compare the baseline to the current DC. SLIC_INFO(axom::fmt::format("Comparing to baseline {}", filename)); success = compareConduit(baselineNode, current, tolerance, info); + count++; if(!success) { info.print(); @@ -181,6 +183,10 @@ bool compareBaseline(const std::vector &baselinePaths, const std::s axom::fmt::format("Could not load {} from {}!", baselineName, path)); } } + if(!success && count == 0) + { + SLIC_INFO(fmt::format("No baselines found for {}", baselineName)); + } return success; } From eabf43416ecfee0effab1282aede6db48de11752 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 1 Aug 2024 17:52:43 -0700 Subject: [PATCH 137/290] Moved some functions into a header. --- src/axom/mir/tests/mir_clipfield.cpp | 281 ++++++------------ .../mir/tests/mir_testing_data_helpers.hpp | 248 ++++++++++++++++ src/axom/mir/tests/mir_views.cpp | 29 ++ src/axom/mir/views/dispatch_coordset.hpp | 225 ++++++++------ 4 files changed, 495 insertions(+), 288 deletions(-) create mode 100644 src/axom/mir/tests/mir_testing_data_helpers.hpp diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index f4c0876ccf..129da77fae 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -7,6 +7,7 @@ #include "axom/core.hpp" #include "axom/mir.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" #include #include @@ -41,10 +42,10 @@ //------------------------------------------------------------------------------ // Uncomment to generate baselines -//#define AXOM_TESTING_GENERATE_BASELINES +#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -//#define AXOM_TESTING_SAVE_VISUALIZATION +#define AXOM_TESTING_SAVE_VISUALIZATION // Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -56,109 +57,6 @@ std::string baselineDirectory() } //------------------------------------------------------------------------------ -void add_distance(conduit::Node &mesh, float dist = 6.5f) -{ - // Make a new distance field. - const conduit::Node &n_coordset = mesh["coordsets"][0]; - axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { - mesh["fields/distance/topology"] = "mesh"; - mesh["fields/distance/association"] = "vertex"; - conduit::Node &n_values = mesh["fields/distance/values"]; - const auto nnodes = coordsetView.size(); - n_values.set(conduit::DataType::float32(nnodes)); - float *valuesPtr = static_cast(n_values.data_ptr()); - for(int index = 0; index < nnodes; index++) - { - const auto pt = coordsetView[index]; - float norm2 = 0.f; - for(int i = 0; i < pt.DIMENSION; i++) norm2 += pt[i] * pt[i]; - valuesPtr[index] = sqrt(norm2) - dist; - } - }); -} - -template -void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) -{ - int d[3] = {0, 0, 0}; - for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; - conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); - - add_distance(mesh); -} - -void mixed3d(conduit::Node &mesh) -{ - // clang-format off - const std::vector conn{{ - // tets - 0,6,1,3, - 3,6,1,9, - 6,7,1,9, - 3,9,1,4, - 9,7,1,4, - 9,7,4,10, - // pyramids - 1,7,8,2,4, - 11,8,7,10,4, - 2,8,11,5,4, - // wedges - 6,7,9,12,13,15, - 9,7,10,15,13,16, - // hex - 7,13,14,8,10,16,17,11 - }}; - const std::vector shapes{{ - 0,0,0,0,0,0, - 1,1,1, - 2,2, - 3 - }}; - const std::vector sizes{{ - 4,4,4,4,4,4, - 5,5,5, - 6,6, - 8 - }}; - const std::vector offsets{{ - 0,4,8,12,16,20, - 24,29,34, - 39,45, - 51 - }}; - constexpr float LOW = -10.f; - constexpr float MID = 0.f; - constexpr float HIGH = 10.f; - const std::vector x{{ - LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH - }}; - const std::vector y{{ - LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID - }}; - const std::vector z{{ - LOW, LOW, LOW, LOW, LOW, LOW, MID, MID, MID, MID, MID, MID, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH - }}; - // clang-format off - - mesh["coordsets/coords/type"] = "explicit"; - mesh["coordsets/coords/values/x"].set(x); - mesh["coordsets/coords/values/y"].set(y); - mesh["coordsets/coords/values/z"].set(z); - mesh["topologies/mesh/type"] = "unstructured"; - mesh["topologies/mesh/coordset"] = "coords"; - mesh["topologies/mesh/elements/shape"] = "mixed"; - mesh["topologies/mesh/elements/connectivity"].set(conn); - mesh["topologies/mesh/elements/shapes"].set(shapes); - mesh["topologies/mesh/elements/sizes"].set(sizes); - mesh["topologies/mesh/elements/offsets"].set(offsets); - mesh["topologies/mesh/elements/shape_map/tet"] = 0; - mesh["topologies/mesh/elements/shape_map/pyramid"] = 1; - mesh["topologies/mesh/elements/shape_map/wedge"] = 2; - mesh["topologies/mesh/elements/shape_map/hex"] = 3; - - add_distance(mesh, 0.f); -} - TEST(mir_clipfield, options) { int nzones = 6; @@ -492,91 +390,6 @@ TEST(mir_clipfield, make_name) } } } -//------------------------------------------------------------------------------ - -void make_one_hex(conduit::Node &hostMesh) -{ - hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set( - std::vector {{0., 1., 1., 0., 0., 1., 1., 0.}}); - hostMesh["coordsets/coords/values/y"].set( - std::vector {{0., 0., 1., 1., 0., 0., 1., 1.}}); - hostMesh["coordsets/coords/values/z"].set( - std::vector {{0., 0., 0., 0., 1., 1., 1., 1.}}); - hostMesh["topologies/topo/type"] = "unstructured"; - hostMesh["topologies/topo/coordset"] = "coords"; - hostMesh["topologies/topo/elements/shape"] = "hex"; - hostMesh["topologies/topo/elements/connectivity"].set( - std::vector {{0, 1, 2, 3, 4, 5, 6, 7}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector {8}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); - hostMesh["fields/distance/topology"] = "topo"; - hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set( - std::vector {{1., -1., -1., -1., -1., -1., -1., -1.}}); -} - -void make_one_tet(conduit::Node &hostMesh) -{ - hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector {{0., 0., 1., 0.}}); - hostMesh["coordsets/coords/values/y"].set(std::vector {{0., 0., 0., 1.}}); - hostMesh["coordsets/coords/values/z"].set(std::vector {{0., 1., 0., 0.}}); - hostMesh["topologies/topo/type"] = "unstructured"; - hostMesh["topologies/topo/coordset"] = "coords"; - hostMesh["topologies/topo/elements/shape"] = "tet"; - hostMesh["topologies/topo/elements/connectivity"].set( - std::vector {{0, 1, 2, 3}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector {4}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); - hostMesh["fields/distance/topology"] = "topo"; - hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set(std::vector {{-1., -1., -1., 1.}}); -} - -void make_one_pyr(conduit::Node &hostMesh) -{ - hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set( - std::vector {{0., 0., 1., 1., 0.5}}); - hostMesh["coordsets/coords/values/y"].set( - std::vector {{0., 0., 0., 0., 1.}}); - hostMesh["coordsets/coords/values/z"].set( - std::vector {{0., 1., 1., 0., 0.5}}); - hostMesh["topologies/topo/type"] = "unstructured"; - hostMesh["topologies/topo/coordset"] = "coords"; - hostMesh["topologies/topo/elements/shape"] = "pyramid"; - hostMesh["topologies/topo/elements/connectivity"].set( - std::vector {{0, 1, 2, 3, 4}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector {5}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); - hostMesh["fields/distance/topology"] = "topo"; - hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set( - std::vector {{1., 1., -1., -1., -1.}}); -} - -void make_one_wdg(conduit::Node &hostMesh) -{ - hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set( - std::vector {{0., 0., 1., 0., 0., 1}}); - hostMesh["coordsets/coords/values/y"].set( - std::vector {{0., 0., 0., 1., 1., 1.}}); - hostMesh["coordsets/coords/values/z"].set( - std::vector {{0., 1., 0., 0., 1., 0.}}); - hostMesh["topologies/topo/type"] = "unstructured"; - hostMesh["topologies/topo/coordset"] = "coords"; - hostMesh["topologies/topo/elements/shape"] = "wedge"; - hostMesh["topologies/topo/elements/connectivity"].set( - std::vector {{0, 1, 2, 3, 4, 5}}); - hostMesh["topologies/topo/elements/sizes"].set(std::vector {6}); - hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); - hostMesh["fields/distance/topology"] = "topo"; - hostMesh["fields/distance/association"] = "vertex"; - hostMesh["fields/distance/values"].set( - std::vector {{1., 1., -1., -1., -1., -1.}}); -} template void test_one_shape(const conduit::Node &hostMesh, const std::string &name) @@ -653,28 +466,28 @@ void test_one_shape_exec(const conduit::Node &hostMesh, const std::string &name) TEST(mir_clipfield, onetet) { conduit::Node hostMesh; - make_one_tet(hostMesh); + axom::mir::testing::data::make_one_tet(hostMesh); test_one_shape_exec>(hostMesh, "one_tet"); } TEST(mir_clipfield, onepyr) { conduit::Node hostMesh; - make_one_pyr(hostMesh); + axom::mir::testing::data::make_one_pyr(hostMesh); test_one_shape_exec>(hostMesh, "one_pyr"); } TEST(mir_clipfield, onewdg) { conduit::Node hostMesh; - make_one_wdg(hostMesh); + axom::mir::testing::data::make_one_wdg(hostMesh); test_one_shape_exec>(hostMesh, "one_wdg"); } TEST(mir_clipfield, onehex) { conduit::Node hostMesh; - make_one_hex(hostMesh); + axom::mir::testing::data::make_one_hex(hostMesh); test_one_shape_exec>(hostMesh, "one_hex"); } @@ -691,7 +504,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; - braid(type, dims, hostMesh); + axom::mir::testing::data::braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); @@ -877,7 +690,7 @@ void braid_rectilinear_clip_test(const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; - braid("rectilinear", dims, hostMesh); + axom::mir::testing::data::braid("rectilinear", dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); @@ -952,6 +765,78 @@ TEST(mir_clipfield, rectilinear3d) #endif } +//------------------------------------------------------------------------------ +template +void strided_structured_clip_test(const std::string &name) +{ + using Indexing = axom::mir::views::StridedStructuredIndexing; + using TopoView = axom::mir::views::StructuredTopologyView; + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::strided_structured(hostMesh); + hostMesh.print(); + + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + +#if 0 + // Create views + axom::StackArray origin {0., 0.}, spacing {1., 1.}; + CoordsetView coordsetView(dims, origin, spacing); + TopoView topoView(Indexing {zoneDims}); + + // Create options to control the clipping. + conduit::Node options; + options["clipField"] = "vert_vals"; + options["clipValue"] = 6.5; + options["inside"] = 1; + options["outside"] = 1; + + // Clip the data + conduit::Node deviceClipMesh; + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostClipMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); +#endif + } +#endif +} + +TEST(mir_clipfield, strided_structured_2d) +{ + strided_structured_clip_test("strided_structured_2d"); + +//#if defined(AXOM_USE_OPENMP) +// strided_structured_clip_test("strided_structured_2d"); +//#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + strided_structured_clip_test("strided_structured_2d"); +#endif + +#if defined(AXOM_USE_HIP) + strided_structured_clip_test("strided_structured_2d"); +#endif +} + template void braid3d_clip_test(const std::string &type, const std::string &name) { @@ -962,7 +847,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) // Create the data const axom::StackArray dims {10, 10, 10}; conduit::Node hostMesh, deviceMesh; - braid(type, dims, hostMesh); + axom::mir::testing::data::braid(type, dims, hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); @@ -1062,7 +947,7 @@ void braid3d_mixed_clip_test(const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; - mixed3d(hostMesh); + axom::mir::testing::data::mixed3d(hostMesh); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp new file mode 100644 index 0000000000..6152723c70 --- /dev/null +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -0,0 +1,248 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_TESTING_DATA_HELPERS_HPP_ +#define AXOM_MIR_TESTING_DATA_HELPERS_HPP_ + +#include "axom/config.hpp" +#include "axom/core.hpp" +#include +#include +#include + +namespace axom +{ +namespace mir +{ +namespace testing +{ +namespace data +{ + +//------------------------------------------------------------------------------ + +void add_distance(conduit::Node &mesh, float dist = 6.5f) +{ + // Make a new distance field. + const conduit::Node &n_coordset = mesh["coordsets"][0]; + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { + mesh["fields/distance/topology"] = "mesh"; + mesh["fields/distance/association"] = "vertex"; + conduit::Node &n_values = mesh["fields/distance/values"]; + const auto nnodes = coordsetView.size(); + n_values.set(conduit::DataType::float32(nnodes)); + float *valuesPtr = static_cast(n_values.data_ptr()); + for(int index = 0; index < nnodes; index++) + { + const auto pt = coordsetView[index]; + float norm2 = 0.f; + for(int i = 0; i < pt.DIMENSION; i++) norm2 += pt[i] * pt[i]; + valuesPtr[index] = sqrt(norm2) - dist; + } + }); +} + +template +void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) +{ + int d[3] = {0, 0, 0}; + for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; + conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); + + add_distance(mesh); +} + +void mixed3d(conduit::Node &mesh) +{ + // clang-format off + const std::vector conn{{ + // tets + 0,6,1,3, + 3,6,1,9, + 6,7,1,9, + 3,9,1,4, + 9,7,1,4, + 9,7,4,10, + // pyramids + 1,7,8,2,4, + 11,8,7,10,4, + 2,8,11,5,4, + // wedges + 6,7,9,12,13,15, + 9,7,10,15,13,16, + // hex + 7,13,14,8,10,16,17,11 + }}; + const std::vector shapes{{ + 0,0,0,0,0,0, + 1,1,1, + 2,2, + 3 + }}; + const std::vector sizes{{ + 4,4,4,4,4,4, + 5,5,5, + 6,6, + 8 + }}; + const std::vector offsets{{ + 0,4,8,12,16,20, + 24,29,34, + 39,45, + 51 + }}; + constexpr float LOW = -10.f; + constexpr float MID = 0.f; + constexpr float HIGH = 10.f; + const std::vector x{{ + LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH, LOW, MID, HIGH + }}; + const std::vector y{{ + LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID, LOW, LOW, LOW, MID, MID, MID + }}; + const std::vector z{{ + LOW, LOW, LOW, LOW, LOW, LOW, MID, MID, MID, MID, MID, MID, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH + }}; + // clang-format off + + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(x); + mesh["coordsets/coords/values/y"].set(y); + mesh["coordsets/coords/values/z"].set(z); + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "mixed"; + mesh["topologies/mesh/elements/connectivity"].set(conn); + mesh["topologies/mesh/elements/shapes"].set(shapes); + mesh["topologies/mesh/elements/sizes"].set(sizes); + mesh["topologies/mesh/elements/offsets"].set(offsets); + mesh["topologies/mesh/elements/shape_map/tet"] = 0; + mesh["topologies/mesh/elements/shape_map/pyramid"] = 1; + mesh["topologies/mesh/elements/shape_map/wedge"] = 2; + mesh["topologies/mesh/elements/shape_map/hex"] = 3; + + add_distance(mesh, 0.f); +} + +void make_one_hex(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 1., 1., 0., 0., 1., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 1., 1., 0., 0., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 0., 0., 0., 1., 1., 1., 1.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "hex"; + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4, 5, 6, 7}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {8}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set( + std::vector {{1., -1., -1., -1., -1., -1., -1., -1.}}); +} + +void make_one_tet(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector {{0., 0., 1., 0.}}); + hostMesh["coordsets/coords/values/y"].set(std::vector {{0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set(std::vector {{0., 1., 0., 0.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "tet"; + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {4}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set(std::vector {{-1., -1., -1., 1.}}); +} + +void make_one_pyr(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 0., 1., 1., 0.5}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 0., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 1., 1., 0., 0.5}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "pyramid"; + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {5}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set( + std::vector {{1., 1., -1., -1., -1.}}); +} + +void make_one_wdg(conduit::Node &hostMesh) +{ + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 0., 1., 0., 0., 1}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 1., 1., 1.}}); + hostMesh["coordsets/coords/values/z"].set( + std::vector {{0., 1., 0., 0., 1., 0.}}); + hostMesh["topologies/topo/type"] = "unstructured"; + hostMesh["topologies/topo/coordset"] = "coords"; + hostMesh["topologies/topo/elements/shape"] = "wedge"; + hostMesh["topologies/topo/elements/connectivity"].set( + std::vector {{0, 1, 2, 3, 4, 5}}); + hostMesh["topologies/topo/elements/sizes"].set(std::vector {6}); + hostMesh["topologies/topo/elements/offsets"].set(std::vector {0}); + hostMesh["fields/distance/topology"] = "topo"; + hostMesh["fields/distance/association"] = "vertex"; + hostMesh["fields/distance/values"].set( + std::vector {{1., 1., -1., -1., -1., -1.}}); +} + +template +void strided_structured(conduit::Node &hostMesh) +{ + // Total padding in each dimension + const conduit::index_t total_elt_pad = 4; // two on each end + const conduit::index_t total_pt_pad = 3; // two on the low end, one on the high end + + // Size of the window in the larger mesh + const conduit::index_t npts_x = 4; + const conduit::index_t npts_y = 3; + const conduit::index_t npts_z = (NDIMS == 3) ? 3 : 0; + const conduit::index_t spz = (NDIMS == 3) ? (npts_z + total_pt_pad) : 0; + + const conduit::index_t nelts_x = npts_x - 1; + const conduit::index_t nelts_y = npts_y - 1; + const conduit::index_t nelts_z = (NDIMS == 3) ? (npts_z - 1) : 0; + const conduit::index_t sez = (NDIMS == 3) ? (nelts_z + total_elt_pad) : 0; + + // Origin: where the data starts in the arrays + const conduit::index_t origin_x = 2; + const conduit::index_t origin_y = 2; + const conduit::index_t origin_z = (NDIMS == 3) ? 2 : 0; + + conduit::Node desc; + desc["vertex_data/shape"].set(std::vector{{npts_x + total_pt_pad, npts_y + total_pt_pad, spz}}); + desc["vertex_data/origin"].set(std::vector{{origin_x, origin_y, origin_z}}); + desc["element_data/shape"].set(std::vector{{nelts_x + total_elt_pad, nelts_y + total_elt_pad, sez}}); + desc["element_data/origin"].set(std::vector{{origin_x, origin_y, origin_z}}); + conduit::blueprint::mesh::examples::strided_structured(desc, npts_x, npts_y, npts_z, hostMesh); +} + +} // end namespace data +} // end namespace testing +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 347eafb39f..eb698c2664 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -7,6 +7,7 @@ #include "axom/core.hpp" #include "axom/mir.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" TEST(mir_views, shape2conduitName) { @@ -59,6 +60,34 @@ TEST(mir_views, explicit_coordsetview) } } +TEST(mir_views, strided_structured) +{ + conduit::Node hostMesh; + axom::mir::testing::data::strided_structured<2>(hostMesh); + hostMesh.print(); + + axom::mir::views::dispatch_explicit_coordset(hostMesh["coordsets/coords"], [&](auto coordsetView) + { +std::cout << "We got a coordset view\n"; + axom::mir::views::dispatch_structured_topology(hostMesh["topologies/mesh"], [&](const std::string &shape, auto topoView) + { +std::cout << "We got a topo view\n"; + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const auto ids = zone.getIds(); + std::cout << "zone " << zoneIndex << ": {"; + for(axom::IndexType i = 0; i < ids.size(); i++) + { + if(i > 0) + std::cout << ", "; + std::cout << ids[i]; + } + std::cout << "}\n"; + }); + }); + }); +} + //------------------------------------------------------------------------------ int main(int argc, char* argv[]) diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 19a12fd99a..662455c738 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -17,6 +17,138 @@ namespace mir { namespace views { + +/** + * \brief Dispatch an uniform coordset to a function. + * + * \tparam FuncType The type of the function / lambda to invoke. It is expected + * that the callable accepts an auto argument for a coordset view. + * + * \param coordset The Conduit node that contains the coordset. + * \param func The function/lambda to invoke using the coordset view. + */ +template +void dispatch_uniform_coordset(const conduit::Node &coordset, FuncType &&func) +{ + const std::string keys[] = {"i", "j", "k"}; + const conduit::Node &n_dims = coordset["dims"]; + const conduit::index_t ndims = n_dims.number_of_children(); + if(ndims == 2) + { + axom::StackArray dims; + axom::StackArray origin {0., 0.}, spacing {1., 1.}; + for(int i = 0; i < ndims; i++) + { + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); + if(coordset.has_child("origin")) + origin[i] = coordset["origin"][i].to_double(); + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"][i].to_double(); + } + + UniformCoordsetView coordView(dims, origin, spacing); + func(coordView); + } + else if(ndims == 3) + { + axom::StackArray dims; + axom::StackArray origin {0., 0., 0.}, spacing {1., 1., 1.}; + for(int i = 0; i < ndims; i++) + { + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); + if(coordset.has_child("origin")) + origin[i] = coordset["origin"][i].to_double(); + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"][i].to_double(); + } + + UniformCoordsetView coordView(dims, origin, spacing); + func(coordView); + } +} + +/** + * \brief Dispatch a rectilinear coordset to a function. + * + * \tparam FuncType The type of the function / lambda to invoke. It is expected + * that the callable accepts an auto argument for a coordset view. + * + * \param coordset The Conduit node that contains the coordset. + * \param func The function/lambda to invoke using the coordset view. + */ +template +void dispatch_rectilinear_coordset(const conduit::Node &coordset, FuncType &&func) +{ + const conduit::Node &values = coordset["values"]; + if(values.number_of_children() == 2) + { + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + [&](auto xView, auto yView) { + RectilinearCoordsetView2 coordView( + xView, + yView); + func(coordView); + }); + } + else if(values.number_of_children() == 3) + { + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + values[2], + [&](auto xView, auto yView, auto zView) { + RectilinearCoordsetView3 coordView( + xView, + yView, + zView); + func(coordView); + }); + } +} + +/** + * \brief Dispatch an explicit coordset to a function. + * + * \tparam FuncType The type of the function / lambda to invoke. It is expected + * that the callable accepts an auto argument for a coordset view. + * + * \param coordset The Conduit node that contains the coordset. + * \param func The function/lambda to invoke using the coordset view. + */ +template +void dispatch_explicit_coordset(const conduit::Node &coordset, FuncType &&func) +{ + const conduit::Node &values = coordset["values"]; + if(values.number_of_children() == 2) + { + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + [&](auto xView, auto yView) { + ExplicitCoordsetView coordView( + xView, + yView); + func(coordView); + }); + } + else if(values.number_of_children() == 3) + { + axom::mir::views::FloatNode_to_ArrayView_same( + values[0], + values[1], + values[2], + [&](auto xView, auto yView, auto zView) { + ExplicitCoordsetView coordView( + xView, + yView, + zView); + func(coordView); + }); + } +} + /** * \brief Given a Conduit/Blueprint coordset, create an appropriate view and * call the supplied function, passing the coordset view to it. @@ -33,103 +165,16 @@ void dispatch_coordset(const conduit::Node &coordset, FuncType &&func) const std::string cstype = coordset["type"].as_string(); if(cstype == "uniform") { - const std::string keys[] = {"i", "j", "k"}; - const conduit::Node &n_dims = coordset["dims"]; - const conduit::index_t ndims = n_dims.number_of_children(); - if(ndims == 2) - { - axom::StackArray dims; - axom::StackArray origin {0., 0.}, spacing {1., 1.}; - for(int i = 0; i < ndims; i++) - { - dims[i] = n_dims.fetch_existing(keys[i]).to_int(); - if(coordset.has_child("origin")) - origin[i] = coordset["origin"][i].to_double(); - if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][i].to_double(); - } - - UniformCoordsetView coordView(dims, origin, spacing); - func(coordView); - } - else if(ndims == 3) - { - axom::StackArray dims; - axom::StackArray origin {0., 0., 0.}, spacing {1., 1., 1.}; - for(int i = 0; i < ndims; i++) - { - dims[i] = n_dims.fetch_existing(keys[i]).to_int(); - if(coordset.has_child("origin")) - origin[i] = coordset["origin"][i].to_double(); - if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][i].to_double(); - } - - UniformCoordsetView coordView(dims, origin, spacing); - func(coordView); - } + dispatch_uniform_coordset(coordset, func); } else if(cstype == "rectilinear") { - const conduit::Node &values = coordset["values"]; - if(values.number_of_children() == 2) - { - axom::mir::views::FloatNode_to_ArrayView_same( - values[0], - values[1], - [&](auto xView, auto yView) { - RectilinearCoordsetView2 coordView( - xView, - yView); - func(coordView); - }); - } - else if(values.number_of_children() == 3) - { - axom::mir::views::FloatNode_to_ArrayView_same( - values[0], - values[1], - values[2], - [&](auto xView, auto yView, auto zView) { - RectilinearCoordsetView3 coordView( - xView, - yView, - zView); - func(coordView); - }); - } + dispatch_rectilinear_coordset(coordset, func); } else if(cstype == "explicit") { // TODO: get the axis names. - // TODO: support strided/structured. - const conduit::Node &values = coordset["values"]; - if(values.number_of_children() == 2) - { - axom::mir::views::FloatNode_to_ArrayView_same( - values[0], - values[1], - [&](auto xView, auto yView) { - ExplicitCoordsetView coordView( - xView, - yView); - func(coordView); - }); - } - else if(values.number_of_children() == 3) - { - axom::mir::views::FloatNode_to_ArrayView_same( - values[0], - values[1], - values[2], - [&](auto xView, auto yView, auto zView) { - ExplicitCoordsetView coordView( - xView, - yView, - zView); - func(coordView); - }); - } + dispatch_explicit_coordset(coordset, func); } } From bbfb6005d40c6b874b99df97018c171ef2f0660a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 2 Aug 2024 11:41:40 -0700 Subject: [PATCH 138/290] More consistent naming, make some view creation functions. --- src/axom/mir/tests/mir_clipfield.cpp | 37 +-- src/axom/mir/tests/mir_views_indexing.cpp | 24 +- .../mir/views/StridedStructuredIndexing.hpp | 24 +- src/axom/mir/views/StructuredIndexing.hpp | 22 +- src/axom/mir/views/StructuredTopologyView.hpp | 14 +- src/axom/mir/views/dispatch_coordset.hpp | 51 ++++ .../views/dispatch_structured_topology.hpp | 276 +++++++++++++----- 7 files changed, 313 insertions(+), 135 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 129da77fae..01e3db8b7a 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -42,10 +42,10 @@ //------------------------------------------------------------------------------ // Uncomment to generate baselines -#define AXOM_TESTING_GENERATE_BASELINES +//#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -#define AXOM_TESTING_SAVE_VISUALIZATION +//#define AXOM_TESTING_SAVE_VISUALIZATION // Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -644,37 +644,6 @@ TEST(mir_clipfield, uniform2d) #endif } -//------------------------------------------------------------------------------ -template -struct make_rectilinear_coordset {}; - -template <> -struct make_rectilinear_coordset<2> -{ - static axom::mir::views::RectilinearCoordsetView2 create(conduit::Node &coordset) - { - conduit::Node &x = coordset["values/x"]; - conduit::Node &y = coordset["values/y"]; - axom::ArrayView xView(static_cast(x.data_ptr()), x.dtype().number_of_elements()); - axom::ArrayView yView(static_cast(y.data_ptr()), y.dtype().number_of_elements()); - return axom::mir::views::RectilinearCoordsetView2(xView, yView); - } -}; -template <> -struct make_rectilinear_coordset<3> -{ - static axom::mir::views::RectilinearCoordsetView3 create(conduit::Node &coordset) - { - conduit::Node &x = coordset["values/x"]; - conduit::Node &y = coordset["values/y"]; - conduit::Node &z = coordset["values/z"]; - axom::ArrayView xView(static_cast(x.data_ptr()), x.dtype().number_of_elements()); - axom::ArrayView yView(static_cast(y.data_ptr()), y.dtype().number_of_elements()); - axom::ArrayView zView(static_cast(z.data_ptr()), z.dtype().number_of_elements()); - return axom::mir::views::RectilinearCoordsetView3(xView, yView, zView); - } -}; - template void braid_rectilinear_clip_test(const std::string &name) { @@ -697,7 +666,7 @@ void braid_rectilinear_clip_test(const std::string &name) #endif // Create views - auto coordsetView = make_rectilinear_coordset::create(hostMesh["coordsets/coords"]); + auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(hostMesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); TopoView topoView(Indexing {zoneDims}); diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index 9d2eebc628..e244125b37 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -12,15 +12,29 @@ //---------------------------------------------------------------------- TEST(mir_views_indexing, strided_structured_indexing_2d) { + /* + + x---x---x---x---x---x---x + | | | | | | | + x---x---*---*---*---*---x *=real node, x=ignored node, O=origin + | | | | | | | + x---x---*---*---*---*---x + | | | | | | | + x---x---O---*---*---*---x + | | | | | | | + x---x---x---x---x---x---x + | | | | | | | + x---x---x---x---x---x---x + + */ using Indexing2D = axom::mir::views::StridedStructuredIndexing; - using LogicalIndex = - typename axom::mir::views::StridedStructuredIndexing::LogicalIndex; - LogicalIndex dims {4, 3}; // window size in 4*3 elements in 6*5 overall + using LogicalIndex = typename Indexing2D::LogicalIndex; + LogicalIndex dims {4, 3}; // window size in 4*3 elements in 7,6 overall LogicalIndex origin {2, 2}; LogicalIndex stride {1, 6}; Indexing2D indexing(dims, origin, stride); - EXPECT_EQ(indexing.dimensions(), 2); + EXPECT_EQ(indexing.dimension(), 2); EXPECT_EQ(indexing.size(), dims[0] * dims[1]); const LogicalIndex logical0_0 {0, 0}; @@ -62,7 +76,7 @@ TEST(mir_views_indexing, strided_structured_indexing_3d) LogicalIndex stride {1, 6, 30}; Indexing3D indexing(dims, origin, stride); - EXPECT_EQ(indexing.dimensions(), 3); + EXPECT_EQ(indexing.dimension(), 3); EXPECT_EQ(indexing.size(), dims[0] * dims[1] * dims[2]); const LogicalIndex logical0_0_0 {0, 0, 0}; diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index a91fbe804e..ed6f234e93 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -30,7 +30,7 @@ class StridedStructuredIndexing using IndexType = IndexT; using LogicalIndex = axom::StackArray; - AXOM_HOST_DEVICE constexpr static int dimensions() { return NDIMS; } + AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } /** * \brief constructor @@ -114,7 +114,7 @@ class StridedStructuredIndexing */ /// @{ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -123,7 +123,7 @@ class StridedStructuredIndexing return logical; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -133,7 +133,7 @@ class StridedStructuredIndexing return logical; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -154,14 +154,14 @@ class StridedStructuredIndexing * \return The index that corresponds to the \a logical index. */ /// @{ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return ((m_offsets[0] + logical[0]) * m_strides[0]); } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { @@ -169,7 +169,7 @@ class StridedStructuredIndexing ((m_offsets[1] + logical[1]) * m_strides[1]); } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { @@ -191,7 +191,7 @@ class StridedStructuredIndexing bool contains(const LogicalIndex &logical) const { bool retval = true; - for(int i = 0; i < dimensions(); i++) + for(int i = 0; i < dimension(); i++) { retval &= (logical[i] >= 0 && logical[i] < m_dimensions[i]); } @@ -220,7 +220,7 @@ class StridedStructuredIndexing StridedStructuredIndexing expand() const { StridedStructuredIndexing retval(*this); - for(int i = 0; i < dimensions(); i++) retval.m_dimensions[i]++; + for(int i = 0; i < dimension(); i++) retval.m_dimensions[i]++; return retval; } @@ -231,7 +231,7 @@ class StridedStructuredIndexing * \return An expanded StridedStructuredIndexing. */ /// @{ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, StridedStructuredIndexing>::type expand() const @@ -241,7 +241,7 @@ class StridedStructuredIndexing return retval; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, StridedStructuredIndexing>::type expand() const @@ -253,7 +253,7 @@ class StridedStructuredIndexing return retval; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, StridedStructuredIndexing>::type expand() const diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index fdedab4318..7165ab8a01 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -29,7 +29,7 @@ class StructuredIndexing using IndexType = IndexT; using LogicalIndex = axom::StackArray; - AXOM_HOST_DEVICE constexpr static int dimensions() { return NDIMS; } + AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } /** * \brief constructor @@ -68,7 +68,7 @@ class StructuredIndexing * * \return The j stride to move up a row. */ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims >= 2, IndexType>::type jStride() const { return m_dimensions[0]; @@ -79,7 +79,7 @@ class StructuredIndexing * * \return The k stride to move forward a "page". */ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type kStride() const { return m_dimensions[0] * m_dimensions[1]; @@ -94,7 +94,7 @@ class StructuredIndexing */ /// @{ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -103,7 +103,7 @@ class StructuredIndexing return logical; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -114,7 +114,7 @@ class StructuredIndexing return logical; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, LogicalIndex>::type IndexToLogicalIndex(IndexType index) const { @@ -137,21 +137,21 @@ class StructuredIndexing * \return The index that corresponds to the \a logical index. */ /// @{ - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return logical[0]; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { return logical[1] * m_dimensions[0] + logical[0]; } - template + template AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type LogicalIndexToIndex(const LogicalIndex &logical) const { @@ -172,7 +172,7 @@ class StructuredIndexing bool contains(const LogicalIndex &logical) const { bool retval = true; - for(int i = 0; i < dimensions(); i++) + for(int i = 0; i < dimension(); i++) { retval &= (logical[i] >= 0 && logical[i] < m_dimensions[i]); } @@ -201,7 +201,7 @@ class StructuredIndexing StructuredIndexing expand() const { StructuredIndexing retval(*this); - for(int i = 0; i < dimensions(); i++) retval.m_dimensions[i]++; + for(int i = 0; i < dimension(); i++) retval.m_dimensions[i]++; return retval; } diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index e07788ac0d..b961799881 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -34,7 +34,7 @@ class StructuredTopologyView * \return The number of dimensions. */ AXOM_HOST_DEVICE - constexpr static int dimension() { return IndexingPolicy::dimensions(); } + constexpr static int dimension() { return IndexingPolicy::dimension(); } /** * \brief Constructor @@ -86,7 +86,7 @@ class StructuredTopologyView // Q: Should we make a for_all() that iterates over multiple ranges? // Q: Should the logical index be passed to the lambda? - if constexpr(IndexingPolicy::dimensions() == 3) + if constexpr(IndexingPolicy::dimension() == 3) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); @@ -114,7 +114,7 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr(IndexingPolicy::dimensions() == 2) + else if constexpr(IndexingPolicy::dimension() == 2) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); @@ -137,7 +137,7 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr(IndexingPolicy::dimensions() == 1) + else if constexpr(IndexingPolicy::dimension() == 1) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); @@ -178,7 +178,7 @@ class StructuredTopologyView // Q: Should we make a for_all() that iterates over multiple ranges? // Q: Should the logical index be passed to the lambda? - if constexpr(IndexingPolicy::dimensions() == 3) + if constexpr(IndexingPolicy::dimension() == 3) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); @@ -207,7 +207,7 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr(IndexingPolicy::dimensions() == 2) + else if constexpr(IndexingPolicy::dimension() == 2) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); @@ -231,7 +231,7 @@ class StructuredTopologyView func(zoneIndex, shape); }); } - else if constexpr(IndexingPolicy::dimensions() == 1) + else if constexpr(IndexingPolicy::dimension() == 1) { const IndexingPolicy zoneIndexing = m_indexing; const IndexingPolicy nodeIndexing = m_indexing.expand(); diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 662455c738..becf2d790e 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -18,6 +18,57 @@ namespace mir namespace views { +/** + * \brief Base template for creating a rectilinear coordset view. + */ +template +struct make_rectilinear_coordset {}; + +/** + * \brief Partial specialization for creating 2D rectilinear coordset view. + */ +template +struct make_rectilinear_coordset +{ + using CoordsetView = axom::mir::views::RectilinearCoordsetView3; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + const conduit::Node &values = coordset.fetch_existing("values"); + axom::ArrayView xView(static_cast(const_cast(values[0].data_ptr())), values[0].dtype().number_of_elements()); + axom::ArrayView yView(static_cast(const_cast(values[1].data_ptr())), values[1].dtype().number_of_elements()); + axom::ArrayView zView(static_cast(const_cast(values[2].data_ptr())), values[2].dtype().number_of_elements()); + return CoordsetView(xView, yView, zView); + } +}; + +/** + * \brief Partial specialization for creating 2D rectilinear coordset view. + */ +template +struct make_rectilinear_coordset +{ + using CoordsetView = axom::mir::views::RectilinearCoordsetView2; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + const conduit::Node &values = coordset.fetch_existing("values"); + axom::ArrayView xView(static_cast(const_cast(values[0].data_ptr())), values[0].dtype().number_of_elements()); + axom::ArrayView yView(static_cast(const_cast(values[1].data_ptr())), values[1].dtype().number_of_elements()); + return CoordsetView(xView, yView); + } +}; + /** * \brief Dispatch an uniform coordset to a function. * diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index 6afb5b6353..47672985d5 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -50,6 +50,208 @@ bool fillFromNode(const conduit::Node &n, return found; } +/** + * \brief Base template for strided structured topology creation + */ +template +struct make_strided_structured {}; + +/** + * \brief Create a 3D structured topology view with strided structured indexing. + */ +template <> +struct make_strided_structured<3> +{ + using Indexing = views::StridedStructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); + + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); + + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + strides[2] = zoneDims[0] * zoneDims[1]; + } + + Indexing zoneIndexing(zoneDims, offsets, strides); + return TopoView(zoneIndexing); + } +}; + +/** + * \brief Create a 2D structured topology view with strided structured indexing. + */ +template <> +struct make_strided_structured<2> +{ + using Indexing = views::StridedStructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + } + + Indexing zoneIndexing( + zoneDims, + offsets, + strides); + return TopoView(zoneIndexing); + + } +}; + +/** + * \brief Create a 1D structured topology view with strided structured indexing. + */ +template <> +struct make_strided_structured<1> +{ + using Indexing = views::StridedStructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); + + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + fillFromNode(topo, stridesKey, strides, 1); + + Indexing zoneIndexing( + zoneDims, + offsets, + strides); + return TopoView(zoneIndexing); + } +}; + +/** + * \brief Base template for structured topology creation + */ +template +struct make_structured {}; + +/** + * \brief Create a 3D structured topology view with normal structured indexing. + */ +template <> +struct make_structured<3> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); + + Indexing zoneIndexing(zoneDims); + return TopoView(zoneIndexing); + } +}; + +/** + * \brief Create a 2D structured topology view with normal structured indexing. + */ +template <> +struct make_structured<2> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + + Indexing zoneIndexing(zoneDims); + return TopoView(zoneIndexing); + } +}; + +/** + * \brief Create a 1D structured topology view with normal structured indexing. + */ +template <> +struct make_structured<1> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + + Indexing zoneIndexing(zoneDims); + return TopoView(zoneIndexing); + } +}; + /** * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. * @@ -63,14 +265,11 @@ bool fillFromNode(const conduit::Node &n, template void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) { - // TODO: add strided structured variant - //StructuredTopologyView> topoView(StructuredIndexing(dims)); - //StructuredTopologyView> topoView(StridedStructuredIndexing(dims, offset, stride)); int ndims = 1; ndims += topo.has_path("elements/dims/j") ? 1 : 0; ndims += topo.has_path("elements/dims/k") ? 1 : 0; - static const std::string offsetsKey("elements/offsets"); - static const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); switch(ndims) { @@ -78,35 +277,14 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) if constexpr(dimension_selected(SelectedDimensions, 3)) { const std::string shape("hex"); - axom::StackArray zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) { - axom::StackArray offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) - { - strides[1] = zoneDims[0]; - strides[2] = zoneDims[0] * zoneDims[1]; - } - - views::StridedStructuredIndexing zoneIndexing( - zoneDims, - offsets, - strides); - views::StructuredTopologyView< - views::StridedStructuredIndexing> - topoView(zoneIndexing); + auto topoView = make_strided_structured<3>::view(topo); func(shape, topoView); } else { - views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> - topoView(zoneIndexing); + auto topoView = make_structured<3>::view(topo); func(shape, topoView); } } @@ -115,33 +293,14 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) if constexpr(dimension_selected(SelectedDimensions, 2)) { const std::string shape("quad"); - axom::StackArray zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) { - axom::StackArray offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) - { - strides[1] = zoneDims[0]; - } - - views::StridedStructuredIndexing zoneIndexing( - zoneDims, - offsets, - strides); - views::StructuredTopologyView< - views::StridedStructuredIndexing> - topoView(zoneIndexing); + auto topoView = make_strided_structured<2>::view(topo); func(shape, topoView); } else { - views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> - topoView(zoneIndexing); + auto topoView = make_structured<2>::view(topo); func(shape, topoView); } } @@ -150,29 +309,14 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) if constexpr(dimension_selected(SelectedDimensions, 1)) { const std::string shape("line"); - axom::StackArray zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) { - axom::StackArray offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - fillFromNode(topo, stridesKey, strides, 1); - - views::StridedStructuredIndexing zoneIndexing( - zoneDims, - offsets, - strides); - views::StructuredTopologyView< - views::StridedStructuredIndexing> - topoView(zoneIndexing); + auto topoView = make_strided_structured<1>::view(topo); func(shape, topoView); } else { - views::StructuredIndexing zoneIndexing(zoneDims); - views::StructuredTopologyView> - topoView(zoneIndexing); + auto topoView = make_structured<1>::view(topo); func(shape, topoView); } } From 4e9b17cbe63facc17593353a4ed9b390a9878265 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 2 Aug 2024 11:42:41 -0700 Subject: [PATCH 139/290] make style --- src/axom/mir/tests/mir_clipfield.cpp | 50 ++++---- src/axom/mir/tests/mir_views.cpp | 42 +++---- src/axom/mir/views/dispatch_coordset.hpp | 24 ++-- .../views/dispatch_structured_topology.hpp | 109 +++++++++--------- 4 files changed, 114 insertions(+), 111 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 01e3db8b7a..92c82533a5 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -666,7 +666,9 @@ void braid_rectilinear_clip_test(const std::string &name) #endif // Create views - auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(hostMesh["coordsets/coords"]); + auto coordsetView = + axom::mir::views::make_rectilinear_coordset::view( + hostMesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); TopoView topoView(Indexing {zoneDims}); @@ -699,7 +701,6 @@ void braid_rectilinear_clip_test(const std::string &name) } } - TEST(mir_clipfield, rectilinear2d) { braid_rectilinear_clip_test("rectilinear2d"); @@ -738,7 +739,8 @@ TEST(mir_clipfield, rectilinear3d) template void strided_structured_clip_test(const std::string &name) { - using Indexing = axom::mir::views::StridedStructuredIndexing; + using Indexing = + axom::mir::views::StridedStructuredIndexing; using TopoView = axom::mir::views::StructuredTopologyView; using CoordsetView = axom::mir::views::ExplicitCoordsetView; @@ -780,11 +782,11 @@ void strided_structured_clip_test(const std::string &name) { std::string baselineName(yamlRoot(name)); const auto paths = baselinePaths(); -#if defined(AXOM_TESTING_GENERATE_BASELINES) + #if defined(AXOM_TESTING_GENERATE_BASELINES) saveBaseline(paths, baselineName, hostClipMesh); -#else + #else EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); -#endif + #endif } #endif } @@ -793,9 +795,9 @@ TEST(mir_clipfield, strided_structured_2d) { strided_structured_clip_test("strided_structured_2d"); -//#if defined(AXOM_USE_OPENMP) -// strided_structured_clip_test("strided_structured_2d"); -//#endif + //#if defined(AXOM_USE_OPENMP) + // strided_structured_clip_test("strided_structured_2d"); + //#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) strided_structured_clip_test("strided_structured_2d"); @@ -927,11 +929,11 @@ void braid3d_mixed_clip_test(const std::string &name) conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); conduit::Node &n_z = deviceMesh.fetch_existing("coordsets/coords/values/z"); const axom::ArrayView x(static_cast(n_x.data_ptr()), - n_x.dtype().number_of_elements()); + n_x.dtype().number_of_elements()); const axom::ArrayView y(static_cast(n_y.data_ptr()), - n_y.dtype().number_of_elements()); + n_y.dtype().number_of_elements()); const axom::ArrayView z(static_cast(n_z.data_ptr()), - n_z.dtype().number_of_elements()); + n_z.dtype().number_of_elements()); CoordsetView coordsetView(x, y, z); conduit::Node &n_device_topo = deviceMesh.fetch_existing("topologies/mesh"); @@ -939,23 +941,17 @@ void braid3d_mixed_clip_test(const std::string &name) conduit::Node &n_shapes = n_device_topo.fetch_existing("elements/shapes"); conduit::Node &n_sizes = n_device_topo.fetch_existing("elements/sizes"); conduit::Node &n_offsets = n_device_topo.fetch_existing("elements/offsets"); - axom::ArrayView connView( - static_cast(n_conn.data_ptr()), - n_conn.dtype().number_of_elements()); + axom::ArrayView connView(static_cast(n_conn.data_ptr()), + n_conn.dtype().number_of_elements()); axom::ArrayView shapesView( static_cast(n_shapes.data_ptr()), n_shapes.dtype().number_of_elements()); - axom::ArrayView sizesView( - static_cast(n_sizes.data_ptr()), - n_sizes.dtype().number_of_elements()); + axom::ArrayView sizesView(static_cast(n_sizes.data_ptr()), + n_sizes.dtype().number_of_elements()); axom::ArrayView offsetsView( static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); - TopoView topoView(n_device_topo, - connView, - shapesView, - sizesView, - offsetsView); + TopoView topoView(n_device_topo, connView, shapesView, sizesView, offsetsView); // Create options to control the clipping. conduit::Node options; @@ -1007,10 +1003,10 @@ TEST(mir_clipfield, mixed) #if defined(DEBUGGING_TEST_CASES) void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { - std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; - // This is on purpose. - while (1) - ; + std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; + // This is on purpose. + while(1) + ; } #endif diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index eb698c2664..6074f349f3 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -66,31 +66,33 @@ TEST(mir_views, strided_structured) axom::mir::testing::data::strided_structured<2>(hostMesh); hostMesh.print(); - axom::mir::views::dispatch_explicit_coordset(hostMesh["coordsets/coords"], [&](auto coordsetView) - { -std::cout << "We got a coordset view\n"; - axom::mir::views::dispatch_structured_topology(hostMesh["topologies/mesh"], [&](const std::string &shape, auto topoView) - { -std::cout << "We got a topo view\n"; - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const auto ids = zone.getIds(); - std::cout << "zone " << zoneIndex << ": {"; - for(axom::IndexType i = 0; i < ids.size(); i++) - { - if(i > 0) - std::cout << ", "; - std::cout << ids[i]; - } - std::cout << "}\n"; - }); + axom::mir::views::dispatch_explicit_coordset( + hostMesh["coordsets/coords"], + [&](auto coordsetView) { + std::cout << "We got a coordset view\n"; + axom::mir::views::dispatch_structured_topology< + axom::mir::views::select_dimensions(2)>( + hostMesh["topologies/mesh"], + [&](const std::string &shape, auto topoView) { + std::cout << "We got a topo view\n"; + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto ids = zone.getIds(); + std::cout << "zone " << zoneIndex << ": {"; + for(axom::IndexType i = 0; i < ids.size(); i++) + { + if(i > 0) std::cout << ", "; + std::cout << ids[i]; + } + std::cout << "}\n"; + }); + }); }); - }); } //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index becf2d790e..668e0e6987 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -17,12 +17,12 @@ namespace mir { namespace views { - /** * \brief Base template for creating a rectilinear coordset view. */ template -struct make_rectilinear_coordset {}; +struct make_rectilinear_coordset +{ }; /** * \brief Partial specialization for creating 2D rectilinear coordset view. @@ -40,9 +40,15 @@ struct make_rectilinear_coordset static CoordsetView view(const conduit::Node &coordset) { const conduit::Node &values = coordset.fetch_existing("values"); - axom::ArrayView xView(static_cast(const_cast(values[0].data_ptr())), values[0].dtype().number_of_elements()); - axom::ArrayView yView(static_cast(const_cast(values[1].data_ptr())), values[1].dtype().number_of_elements()); - axom::ArrayView zView(static_cast(const_cast(values[2].data_ptr())), values[2].dtype().number_of_elements()); + axom::ArrayView xView( + static_cast(const_cast(values[0].data_ptr())), + values[0].dtype().number_of_elements()); + axom::ArrayView yView( + static_cast(const_cast(values[1].data_ptr())), + values[1].dtype().number_of_elements()); + axom::ArrayView zView( + static_cast(const_cast(values[2].data_ptr())), + values[2].dtype().number_of_elements()); return CoordsetView(xView, yView, zView); } }; @@ -63,8 +69,12 @@ struct make_rectilinear_coordset static CoordsetView view(const conduit::Node &coordset) { const conduit::Node &values = coordset.fetch_existing("values"); - axom::ArrayView xView(static_cast(const_cast(values[0].data_ptr())), values[0].dtype().number_of_elements()); - axom::ArrayView yView(static_cast(const_cast(values[1].data_ptr())), values[1].dtype().number_of_elements()); + axom::ArrayView xView( + static_cast(const_cast(values[0].data_ptr())), + values[0].dtype().number_of_elements()); + axom::ArrayView yView( + static_cast(const_cast(values[1].data_ptr())), + values[1].dtype().number_of_elements()); return CoordsetView(xView, yView); } }; diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index 47672985d5..fc4a8fe77a 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -54,7 +54,8 @@ bool fillFromNode(const conduit::Node &n, * \brief Base template for strided structured topology creation */ template -struct make_strided_structured {}; +struct make_strided_structured +{ }; /** * \brief Create a 3D structured topology view with strided structured indexing. @@ -73,23 +74,23 @@ struct make_strided_structured<3> */ static TopoView view(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); - LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) - { - strides[1] = zoneDims[0]; - strides[2] = zoneDims[0] * zoneDims[1]; - } + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + strides[2] = zoneDims[0] * zoneDims[1]; + } - Indexing zoneIndexing(zoneDims, offsets, strides); + Indexing zoneIndexing(zoneDims, offsets, strides); return TopoView(zoneIndexing); } }; @@ -111,25 +112,21 @@ struct make_strided_structured<2> */ static TopoView view(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) - { - strides[1] = zoneDims[0]; - } - - Indexing zoneIndexing( - zoneDims, - offsets, - strides); - return TopoView(zoneIndexing); + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + if(!fillFromNode(topo, stridesKey, strides, 1)) + { + strides[1] = zoneDims[0]; + } + + Indexing zoneIndexing(zoneDims, offsets, strides); + return TopoView(zoneIndexing); } }; @@ -150,21 +147,18 @@ struct make_strided_structured<1> */ static TopoView view(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/offsets"); + const std::string stridesKey("elements/strides"); - LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - fillFromNode(topo, stridesKey, strides, 1); + LogicalIndex offsets, strides; + fillFromNode(topo, offsetsKey, offsets, 0); + fillFromNode(topo, stridesKey, strides, 1); - Indexing zoneIndexing( - zoneDims, - offsets, - strides); - return TopoView(zoneIndexing); + Indexing zoneIndexing(zoneDims, offsets, strides); + return TopoView(zoneIndexing); } }; @@ -172,7 +166,8 @@ struct make_strided_structured<1> * \brief Base template for structured topology creation */ template -struct make_structured {}; +struct make_structured +{ }; /** * \brief Create a 3D structured topology view with normal structured indexing. @@ -191,12 +186,12 @@ struct make_structured<3> */ static TopoView view(const conduit::Node &topo) { - LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - Indexing zoneIndexing(zoneDims); + Indexing zoneIndexing(zoneDims); return TopoView(zoneIndexing); } }; @@ -218,11 +213,11 @@ struct make_structured<2> */ static TopoView view(const conduit::Node &topo) { - LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - Indexing zoneIndexing(zoneDims); + Indexing zoneIndexing(zoneDims); return TopoView(zoneIndexing); } }; @@ -244,10 +239,10 @@ struct make_structured<1> */ static TopoView view(const conduit::Node &topo) { - LogicalIndex zoneDims; - zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); + LogicalIndex zoneDims; + zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - Indexing zoneIndexing(zoneDims); + Indexing zoneIndexing(zoneDims); return TopoView(zoneIndexing); } }; From 23aa1f72a13cbbc6638a7f7d1cc977ef167bc96a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 2 Aug 2024 19:31:50 -0700 Subject: [PATCH 140/290] progress on strided structured --- src/axom/core/StackArray.hpp | 24 ++ src/axom/mir/ClipField.hpp | 37 +++- src/axom/mir/FieldBlender.hpp | 45 +++- src/axom/mir/tests/mir_clipfield.cpp | 37 ++-- src/axom/mir/tests/mir_views.cpp | 51 ++++- src/axom/mir/tests/mir_views_indexing.cpp | 64 ++++-- .../mir/views/StridedStructuredIndexing.hpp | 207 ++++++++++++++---- src/axom/mir/views/StructuredIndexing.hpp | 77 ++++++- src/axom/mir/views/StructuredTopologyView.hpp | 39 +++- .../UnstructuredTopologyMixedShapeView.hpp | 6 + .../UnstructuredTopologyPolyhedralView.hpp | 6 + .../UnstructuredTopologySingleShapeView.hpp | 6 + src/axom/mir/views/dispatch_coordset.hpp | 2 +- .../views/dispatch_structured_topology.hpp | 115 ++++++---- 14 files changed, 564 insertions(+), 152 deletions(-) diff --git a/src/axom/core/StackArray.hpp b/src/axom/core/StackArray.hpp index cae02871f1..41136f6f2d 100644 --- a/src/axom/core/StackArray.hpp +++ b/src/axom/core/StackArray.hpp @@ -10,6 +10,8 @@ #include "axom/core/Macros.hpp" // for axom macros #include "axom/core/Types.hpp" // for axom types +#include + namespace axom { /*! @@ -143,6 +145,28 @@ AXOM_HOST_DEVICE bool operator<(const StackArray& lhs, return false; } +/** + * \brief Print the StackArray to a stream. + * \param os The stream to use. + * \param obj The StackArray to print. + * \return The input stream. + */ +template +std::ostream& operator<<(std::ostream& os, const StackArray& obj) +{ + os << "("; + for(int i = 0; i < N; i++) + { + if(i > 0) + { + os << ", "; + } + os << obj.m_data[i]; + } + os << ")"; + return os; +} + } /* namespace axom */ #endif /* AXOM_STACKARRAY_HPP_ */ diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 76d2c83086..aaf730738b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1701,11 +1701,38 @@ class ClipField } else if(association == "vertex") { - axom::mir::utilities::blueprint::FieldBlender< - ExecSpace, - axom::mir::utilities::blueprint::SelectThroughArrayView> - b; - b.execute(blend, n_field, n_out_fields[it->second]); + bool handled = false; + // If we have fields that look like strided structured, try and support that + // if the topology view supports it. + if(n_field.has_path("offsets") || n_field.has_path("strides")) + { + if constexpr (TopologyView::supports_strided_structured_indexing()) + { + // Make node indexing that the field blender can use. + using Indexing = typename TopologyView::IndexingPolicy; + using IndexerType = axom::mir::utilities::blueprint::MapNodesThroughIndexing; + IndexerType indexing; + indexing.m_indexing = m_topologyView.indexing().expand(); + + // Blend the field. + axom::mir::utilities::blueprint::FieldBlender< + ExecSpace, + axom::mir::utilities::blueprint::SelectThroughArrayView, + IndexerType> + b(indexing); + b.execute(blend, n_field, n_out_fields[it->second]); + handled = true; + } + } + if(!handled) + { + axom::mir::utilities::blueprint::FieldBlender< + ExecSpace, + axom::mir::utilities::blueprint::SelectThroughArrayView> + b; + b.execute(blend, n_field, n_out_fields[it->second]); + } + n_out_fields[it->second]["topology"] = topologyName; } } diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index fab733c843..845fe18d2e 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -70,6 +70,30 @@ struct SelectThroughArrayView } }; +/// Does no node index mapping. +struct NoMapping +{ + AXOM_HOST_DEVICE + inline axom::IndexType operator [](axom::IndexType index) const + { + return index; + } +}; + +/// Maps node indices to their local indexing used in vertex fields. +template +struct MapNodesThroughIndexing +{ + AXOM_HOST_DEVICE + inline axom::IndexType operator [](axom::IndexType index) const + { + return m_indexing.GlobalToLocal(index); + } + + Indexing m_indexing{}; +}; + + /** * \accelerated * \class FieldBlender @@ -78,11 +102,20 @@ struct SelectThroughArrayView * * \tparam ExecSpace The execution space where the work will occur. * \tparam SelectionPolicy The selection policy to use. + * \tparam NodeIndexingPolicy How/if changes in node indexing occur when accessing nodes. */ -template +template class FieldBlender { public: + /// Constructor + FieldBlender() : m_nodeIndexing() + { } + + /// Constructor + FieldBlender(const NodeIndexingPolicy &indexing) : m_nodeIndexing(indexing) + { } + /** * \brief Create a new blended field from the \a n_input field and place it in \a n_output. * @@ -116,6 +149,7 @@ class FieldBlender } private: + /** * \brief Blend data for a single field component. * @@ -144,7 +178,10 @@ class FieldBlender using accum_type = typename axom::mir::utilities::accumulation_traits::type; + // Make capturable objects for the lambda. const BlendData deviceBlend(blend); + const NodeIndexingPolicy nodeIndexing(m_nodeIndexing); + axom::for_all( outputSize, AXOM_LAMBDA(auto bgid) { @@ -158,7 +195,8 @@ class FieldBlender accum_type blended = 0; for(IndexType i = start; i < end; i++) { - const auto index = deviceBlend.m_blendIdsView[i]; + // Get the index that we're using, potentially mapping the node value. + const auto index = nodeIndexing[deviceBlend.m_blendIdsView[i]]; const auto weight = deviceBlend.m_blendCoeffView[i]; blended += static_cast(compView[index]) * weight; } @@ -166,6 +204,9 @@ class FieldBlender }); }); } + +private: + NodeIndexingPolicy m_nodeIndexing {}; }; } // end namespace blueprint diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 92c82533a5..44e2f64d09 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -42,10 +42,10 @@ //------------------------------------------------------------------------------ // Uncomment to generate baselines -//#define AXOM_TESTING_GENERATE_BASELINES +#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -//#define AXOM_TESTING_SAVE_VISUALIZATION +#define AXOM_TESTING_SAVE_VISUALIZATION // Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -754,29 +754,31 @@ void strided_structured_clip_test(const std::string &name) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif -#if 0 - // Create views - axom::StackArray origin {0., 0.}, spacing {1., 1.}; - CoordsetView coordsetView(dims, origin, spacing); - TopoView topoView(Indexing {zoneDims}); + conduit::Node options, deviceClipMesh, hostClipMesh; // Create options to control the clipping. - conduit::Node options; options["clipField"] = "vert_vals"; options["clipValue"] = 6.5; options["inside"] = 1; options["outside"] = 1; - // Clip the data - conduit::Node deviceClipMesh; - axom::mir::clipping::ClipField clipper( - topoView, - coordsetView); - clipper.execute(deviceMesh, options, deviceClipMesh); + // Create views + axom::mir::views::dispatch_explicit_coordset(deviceMesh["coordsets/coords"], [&](auto coordsetView) + { + auto topoView = axom::mir::views::make_strided_structured<2>::view(deviceMesh["topologies/mesh"]); - // Copy device->host - conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + using CoordsetView = decltype(coordsetView); + using TopoView = decltype(topoView); + + // Clip the data + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + }); // Handle baseline comparison. { @@ -788,7 +790,6 @@ void strided_structured_clip_test(const std::string &name) EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); #endif } -#endif } TEST(mir_clipfield, strided_structured_2d) diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 6074f349f3..36b8c4e3e8 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -7,8 +7,11 @@ #include "axom/core.hpp" #include "axom/mir.hpp" +#include "axom/primal.hpp" #include "axom/mir/tests/mir_testing_data_helpers.hpp" +#include + TEST(mir_views, shape2conduitName) { EXPECT_EQ(axom::mir::views::LineShape::name(), "line"); @@ -64,27 +67,61 @@ TEST(mir_views, strided_structured) { conduit::Node hostMesh; axom::mir::testing::data::strided_structured<2>(hostMesh); - hostMesh.print(); +// hostMesh.print(); + + // These are the expected zone ids for this strided structured mesh. + const axom::Array expectedZones{{ + 16, 17, 24, 23, + 17, 18, 25, 24, + 18, 19, 26, 25, + 23, 24, 31, 30, + 24, 25, 32, 31, + 25, 26, 33, 32 + }}; + auto expectedZonesView = expectedZones.view(); axom::mir::views::dispatch_explicit_coordset( hostMesh["coordsets/coords"], [&](auto coordsetView) { - std::cout << "We got a coordset view\n"; + axom::mir::views::dispatch_structured_topology< axom::mir::views::select_dimensions(2)>( hostMesh["topologies/mesh"], [&](const std::string &shape, auto topoView) { - std::cout << "We got a topo view\n"; + + // Traverse the zones in the mesh and check the zone ids. topoView.template for_all_zones( AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + + // Check zone ids. const auto ids = zone.getIds(); - std::cout << "zone " << zoneIndex << ": {"; for(axom::IndexType i = 0; i < ids.size(); i++) { - if(i > 0) std::cout << ", "; - std::cout << ids[i]; + EXPECT_EQ(expectedZonesView[zoneIndex * 4 + i], ids[i]); + } + + // Check coordinates + const auto nodeIndexing = topoView.indexing().expand(); + for(axom::IndexType i = 0; i < ids.size(); i++) + { + // Get coordinate from coordsetView. + const auto pt = coordsetView[ids[i]]; + + // Get the logical local id for the id. + const auto index = nodeIndexing.GlobalToLocal(ids[i]); + const auto logical = nodeIndexing.IndexToLogicalIndex(index); + + // Expected coordinate + double x = (3. + 1. / 3.) * static_cast(logical[0] - 1); + const double yvals[] = {-2, 2, 6}; + double y = yvals[logical[1]]; + + const double dx = pt[0] - x; + const double dy = pt[1] - y; + double d = sqrt(dx*dx + dy*dy); + + EXPECT_TRUE(d < 1.e-10); } - std::cout << "}\n"; }); }); }); diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index e244125b37..3de1b81306 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -16,7 +16,7 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) x---x---x---x---x---x---x | | | | | | | - x---x---*---*---*---*---x *=real node, x=ignored node, O=origin + x---x---*---*---*---*---x *=real node, x=ignored node, O=origin node 16 | | | | | | | x---x---*---*---*---*---x | | | | | | | @@ -27,27 +27,17 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) x---x---x---x---x---x---x */ - using Indexing2D = axom::mir::views::StridedStructuredIndexing; - using LogicalIndex = typename Indexing2D::LogicalIndex; + using Indexing = axom::mir::views::StridedStructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; LogicalIndex dims {4, 3}; // window size in 4*3 elements in 7,6 overall LogicalIndex origin {2, 2}; - LogicalIndex stride {1, 6}; - Indexing2D indexing(dims, origin, stride); + LogicalIndex stride {1, 7}; + Indexing indexing(dims, origin, stride); EXPECT_EQ(indexing.dimension(), 2); EXPECT_EQ(indexing.size(), dims[0] * dims[1]); - const LogicalIndex logical0_0 {0, 0}; - const auto index0_0 = indexing.LogicalIndexToIndex(logical0_0); - EXPECT_EQ(index0_0, 14); - - const LogicalIndex logical2_2 {2, 2}; - const auto index2_2 = indexing.LogicalIndexToIndex(logical2_2); - EXPECT_EQ(index2_2, 28); - - LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2); - EXPECT_TRUE(logical == logical2_2); - + // Iterate over local for(int j = 0; j < dims[1]; j++) { for(int i = 0; i < dims[0]; i++) @@ -56,10 +46,46 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) const auto flat = indexing.LogicalIndexToIndex(logical); const auto logical2 = indexing.IndexToLogicalIndex(flat); EXPECT_EQ(logical, logical2); + + EXPECT_EQ(logical, indexing.GlobalToLocal(indexing.LocalToGlobal(logical))); + EXPECT_EQ(flat, indexing.GlobalToLocal(indexing.LocalToGlobal(flat))); } } - EXPECT_TRUE(indexing.contains(logical0_0)); + // Iterate over global + int index = 0; + for(int j = 0; j < dims[1] + origin[1]; j++) + { + for(int i = 0; i < stride[1]; i++, index++) + { + LogicalIndex logical {i, j}; + const auto flat = indexing.GlobalToGlobal(logical); + + // flat should start at 0 and increase + EXPECT_EQ(flat, index); + + // Global flat back to logical. + LogicalIndex logical2 = indexing.GlobalToGlobal(flat); + EXPECT_EQ(logical, logical2); + + // If we're in a valid region for the local window, try some other things. + if(i >= origin[0] && i < origin[0] + dims[0] && + j >= origin[1] && j < origin[1] + dims[1]) + { + const auto logicalLocal = indexing.GlobalToLocal(logical); + const auto flatLocal = indexing.GlobalToLocal(flat); + + // Flat local back to flat global + EXPECT_EQ(flat, indexing.LocalToGlobal(flatLocal)); + + // Logical local back to logical global + EXPECT_EQ(logical, indexing.LocalToGlobal(logicalLocal)); + } + } + } + + // Check whether these local points exist. + EXPECT_TRUE(indexing.contains(LogicalIndex{0,0})); EXPECT_TRUE(indexing.contains(LogicalIndex {dims[0] - 1, dims[1] - 1})); EXPECT_FALSE(indexing.contains(LogicalIndex {4, 0})); EXPECT_FALSE(indexing.contains(LogicalIndex {4, 3})); @@ -81,11 +107,11 @@ TEST(mir_views_indexing, strided_structured_indexing_3d) const LogicalIndex logical0_0_0 {0, 0, 0}; const auto index0_0_0 = indexing.LogicalIndexToIndex(logical0_0_0); - EXPECT_EQ(index0_0_0, 2 * 30 + 14); + EXPECT_EQ(index0_0_0, 0); const LogicalIndex logical2_2_2 {2, 2, 2}; const auto index2_2_2 = indexing.LogicalIndexToIndex(logical2_2_2); - EXPECT_EQ(index2_2_2, (2 + 2) * 30 + 28); + EXPECT_EQ(index2_2_2, 2 + 2*dims[0] + 2*dims[0]*dims[1]); LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2_2); EXPECT_TRUE(logical == logical2_2_2); diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index ed6f234e93..a2353e8ea1 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -22,6 +22,26 @@ namespace views * \brief This class encapsulates data for strided structured indexing and provides methods for creating/manipulating indices. * * \tparam NDIMS The number of dimensions. + * + * + * Strided structured indexing lets us index part of a larger overall indexing space. + * We can index it using indices local to the selected window and the class provides a + * mechanism to return indices in the overall indexing space. + * + * Example: + * + * x---x---x---x---x---x---x + * | | | | | | | + * x---x---*---*---*---*---x *=real node, x=ignored node, O=origin node 16 + * | | | | | | | + * x---x---*---*---*---*---x dims={4,3} + * | | | | | | | origin={2,2} + * x---x---O---*---*---*---x stride={1,7} + * | | | | | | | + * x---x---x---x---x---x---x + * | | | | | | | + * x---x---x---x---x---x---x + * */ template class StridedStructuredIndexing @@ -32,6 +52,12 @@ class StridedStructuredIndexing AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } + /** + * \brief Return whether the view supports strided structured indexing. + * \return true + */ + AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() { return true; } + /** * \brief constructor */ @@ -106,11 +132,121 @@ class StridedStructuredIndexing } /** - * \brief Turn an index into a logical index. + * \brief Turn a global logical index into an index. + * \param global The global logical index to convert. + * \return The global index. + */ + AXOM_HOST_DEVICE + IndexType GlobalToGlobal(const LogicalIndex &global) const + { + IndexType gl{}; + for(int i = 0; i < NDIMS; i++) + { + gl += global[i] * m_strides[i]; + } + return gl; + } + + /** + * \brief Turn a global index into a global logical index. + * + * \param global The index to convert. + * + * \return The local index that corresponds to the \a local. + */ + /// @{ + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, LogicalIndex>::type + GlobalToGlobal(IndexType global) const + { + LogicalIndex gl; + gl[0] = global; + return global; + } + + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, LogicalIndex>::type + GlobalToGlobal(IndexType global) const + { + LogicalIndex gl; + gl[0] = global % m_strides[1]; + gl[1] = global / m_strides[1]; + return gl; + } + + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, LogicalIndex>::type + GlobalToGlobal(IndexType global) const + { + LogicalIndex gl; + gl[0] = global % m_strides[1]; + gl[1] = (global % m_strides[2]) / m_strides[1]; + gl[2] = global / m_strides[2]; + return gl; + } + /// @} + + /** + * \brief Convert global logical index to a local one. + * \param local The local logical index. + * \return local logical index. + */ + AXOM_HOST_DEVICE + LogicalIndex GlobalToLocal(const LogicalIndex &global) const + { + LogicalIndex local(global); + for(int i = 0; i < NDIMS; i++) + { + local[i] -= m_offsets[i]; + } + return local; + } + + /** + * \brief Turn a global index into a local index. + * + * \param global The index to convert. + * + * \return The local index that corresponds to the \a local. + */ + IndexType GlobalToLocal(IndexType global) const + { + return LogicalIndexToIndex(GlobalToLocal(GlobalToGlobal(global))); + } + + /** + * \brief Convert local logical index to a global one. + * \param local The local logical index. + * \return global logical index. + */ + AXOM_HOST_DEVICE + LogicalIndex LocalToGlobal(const LogicalIndex &local) const + { + LogicalIndex global(local); + for(int i = 0; i < NDIMS; i++) + { + global[i] += m_offsets[i]; + } + return global; + } + + /** + * \brief Convert local logical index to a global one. + * \param local The local logical index. + * \return local logical index. + */ + AXOM_HOST_DEVICE + IndexType LocalToGlobal(IndexType local) const + { + return GlobalToGlobal(LocalToGlobal(IndexToLogicalIndex(local))); + } + + /** + * \brief Turn a local index into a local logical index. * * \param index The index to convert. * - * \return The logical index that corresponds to the \a index. + * \return The local logical index that corresponds to the \a index. */ /// @{ @@ -119,7 +255,7 @@ class StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = index - m_offsets[0]; + logical[0] = index; return logical; } @@ -128,8 +264,9 @@ class StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = index % m_strides[1] - m_offsets[0]; - logical[1] = index / m_strides[1] - m_offsets[1]; + const auto nx = m_dimensions[0]; + logical[0] = index % nx; + logical[1] = index / nx; return logical; } @@ -138,48 +275,36 @@ class StridedStructuredIndexing IndexToLogicalIndex(IndexType index) const { LogicalIndex logical; - logical[0] = (index % m_strides[1]) - m_offsets[0]; - logical[1] = ((index % m_strides[2]) / m_strides[1]) - m_offsets[1]; - logical[2] = (index / m_strides[2]) - m_offsets[2]; + const auto nx = m_dimensions[0]; + const auto nxy = nx * m_dimensions[1]; + logical[0] = index % nx; + logical[1] = (index % nxy) / nx; + logical[2] = index / nxy; return logical; } - /// @} + /** - * \brief Turn a logical index into a flat index. + * \brief Turn a local logical index into a local flat index. * * \param logical The logical indexto convert to a flat index. * * \return The index that corresponds to the \a logical index. */ - /// @{ - template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, IndexType>::type - LogicalIndexToIndex(const LogicalIndex &logical) const - { - return ((m_offsets[0] + logical[0]) * m_strides[0]); - } - - template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, IndexType>::type - LogicalIndexToIndex(const LogicalIndex &logical) const - { - return ((m_offsets[0] + logical[0]) * m_strides[0]) + - ((m_offsets[1] + logical[1]) * m_strides[1]); - } - - template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, IndexType>::type - LogicalIndexToIndex(const LogicalIndex &logical) const + AXOM_HOST_DEVICE + IndexType LogicalIndexToIndex(const LogicalIndex &logical) const { - return ((m_offsets[0] + logical[0]) * m_strides[0]) + - ((m_offsets[1] + logical[1]) * m_strides[1]) + - ((m_offsets[2] + logical[2]) * m_strides[2]); + IndexType index {}; + IndexType stride {1}; + for(int i = 0; i < NDIMS; i++) + { + index += logical[i] * stride; + stride *= m_dimensions[i]; + } + return index; } - /// @} - /** * \brief Determines whether the indexing contains the supplied logical index. * @@ -211,20 +336,6 @@ class StridedStructuredIndexing return contains(IndexToLogicalIndex(index)); } - /** - * \brief Expand the current StridedStructuredIndexing by one in each dimension. - * - * \return An expanded StridedStructuredIndexing. - */ - AXOM_HOST_DEVICE - StridedStructuredIndexing expand() const - { - StridedStructuredIndexing retval(*this); - for(int i = 0; i < dimension(); i++) retval.m_dimensions[i]++; - - return retval; - } - /** * \brief Expand the current StridedStructuredIndexing by one in each dimension. * diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index 7165ab8a01..35ad92eaab 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -31,6 +31,12 @@ class StructuredIndexing AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } + /** + * \brief Return whether the view supports strided structured indexing. + * \return false + */ + AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() { return false; } + /** * \brief constructor * @@ -85,6 +91,72 @@ class StructuredIndexing return m_dimensions[0] * m_dimensions[1]; } + /** + * \brief Turn a global logical index into an index. + * \param global The global logical index to convert. + * \return The global index. + */ + AXOM_HOST_DEVICE + inline IndexType GlobalToGlobal(const LogicalIndex &global) const + { + return LogicalIndexToIndex(global); + } + + /** + * \brief Turn a global index into a global logical index. + * \param global The global index to convert. + * \return The global logical index. + */ + AXOM_HOST_DEVICE + inline LogicalIndex GlobalToGlobal(IndexType global) const + { + return IndexToLogicalIndex(global); + } + + /** + * \brief Turn global logical index to local logical index. no-op. + * \param index The index to convert. + * \return Same as the input in this case. + */ + AXOM_HOST_DEVICE + inline LogicalIndex GlobalToLocal(const LogicalIndex &index) const + { + return index; + } + + /** + * \brief Turn global index to local index. no-op. + * \param index The index to convert. + * \return Same as the input in this case. + */ + AXOM_HOST_DEVICE + inline IndexType GlobalToLocal(IndexType index) const + { + return index; + } + + /** + * \brief Turn local logical index to global logical index. no-op. + * \param index The index to convert. + * \return Same as the input in this case. + */ + AXOM_HOST_DEVICE + inline LogicalIndex LocalToGlobal(const LogicalIndex &index) const + { + return index; + } + + /** + * \brief Turn local index to global index. no-op. + * \param index The index to convert. + * \return Same as the input in this case. + */ + AXOM_HOST_DEVICE + inline IndexType LocalToGlobal(IndexType index) const + { + return index; + } + /** * \brief Turn an index into a logical index. * @@ -201,7 +273,10 @@ class StructuredIndexing StructuredIndexing expand() const { StructuredIndexing retval(*this); - for(int i = 0; i < dimension(); i++) retval.m_dimensions[i]++; + for(int i = 0; i < dimension(); i++) + { + retval.m_dimensions[i]++; + } return retval; } diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index b961799881..9c5291bae5 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -36,6 +36,12 @@ class StructuredTopologyView AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } + /** + * \brief Return whether the indexing supports strided structured indexing. + * \return True if the indexing supports strided structured indexing, false otherwise. + */ + constexpr static bool supports_strided_structured_indexing() { return IndexingPolicy::supports_strided_structured_indexing(); } + /** * \brief Constructor * @@ -65,11 +71,20 @@ class StructuredTopologyView * * \return The mesh logical dimensions. */ + AXOM_HOST_DEVICE const LogicalIndex &logicalDimensions() const { return m_indexing.logicalDimensions(); } + /** + * \brief Return indexing object. + * + * \return The indexing object. + */ + AXOM_HOST_DEVICE + const IndexingPolicy &indexing() const { return m_indexing; } + /** * \brief Execute a function for each zone in the mesh using axom::for_all. * @@ -97,11 +112,11 @@ class StructuredTopologyView AXOM_LAMBDA(auto zoneIndex) { using ShapeType = HexShape; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); IndexType data[8]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -125,10 +140,10 @@ class StructuredTopologyView AXOM_LAMBDA(auto zoneIndex) { using ShapeType = QuadShape; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); IndexType data[4]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -148,9 +163,9 @@ class StructuredTopologyView AXOM_LAMBDA(auto zoneIndex) { using ShapeType = LineShape; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[2]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; const ShapeType shape(axom::ArrayView(data, 2)); @@ -190,11 +205,11 @@ class StructuredTopologyView using ShapeType = HexShape; const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); IndexType data[8]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -219,10 +234,10 @@ class StructuredTopologyView using ShapeType = QuadShape; const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); IndexType data[4]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -243,9 +258,9 @@ class StructuredTopologyView using ShapeType = LineShape; const auto zoneIndex = idsView[selectIndex]; - const auto logical = zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[2]; - data[0] = nodeIndexing.LogicalIndexToIndex(logical); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; const ShapeType shape(axom::ArrayView(data, 2)); diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index c99b20844a..e025610943 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -121,6 +121,12 @@ class UnstructuredTopologyMixedShapeView */ static constexpr int dimension() { return -1; } + /** + * \brief Return whether the view supports strided structured indexing. + * \return false + */ + static constexpr bool supports_strided_structured_indexing() { return false; } + /** * \brief Return the number of zones. * diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index b1cb5efc46..b53ee953c7 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -194,6 +194,12 @@ class UnstructuredTopologyPolyhedralView */ static constexpr int dimension() { return 3; } + /** + * \brief Return whether the view supports strided structured indexing. + * \return false + */ + static constexpr bool supports_strided_structured_indexing() { return false; } + template void for_all_zones(FuncType &&func) const { diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index a6d587f75a..c47fd7de5d 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -65,6 +65,12 @@ class UnstructuredTopologySingleShapeView */ static constexpr int dimension() { return ShapeT::dimension(); } + /** + * \brief Return whether the view supports strided structured indexing. + * \return false + */ + static constexpr bool supports_strided_structured_indexing() { return false; } + /** * \brief Return the number of zones. * diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 668e0e6987..3a64ea17eb 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -25,7 +25,7 @@ struct make_rectilinear_coordset { }; /** - * \brief Partial specialization for creating 2D rectilinear coordset view. + * \brief Partial specialization for creating 3D rectilinear coordset view. */ template struct make_rectilinear_coordset diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index fc4a8fe77a..ce3ebdff40 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -34,19 +34,18 @@ namespace views template bool fillFromNode(const conduit::Node &n, const std::string &key, - ArrayType &arr, - int fillValue) + ArrayType &arr) { bool found = false; if((found = n.has_path(key)) == true) { const auto acc = n.fetch_existing(key).as_int_accessor(); - for(int i = 0; i < arr.size(); i++) arr[i] = acc[i]; - } - else - { - for(int i = 0; i < arr.size(); i++) arr[i] = fillValue; + for(int i = 0; i < arr.size(); i++) + { + arr[i] = acc[i]; + } } + return found; } @@ -68,30 +67,45 @@ struct make_strided_structured<3> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) + LogicalIndex offsets{{0, 0, 0}}, strides{{1, 1, 1}}; + fillFromNode(topo, offsetsKey, offsets); + if(fillFromNode(topo, stridesKey, strides)) + { + // Make zone striding. + strides[1]--; + strides[2] = strides[1] * (zoneDims[1] - 1); + } + else { strides[1] = zoneDims[0]; strides[2] = zoneDims[0] * zoneDims[1]; } - Indexing zoneIndexing(zoneDims, offsets, strides); - return TopoView(zoneIndexing); + return Indexing(zoneDims, offsets, strides); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; @@ -106,27 +120,41 @@ struct make_strided_structured<2> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing. */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - if(!fillFromNode(topo, stridesKey, strides, 1)) + LogicalIndex offsets{{0, 0}}, strides{{1, 1}}; + fillFromNode(topo, offsetsKey, offsets); + if(fillFromNode(topo, stridesKey, strides)) + { + // Make zone striding. + strides[1]--; + } + else { strides[1] = zoneDims[0]; } - Indexing zoneIndexing(zoneDims, offsets, strides); - return TopoView(zoneIndexing); + return Indexing(zoneDims, offsets, strides); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; @@ -141,24 +169,33 @@ struct make_strided_structured<1> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indxing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing. */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - LogicalIndex offsets, strides; - fillFromNode(topo, offsetsKey, offsets, 0); - fillFromNode(topo, stridesKey, strides, 1); + LogicalIndex offsets{0}, strides{1}; + fillFromNode(topo, offsetsKey, offsets); + fillFromNode(topo, stridesKey, strides); - Indexing zoneIndexing(zoneDims, offsets, strides); - return TopoView(zoneIndexing); + return Indexing(zoneDims, offsets, strides); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; @@ -263,8 +300,8 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) int ndims = 1; ndims += topo.has_path("elements/dims/j") ? 1 : 0; ndims += topo.has_path("elements/dims/k") ? 1 : 0; - const std::string offsetsKey("elements/offsets"); - const std::string stridesKey("elements/strides"); + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); switch(ndims) { From 817fc1df77eb2c164a46271a65ccf2a8f153e9c1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 6 Aug 2024 16:32:25 -0700 Subject: [PATCH 141/290] Improvements for strided structured. --- src/axom/mir/ClipField.hpp | 75 +++++++++++++++---- src/axom/mir/FieldBlender.hpp | 59 ++++++--------- src/axom/mir/FieldSlicer.hpp | 41 ++++++++-- src/axom/mir/blueprint_utilities.hpp | 71 ++++++++++++++++++ src/axom/mir/tests/mir_clipfield.cpp | 1 + .../mir/views/StridedStructuredIndexing.hpp | 6 +- .../views/dispatch_structured_topology.hpp | 60 +++++++++++---- src/axom/mir/views/view_traits.hpp | 49 ++++++++++++ 8 files changed, 283 insertions(+), 79 deletions(-) create mode 100644 src/axom/mir/views/view_traits.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index aaf730738b..3f88c5ed40 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -13,7 +13,8 @@ #include "axom/mir/CoordsetBlender.hpp" #include "axom/mir/FieldSlicer.hpp" #include "axom/mir/blueprint_utilities.hpp" -#include "axom/mir/utilities.hpp" // for cpp2conduit +#include "axom/mir/utilities.hpp" +#include "axom/mir/views/view_traits.hpp" #include #include @@ -212,6 +213,12 @@ template class ClipOptions { public: + /** + * \brief Constructor + * + * \param nzones The total number of zones in the associated topology. + * \param options The node that contains the clipping options. + */ ClipOptions(axom::IndexType nzones, const conduit::Node &options) : m_nzones(nzones) , m_options(options) @@ -221,6 +228,8 @@ class ClipOptions /** * \brief Return a view that contains the list of selected zone ids for the mesh. * \return A view that contains the list of selected zone ids for the mesh. + * + * \note The data for the view is generated if it has not yet been built. */ axom::ArrayView selectedZonesView() { @@ -355,6 +364,10 @@ class ClipOptions * \brief The options may contain a "selectedZones" member that is a list of zones * that will be operated on. If such an array is present, copy and sort it. * If the zone list is not present, make an array that selects every zone. + * + * \note selectedZones should contain local zone numbers, which in the case of + * strided-structured indexing are the [0..n) zone numbers that exist only + * within the selected window. */ void buildSelectedZones() { @@ -959,8 +972,10 @@ class ClipField conduit::Node &n_newFields) { const auto allocatorID = axom::execution_space::allocatorID(); - const auto nzones = m_topologyView.numberOfZones(); - ClipOptions opts(nzones, n_options); + + // Make the selected zones and get the size. + ClipOptions opts(m_topologyView.numberOfZones(), n_options); + const auto nzones = opts.selectedZonesView().size(); // Get the clip field. std::string clipFieldName = opts.clipField(); @@ -1086,13 +1101,18 @@ class ClipField fragmentData.m_finalNumZones, allocatorID); auto sliceIndicesView = sliceIndices.view(); + + // Fill in sliceIndicesView. + const auto selectedZonesView = opts.selectedZonesView(); axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { - const auto start = fragmentData.m_fragmentOffsetsView[zoneIndex]; - for(int i = 0; i < fragmentData.m_fragmentsView[zoneIndex]; i++) + AXOM_LAMBDA(auto index) { + const auto zoneIndex = selectedZonesView[index]; + const auto start = fragmentData.m_fragmentOffsetsView[index]; + for(int i = 0; i < fragmentData.m_fragmentsView[index]; i++) sliceIndicesView[start + i] = zoneIndex; }); + slice.m_indicesView = sliceIndicesView; makeFields(blend, slice, @@ -1695,35 +1715,60 @@ class ClipField const std::string association = n_field["association"].as_string(); if(association == "element") { - axom::mir::utilities::blueprint::FieldSlicer s; - s.execute(slice, n_field, n_out_fields[it->second]); + bool handled = false; + // Conditionally support strided-structured. + if constexpr (axom::mir::views::view_traits::supports_strided_structured()) + { + if(n_field.has_path("offsets") && n_field.has_path("strides")) + { + using Indexing = typename TopologyView::IndexingPolicy; + using IndexingPolicy = axom::mir::utilities::blueprint::LocalToGlobalIndexing; + IndexingPolicy indexing; + indexing.m_indexing = m_topologyView.indexing(); + + axom::mir::utilities::blueprint::FieldSlicer s(indexing); + s.execute(slice, n_field, n_out_fields[it->second]); + handled = true; + } + } + if(!handled) + { + axom::mir::utilities::blueprint::FieldSlicer s; + s.execute(slice, n_field, n_out_fields[it->second]); + } + n_out_fields[it->second]["topology"] = topologyName; } else if(association == "vertex") { bool handled = false; - // If we have fields that look like strided structured, try and support that - // if the topology view supports it. - if(n_field.has_path("offsets") || n_field.has_path("strides")) +#if 0 + // Node indices in the blend groups are global indices. This means that, provided the + // field's offsets/strides match the topology's (and why would they not?), we can skip + // strided structured support for now. + + // Conditionally support strided-structured. + if constexpr (axom::mir::views::view_traits::supports_strided_structured()) { - if constexpr (TopologyView::supports_strided_structured_indexing()) + if(n_field.has_path("offsets") && n_field.has_path("strides")) { // Make node indexing that the field blender can use. using Indexing = typename TopologyView::IndexingPolicy; - using IndexerType = axom::mir::utilities::blueprint::MapNodesThroughIndexing; - IndexerType indexing; + using IndexingPolicy = axom::mir::utilities::blueprint::GlobalToLocalIndexing; + IndexingPolicy indexing; indexing.m_indexing = m_topologyView.indexing().expand(); // Blend the field. axom::mir::utilities::blueprint::FieldBlender< ExecSpace, axom::mir::utilities::blueprint::SelectThroughArrayView, - IndexerType> + IndexingPolicy> b(indexing); b.execute(blend, n_field, n_out_fields[it->second]); handled = true; } } +#endif if(!handled) { axom::mir::utilities::blueprint::FieldBlender< diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 845fe18d2e..230011765f 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -70,30 +70,6 @@ struct SelectThroughArrayView } }; -/// Does no node index mapping. -struct NoMapping -{ - AXOM_HOST_DEVICE - inline axom::IndexType operator [](axom::IndexType index) const - { - return index; - } -}; - -/// Maps node indices to their local indexing used in vertex fields. -template -struct MapNodesThroughIndexing -{ - AXOM_HOST_DEVICE - inline axom::IndexType operator [](axom::IndexType index) const - { - return m_indexing.GlobalToLocal(index); - } - - Indexing m_indexing{}; -}; - - /** * \accelerated * \class FieldBlender @@ -102,18 +78,21 @@ struct MapNodesThroughIndexing * * \tparam ExecSpace The execution space where the work will occur. * \tparam SelectionPolicy The selection policy to use. - * \tparam NodeIndexingPolicy How/if changes in node indexing occur when accessing nodes. + * \tparam IndexingPolicy A class that provides operator[] that can transform node indices. */ -template +template class FieldBlender { public: /// Constructor - FieldBlender() : m_nodeIndexing() + FieldBlender() : m_indexing() { } - /// Constructor - FieldBlender(const NodeIndexingPolicy &indexing) : m_nodeIndexing(indexing) + /** + * \brief Constructor + * \param indexing An object used to transform node indices. + */ + FieldBlender(const IndexingPolicy &indexing) : m_indexing(indexing) { } /** @@ -139,12 +118,12 @@ class FieldBlender { const conduit::Node &n_comp = n_input_values[i]; conduit::Node &n_out_comp = n_output_values[n_comp.name()]; - blendSingleComponent(blend, n_comp, n_out_comp); + blendSingleComponent(blend, n_input, n_comp, n_out_comp); } } else { - blendSingleComponent(blend, n_input_values, n_output_values); + blendSingleComponent(blend, n_input, n_input_values, n_output_values); } } @@ -154,10 +133,12 @@ class FieldBlender * \brief Blend data for a single field component. * * \param blend The BlendData that will be used to make the new field. + * \param n_input The node that contains the field. * \param n_values The input values that we're blending. * \param n_output_values The output node that will contain the new field. */ void blendSingleComponent(const BlendData &blend, + const conduit::Node &n_input, const conduit::Node &n_values, conduit::Node &n_output_values) const { @@ -165,7 +146,7 @@ class FieldBlender // groups. If the user did not provide that, use all blend groups. const auto outputSize = SelectionPolicy::size(blend); - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + // Allocate Conduit data through Axom. utilities::blueprint::ConduitAllocateThroughAxom c2a; n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); @@ -178,9 +159,11 @@ class FieldBlender using accum_type = typename axom::mir::utilities::accumulation_traits::type; - // Make capturable objects for the lambda. + // Let the indexing object update itself from the node strides/offsets. + IndexingPolicy deviceIndexing(m_indexing); + deviceIndexing.update(n_input); + const BlendData deviceBlend(blend); - const NodeIndexingPolicy nodeIndexing(m_nodeIndexing); axom::for_all( outputSize, @@ -195,10 +178,10 @@ class FieldBlender accum_type blended = 0; for(IndexType i = start; i < end; i++) { - // Get the index that we're using, potentially mapping the node value. - const auto index = nodeIndexing[deviceBlend.m_blendIdsView[i]]; + const auto index = deviceBlend.m_blendIdsView[i]; const auto weight = deviceBlend.m_blendCoeffView[i]; - blended += static_cast(compView[index]) * weight; + const auto transformedIndex = deviceIndexing[index]; + blended += static_cast(compView[transformedIndex]) * weight; } outView[bgid] = static_cast(blended); }); @@ -206,7 +189,7 @@ class FieldBlender } private: - NodeIndexingPolicy m_nodeIndexing {}; + IndexingPolicy m_indexing {}; }; } // end namespace blueprint diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 13d424bc3d..12a863d6a3 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -32,11 +32,28 @@ struct SliceData * \class FieldSlicer * * \brief This class uses SliceData to generate a new sliced field from an input field. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam IndexingPolicy A class that provides operator[] that can transform zone indices. + * */ -template +template class FieldSlicer { public: + /// Constructor + FieldSlicer() : m_indexing() + { + } + + /** + * \brief Constructor + * \param indexing An object used to transform node indices. + */ + FieldSlicer(const IndexingPolicy &indexing) : m_indexing(indexing) + { + } + /** * \brief Execute the slice on the \a n_input field and store the new sliced field in \a n_output. * @@ -62,12 +79,12 @@ class FieldSlicer { const conduit::Node &n_comp = n_input_values[i]; conduit::Node &n_out_comp = n_output_values[n_comp.name()]; - sliceSingleComponent(slice, n_comp, n_out_comp); + sliceSingleComponent(slice, n_input, n_comp, n_out_comp); } } else { - sliceSingleComponent(slice, n_input_values, n_output_values); + sliceSingleComponent(slice, n_input, n_input_values, n_output_values); } } @@ -76,16 +93,18 @@ class FieldSlicer * \brief Slice data for a single field component. * * \param slice The SliceData that will be used to make the new field. + * \param n_input The node that contains the field. * \param n_values The input values that we're slicing. * \param n_output_values The output node that will contain the new field. */ void sliceSingleComponent(const SliceData &slice, + const conduit::Node &n_input, const conduit::Node &n_values, conduit::Node &n_output_values) const { const auto outputSize = slice.m_indicesView.size(); - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + // Allocate Conduit data through Axom. utilities::blueprint::ConduitAllocateThroughAxom c2a; n_output_values.set_allocator(c2a.getConduitAllocatorID()); n_output_values.set(conduit::DataType(n_values.dtype().id(), outputSize)); @@ -94,14 +113,24 @@ class FieldSlicer n_values, n_output_values, [&](auto valuesView, auto outputView) { - const SliceData deviceSlice(slice); + + // Let the indexing object update itself from the node strides/offsets. + IndexingPolicy deviceIndexing(m_indexing); + deviceIndexing.update(n_input); + + SliceData deviceSlice(slice); axom::for_all( outputSize, AXOM_LAMBDA(auto index) { - outputView[index] = valuesView[deviceSlice.m_indicesView[index]]; + const auto zoneIndex = deviceSlice.m_indicesView[index]; + const auto transformedIndex = deviceIndexing[zoneIndex]; + outputView[index] = valuesView[transformedIndex]; }); }); } + +private: + IndexingPolicy m_indexing {}; }; } // end namespace blueprint diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index af94fcd5e6..fc38537ddf 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -152,6 +152,77 @@ class ConduitAllocateThroughAxom } }; +//------------------------------------------------------------------------------ + +/** + * \brief Returns the input index (no changes). + */ +struct DirectIndexing +{ + void update(const conduit::Node &) + { + } + + /** + * \brief Return the input index (no changes). + * \param index The input index. + * \return The input index. + */ + AXOM_HOST_DEVICE + inline axom::IndexType operator [](axom::IndexType index) const + { + return index; + } +}; + +/** + * \brief Maps a local index to a global index through an indexing object (used for strided structured grids). + * \tparam Indexing A StridedStructuredIndexing of some dimension. + */ +template +struct LocalToGlobalIndexing +{ + /** + * \brief Update the indexing offsets/strides from a Conduit node. + * \param field The Conduit node for a field. + */ + void update(const conduit::Node &field) + { + fillFromNode(field, "offsets", m_indexing.m_offsets); + fillFromNode(field, "strides", m_indexing.m_strides); + } + + /** + * \brief Transforms the index from local to global through an indexing object. + * \param index The local index + * \return The global index for the field. + */ + AXOM_HOST_DEVICE + inline axom::IndexType operator [](axom::IndexType index) const + { + return m_indexing.LocalToGlobal(index); + } + + template + static bool fillFromNode(const conduit::Node &n, + const std::string &key, + ArrayType &arr) + { + bool found = false; + if((found = n.has_path(key)) == true) + { + const auto acc = n.fetch_existing(key).as_int_accessor(); + for(int i = 0; i < arr.size(); i++) + { + arr[i] = acc[i]; + } + } + return found; + } + + Indexing m_indexing{}; +}; + //------------------------------------------------------------------------------ /** * \accelerated diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 44e2f64d09..6d86bbd52a 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -752,6 +752,7 @@ void strided_structured_clip_test(const std::string &name) axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); #endif conduit::Node options, deviceClipMesh, hostClipMesh; diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index a2353e8ea1..af92fb6b4c 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -44,11 +44,10 @@ namespace views * */ template -class StridedStructuredIndexing +struct StridedStructuredIndexing { -public: using IndexType = IndexT; - using LogicalIndex = axom::StackArray; + using LogicalIndex = axom::StackArray; AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } @@ -382,7 +381,6 @@ class StridedStructuredIndexing /// @} -private: LogicalIndex m_dimensions {}; LogicalIndex m_offsets {}; LogicalIndex m_strides {}; diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index ce3ebdff40..d460d499b2 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -21,6 +21,8 @@ namespace mir { namespace views { + +//------------------------------------------------------------------------------ /** * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. * @@ -169,7 +171,7 @@ struct make_strided_structured<1> using TopoView = views::StructuredTopologyView; /** - * \brief Create the indxing and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. */ @@ -217,19 +219,28 @@ struct make_structured<3> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing. */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - Indexing zoneIndexing(zoneDims); - return TopoView(zoneIndexing); + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; @@ -244,18 +255,26 @@ struct make_structured<2> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing. */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); + return Indexing(zoneDims); + } - Indexing zoneIndexing(zoneDims); - return TopoView(zoneIndexing); + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; @@ -270,17 +289,26 @@ struct make_structured<1> using TopoView = views::StructuredTopologyView; /** - * \brief Create the topology view and initialize it from the topology. + * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. - * \return The topology view. + * \return The indexing. */ - static TopoView view(const conduit::Node &topo) + static Indexing indexing(const conduit::Node &topo) { LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - Indexing zoneIndexing(zoneDims); - return TopoView(zoneIndexing); + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); } }; diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp new file mode 100644 index 0000000000..974efaff7e --- /dev/null +++ b/src/axom/mir/views/view_traits.hpp @@ -0,0 +1,49 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_VIEW_TRAITS_HPP_ +#define AXOM_MIR_VIEW_TRAITS_HPP_ + +#include "axom/mir/views/StructuredTopologyView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +/// General traits for topology views. +template +struct view_traits +{ + static constexpr bool supports_strided_structured() { return false; } +}; + +/// If StructuredTopologyView was instantiated with StridedStructuredIndexing +/// (of varying dimensions) then say that strided structured is supported. +template +struct view_traits>> +{ + static constexpr bool supports_strided_structured() { return true; } +}; + +template +struct view_traits>> +{ + static constexpr bool supports_strided_structured() { return true; } +}; + +template +struct view_traits>> +{ + static constexpr bool supports_strided_structured() { return true; } +}; + +} // end namespace views +} // end namespace mir +} // end namespace axom + +#endif From 1bf8fcd9e7b6dfc4b25494fbcc5530016b73c3f2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 6 Aug 2024 17:38:32 -0700 Subject: [PATCH 142/290] Improved strided structure support --- src/axom/mir/ClipField.hpp | 60 +++++++++++++-------- src/axom/mir/FieldBlender.hpp | 10 +--- src/axom/mir/FieldSlicer.hpp | 9 +--- src/axom/mir/blueprint_utilities.hpp | 80 ++++++++++++++++++++-------- 4 files changed, 100 insertions(+), 59 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 3f88c5ed40..8c1eacba07 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1722,9 +1722,10 @@ class ClipField if(n_field.has_path("offsets") && n_field.has_path("strides")) { using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = axom::mir::utilities::blueprint::LocalToGlobalIndexing; + using IndexingPolicy = axom::mir::utilities::blueprint::SSElementFieldIndexing; IndexingPolicy indexing; indexing.m_indexing = m_topologyView.indexing(); + indexing.update(n_field); axom::mir::utilities::blueprint::FieldSlicer s(indexing); s.execute(slice, n_field, n_out_fields[it->second]); @@ -1742,10 +1743,11 @@ class ClipField else if(association == "vertex") { bool handled = false; -#if 0 + // Node indices in the blend groups are global indices. This means that, provided the // field's offsets/strides match the topology's (and why would they not?), we can skip - // strided structured support for now. + // strided structured support for now. Enable this code if the field offsets/strides + // do not match the topo's offsets/strides. // Conditionally support strided-structured. if constexpr (axom::mir::views::view_traits::supports_strided_structured()) @@ -1754,23 +1756,32 @@ class ClipField { // Make node indexing that the field blender can use. using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = axom::mir::utilities::blueprint::GlobalToLocalIndexing; + using IndexingPolicy = axom::mir::utilities::blueprint::SSVertexFieldIndexing; IndexingPolicy indexing; - indexing.m_indexing = m_topologyView.indexing().expand(); - - // Blend the field. - axom::mir::utilities::blueprint::FieldBlender< - ExecSpace, - axom::mir::utilities::blueprint::SelectThroughArrayView, - IndexingPolicy> - b(indexing); - b.execute(blend, n_field, n_out_fields[it->second]); - handled = true; + indexing.m_topoIndexing = m_topologyView.indexing().expand(); + indexing.m_fieldIndexing = m_topologyView.indexing().expand(); + indexing.update(n_field); + + // If the topo and field offsets/strides are different then we need to go through + // SSVertexFieldIndexing. Otherwise, we can let the normal case further below + // handle the field. + if(indexing.m_topoIndexing.m_offsets != indexing.m_fieldIndexing.m_offsets || + indexing.m_topoIndexing.m_strides != indexing.m_fieldIndexing.m_strides) + { + // Blend the field. + axom::mir::utilities::blueprint::FieldBlender< + ExecSpace, + axom::mir::utilities::blueprint::SelectThroughArrayView, + IndexingPolicy> + b(indexing); + b.execute(blend, n_field, n_out_fields[it->second]); + handled = true; + } } } -#endif if(!handled) { + // Blend the field. axom::mir::utilities::blueprint::FieldBlender< ExecSpace, axom::mir::utilities::blueprint::SelectThroughArrayView> @@ -1795,7 +1806,7 @@ class ClipField * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeOriginalElements(FragmentData fragmentData, - const ClipOptions &opts, + ClipOptions &opts, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const @@ -1806,7 +1817,8 @@ class ClipField utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); - const auto nzones = m_topologyView.numberOfZones(); + const auto selectedZonesView = opts.selectedZonesView(); + const auto nzones = selectedZonesView.size(); if(n_fields.has_child("originalElements")) { @@ -1827,9 +1839,10 @@ class ClipField fragmentData.m_finalNumZones); axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + AXOM_LAMBDA(auto index) { + const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; + const int nFragments = fragmentData.m_fragmentsView[index]; + const auto zoneIndex = selectedZonesView[index]; for(int i = 0; i < nFragments; i++) valuesView[sizeIndex + i] = origValuesView[zoneIndex]; }); @@ -1849,9 +1862,10 @@ class ClipField fragmentData.m_finalNumZones); axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; - int nFragments = fragmentData.m_fragmentsView[zoneIndex]; + AXOM_LAMBDA(auto index) { + const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; + const int nFragments = fragmentData.m_fragmentsView[index]; + const auto zoneIndex = selectedZonesView[index]; for(int i = 0; i < nFragments; i++) valuesView[sizeIndex + i] = zoneIndex; }); diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 230011765f..854dee7972 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -118,12 +118,12 @@ class FieldBlender { const conduit::Node &n_comp = n_input_values[i]; conduit::Node &n_out_comp = n_output_values[n_comp.name()]; - blendSingleComponent(blend, n_input, n_comp, n_out_comp); + blendSingleComponent(blend, n_comp, n_out_comp); } } else { - blendSingleComponent(blend, n_input, n_input_values, n_output_values); + blendSingleComponent(blend, n_input_values, n_output_values); } } @@ -133,12 +133,10 @@ class FieldBlender * \brief Blend data for a single field component. * * \param blend The BlendData that will be used to make the new field. - * \param n_input The node that contains the field. * \param n_values The input values that we're blending. * \param n_output_values The output node that will contain the new field. */ void blendSingleComponent(const BlendData &blend, - const conduit::Node &n_input, const conduit::Node &n_values, conduit::Node &n_output_values) const { @@ -159,12 +157,8 @@ class FieldBlender using accum_type = typename axom::mir::utilities::accumulation_traits::type; - // Let the indexing object update itself from the node strides/offsets. IndexingPolicy deviceIndexing(m_indexing); - deviceIndexing.update(n_input); - const BlendData deviceBlend(blend); - axom::for_all( outputSize, AXOM_LAMBDA(auto bgid) { diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 12a863d6a3..4b5e152970 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -79,12 +79,12 @@ class FieldSlicer { const conduit::Node &n_comp = n_input_values[i]; conduit::Node &n_out_comp = n_output_values[n_comp.name()]; - sliceSingleComponent(slice, n_input, n_comp, n_out_comp); + sliceSingleComponent(slice, n_comp, n_out_comp); } } else { - sliceSingleComponent(slice, n_input, n_input_values, n_output_values); + sliceSingleComponent(slice, n_input_values, n_output_values); } } @@ -93,12 +93,10 @@ class FieldSlicer * \brief Slice data for a single field component. * * \param slice The SliceData that will be used to make the new field. - * \param n_input The node that contains the field. * \param n_values The input values that we're slicing. * \param n_output_values The output node that will contain the new field. */ void sliceSingleComponent(const SliceData &slice, - const conduit::Node &n_input, const conduit::Node &n_values, conduit::Node &n_output_values) const { @@ -114,10 +112,7 @@ class FieldSlicer n_output_values, [&](auto valuesView, auto outputView) { - // Let the indexing object update itself from the node strides/offsets. IndexingPolicy deviceIndexing(m_indexing); - deviceIndexing.update(n_input); - SliceData deviceSlice(slice); axom::for_all( outputSize, diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index fc38537ddf..80f60ff4b2 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -152,6 +152,23 @@ class ConduitAllocateThroughAxom } }; +//------------------------------------------------------------------------------ +template +bool fillFromNode(const conduit::Node &n, + const std::string &key, + ArrayType &arr) +{ + bool found = false; + if((found = n.has_path(key)) == true) + { + const auto acc = n.fetch_existing(key).as_int_accessor(); + for(int i = 0; i < arr.size(); i++) + { + arr[i] = acc[i]; + } + } + return found; +} //------------------------------------------------------------------------------ /** @@ -159,10 +176,6 @@ class ConduitAllocateThroughAxom */ struct DirectIndexing { - void update(const conduit::Node &) - { - } - /** * \brief Return the input index (no changes). * \param index The input index. @@ -175,12 +188,13 @@ struct DirectIndexing } }; +//------------------------------------------------------------------------------ /** - * \brief Maps a local index to a global index through an indexing object (used for strided structured grids). + * \brief Help turn slice data zone indices into strided structured element field indices. * \tparam Indexing A StridedStructuredIndexing of some dimension. */ template -struct LocalToGlobalIndexing +struct SSElementFieldIndexing { /** * \brief Update the indexing offsets/strides from a Conduit node. @@ -203,24 +217,48 @@ struct LocalToGlobalIndexing return m_indexing.LocalToGlobal(index); } - template - static bool fillFromNode(const conduit::Node &n, - const std::string &key, - ArrayType &arr) + Indexing m_indexing{}; +}; + +//------------------------------------------------------------------------------ +/** + * \brief Help turn blend group node indices (global) into vertex field indices. + * \tparam Indexing A StridedStructuredIndexing of some dimension. + */ +template +struct SSVertexFieldIndexing +{ + /** + * \brief Update the indexing offsets/strides from a Conduit node. + * \param field The Conduit node for a field. + */ + void update(const conduit::Node &field) { - bool found = false; - if((found = n.has_path(key)) == true) - { - const auto acc = n.fetch_existing(key).as_int_accessor(); - for(int i = 0; i < arr.size(); i++) - { - arr[i] = acc[i]; - } - } - return found; + fillFromNode(field, "offsets", m_fieldIndexing.m_offsets); + fillFromNode(field, "strides", m_fieldIndexing.m_strides); } - Indexing m_indexing{}; + /** + * \brief Transforms the index from local to global through an indexing object. + * \param index The global index + * \return The global index for the field. + */ + AXOM_HOST_DEVICE + inline axom::IndexType operator [](axom::IndexType index) const + { + // Make the global index into a global logical in the topo. + const auto topoGlobalLogical = m_topoIndexing.GlobalToGlobal(index); + // Make the global logical into a local logical in the topo. + const auto topoLocalLogical = m_topoIndexing.GlobalToLocal(topoGlobalLogical); + // Make the global logical index in the field. + const auto fieldGlobalLogical = m_fieldIndexing.LocalToGlobal(topoLocalLogical); + // Make the global index in the field. + const auto fieldGlobalIndex = m_fieldIndexing.GlobalToGlobal(fieldGlobalLogical); + return fieldGlobalIndex; + } + + Indexing m_topoIndexing {}; + Indexing m_fieldIndexing {}; }; //------------------------------------------------------------------------------ From 21fc10d8ddb4bcae2713674d113c5731f551ac99 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 6 Aug 2024 17:39:55 -0700 Subject: [PATCH 143/290] make style --- src/axom/mir/ClipField.hpp | 21 ++++++--- src/axom/mir/FieldBlender.hpp | 10 ++--- src/axom/mir/FieldSlicer.hpp | 9 +--- src/axom/mir/blueprint_utilities.hpp | 18 ++++---- src/axom/mir/tests/mir_clipfield.cpp | 45 ++++++++++--------- src/axom/mir/tests/mir_views.cpp | 18 +++----- src/axom/mir/tests/mir_views_indexing.cpp | 10 ++--- .../mir/views/StridedStructuredIndexing.hpp | 8 ++-- src/axom/mir/views/StructuredIndexing.hpp | 15 +++---- src/axom/mir/views/StructuredTopologyView.hpp | 23 +++++++--- .../views/dispatch_structured_topology.hpp | 11 ++--- 11 files changed, 95 insertions(+), 93 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 8c1eacba07..8a3c646619 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1717,17 +1717,20 @@ class ClipField { bool handled = false; // Conditionally support strided-structured. - if constexpr (axom::mir::views::view_traits::supports_strided_structured()) + if constexpr(axom::mir::views::view_traits< + TopologyView>::supports_strided_structured()) { if(n_field.has_path("offsets") && n_field.has_path("strides")) { using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = axom::mir::utilities::blueprint::SSElementFieldIndexing; + using IndexingPolicy = + axom::mir::utilities::blueprint::SSElementFieldIndexing; IndexingPolicy indexing; indexing.m_indexing = m_topologyView.indexing(); indexing.update(n_field); - axom::mir::utilities::blueprint::FieldSlicer s(indexing); + axom::mir::utilities::blueprint::FieldSlicer s( + indexing); s.execute(slice, n_field, n_out_fields[it->second]); handled = true; } @@ -1750,13 +1753,15 @@ class ClipField // do not match the topo's offsets/strides. // Conditionally support strided-structured. - if constexpr (axom::mir::views::view_traits::supports_strided_structured()) + if constexpr(axom::mir::views::view_traits< + TopologyView>::supports_strided_structured()) { if(n_field.has_path("offsets") && n_field.has_path("strides")) { // Make node indexing that the field blender can use. using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = axom::mir::utilities::blueprint::SSVertexFieldIndexing; + using IndexingPolicy = + axom::mir::utilities::blueprint::SSVertexFieldIndexing; IndexingPolicy indexing; indexing.m_topoIndexing = m_topologyView.indexing().expand(); indexing.m_fieldIndexing = m_topologyView.indexing().expand(); @@ -1765,8 +1770,10 @@ class ClipField // If the topo and field offsets/strides are different then we need to go through // SSVertexFieldIndexing. Otherwise, we can let the normal case further below // handle the field. - if(indexing.m_topoIndexing.m_offsets != indexing.m_fieldIndexing.m_offsets || - indexing.m_topoIndexing.m_strides != indexing.m_fieldIndexing.m_strides) + if(indexing.m_topoIndexing.m_offsets != + indexing.m_fieldIndexing.m_offsets || + indexing.m_topoIndexing.m_strides != + indexing.m_fieldIndexing.m_strides) { // Blend the field. axom::mir::utilities::blueprint::FieldBlender< diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 854dee7972..dae3defb46 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -85,15 +85,13 @@ class FieldBlender { public: /// Constructor - FieldBlender() : m_indexing() - { } + FieldBlender() : m_indexing() { } /** * \brief Constructor * \param indexing An object used to transform node indices. */ - FieldBlender(const IndexingPolicy &indexing) : m_indexing(indexing) - { } + FieldBlender(const IndexingPolicy &indexing) : m_indexing(indexing) { } /** * \brief Create a new blended field from the \a n_input field and place it in \a n_output. @@ -128,7 +126,6 @@ class FieldBlender } private: - /** * \brief Blend data for a single field component. * @@ -175,7 +172,8 @@ class FieldBlender const auto index = deviceBlend.m_blendIdsView[i]; const auto weight = deviceBlend.m_blendCoeffView[i]; const auto transformedIndex = deviceIndexing[index]; - blended += static_cast(compView[transformedIndex]) * weight; + blended += + static_cast(compView[transformedIndex]) * weight; } outView[bgid] = static_cast(blended); }); diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 4b5e152970..2b2d42887b 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -42,17 +42,13 @@ class FieldSlicer { public: /// Constructor - FieldSlicer() : m_indexing() - { - } + FieldSlicer() : m_indexing() { } /** * \brief Constructor * \param indexing An object used to transform node indices. */ - FieldSlicer(const IndexingPolicy &indexing) : m_indexing(indexing) - { - } + FieldSlicer(const IndexingPolicy &indexing) : m_indexing(indexing) { } /** * \brief Execute the slice on the \a n_input field and store the new sliced field in \a n_output. @@ -111,7 +107,6 @@ class FieldSlicer n_values, n_output_values, [&](auto valuesView, auto outputView) { - IndexingPolicy deviceIndexing(m_indexing); SliceData deviceSlice(slice); axom::for_all( diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 80f60ff4b2..3ee5aa6465 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -154,9 +154,7 @@ class ConduitAllocateThroughAxom //------------------------------------------------------------------------------ template -bool fillFromNode(const conduit::Node &n, - const std::string &key, - ArrayType &arr) +bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr) { bool found = false; if((found = n.has_path(key)) == true) @@ -182,7 +180,7 @@ struct DirectIndexing * \return The input index. */ AXOM_HOST_DEVICE - inline axom::IndexType operator [](axom::IndexType index) const + inline axom::IndexType operator[](axom::IndexType index) const { return index; } @@ -212,12 +210,12 @@ struct SSElementFieldIndexing * \return The global index for the field. */ AXOM_HOST_DEVICE - inline axom::IndexType operator [](axom::IndexType index) const + inline axom::IndexType operator[](axom::IndexType index) const { return m_indexing.LocalToGlobal(index); } - Indexing m_indexing{}; + Indexing m_indexing {}; }; //------------------------------------------------------------------------------ @@ -244,16 +242,18 @@ struct SSVertexFieldIndexing * \return The global index for the field. */ AXOM_HOST_DEVICE - inline axom::IndexType operator [](axom::IndexType index) const + inline axom::IndexType operator[](axom::IndexType index) const { // Make the global index into a global logical in the topo. const auto topoGlobalLogical = m_topoIndexing.GlobalToGlobal(index); // Make the global logical into a local logical in the topo. const auto topoLocalLogical = m_topoIndexing.GlobalToLocal(topoGlobalLogical); // Make the global logical index in the field. - const auto fieldGlobalLogical = m_fieldIndexing.LocalToGlobal(topoLocalLogical); + const auto fieldGlobalLogical = + m_fieldIndexing.LocalToGlobal(topoLocalLogical); // Make the global index in the field. - const auto fieldGlobalIndex = m_fieldIndexing.GlobalToGlobal(fieldGlobalLogical); + const auto fieldGlobalIndex = + m_fieldIndexing.GlobalToGlobal(fieldGlobalLogical); return fieldGlobalIndex; } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 6d86bbd52a..6ec87eb3ec 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -752,7 +752,9 @@ void strided_structured_clip_test(const std::string &name) axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); + conduit::relay::io::blueprint::save_mesh(hostMesh, + name + "_orig_yaml", + "yaml"); #endif conduit::Node options, deviceClipMesh, hostClipMesh; @@ -764,32 +766,35 @@ void strided_structured_clip_test(const std::string &name) options["outside"] = 1; // Create views - axom::mir::views::dispatch_explicit_coordset(deviceMesh["coordsets/coords"], [&](auto coordsetView) - { - auto topoView = axom::mir::views::make_strided_structured<2>::view(deviceMesh["topologies/mesh"]); - - using CoordsetView = decltype(coordsetView); - using TopoView = decltype(topoView); - - // Clip the data - axom::mir::clipping::ClipField clipper( - topoView, - coordsetView); - clipper.execute(deviceMesh, options, deviceClipMesh); - - // Copy device->host - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); - }); + axom::mir::views::dispatch_explicit_coordset( + deviceMesh["coordsets/coords"], + [&](auto coordsetView) { + auto topoView = axom::mir::views::make_strided_structured<2>::view( + deviceMesh["topologies/mesh"]); + + using CoordsetView = decltype(coordsetView); + using TopoView = decltype(topoView); + + // Clip the data + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, options, deviceClipMesh); + + // Copy device->host + axom::mir::utilities::blueprint::copy(hostClipMesh, + deviceClipMesh); + }); // Handle baseline comparison. { std::string baselineName(yamlRoot(name)); const auto paths = baselinePaths(); - #if defined(AXOM_TESTING_GENERATE_BASELINES) +#if defined(AXOM_TESTING_GENERATE_BASELINES) saveBaseline(paths, baselineName, hostClipMesh); - #else +#else EXPECT_TRUE(compareBaseline(paths, baselineName, hostClipMesh)); - #endif +#endif } } diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 36b8c4e3e8..82e8260ec0 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -67,32 +67,24 @@ TEST(mir_views, strided_structured) { conduit::Node hostMesh; axom::mir::testing::data::strided_structured<2>(hostMesh); -// hostMesh.print(); + // hostMesh.print(); // These are the expected zone ids for this strided structured mesh. - const axom::Array expectedZones{{ - 16, 17, 24, 23, - 17, 18, 25, 24, - 18, 19, 26, 25, - 23, 24, 31, 30, - 24, 25, 32, 31, - 25, 26, 33, 32 - }}; + const axom::Array expectedZones {{16, 17, 24, 23, 17, 18, 25, 24, + 18, 19, 26, 25, 23, 24, 31, 30, + 24, 25, 32, 31, 25, 26, 33, 32}}; auto expectedZonesView = expectedZones.view(); axom::mir::views::dispatch_explicit_coordset( hostMesh["coordsets/coords"], [&](auto coordsetView) { - axom::mir::views::dispatch_structured_topology< axom::mir::views::select_dimensions(2)>( hostMesh["topologies/mesh"], [&](const std::string &shape, auto topoView) { - // Traverse the zones in the mesh and check the zone ids. topoView.template for_all_zones( AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - // Check zone ids. const auto ids = zone.getIds(); for(axom::IndexType i = 0; i < ids.size(); i++) @@ -118,7 +110,7 @@ TEST(mir_views, strided_structured) const double dx = pt[0] - x; const double dy = pt[1] - y; - double d = sqrt(dx*dx + dy*dy); + double d = sqrt(dx * dx + dy * dy); EXPECT_TRUE(d < 1.e-10); } diff --git a/src/axom/mir/tests/mir_views_indexing.cpp b/src/axom/mir/tests/mir_views_indexing.cpp index 3de1b81306..6165aa8735 100644 --- a/src/axom/mir/tests/mir_views_indexing.cpp +++ b/src/axom/mir/tests/mir_views_indexing.cpp @@ -69,8 +69,8 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) EXPECT_EQ(logical, logical2); // If we're in a valid region for the local window, try some other things. - if(i >= origin[0] && i < origin[0] + dims[0] && - j >= origin[1] && j < origin[1] + dims[1]) + if(i >= origin[0] && i < origin[0] + dims[0] && j >= origin[1] && + j < origin[1] + dims[1]) { const auto logicalLocal = indexing.GlobalToLocal(logical); const auto flatLocal = indexing.GlobalToLocal(flat); @@ -81,11 +81,11 @@ TEST(mir_views_indexing, strided_structured_indexing_2d) // Logical local back to logical global EXPECT_EQ(logical, indexing.LocalToGlobal(logicalLocal)); } - } + } } // Check whether these local points exist. - EXPECT_TRUE(indexing.contains(LogicalIndex{0,0})); + EXPECT_TRUE(indexing.contains(LogicalIndex {0, 0})); EXPECT_TRUE(indexing.contains(LogicalIndex {dims[0] - 1, dims[1] - 1})); EXPECT_FALSE(indexing.contains(LogicalIndex {4, 0})); EXPECT_FALSE(indexing.contains(LogicalIndex {4, 3})); @@ -111,7 +111,7 @@ TEST(mir_views_indexing, strided_structured_indexing_3d) const LogicalIndex logical2_2_2 {2, 2, 2}; const auto index2_2_2 = indexing.LogicalIndexToIndex(logical2_2_2); - EXPECT_EQ(index2_2_2, 2 + 2*dims[0] + 2*dims[0]*dims[1]); + EXPECT_EQ(index2_2_2, 2 + 2 * dims[0] + 2 * dims[0] * dims[1]); LogicalIndex logical = indexing.IndexToLogicalIndex(index2_2_2); EXPECT_TRUE(logical == logical2_2_2); diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index af92fb6b4c..6b9707167b 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -55,7 +55,10 @@ struct StridedStructuredIndexing * \brief Return whether the view supports strided structured indexing. * \return true */ - AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() { return true; } + AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() + { + return true; + } /** * \brief constructor @@ -138,7 +141,7 @@ struct StridedStructuredIndexing AXOM_HOST_DEVICE IndexType GlobalToGlobal(const LogicalIndex &global) const { - IndexType gl{}; + IndexType gl {}; for(int i = 0; i < NDIMS; i++) { gl += global[i] * m_strides[i]; @@ -283,7 +286,6 @@ struct StridedStructuredIndexing } /// @} - /** * \brief Turn a local logical index into a local flat index. * diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index 35ad92eaab..95d0782916 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -35,7 +35,10 @@ class StructuredIndexing * \brief Return whether the view supports strided structured indexing. * \return false */ - AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() { return false; } + AXOM_HOST_DEVICE static constexpr bool supports_strided_structured_indexing() + { + return false; + } /** * \brief constructor @@ -130,10 +133,7 @@ class StructuredIndexing * \return Same as the input in this case. */ AXOM_HOST_DEVICE - inline IndexType GlobalToLocal(IndexType index) const - { - return index; - } + inline IndexType GlobalToLocal(IndexType index) const { return index; } /** * \brief Turn local logical index to global logical index. no-op. @@ -152,10 +152,7 @@ class StructuredIndexing * \return Same as the input in this case. */ AXOM_HOST_DEVICE - inline IndexType LocalToGlobal(IndexType index) const - { - return index; - } + inline IndexType LocalToGlobal(IndexType index) const { return index; } /** * \brief Turn an index into a logical index. diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 9c5291bae5..ec44f8caf5 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -40,7 +40,10 @@ class StructuredTopologyView * \brief Return whether the indexing supports strided structured indexing. * \return True if the indexing supports strided structured indexing, false otherwise. */ - constexpr static bool supports_strided_structured_indexing() { return IndexingPolicy::supports_strided_structured_indexing(); } + constexpr static bool supports_strided_structured_indexing() + { + return IndexingPolicy::supports_strided_structured_indexing(); + } /** * \brief Constructor @@ -116,7 +119,8 @@ class StructuredTopologyView const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); IndexType data[8]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -143,7 +147,8 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); IndexType data[4]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -165,7 +170,8 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[2]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; const ShapeType shape(axom::ArrayView(data, 2)); @@ -209,7 +215,8 @@ class StructuredTopologyView const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); IndexType data[8]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -237,7 +244,8 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); IndexType data[4]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -260,7 +268,8 @@ class StructuredTopologyView const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); IndexType data[2]; - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; const ShapeType shape(axom::ArrayView(data, 2)); diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index d460d499b2..cf503f6d8d 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -21,7 +21,6 @@ namespace mir { namespace views { - //------------------------------------------------------------------------------ /** * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. @@ -34,9 +33,7 @@ namespace views * \param fillValue the value to use if the array is not found. */ template -bool fillFromNode(const conduit::Node &n, - const std::string &key, - ArrayType &arr) +bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr) { bool found = false; if((found = n.has_path(key)) == true) @@ -83,7 +80,7 @@ struct make_strided_structured<3> zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); - LogicalIndex offsets{{0, 0, 0}}, strides{{1, 1, 1}}; + LogicalIndex offsets {{0, 0, 0}}, strides {{1, 1, 1}}; fillFromNode(topo, offsetsKey, offsets); if(fillFromNode(topo, stridesKey, strides)) { @@ -134,7 +131,7 @@ struct make_strided_structured<2> zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); - LogicalIndex offsets{{0, 0}}, strides{{1, 1}}; + LogicalIndex offsets {{0, 0}}, strides {{1, 1}}; fillFromNode(topo, offsetsKey, offsets); if(fillFromNode(topo, stridesKey, strides)) { @@ -183,7 +180,7 @@ struct make_strided_structured<1> LogicalIndex zoneDims; zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); - LogicalIndex offsets{0}, strides{1}; + LogicalIndex offsets {0}, strides {1}; fillFromNode(topo, offsetsKey, offsets); fillFromNode(topo, stridesKey, strides); From 1dd88a4065adeb857a4adbe07167d33c76194805 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 6 Aug 2024 18:11:45 -0700 Subject: [PATCH 144/290] Try selecting a few zones. Remove a method from views. --- src/axom/mir/ClipField.hpp | 48 +++++++++---------- src/axom/mir/tests/mir_clipfield.cpp | 39 +++++++++------ src/axom/mir/tests/mir_views.cpp | 11 +++-- src/axom/mir/views/StructuredTopologyView.hpp | 15 ++---- .../UnstructuredTopologyMixedShapeView.hpp | 10 +--- .../UnstructuredTopologyPolyhedralView.hpp | 10 +--- .../UnstructuredTopologySingleShapeView.hpp | 10 +--- 7 files changed, 65 insertions(+), 78 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 8a3c646619..bd3ae04d75 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1045,7 +1045,7 @@ class ClipField fragmentData, opts, n_clip_field_values); - computeFragmentSizes(fragmentData); + computeFragmentSizes(fragmentData, opts); computeFragmentOffsets(fragmentData); IndexType blendGroupsSize = 0, blendGroupLenSize = 0; @@ -1216,11 +1216,11 @@ class ClipField m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); - zoneData.m_clipCasesView[zoneIndex] = clipcase; + zoneData.m_clipCasesView[szIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); @@ -1290,7 +1290,7 @@ class ClipField } // Save the flags for the points that were used in this zone - zoneData.m_pointsUsedView[zoneIndex] = ptused; + zoneData.m_pointsUsedView[szIndex] = ptused; // Count which points in the original cell are used. for(IndexType pid = P0; pid <= P7; pid++) @@ -1311,12 +1311,12 @@ class ClipField } // Save the results. - fragmentData.m_fragmentsView[zoneIndex] = thisFragments; - fragmentData.m_fragmentsSizeView[zoneIndex] = thisFragmentsNumIds; + fragmentData.m_fragmentsView[szIndex] = thisFragments; + fragmentData.m_fragmentsSizeView[szIndex] = thisFragmentsNumIds; // Set blend group sizes for this zone. - blendGroupsView[zoneIndex] = thisBlendGroups; - blendGroupsLenView[zoneIndex] = thisBlendGroupLen; + blendGroupsView[szIndex] = thisBlendGroups; + blendGroupsLenView[szIndex] = thisBlendGroupLen; }); }); } @@ -1326,16 +1326,16 @@ class ClipField * * \param[inout] fragmentData The object that contains data about the zone fragments. */ - void computeFragmentSizes(FragmentData &fragmentData) const + void computeFragmentSizes(FragmentData &fragmentData, ClipOptions &opts) const { - const auto nzones = m_topologyView.numberOfZones(); + const auto nzones = opts.selectedZonesView().size(); // Sum the number of fragments. RAJA::ReduceSum fragment_sum(0); const auto fragmentsView = fragmentData.m_fragmentsView; axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { fragment_sum += fragmentsView[zoneIndex]; }); + AXOM_LAMBDA(auto szIndex) { fragment_sum += fragmentsView[szIndex]; }); fragmentData.m_finalNumZones = fragment_sum.get(); // Sum the fragment connectivity sizes. @@ -1343,8 +1343,8 @@ class ClipField const auto fragmentsSizeView = fragmentData.m_fragmentsSizeView; axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { - fragment_nids_sum += fragmentsSizeView[zoneIndex]; + AXOM_LAMBDA(auto szIndex) { + fragment_nids_sum += fragmentsSizeView[szIndex]; }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); } @@ -1385,19 +1385,19 @@ class ClipField m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. - const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; + const auto clipcase = zoneData.m_clipCasesView[szIndex]; // Iterate over the shapes in this clip case to determine the number of blend groups. const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; // These are the points used in this zone's fragments. - const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; + const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; // Get the blend groups for this zone. - auto groups = builder.blendGroupsForZone(zoneIndex); + auto groups = builder.blendGroupsForZone(szIndex); auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -1565,18 +1565,18 @@ class ClipField RAJA::ReduceBitOr shapesUsed_reduce(0); m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // If there are no fragments, return from lambda. - if(fragmentData.m_fragmentsView[zoneIndex] == 0) return; + if(fragmentData.m_fragmentsView[szIndex] == 0) return; // Seek to the start of the blend groups for this zone. - auto groups = builder.blendGroupsForZone(zoneIndex); + auto groups = builder.blendGroupsForZone(szIndex); // Go through the points in the order they would have been added as blend // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index // in the final points. - const BitSet ptused = zoneData.m_pointsUsedView[zoneIndex]; + const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; ConnectivityType point_2_new[N3 + 1]; for(unsigned char pid = N0; pid <= N3; pid++) { @@ -1604,13 +1604,13 @@ class ClipField } // This is where the output fragment connectivity start for this zone - int outputIndex = fragmentData.m_fragmentSizeOffsetsView[zoneIndex]; + int outputIndex = fragmentData.m_fragmentSizeOffsetsView[szIndex]; // This is where the output fragment sizes/shapes start for this zone. - int sizeIndex = fragmentData.m_fragmentOffsetsView[zoneIndex]; + int sizeIndex = fragmentData.m_fragmentOffsetsView[szIndex]; BitSet shapesUsed = 0; // Iterate over the selected fragments and emit connectivity for them. - const auto clipcase = zoneData.m_clipCasesView[zoneIndex]; + const auto clipcase = zoneData.m_clipCasesView[szIndex]; const auto clipTableIndex = details::getClipTableIndex(zone.id()); const auto ctView = clipTableViews[clipTableIndex]; auto it = ctView.begin(clipcase); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 6ec87eb3ec..2e3ac9cab2 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -737,7 +737,7 @@ TEST(mir_clipfield, rectilinear3d) //------------------------------------------------------------------------------ template -void strided_structured_clip_test(const std::string &name) +void strided_structured_clip_test(const std::string &name, const conduit::Node &options) { using Indexing = axom::mir::views::StridedStructuredIndexing; @@ -757,13 +757,7 @@ void strided_structured_clip_test(const std::string &name) "yaml"); #endif - conduit::Node options, deviceClipMesh, hostClipMesh; - - // Create options to control the clipping. - options["clipField"] = "vert_vals"; - options["clipValue"] = 6.5; - options["inside"] = 1; - options["outside"] = 1; + conduit::Node deviceClipMesh, hostClipMesh; // Create views axom::mir::views::dispatch_explicit_coordset( @@ -798,23 +792,38 @@ void strided_structured_clip_test(const std::string &name) } } -TEST(mir_clipfield, strided_structured_2d) +void strided_structured_clip_test_exec(const std::string &name, const conduit::Node &options) { - strided_structured_clip_test("strided_structured_2d"); + strided_structured_clip_test(name, options); - //#if defined(AXOM_USE_OPENMP) - // strided_structured_clip_test("strided_structured_2d"); - //#endif +#if defined(AXOM_USE_OPENMP) + strided_structured_clip_test(name, options); +#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - strided_structured_clip_test("strided_structured_2d"); + strided_structured_clip_test(name, options); #endif #if defined(AXOM_USE_HIP) - strided_structured_clip_test("strided_structured_2d"); + strided_structured_clip_test(name, options); #endif } +TEST(mir_clipfield, strided_structured_2d) +{ + // Create options to control the clipping. + conduit::Node options; + options["clipField"] = "vert_vals"; + options["clipValue"] = 6.5; + options["inside"] = 1; + options["outside"] = 1; + strided_structured_clip_test_exec("strided_structured_2d", options); + + // Clip strided structure on some selected zones. + options["selectedZones"].set(std::vector{{0,2,3,5}}); + strided_structured_clip_test_exec("strided_structured_2d_sel", options); +} + template void braid3d_clip_test(const std::string &type, const std::string &name) { diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 82e8260ec0..982879b3e9 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -70,9 +70,14 @@ TEST(mir_views, strided_structured) // hostMesh.print(); // These are the expected zone ids for this strided structured mesh. - const axom::Array expectedZones {{16, 17, 24, 23, 17, 18, 25, 24, - 18, 19, 26, 25, 23, 24, 31, 30, - 24, 25, 32, 31, 25, 26, 33, 32}}; + // clang-format off + const axom::Array expectedZones {{16, 17, 24, 23, + 17, 18, 25, 24, + 18, 19, 26, 25, + 23, 24, 31, 30, + 24, 25, 32, 31, + 25, 26, 33, 32}}; + // clang-format on auto expectedZonesView = expectedZones.view(); axom::mir::views::dispatch_explicit_coordset( diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index ec44f8caf5..b66ae892a9 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -36,15 +36,6 @@ class StructuredTopologyView AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } - /** - * \brief Return whether the indexing supports strided structured indexing. - * \return True if the indexing supports strided structured indexing, false otherwise. - */ - constexpr static bool supports_strided_structured_indexing() - { - return IndexingPolicy::supports_strided_structured_indexing(); - } - /** * \brief Constructor * @@ -226,7 +217,7 @@ class StructuredTopologyView data[7] = data[3] + kp; const ShapeType shape(axom::ArrayView(data, 8)); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 2) @@ -251,7 +242,7 @@ class StructuredTopologyView data[3] = data[2] - 1; const ShapeType shape(axom::ArrayView(data, 4)); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 1) @@ -273,7 +264,7 @@ class StructuredTopologyView data[1] = data[0] + 1; const ShapeType shape(axom::ArrayView(data, 2)); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } } diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index e025610943..23c920e136 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -121,12 +121,6 @@ class UnstructuredTopologyMixedShapeView */ static constexpr int dimension() { return -1; } - /** - * \brief Return whether the view supports strided structured indexing. - * \return false - */ - static constexpr bool supports_strided_structured_indexing() { return false; } - /** * \brief Return the number of zones. * @@ -201,7 +195,7 @@ class UnstructuredTopologyMixedShapeView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(int selectIndex) { + AXOM_LAMBDA(auto selectIndex) { const auto zoneIndex = deviceSelectedIdsView[selectIndex]; const ConnectivityView shapeData( connectivityView.data() + offsets[zoneIndex], @@ -209,7 +203,7 @@ class UnstructuredTopologyMixedShapeView const auto shapeID = shapeMap[shapes[zoneIndex]]; // TODO: SLIC_ASSERT(shapeID > 0); const ShapeType shape(shapeID, shapeData); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index b53ee953c7..6538adcbef 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -194,12 +194,6 @@ class UnstructuredTopologyPolyhedralView */ static constexpr int dimension() { return 3; } - /** - * \brief Return whether the view supports strided structured indexing. - * \return false - */ - static constexpr bool supports_strided_structured_indexing() { return false; } - template void for_all_zones(FuncType &&func) const { @@ -225,10 +219,10 @@ class UnstructuredTopologyPolyhedralView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(int selectIndex) { + AXOM_LAMBDA(auto selectIndex) { const auto zoneIndex = idsView[selectIndex]; const PolyhedronShape shape(sd, zoneIndex); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index c47fd7de5d..2ec9f6c45d 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -65,12 +65,6 @@ class UnstructuredTopologySingleShapeView */ static constexpr int dimension() { return ShapeT::dimension(); } - /** - * \brief Return whether the view supports strided structured indexing. - * \return false - */ - static constexpr bool supports_strided_structured_indexing() { return false; } - /** * \brief Return the number of zones. * @@ -156,7 +150,7 @@ class UnstructuredTopologySingleShapeView connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } else @@ -170,7 +164,7 @@ class UnstructuredTopologySingleShapeView connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); + func(selectIndex, zoneIndex, shape); }); } } From 11c0135c74f383bf03f55a63fbc74f06264e2be7 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 6 Aug 2024 18:17:11 -0700 Subject: [PATCH 145/290] make style --- src/axom/mir/ClipField.hpp | 3 ++- src/axom/mir/tests/mir_clipfield.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index bd3ae04d75..05e9f95877 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1326,7 +1326,8 @@ class ClipField * * \param[inout] fragmentData The object that contains data about the zone fragments. */ - void computeFragmentSizes(FragmentData &fragmentData, ClipOptions &opts) const + void computeFragmentSizes(FragmentData &fragmentData, + ClipOptions &opts) const { const auto nzones = opts.selectedZonesView().size(); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 2e3ac9cab2..c8c2bb97c3 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -737,7 +737,8 @@ TEST(mir_clipfield, rectilinear3d) //------------------------------------------------------------------------------ template -void strided_structured_clip_test(const std::string &name, const conduit::Node &options) +void strided_structured_clip_test(const std::string &name, + const conduit::Node &options) { using Indexing = axom::mir::views::StridedStructuredIndexing; @@ -792,7 +793,8 @@ void strided_structured_clip_test(const std::string &name, const conduit::Node & } } -void strided_structured_clip_test_exec(const std::string &name, const conduit::Node &options) +void strided_structured_clip_test_exec(const std::string &name, + const conduit::Node &options) { strided_structured_clip_test(name, options); @@ -820,7 +822,7 @@ TEST(mir_clipfield, strided_structured_2d) strided_structured_clip_test_exec("strided_structured_2d", options); // Clip strided structure on some selected zones. - options["selectedZones"].set(std::vector{{0,2,3,5}}); + options["selectedZones"].set(std::vector {{0, 2, 3, 5}}); strided_structured_clip_test_exec("strided_structured_2d_sel", options); } From 90ab9c28ef83847e9df86f431b2232c4551dff2a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:09:16 -0700 Subject: [PATCH 146/290] fix macro --- src/axom/mir/views/RectilinearCoordsetView.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index b61461bb99..019bf31b59 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -3,8 +3,8 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#ifndef AXOM_MIR_UNIFORm_coordinates_VIEW_HPP_ -#define AXOM_MIR_UNIFORm_coordinates_VIEW_HPP_ +#ifndef AXOM_MIR_RECTILINEAR_COORDSET_VIEW_HPP_ +#define AXOM_MIR_RECTILINEAR_COORDSET_VIEW_HPP_ #include "axom/core/StackArray.hpp" #include "axom/core/ArrayView.hpp" From 15fdbf5763a090990ffa3cbb63c6d1980686eee1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:09:29 -0700 Subject: [PATCH 147/290] Fix types --- src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 23c920e136..b37c1de4ae 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -248,10 +248,10 @@ class UnstructuredTopologyMixedShapeView } const conduit::Node &m_topo; - axom::ArrayView m_connectivity; - axom::ArrayView m_shapes; - axom::ArrayView m_sizes; - axom::ArrayView m_offsets; + ConnectivityView m_connectivity; + ConnectivityView m_shapes; + ConnectivityView m_sizes; + ConnectivityView m_offsets; }; } // end namespace views From a5cfcc80bddf1772c23b3334a49fa62f5034c4be Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:09:50 -0700 Subject: [PATCH 148/290] fix recenter --- src/axom/mir/RecenterField.hpp | 144 +++++++++++++++++---------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index cad7cdfddc..70c91a1361 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -8,6 +8,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" +#include "axom/mir/utilities.hpp" #include #include @@ -16,23 +17,11 @@ namespace axom { namespace mir { -/** - * \brief This struct contains the type that should be used to accumulate values of type T. - */ -template -struct accumulate_traits -{ - using value_type = double; -}; - -template <> -struct accumulate_traits -{ - using value_type = float; -}; /** * \brief Convert a field with one association type to a field of another association type using an o2mrelation. + * + * \tparam ExecSpace The execution space where the algorithm runs. */ template class RecenterField @@ -45,65 +34,29 @@ class RecenterField * \param relation The node that contains an o2mrelation with nodes to zones. * \param outField[out] The node that will contain the new field. */ - static void execute(const conduit::Node &field, - const conduit::Node &relation, - conduit::Node &outField); + void execute(const conduit::Node &field, + const conduit::Node &relation, + conduit::Node &outField) const; + +private: + /** + * \brief Recenter a single field component. + * + * \param relation The node that contains an o2mrelation with nodes to zones. + * \param n_comp The input component. + * \param n_out[out] The node that will contain the new field. + */ + void recenterSingleComponent(const conduit::Node &n_comp, + const conduit::Node &relation, + conduit::Node &n_out) const; }; template void RecenterField::execute(const conduit::Node &field, const conduit::Node &relation, - conduit::Node &outField) + conduit::Node &outField) const { - auto handleComponent = [](const conduit::Node &relation, - const conduit::Node &n_comp, - conduit::Node &n_out, - int allocatorID) { - // Get the data field for the o2m relation. - const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); - - // Use the o2mrelation to average data from n_comp to the n_out. - const conduit::Node &n_relvalues = relation[data_paths[0]]; - const conduit::Node &n_sizes = relation["sizes"]; - const conduit::Node &n_offsets = relation["offsets"]; - views::IndexNode_to_ArrayView_same( - n_relvalues, - n_sizes, - n_offsets, - [&](auto relView, auto sizesView, auto offsetsView) { - const auto relSize = sizesView.size(); - - // Allocate data for n_out (same type as n_comp). - n_out.set_allocator(allocatorID); - n_out.set(n_comp.dtype().id(), relSize); - - views::Node_to_ArrayView_same( - n_comp, - n_out, - [&](auto compView, auto outView) { - using Precision = typename decltype(compView)::value_type; - using AccumType = typename accumulate_traits::value_type; - axom::for_all( - relSize, - AXOM_LAMBDA(int relIndex) { - const auto n = sizesView[relIndex]; - const auto offset = offsetsView[relIndex]; - - AccumType sum = 0; - for(int i = 0; i < n; i++) - { - const auto id = relView[offset + i]; - sum += static_cast(compView[id]); - } - - outView[relIndex] = static_cast(sum / n); - }); - }); - }); - }; - const std::string association = field.fetch_existing("association").as_string(); - const auto allocatorID = axom::execution_space::allocatorID(); // Assume that we're flipping the association. outField["association"] = (association == "element") ? "vertex" : "element"; @@ -116,18 +69,67 @@ void RecenterField::execute(const conduit::Node &field, for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { const conduit::Node &n_comp = n_values[c]; - handleComponent(relation, - n_comp, - outField["values"][n_comp.name()], - allocatorID); + recenterSingleComponent(relation, + outField["values"][n_comp.name()], + n_comp); } } else { - handleComponent(relation, n_values, outField["values"], allocatorID); + recenterSingleComponent(relation, outField["values"], n_values); } } +template +void RecenterField::recenterSingleComponent( + const conduit::Node &n_comp, + const conduit::Node &relation, + conduit::Node &n_out) const +{ + // Get the data field for the o2m relation. + const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); + + // Use the o2mrelation to average data from n_comp to the n_out. + const conduit::Node &n_relvalues = relation[data_paths[0]]; + const conduit::Node &n_sizes = relation["sizes"]; + const conduit::Node &n_offsets = relation["offsets"]; + views::IndexNode_to_ArrayView_same( + n_relvalues, + n_sizes, + n_offsets, + [&](auto relView, auto sizesView, auto offsetsView) { + const auto relSize = sizesView.size(); + + // Allocate Conduit data through Axom. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + n_out.set_allocator(c2a.getConduitAllocatorID()); + n_out.set(n_comp.dtype().id(), relSize); + + views::Node_to_ArrayView_same( + n_comp, + n_out, + [&](auto compView, auto outView) { + using Precision = typename decltype(compView)::value_type; + using AccumType = typename axom::mir::utilities::accumulation_traits::value_type; + axom::for_all( + relSize, + AXOM_LAMBDA(int relIndex) { + const auto n = sizesView[relIndex]; + const auto offset = offsetsView[relIndex]; + + AccumType sum = 0; + for(int i = 0; i < n; i++) + { + const auto id = relView[offset + i]; + sum += static_cast(compView[id]); + } + + outView[relIndex] = static_cast(sum / n); + }); + }); + }); +} + } // end namespace mir } // end namespace axom From 4d0adddb54ea5a1a991a4bd69ffc5bb2a7142351 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:14:44 -0700 Subject: [PATCH 149/290] Fixes --- src/axom/mir/ClipField.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 05e9f95877..fa5d552b50 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1216,7 +1216,7 @@ class ClipField m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); @@ -1386,7 +1386,7 @@ class ClipField m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -1558,7 +1558,7 @@ class ClipField n_color_values.set_allocator(conduitAllocatorID); n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); auto colorView = axom::ArrayView( - static_cast(n_color_values.data_ptr()), + static_cast(n_color_values.data_ptr()), fragmentData.m_finalNumZones); // Here we fill in the new connectivity, sizes, shapes. @@ -1566,7 +1566,7 @@ class ClipField RAJA::ReduceBitOr shapesUsed_reduce(0); m_topologyView.template for_selected_zones( opts.selectedZonesView(), - AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; From 6a78310512a5a00d49327f85b60195ec0192d3c4 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:15:45 -0700 Subject: [PATCH 150/290] Adding ClipFieldFilter --- src/axom/mir/CMakeLists.txt | 4 + src/axom/mir/ClipFieldFilter.cpp | 101 ++++++++++++++++++ src/axom/mir/ClipFieldFilter.hpp | 83 +++++++++++++++ src/axom/mir/ClipFieldFilterDevice.hpp | 126 +++++++++++++++++++++++ src/axom/mir/tests/mir_clipfield.cpp | 11 +- src/axom/mir/views/dispatch_topology.hpp | 5 +- 6 files changed, 320 insertions(+), 10 deletions(-) create mode 100644 src/axom/mir/ClipFieldFilter.cpp create mode 100644 src/axom/mir/ClipFieldFilter.hpp create mode 100644 src/axom/mir/ClipFieldFilterDevice.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 3b3077948b..f2b230e56a 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -29,6 +29,9 @@ set(mir_headers blueprint_utilities.hpp ClipField.hpp + ClipFieldFilterDevice.hpp + ClipFieldFilter.hpp + CoordsetBlender.hpp EquiZAlgorithm.hpp FieldBlender.hpp @@ -71,6 +74,7 @@ set(mir_sources CellClipper.cpp CellGenerator.cpp + ClipFieldFilter.cpp blueprint_utilities.cpp MIRAlgorithm.cpp EquiZAlgorithm.cpp diff --git a/src/axom/mir/ClipFieldFilter.cpp b/src/axom/mir/ClipFieldFilter.cpp new file mode 100644 index 0000000000..95a6500669 --- /dev/null +++ b/src/axom/mir/ClipFieldFilter.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/mir/ClipFieldFilter.hpp" + +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #endif + + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #endif +#endif +// clang-format on + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + + void ClipFieldFilter::execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) + { + ClipOptions opts(0, n_options); + const std::string clipFieldName = opts.clipField(); + + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); + const std::string &topoName = n_clipField["topology"].as_string(); + const conduit::Node &n_topo = + n_input.fetch_existing("topologies/" + topoName); + const std::string &coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); + + execute(n_topo, + n_coordset, + n_fields, + n_options, + n_output["topologies/" + opts.topologyName(topoName)], + n_output["coordsets/" + opts.coordsetName(coordsetName)], + n_output["fields"]); + } + +void ClipFieldFilter::execute(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) +{ + // Instantiate the algorithm for the right device and invoke it. + if(m_runtime == axom::runtime_policy::Policy::seq) + { + ClipFieldFilterDevice clipper; + clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + } +#if 0 +#if defined(AXOM_USE_OPENMP) + else if(m_runtime == axom::runtime_policy::Policy::omp) + { + ClipFieldFilterDevice clipper; + clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + } +#endif +#if defined(AXOM_USE_CUDA) + else if(m_runtime == axom::runtime_policy::Policy::cuda) + { + ClipFieldFilterDevice clipper; + clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + } +#endif +#if defined(AXOM_USE_HIP) + else if(m_runtime == axom::runtime_policy::Policy::hip) + { + ClipFieldFilterDevice clipper; + clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + } +#endif +#endif +} + +} // end namespace clipping +} // end namespace mir +} // end namespace axom diff --git a/src/axom/mir/ClipFieldFilter.hpp b/src/axom/mir/ClipFieldFilter.hpp new file mode 100644 index 0000000000..45383b9d1c --- /dev/null +++ b/src/axom/mir/ClipFieldFilter.hpp @@ -0,0 +1,83 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_CLIP_FIELD_FILTER_HPP_ +#define AXOM_MIR_CLIP_FIELD_FILTER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir.hpp" + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + +/** + * \brief This class runs a ClipField algorithm. + * + */ +class ClipFieldFilter +{ +public: + /// Constructor + ClipFieldFilter() : m_runtime(axom::runtime_policy::Policy::seq) + { } + + /** + * \brief Set the runtime policy. + * \param value The new runtime policy. + */ + void setRuntime(axom::runtime_policy::Policy value) { m_runtime = value; } + + /** + * \brief Get the runtime policy. + * + */ + axom::runtime_policy::Policy runtime() const { return m_runtime; } + + /** + * \brief Execute the clipping operation using the specified options. + * + * \param[in] n_input The Conduit node that contains the topology, coordsets, and fields. + * \param[in] n_options A Conduit node that contains clipping options. + * \param[out] n_output A Conduit node that will hold the clipped output mesh. This should be a different node from \a n_input. + * + * \note The clipField field must currently be vertex-associated. + */ + void execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output); + + /** + * \brief Execute the clipping operation using the specified options. + * + * \param[in] n_topo The node that contains the input mesh topology. + * \param[in] n_coordset The node that contains the input mesh coordset. + * \param[in] n_fields The node that contains the input fields. + * \param[in] n_options A Conduit node that contains clipping options. + * \param[out] n_newTopo A node that will contain the new clipped topology. + * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. + * \param[out] n_newFields A node that will contain the new fields for the clipped topology. + * + * \note The clipField field must currently be vertex-associated. Also, the output topology will be an unstructured topology with mixed shape types. + */ + void execute(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields); + +private: + axom::runtime_policy::Policy m_runtime; +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp new file mode 100644 index 0000000000..5b39a9ae36 --- /dev/null +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -0,0 +1,126 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_CLIP_FIELD_FILTER_DEVICE_HPP_ +#define AXOM_MIR_CLIP_FIELD_FILTER_DEVICE_HPP_ + +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/mir/views/dispatch_coordset.hpp" +#include "axom/mir/views/dispatch_topology.hpp" + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + +/** + * \brief This class instantiates ClipField so it runs on specific device but + * can operate on a variety of Blueprint topologies/coordsets/types. + * + * \tparam ExecSpace The execution space where the algorithm will run. + */ +template +class ClipFieldFilterDevice +{ +public: + /// Constructor + ClipFieldFilterDevice() + { } + + /** + * \brief Execute the clipping operation using the specified options. + * + * \param[in] n_input The Conduit node that contains the topology, coordsets, and fields. + * \param[in] n_options A Conduit node that contains clipping options. + * \param[out] n_output A Conduit node that will hold the clipped output mesh. This should be a different node from \a n_input. + * + * \note The clipField field must currently be vertex-associated. + */ + void execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) + { + ClipOptions opts(0, n_options); + const std::string clipFieldName = opts.clipField(); + + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); + const std::string &topoName = n_clipField["topology"].as_string(); + const conduit::Node &n_topo = + n_input.fetch_existing("topologies/" + topoName); + const std::string &coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); + + execute(n_topo, + n_coordset, + n_fields, + n_options, + n_output["topologies/" + opts.topologyName(topoName)], + n_output["coordsets/" + opts.coordsetName(coordsetName)], + n_output["fields"]); + } + + /** + * \brief Returns a bitset that records which shapes are supported. + * \return A bitset that encodes the supported shapes. + */ + static constexpr int supported_shapes() + { + int bitset = 0; + axom::utilities::setBitOn(bitset, views::Tri_ShapeID); + axom::utilities::setBitOn(bitset, views::Quad_ShapeID); + axom::utilities::setBitOn(bitset, views::Pyramid_ShapeID); + axom::utilities::setBitOn(bitset, views::Wedge_ShapeID); + axom::utilities::setBitOn(bitset, views::Hex_ShapeID); + axom::utilities::setBitOn(bitset, views::Mixed_ShapeID); + return bitset; + } + + /** + * \brief Execute the clipping operation using the specified options. + * + * \param[in] n_topo The node that contains the input mesh topology. + * \param[in] n_coordset The node that contains the input mesh coordset. + * \param[in] n_fields The node that contains the input fields. + * \param[in] n_options A Conduit node that contains clipping options. + * \param[out] n_newTopo A node that will contain the new clipped topology. + * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. + * \param[out] n_newFields A node that will contain the new fields for the clipped topology. + * + * \note The clipField field must currently be vertex-associated. Also, the output topology will be an unstructured topology with mixed shape types. + */ + void execute(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) + { + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) + { + // We dispatch to 2D/3D topologies with supported shapes. + constexpr int dims = axom::mir::views::select_dimensions(2,3); + constexpr int shapes = supported_shapes(); + axom::mir::views::dispatch_topology(n_topo, n_coordset, [&](const std::string &/*shape*/, auto topologyView) + { + using TopologyView = decltype(topologyView); + using CoordsetView = decltype(coordsetView); + + ClipField clipper(topologyView, coordsetView); + clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + }); + }); + } +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index c8c2bb97c3..e7e6d69447 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -42,10 +42,10 @@ //------------------------------------------------------------------------------ // Uncomment to generate baselines -#define AXOM_TESTING_GENERATE_BASELINES +//#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -#define AXOM_TESTING_SAVE_VISUALIZATION +//#define AXOM_TESTING_SAVE_VISUALIZATION // Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -740,15 +740,10 @@ template void strided_structured_clip_test(const std::string &name, const conduit::Node &options) { - using Indexing = - axom::mir::views::StridedStructuredIndexing; - using TopoView = axom::mir::views::StructuredTopologyView; - using CoordsetView = axom::mir::views::ExplicitCoordsetView; - // Create the data conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::strided_structured(hostMesh); - hostMesh.print(); + //hostMesh.print(); axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index 04e9ea646b..8131bd7de2 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -25,12 +25,13 @@ namespace views * * \tparam FuncType The function/lambda type to invoke on the view. * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension + * \tparam ShapeTypes A bitset containing the shape types that will be supported for unstructured. * * \param topo The node that contains the rectilinear topology. * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) @@ -44,7 +45,7 @@ void dispatch_topology(const conduit::Node &topo, else if(type == "structured") dispatch_structured_topology(topo, func); else if(type == "unstructured") - dispatch_unstructured_topology(topo, func); + dispatch_unstructured_topology(topo, func); } } // end namespace views From 2069e10df0d4e0aa14c47f8c27745499f14f896f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:56:07 -0700 Subject: [PATCH 151/290] Cut down code size --- src/axom/mir/ClipField.hpp | 85 +++++++++++++++------------- src/axom/mir/blueprint_utilities.hpp | 17 ++++++ 2 files changed, 62 insertions(+), 40 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index fa5d552b50..29a206ac21 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -255,10 +255,10 @@ class ClipOptions * \brief Return the clip value. * \return The clip value. */ - float clipValue() const + double clipValue() const { return m_options.has_child("clipValue") - ? m_options.fetch_existing("clipValue").to_float() + ? m_options.fetch_existing("clipValue").to_double() : 0.f; } @@ -971,17 +971,39 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) { + namespace bputils = axom::mir::utilities::blueprint; const auto allocatorID = axom::execution_space::allocatorID(); // Make the selected zones and get the size. ClipOptions opts(m_topologyView.numberOfZones(), n_options); const auto nzones = opts.selectedZonesView().size(); - // Get the clip field. + // Get the clip field. Make sure it is double. That lets us make less code down the line. std::string clipFieldName = opts.clipField(); const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); const conduit::Node &n_clip_field_values = n_clip_field["values"]; SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); + axom::Array clipFieldData; + axom::ArrayView clipFieldView; + if(n_clip_field_values.dtype().is_float64()) + { + // Make a view. + clipFieldView = bputils::make_array_view(n_clip_field_values); + } + else + { + // Convert to double. + const IndexType n = static_cast(n_clip_field_values.dtype().number_of_elements()); + clipFieldData = axom::Array(n, n, allocatorID); + clipFieldView = clipFieldData.view(); + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) + { + axom::for_all(n, AXOM_LAMBDA(auto index) + { + clipFieldView[index] = static_cast(clipFieldViewSrc[index]); + }); + }); + } // Load clip table data and make views. m_clipTables.load(m_topologyView.dimension()); @@ -1044,7 +1066,7 @@ class ClipField zoneData, fragmentData, opts, - n_clip_field_values); + clipFieldView); computeFragmentSizes(fragmentData, opts); computeFragmentOffsets(fragmentData); @@ -1074,7 +1096,7 @@ class ClipField blendGroupStart.view(), blendIds.view(), blendCoeff.view()); - makeBlendGroups(clipTableViews, builder, zoneData, opts, n_clip_field_values); + makeBlendGroups(clipTableViews, builder, zoneData, opts, clipFieldView); // Make the blend groups unique axom::Array uNames; @@ -1083,7 +1105,7 @@ class ClipField uNames, uIndices); builder.setUniqueNames(uNames.view(), uIndices.view()); - axom::mir::utilities::blueprint::BlendData blend = builder.makeBlendData(); + bputils::BlendData blend = builder.makeBlendData(); // Make the clipped mesh makeConnectivity(clipTableViews, @@ -1096,7 +1118,7 @@ class ClipField n_newFields); makeCoordset(blend, n_coordset, n_newCoordset); - axom::mir::utilities::blueprint::SliceData slice; + bputils::SliceData slice; axom::Array sliceIndices(fragmentData.m_finalNumZones, fragmentData.m_finalNumZones, allocatorID); @@ -1195,7 +1217,7 @@ class ClipField * \param[in] zoneData This object holds views to per-zone data. * \param[in] fragmentData This object holds views to per-fragment data. * \param[inout] opts Clipping options. - * \param[in] n_clip_field_values The node that contains clipping field values. + * \param[in] clipFieldView The view that contains clipping field values. * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ @@ -1204,11 +1226,9 @@ class ClipField ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, - const conduit::Node &n_clip_field_values) const + const axom::ArrayView &clipFieldView) const { - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { - using clip_value_type = typename decltype(clipFieldView)::value_type; - const auto clipValue = static_cast(opts.clipValue()); + const auto clipValue = opts.clipValue(); const auto selection = getSelection(opts); auto blendGroupsView = builder.state().m_blendGroupsView; @@ -1318,7 +1338,6 @@ class ClipField blendGroupsView[szIndex] = thisBlendGroups; blendGroupsLenView[szIndex] = thisBlendGroupLen; }); - }); } /** @@ -1370,7 +1389,7 @@ class ClipField * \param[in] builder This object holds views to blend group data and helps with building/access. * \param[in] zoneData This object holds views to per-zone data. * \param[inout] opts Clipping options. - * \param[in] n_clip_field_values The node that contains clipping field values. + * \param[in] clipFieldView The view that contains clipping field values. * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ @@ -1378,9 +1397,8 @@ class ClipField BlendGroupBuilderType builder, ZoneData zoneData, ClipOptions &opts, - const conduit::Node &n_clip_field_values) const + const axom::ArrayView &clipFieldView) const { - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldView) { const auto clipValue = opts.clipValue(); const auto selection = getSelection(opts); @@ -1480,7 +1498,6 @@ class ClipField } } }); - }); } /** @@ -1506,8 +1523,9 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { + namespace bputils = axom::mir::utilities::blueprint; constexpr auto connTypeID = - axom::mir::utilities::blueprint::cpp2conduit::id; + bputils::cpp2conduit::id; const auto selection = getSelection(opts); n_newTopo.reset(); @@ -1522,33 +1540,25 @@ class ClipField conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(conduitAllocatorID); n_conn.set(conduit::DataType(connTypeID, fragmentData.m_finalConnSize)); - auto connView = axom::ArrayView( - static_cast(n_conn.data_ptr()), - fragmentData.m_finalConnSize); + auto connView = bputils::make_array_view(n_conn); // Allocate shapes. conduit::Node &n_shapes = n_newTopo["elements/shapes"]; n_shapes.set_allocator(conduitAllocatorID); n_shapes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto shapesView = axom::ArrayView( - static_cast(n_shapes.data_ptr()), - fragmentData.m_finalNumZones); + auto shapesView = bputils::make_array_view(n_shapes); // Allocate sizes. conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(conduitAllocatorID); n_sizes.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto sizesView = axom::ArrayView( - static_cast(n_sizes.data_ptr()), - fragmentData.m_finalNumZones); + auto sizesView = bputils::make_array_view(n_sizes); // Allocate offsets. conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(conduitAllocatorID); n_offsets.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto offsetsView = axom::ArrayView( - static_cast(n_offsets.data_ptr()), - fragmentData.m_finalNumZones); + auto offsetsView = bputils::make_array_view(n_offsets); // Allocate a color variable to keep track of the "color" of the fragments. conduit::Node &n_color = n_newFields[opts.colorField()]; @@ -1557,9 +1567,7 @@ class ClipField conduit::Node &n_color_values = n_color["values"]; n_color_values.set_allocator(conduitAllocatorID); n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); - auto colorView = axom::ArrayView( - static_cast(n_color_values.data_ptr()), - fragmentData.m_finalNumZones); + auto colorView = bputils::make_array_view(n_color_values); // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. @@ -1819,8 +1827,9 @@ class ClipField conduit::Node &n_newTopo, conduit::Node &n_newFields) const { + namespace bputils = axom::mir::utilities::blueprint; constexpr auto connTypeID = - axom::mir::utilities::blueprint::cpp2conduit::id; + bputils::cpp2conduit::id; utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); @@ -1842,9 +1851,7 @@ class ClipField n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(n_orig_values.dtype().id(), fragmentData.m_finalNumZones)); - auto valuesView = axom::ArrayView( - static_cast(n_values.data_ptr()), - fragmentData.m_finalNumZones); + auto valuesView = bputils::make_array_view(n_values); axom::for_all( nzones, AXOM_LAMBDA(auto index) { @@ -1865,9 +1872,7 @@ class ClipField conduit::Node &n_values = n_orig["values"]; n_values.set_allocator(conduitAllocatorID); n_values.set(conduit::DataType(connTypeID, fragmentData.m_finalNumZones)); - auto valuesView = axom::ArrayView( - static_cast(n_values.data_ptr()), - fragmentData.m_finalNumZones); + auto valuesView = bputils::make_array_view(n_values); axom::for_all( nzones, AXOM_LAMBDA(auto index) { diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 3ee5aa6465..80e24fd441 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -113,6 +113,23 @@ struct cpp2conduit static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; }; +//------------------------------------------------------------------------------ + +/** + * \brief Make an axom::ArrayView from a Conduit node. + */ +template +inline axom::ArrayView make_array_view(conduit::Node &n) +{ + return axom::ArrayView(static_cast(n.data_ptr()), n.dtype().number_of_elements()); +} + +template +inline axom::ArrayView make_array_view(const conduit::Node &n) +{ + return axom::ArrayView(static_cast(const_cast(n.data_ptr())), n.dtype().number_of_elements()); +} + //------------------------------------------------------------------------------ /** * \brief This class registers a Conduit allocator that can make Conduit allocate From 7c5eb9134ae3e4053ac3436a17572aad8b454a64 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 16:57:36 -0700 Subject: [PATCH 152/290] make style --- src/axom/mir/ClipField.hpp | 363 +++++++++--------- src/axom/mir/ClipFieldFilter.cpp | 76 ++-- src/axom/mir/ClipFieldFilter.hpp | 4 +- src/axom/mir/ClipFieldFilterDevice.hpp | 32 +- src/axom/mir/RecenterField.hpp | 47 +-- src/axom/mir/blueprint_utilities.hpp | 6 +- .../mir/tests/mir_testing_data_helpers.hpp | 1 - src/axom/mir/tests/mir_testing_helpers.hpp | 37 +- src/axom/mir/views/dispatch_topology.hpp | 4 +- src/axom/mir/views/view_traits.hpp | 1 - 10 files changed, 293 insertions(+), 278 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 29a206ac21..dc2573a254 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -993,15 +993,16 @@ class ClipField else { // Convert to double. - const IndexType n = static_cast(n_clip_field_values.dtype().number_of_elements()); + const IndexType n = + static_cast(n_clip_field_values.dtype().number_of_elements()); clipFieldData = axom::Array(n, n, allocatorID); clipFieldView = clipFieldData.view(); - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) - { - axom::for_all(n, AXOM_LAMBDA(auto index) - { - clipFieldView[index] = static_cast(clipFieldViewSrc[index]); - }); + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { + axom::for_all( + n, + AXOM_LAMBDA(auto index) { + clipFieldView[index] = static_cast(clipFieldViewSrc[index]); + }); }); } @@ -1061,12 +1062,7 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, - builder, - zoneData, - fragmentData, - opts, - clipFieldView); + computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, clipFieldView); computeFragmentSizes(fragmentData, opts); computeFragmentOffsets(fragmentData); @@ -1228,116 +1224,115 @@ class ClipField ClipOptions &opts, const axom::ArrayView &clipFieldView) const { - const auto clipValue = opts.clipValue(); - const auto selection = getSelection(opts); - - auto blendGroupsView = builder.state().m_blendGroupsView; - auto blendGroupsLenView = builder.state().m_blendGroupsLenView; - - m_topologyView.template for_selected_zones( - opts.selectedZonesView(), - AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { - // Get the clip case for the current zone. - const auto clipcase = - details::clip_case(zone, clipFieldView, clipValue); - zoneData.m_clipCasesView[szIndex] = clipcase; - - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto &ctView = clipTableViews[clipTableIndex]; - - int thisBlendGroups = - 0; // The number of blend groups produced in this case. - int thisBlendGroupLen = 0; // The total length of the blend groups. - int thisFragments = - 0; // The number of zone fragments produced in this case. - int thisFragmentsNumIds = - 0; // The number of points used to make all the fragment zones. - BitSet ptused = 0; // A bitset indicating which ST_XX nodes are used. - - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) - { - // Get the current shape in the clip case. - const auto fragment = *it; + const auto clipValue = opts.clipValue(); + const auto selection = getSelection(opts); + + auto blendGroupsView = builder.state().m_blendGroupsView; + auto blendGroupsLenView = builder.state().m_blendGroupsLenView; + + m_topologyView.template for_selected_zones( + opts.selectedZonesView(), + AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { + // Get the clip case for the current zone. + const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); + zoneData.m_clipCasesView[szIndex] = clipcase; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto &ctView = clipTableViews[clipTableIndex]; + + int thisBlendGroups = + 0; // The number of blend groups produced in this case. + int thisBlendGroupLen = 0; // The total length of the blend groups. + int thisFragments = + 0; // The number of zone fragments produced in this case. + int thisFragmentsNumIds = + 0; // The number of points used to make all the fragment zones. + BitSet ptused = 0; // A bitset indicating which ST_XX nodes are used. + + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) + { + // Get the current shape in the clip case. + const auto fragment = *it; - if(fragment[0] == ST_PNT) + if(fragment[0] == ST_PNT) + { + if(details::generatedPointIsSelected(fragment[2], selection)) { - if(details::generatedPointIsSelected(fragment[2], selection)) + const int nIds = static_cast(fragment[3]); + + for(int ni = 0; ni < nIds; ni++) { - const int nIds = static_cast(fragment[3]); + const auto pid = fragment[4 + ni]; - for(int ni = 0; ni < nIds; ni++) + // Increase the blend size to include this center point. + if(pid <= P7) { - const auto pid = fragment[4 + ni]; - - // Increase the blend size to include this center point. - if(pid <= P7) - { - // corner point - thisBlendGroupLen++; - } - else if(pid >= EA && pid <= EL) - { - // edge point - thisBlendGroupLen += 2; - } + // corner point + thisBlendGroupLen++; } + else if(pid >= EA && pid <= EL) + { + // edge point + thisBlendGroupLen += 2; + } + } - // This center or face point counts as a blend group. - thisBlendGroups++; + // This center or face point counts as a blend group. + thisBlendGroups++; - // Mark the point used. - axom::utilities::setBitOn(ptused, N0 + fragment[1]); - } + // Mark the point used. + axom::utilities::setBitOn(ptused, N0 + fragment[1]); } - else + } + else + { + if(details::shapeIsSelected(fragment[1], selection)) { - if(details::shapeIsSelected(fragment[1], selection)) - { - thisFragments++; - const int nIdsThisFragment = fragment.size() - 2; - thisFragmentsNumIds += nIdsThisFragment; + thisFragments++; + const int nIdsThisFragment = fragment.size() - 2; + thisFragmentsNumIds += nIdsThisFragment; - // Mark the points this fragment used. - for(int i = 2; i < fragment.size(); i++) - { - axom::utilities::setBitOn(ptused, fragment[i]); - } + // Mark the points this fragment used. + for(int i = 2; i < fragment.size(); i++) + { + axom::utilities::setBitOn(ptused, fragment[i]); } } } + } - // Save the flags for the points that were used in this zone - zoneData.m_pointsUsedView[szIndex] = ptused; + // Save the flags for the points that were used in this zone + zoneData.m_pointsUsedView[szIndex] = ptused; - // Count which points in the original cell are used. - for(IndexType pid = P0; pid <= P7; pid++) - { - const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + // Count which points in the original cell are used. + for(IndexType pid = P0; pid <= P7; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += incr; // {p0} - thisBlendGroups += incr; - } + thisBlendGroupLen += incr; // {p0} + thisBlendGroups += incr; + } - // Count edges that are used. - for(IndexType pid = EA; pid <= EL; pid++) - { - const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; + // Count edges that are used. + for(IndexType pid = EA; pid <= EL; pid++) + { + const int incr = axom::utilities::bitIsSet(ptused, pid) ? 1 : 0; - thisBlendGroupLen += 2 * incr; // {p0 p1} - thisBlendGroups += incr; - } + thisBlendGroupLen += 2 * incr; // {p0 p1} + thisBlendGroups += incr; + } - // Save the results. - fragmentData.m_fragmentsView[szIndex] = thisFragments; - fragmentData.m_fragmentsSizeView[szIndex] = thisFragmentsNumIds; + // Save the results. + fragmentData.m_fragmentsView[szIndex] = thisFragments; + fragmentData.m_fragmentsSizeView[szIndex] = thisFragmentsNumIds; - // Set blend group sizes for this zone. - blendGroupsView[szIndex] = thisBlendGroups; - blendGroupsLenView[szIndex] = thisBlendGroupLen; - }); + // Set blend group sizes for this zone. + blendGroupsView[szIndex] = thisBlendGroups; + blendGroupsLenView[szIndex] = thisBlendGroupLen; + }); } /** @@ -1399,105 +1394,105 @@ class ClipField ClipOptions &opts, const axom::ArrayView &clipFieldView) const { - const auto clipValue = opts.clipValue(); - const auto selection = getSelection(opts); + const auto clipValue = opts.clipValue(); + const auto selection = getSelection(opts); - m_topologyView.template for_selected_zones( - opts.selectedZonesView(), - AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { - // Get the clip case for the current zone. - const auto clipcase = zoneData.m_clipCasesView[szIndex]; + m_topologyView.template for_selected_zones( + opts.selectedZonesView(), + AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { + // Get the clip case for the current zone. + const auto clipcase = zoneData.m_clipCasesView[szIndex]; + + // Iterate over the shapes in this clip case to determine the number of blend groups. + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto &ctView = clipTableViews[clipTableIndex]; - // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto &ctView = clipTableViews[clipTableIndex]; + // These are the points used in this zone's fragments. + const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; - // These are the points used in this zone's fragments. - const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; + // Get the blend groups for this zone. + auto groups = builder.blendGroupsForZone(szIndex); - // Get the blend groups for this zone. - auto groups = builder.blendGroupsForZone(szIndex); + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) + { + // Get the current shape in the clip case. + const auto fragment = *it; - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) + if(fragment[0] == ST_PNT) { - // Get the current shape in the clip case. - const auto fragment = *it; - - if(fragment[0] == ST_PNT) + if(details::generatedPointIsSelected(fragment[2], selection)) { - if(details::generatedPointIsSelected(fragment[2], selection)) + const int nIds = static_cast(fragment[3]); + const auto one_over_n = 1.f / static_cast(nIds); + + groups.beginGroup(); + for(int ni = 0; ni < nIds; ni++) { - const int nIds = static_cast(fragment[3]); - const auto one_over_n = 1.f / static_cast(nIds); + const auto ptid = fragment[4 + ni]; - groups.beginGroup(); - for(int ni = 0; ni < nIds; ni++) + // Add the point to the blend group. + if(ptid <= P7) { - const auto ptid = fragment[4 + ni]; - - // Add the point to the blend group. - if(ptid <= P7) - { - // corner point. - groups.add(zone.getId(ptid), one_over_n); - } - else if(ptid >= EA && ptid <= EL) - { - // edge point. - const auto edgeIndex = ptid - EA; - const auto edge = zone.getEdge(edgeIndex); - const auto id0 = zone.getId(edge[0]); - const auto id1 = zone.getId(edge[1]); - - // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); - - groups.add(id0, one_over_n * (1.f - t)); - groups.add(id1, one_over_n * t); - } + // corner point. + groups.add(zone.getId(ptid), one_over_n); + } + else if(ptid >= EA && ptid <= EL) + { + // edge point. + const auto edgeIndex = ptid - EA; + const auto edge = zone.getEdge(edgeIndex); + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); + + groups.add(id0, one_over_n * (1.f - t)); + groups.add(id1, one_over_n * t); } - groups.endGroup(); } + groups.endGroup(); } } + } - // Add blend group for each original point that was used. - for(IndexType pid = P0; pid <= P7; pid++) + // Add blend group for each original point that was used. + for(IndexType pid = P0; pid <= P7; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) { - if(axom::utilities::bitIsSet(ptused, pid)) - { - groups.beginGroup(); - groups.add(zone.getId(pid), 1.f); - groups.endGroup(); - } + groups.beginGroup(); + groups.add(zone.getId(pid), 1.f); + groups.endGroup(); } + } - // Add blend group for each edge point that was used. - for(IndexType pid = EA; pid <= EL; pid++) + // Add blend group for each edge point that was used. + for(IndexType pid = EA; pid <= EL; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) { - if(axom::utilities::bitIsSet(ptused, pid)) - { - const auto edgeIndex = pid - EA; - const auto edge = zone.getEdge(edgeIndex); - const auto id0 = zone.getId(edge[0]); - const auto id1 = zone.getId(edge[1]); - - // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); - - groups.beginGroup(); - groups.add(id0, 1.f - t); - groups.add(id1, t); - groups.endGroup(); - } + const auto edgeIndex = pid - EA; + const auto edge = zone.getEdge(edgeIndex); + const auto id0 = zone.getId(edge[0]); + const auto id1 = zone.getId(edge[1]); + + // Figure out the blend for edge. + const float t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); + + groups.beginGroup(); + groups.add(id0, 1.f - t); + groups.add(id1, t); + groups.endGroup(); } - }); + } + }); } /** @@ -1524,8 +1519,7 @@ class ClipField conduit::Node &n_newFields) const { namespace bputils = axom::mir::utilities::blueprint; - constexpr auto connTypeID = - bputils::cpp2conduit::id; + constexpr auto connTypeID = bputils::cpp2conduit::id; const auto selection = getSelection(opts); n_newTopo.reset(); @@ -1828,8 +1822,7 @@ class ClipField conduit::Node &n_newFields) const { namespace bputils = axom::mir::utilities::blueprint; - constexpr auto connTypeID = - bputils::cpp2conduit::id; + constexpr auto connTypeID = bputils::cpp2conduit::id; utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); diff --git a/src/axom/mir/ClipFieldFilter.cpp b/src/axom/mir/ClipFieldFilter.cpp index 95a6500669..43394e4778 100644 --- a/src/axom/mir/ClipFieldFilter.cpp +++ b/src/axom/mir/ClipFieldFilter.cpp @@ -31,68 +31,72 @@ namespace mir { namespace clipping { +void ClipFieldFilter::execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) +{ + ClipOptions opts(0, n_options); + const std::string clipFieldName = opts.clipField(); - void ClipFieldFilter::execute(const conduit::Node &n_input, - const conduit::Node &n_options, - conduit::Node &n_output) - { - ClipOptions opts(0, n_options); - const std::string clipFieldName = opts.clipField(); - - const conduit::Node &n_fields = n_input.fetch_existing("fields"); - const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); - const std::string &topoName = n_clipField["topology"].as_string(); - const conduit::Node &n_topo = - n_input.fetch_existing("topologies/" + topoName); - const std::string &coordsetName = n_topo["coordset"].as_string(); - const conduit::Node &n_coordset = - n_input.fetch_existing("coordsets/" + coordsetName); + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); + const std::string &topoName = n_clipField["topology"].as_string(); + const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); + const std::string &coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); - execute(n_topo, - n_coordset, - n_fields, - n_options, - n_output["topologies/" + opts.topologyName(topoName)], - n_output["coordsets/" + opts.coordsetName(coordsetName)], - n_output["fields"]); - } + execute(n_topo, + n_coordset, + n_fields, + n_options, + n_output["topologies/" + opts.topologyName(topoName)], + n_output["coordsets/" + opts.coordsetName(coordsetName)], + n_output["fields"]); +} void ClipFieldFilter::execute(const conduit::Node &n_topo, - const conduit::Node &n_coordset, - const conduit::Node &n_fields, - const conduit::Node &n_options, - conduit::Node &n_newTopo, - conduit::Node &n_newCoordset, - conduit::Node &n_newFields) + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) { // Instantiate the algorithm for the right device and invoke it. if(m_runtime == axom::runtime_policy::Policy::seq) { ClipFieldFilterDevice clipper; - clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); + clipper.execute(n_topo, + n_coordset, + n_fields, + n_options, + n_newTopo, + n_newCoordset, + n_newFields); } #if 0 -#if defined(AXOM_USE_OPENMP) + #if defined(AXOM_USE_OPENMP) else if(m_runtime == axom::runtime_policy::Policy::omp) { ClipFieldFilterDevice clipper; clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); } -#endif -#if defined(AXOM_USE_CUDA) + #endif + #if defined(AXOM_USE_CUDA) else if(m_runtime == axom::runtime_policy::Policy::cuda) { ClipFieldFilterDevice clipper; clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); } -#endif -#if defined(AXOM_USE_HIP) + #endif + #if defined(AXOM_USE_HIP) else if(m_runtime == axom::runtime_policy::Policy::hip) { ClipFieldFilterDevice clipper; clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); } -#endif + #endif #endif } diff --git a/src/axom/mir/ClipFieldFilter.hpp b/src/axom/mir/ClipFieldFilter.hpp index 45383b9d1c..bc5f01dd0e 100644 --- a/src/axom/mir/ClipFieldFilter.hpp +++ b/src/axom/mir/ClipFieldFilter.hpp @@ -14,7 +14,6 @@ namespace mir { namespace clipping { - /** * \brief This class runs a ClipField algorithm. * @@ -23,8 +22,7 @@ class ClipFieldFilter { public: /// Constructor - ClipFieldFilter() : m_runtime(axom::runtime_policy::Policy::seq) - { } + ClipFieldFilter() : m_runtime(axom::runtime_policy::Policy::seq) { } /** * \brief Set the runtime policy. diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp index 5b39a9ae36..5278c984cf 100644 --- a/src/axom/mir/ClipFieldFilterDevice.hpp +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -16,7 +16,6 @@ namespace mir { namespace clipping { - /** * \brief This class instantiates ClipField so it runs on specific device but * can operate on a variety of Blueprint topologies/coordsets/types. @@ -28,8 +27,7 @@ class ClipFieldFilterDevice { public: /// Constructor - ClipFieldFilterDevice() - { } + ClipFieldFilterDevice() { } /** * \brief Execute the clipping operation using the specified options. @@ -102,19 +100,27 @@ class ClipFieldFilterDevice conduit::Node &n_newCoordset, conduit::Node &n_newFields) { - axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) - { + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { // We dispatch to 2D/3D topologies with supported shapes. - constexpr int dims = axom::mir::views::select_dimensions(2,3); + constexpr int dims = axom::mir::views::select_dimensions(2, 3); constexpr int shapes = supported_shapes(); - axom::mir::views::dispatch_topology(n_topo, n_coordset, [&](const std::string &/*shape*/, auto topologyView) - { - using TopologyView = decltype(topologyView); - using CoordsetView = decltype(coordsetView); + axom::mir::views::dispatch_topology( + n_topo, + n_coordset, + [&](const std::string & /*shape*/, auto topologyView) { + using TopologyView = decltype(topologyView); + using CoordsetView = decltype(coordsetView); - ClipField clipper(topologyView, coordsetView); - clipper.execute(n_topo, n_coordset, n_fields, n_options, n_newTopo, n_newCoordset, n_newFields); - }); + ClipField clipper(topologyView, + coordsetView); + clipper.execute(n_topo, + n_coordset, + n_fields, + n_options, + n_newTopo, + n_newCoordset, + n_newFields); + }); }); } }; diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 70c91a1361..f3d274676a 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -17,7 +17,6 @@ namespace axom { namespace mir { - /** * \brief Convert a field with one association type to a field of another association type using an o2mrelation. * @@ -69,9 +68,7 @@ void RecenterField::execute(const conduit::Node &field, for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { const conduit::Node &n_comp = n_values[c]; - recenterSingleComponent(relation, - outField["values"][n_comp.name()], - n_comp); + recenterSingleComponent(relation, outField["values"][n_comp.name()], n_comp); } } else @@ -105,28 +102,26 @@ void RecenterField::recenterSingleComponent( n_out.set_allocator(c2a.getConduitAllocatorID()); n_out.set(n_comp.dtype().id(), relSize); - views::Node_to_ArrayView_same( - n_comp, - n_out, - [&](auto compView, auto outView) { - using Precision = typename decltype(compView)::value_type; - using AccumType = typename axom::mir::utilities::accumulation_traits::value_type; - axom::for_all( - relSize, - AXOM_LAMBDA(int relIndex) { - const auto n = sizesView[relIndex]; - const auto offset = offsetsView[relIndex]; - - AccumType sum = 0; - for(int i = 0; i < n; i++) - { - const auto id = relView[offset + i]; - sum += static_cast(compView[id]); - } - - outView[relIndex] = static_cast(sum / n); - }); - }); + views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) { + using Precision = typename decltype(compView)::value_type; + using AccumType = + typename axom::mir::utilities::accumulation_traits::value_type; + axom::for_all( + relSize, + AXOM_LAMBDA(int relIndex) { + const auto n = sizesView[relIndex]; + const auto offset = offsetsView[relIndex]; + + AccumType sum = 0; + for(int i = 0; i < n; i++) + { + const auto id = relView[offset + i]; + sum += static_cast(compView[id]); + } + + outView[relIndex] = static_cast(sum / n); + }); + }); }); } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 80e24fd441..0020cfa6ce 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -121,13 +121,15 @@ struct cpp2conduit template inline axom::ArrayView make_array_view(conduit::Node &n) { - return axom::ArrayView(static_cast(n.data_ptr()), n.dtype().number_of_elements()); + return axom::ArrayView(static_cast(n.data_ptr()), + n.dtype().number_of_elements()); } template inline axom::ArrayView make_array_view(const conduit::Node &n) { - return axom::ArrayView(static_cast(const_cast(n.data_ptr())), n.dtype().number_of_elements()); + return axom::ArrayView(static_cast(const_cast(n.data_ptr())), + n.dtype().number_of_elements()); } //------------------------------------------------------------------------------ diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index 6152723c70..9eb30430ca 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -19,7 +19,6 @@ namespace testing { namespace data { - //------------------------------------------------------------------------------ void add_distance(conduit::Node &mesh, float dist = 6.5f) diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index d01e7c5f98..326b28f977 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -17,20 +17,32 @@ // Define better names for the execution spaces. This header needs to be included // after the ExecSpace types are defined. template -struct execution_name { static std::string name() { return "seq"; } }; +struct execution_name +{ + static std::string name() { return "seq"; } +}; -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) #if defined(AXOM_USE_OPENMP) - template <> - struct execution_name { static std::string name() { return "omp"; } }; +template <> +struct execution_name +{ + static std::string name() { return "omp"; } +}; #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - template <> - struct execution_name { static std::string name() { return "cuda"; } }; +template <> +struct execution_name +{ + static std::string name() { return "cuda"; } +}; #endif #if defined(AXOM_USE_HIP) - template <> - struct execution_name { static std::string name() { return "hip"; } }; +template <> +struct execution_name +{ + static std::string name() { return "hip"; } +}; #endif #endif @@ -120,7 +132,9 @@ void saveBaseline(const std::string &filename, const conduit::Node &n) #endif } -void saveBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node &n) +void saveBaseline(const std::vector &baselinePaths, + const std::string &baselineName, + const conduit::Node &n) { for(const auto &path : baselinePaths) { @@ -152,7 +166,10 @@ std::vector baselinePaths() return paths; } -bool compareBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node ¤t, double tolerance = 1.e-10) +bool compareBaseline(const std::vector &baselinePaths, + const std::string &baselineName, + const conduit::Node ¤t, + double tolerance = 1.e-10) { bool success = false; int count = 0; diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index 8131bd7de2..5d0e43d262 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -31,7 +31,9 @@ namespace views * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ -template +template void dispatch_topology(const conduit::Node &topo, const conduit::Node &coordset, FuncType &&func) diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index 974efaff7e..50ae53c396 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -14,7 +14,6 @@ namespace mir { namespace views { - /// General traits for topology views. template struct view_traits From 64c60244ad59b8650ae4708fc7693570fb7101e5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 18:47:38 -0700 Subject: [PATCH 153/290] Combined some structured topo dispatch --- src/axom/mir/ClipField.hpp | 46 +++--- src/axom/mir/ClipFieldFilterDevice.hpp | 35 ++-- src/axom/mir/views/StructuredTopologyView.hpp | 15 ++ .../views/dispatch_rectilinear_topology.hpp | 153 +++++++++++++++--- .../views/dispatch_structured_topology.hpp | 80 +++++++-- src/axom/mir/views/dispatch_topology.hpp | 17 +- .../mir/views/dispatch_uniform_topology.hpp | 150 ++++++++++++++--- 7 files changed, 399 insertions(+), 97 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index dc2573a254..f55dee955f 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -165,14 +165,15 @@ inline bool shapeIsSelected(unsigned char color, int selection) * * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. */ +template AXOM_HOST_DEVICE -inline float computeWeight(float d0, float d1, float clipValue) +inline T computeWeight(T d0, T d1, T clipValue) { - constexpr float tiny = 1.e-09; + constexpr T tiny = 1.e-09; return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / (axom::utilities::abs(d1 - d0) + tiny), - 0.f, - 1.f); + T(0), + T(1)); } // TODO: Could we make ZoneType be a concept? @@ -255,10 +256,10 @@ class ClipOptions * \brief Return the clip value. * \return The clip value. */ - double clipValue() const + float clipValue() const { return m_options.has_child("clipValue") - ? m_options.fetch_existing("clipValue").to_double() + ? m_options.fetch_existing("clipValue").to_float() : 0.f; } @@ -902,6 +903,7 @@ class ClipField using reduce_policy = typename axom::execution_space::reduce_policy; using ConnectivityType = typename TopologyView::ConnectivityType; using BlendGroupBuilderType = BlendGroupBuilder; + using ClipFieldType = float; /** * \brief Constructor @@ -983,25 +985,25 @@ class ClipField const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); const conduit::Node &n_clip_field_values = n_clip_field["values"]; SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - axom::Array clipFieldData; - axom::ArrayView clipFieldView; - if(n_clip_field_values.dtype().is_float64()) + axom::Array clipFieldData; + axom::ArrayView clipFieldView; + if(n_clip_field_values.dtype().is_float32()) { // Make a view. - clipFieldView = bputils::make_array_view(n_clip_field_values); + clipFieldView = bputils::make_array_view(n_clip_field_values); } else { // Convert to double. const IndexType n = static_cast(n_clip_field_values.dtype().number_of_elements()); - clipFieldData = axom::Array(n, n, allocatorID); + clipFieldData = axom::Array(n, n, allocatorID); clipFieldView = clipFieldData.view(); views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { axom::for_all( n, AXOM_LAMBDA(auto index) { - clipFieldView[index] = static_cast(clipFieldViewSrc[index]); + clipFieldView[index] = static_cast(clipFieldViewSrc[index]); }); }); } @@ -1222,9 +1224,9 @@ class ClipField ZoneData zoneData, FragmentData fragmentData, ClipOptions &opts, - const axom::ArrayView &clipFieldView) const + const axom::ArrayView &clipFieldView) const { - const auto clipValue = opts.clipValue(); + const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); auto blendGroupsView = builder.state().m_blendGroupsView; @@ -1392,9 +1394,9 @@ class ClipField BlendGroupBuilderType builder, ZoneData zoneData, ClipOptions &opts, - const axom::ArrayView &clipFieldView) const + const axom::ArrayView &clipFieldView) const { - const auto clipValue = opts.clipValue(); + const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); m_topologyView.template for_selected_zones( @@ -1447,9 +1449,9 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); + const auto t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); groups.add(id0, one_over_n * (1.f - t)); groups.add(id1, one_over_n * t); @@ -1482,9 +1484,9 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const float t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); + const auto t = details::computeWeight(clipFieldView[id0], + clipFieldView[id1], + clipValue); groups.beginGroup(); groups.add(id0, 1.f - t); diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp index 5278c984cf..4ce2aac0fc 100644 --- a/src/axom/mir/ClipFieldFilterDevice.hpp +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -100,26 +100,41 @@ class ClipFieldFilterDevice conduit::Node &n_newCoordset, conduit::Node &n_newFields) { + // NOTE - there are 2 dispatches here so we can get coordset and topology views. + // This instantiates the lambda that creates the ClipField object for + // the various view types so we can handle most Blueprint topologies. + // However, this expansion makes this file take a long time to build. + // + // Perhaps we could split the topology dispatch somehow to split the + // compilation responsibility among more cores for a faster build. + // axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { // We dispatch to 2D/3D topologies with supported shapes. constexpr int dims = axom::mir::views::select_dimensions(2, 3); constexpr int shapes = supported_shapes(); axom::mir::views::dispatch_topology( n_topo, - n_coordset, [&](const std::string & /*shape*/, auto topologyView) { + using TopologyView = decltype(topologyView); using CoordsetView = decltype(coordsetView); - ClipField clipper(topologyView, - coordsetView); - clipper.execute(n_topo, - n_coordset, - n_fields, - n_options, - n_newTopo, - n_newCoordset, - n_newFields); + // Don't allow 3D topologies with 2D coordsets. + constexpr int RuntimeDimension = -1; + if constexpr ((TopologyView::dimension() == 2) || + (TopologyView::dimension() == 3 && CoordsetView::dimension() == 3) || + (TopologyView::dimension() == RuntimeDimension)) + { + ClipField clipper(topologyView, + coordsetView); + clipper.execute(n_topo, + n_coordset, + n_fields, + n_options, + n_newTopo, + n_newCoordset, + n_newFields); + } }); }); } diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index b66ae892a9..e3a61b8b44 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -36,6 +36,13 @@ class StructuredTopologyView AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } + /** + * \brief Constructor + */ + AXOM_HOST_DEVICE + StructuredTopologyView() : m_indexing() + { } + /** * \brief Constructor * @@ -71,6 +78,14 @@ class StructuredTopologyView return m_indexing.logicalDimensions(); } + /** + * \brief Return indexing object. + * + * \return The indexing object. + */ + AXOM_HOST_DEVICE + IndexingPolicy &indexing() { return m_indexing; } + /** * \brief Return indexing object. * diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index d2734cc3fd..02b66d9560 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -18,6 +18,127 @@ namespace mir { namespace views { + +template +struct make_rectilinear {}; + +/** + * \brief Create a 3D structured topology view with normal structured indexing. + */ +template<> +struct make_rectilinear<3> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topo. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + LogicalIndex zoneDims; + zoneDims[0] = + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = + coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + zoneDims[2] = + coordset->fetch_existing(axes[2]).dtype().number_of_elements() - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topo. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + +/** + * \brief Create a 2D structured topology view with normal structured indexing. + */ +template<> +struct make_rectilinear<2> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topology. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + LogicalIndex zoneDims; + zoneDims[0] = + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = + coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + +/** + * \brief Create a 1D structured topology view with normal structured indexing. + */ +template<> +struct make_rectilinear<1> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topology. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + LogicalIndex zoneDims; + zoneDims[0] = + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + /** * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. * @@ -25,29 +146,21 @@ namespace views * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. * * \param topo The node that contains the rectilinear topology. - * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. */ template -void dispatch_rectilinear_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), - const conduit::Node &coordset, +void dispatch_rectilinear_topology(const conduit::Node &topo, FuncType &&func) { - const auto axes = conduit::blueprint::mesh::utils::coordset::axes(coordset); + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); switch(axes.size()) { case 3: if constexpr(dimension_selected(SelectedDimensions, 3)) { - axom::StackArray zoneDims; - zoneDims[0] = - coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = - coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; - zoneDims[2] = - coordset.fetch_existing(axes[2]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_rectilinear<3>::view(topo); const std::string shape("hex"); func(shape, topoView); } @@ -55,13 +168,7 @@ void dispatch_rectilinear_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), case 2: if constexpr(dimension_selected(SelectedDimensions, 2)) { - axom::StackArray zoneDims; - zoneDims[0] = - coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = - coordset.fetch_existing(axes[1]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_rectilinear<2>::view(topo); const std::string shape("quad"); func(shape, topoView); } @@ -69,11 +176,7 @@ void dispatch_rectilinear_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), case 1: if constexpr(dimension_selected(SelectedDimensions, 1)) { - axom::StackArray zoneDims; - zoneDims[0] = - coordset.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_rectilinear<1>::view(topo); const std::string shape("line"); func(shape, topoView); } diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index cf503f6d8d..df0f99a221 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -316,7 +316,6 @@ struct make_structured<1> * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension * * \param topo The node that contains the rectilinear topology. - * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ template @@ -387,22 +386,81 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. dimension * * \param topo The node that contains the topology. - * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. - + * + * \note We try to initialize the topoView for each dimension and share the dispatch. */ template void dispatch_structured_topologies(const conduit::Node &topo, - const conduit::Node &coordset, FuncType &&func) { - const std::string type = topo["type"].as_string(); - if(type == "uniform") - dispatch_uniform_topology(topo, coordset, func); - else if(type == "rectilinear") - dispatch_rectilinear_topology(topo, coordset, func); - else if(type == "structured") - dispatch_structured_topology(topo, func); + const std::string offsetsKey("offsets"), stridesKey("strides"); + const std::string type = topo.fetch_existing("type").as_string(); + auto ndims = conduit::blueprint::mesh::utils::topology::dims(topo); + switch(ndims) + { + case 3: + if constexpr(dimension_selected(SelectedDimensions, 3)) + { + const std::string shape("hex"); + + if(type == "structured" && topo.has_path(offsetsKey) && topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<3>::view(topo); + func(shape, topoView); + } + else + { + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<3>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<3>::view(topo); + else if(type == "structured") + topoView = make_structured<3>::view(topo); + func(shape, topoView); + } + } + break; + case 2: + if constexpr(dimension_selected(SelectedDimensions, 2)) + { + const std::string shape("quad"); + if(type == "structured" && topo.has_path(offsetsKey) && topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<2>::view(topo); + func(shape, topoView); + } + else + { + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<2>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<2>::view(topo); + else if(type == "structured") + topoView = make_structured<2>::view(topo); + func(shape, topoView); + } + } + break; + case 1: + if constexpr(dimension_selected(SelectedDimensions, 1)) + { + const std::string shape("line"); + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<1>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<1>::view(topo); + else if(type == "structured") + topoView = make_structured<1>::view(topo); + func(shape, topoView); + } + } } } // end namespace views diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index 5d0e43d262..c7dc26dee8 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -28,26 +28,25 @@ namespace views * \tparam ShapeTypes A bitset containing the shape types that will be supported for unstructured. * * \param topo The node that contains the rectilinear topology. - * \param coordset The coordset node that contains the topology dimensions. * \param func The function to invoke using the view. It should accept a string with the shape name and an auto parameter for the view. */ template void dispatch_topology(const conduit::Node &topo, - const conduit::Node &coordset, FuncType &&func) { const auto type = topo.fetch_existing("type").as_string(); - if(type == "uniform") - dispatch_uniform_topology(topo, coordset, func); - else if(type == "rectilinear") - dispatch_rectilinear_topology(topo, coordset, func); - else if(type == "structured") - dispatch_structured_topology(topo, func); - else if(type == "unstructured") + if(type == "unstructured") + { dispatch_unstructured_topology(topo, func); + } + else if(type == "uniform" || type == "rectilinear" || type == "structured") + { + // Dispatch the structured topologies together because we can be smarter about the views. + dispatch_structured_topologies(topo, func); + } } } // end namespace views diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index a74b0bc7a8..416d6c49af 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -8,7 +8,9 @@ #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/dispatch_utilities.hpp" + #include +#include namespace axom { @@ -16,33 +18,148 @@ namespace mir { namespace views { + +/** + * \brief Base template for uniform topology creation + */ +template +struct make_uniform +{ }; + +/** + * \brief Create a 3D structured topology view with normal structured indexing. + */ +template <> +struct make_uniform<3> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topology. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const conduit::Node &n_dims = coordset->fetch_existing("dims"); + LogicalIndex zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + zoneDims[1] = n_dims.as_int_accessor()[1] - 1; + zoneDims[2] = n_dims.as_int_accessor()[2] - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + +/** + * \brief Create a 2D structured topology view with normal structured indexing. + */ +template <> +struct make_uniform<2> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topology. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const conduit::Node &n_dims = coordset->fetch_existing("dims"); + LogicalIndex zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + zoneDims[1] = n_dims.as_int_accessor()[1] - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + +/** + * \brief Create a 1D structured topology view with normal structured indexing. + */ +template <> +struct make_uniform<1> +{ + using Indexing = views::StructuredIndexing; + using LogicalIndex = typename Indexing::LogicalIndex; + using TopoView = views::StructuredTopologyView; + + /** + * \brief Create the indexing and initialize it from the topology. + * \param topo The node containing the topology. + * \return The indexing. + */ + static Indexing indexing(const conduit::Node &topo) + { + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const conduit::Node &n_dims = coordset->fetch_existing("dims"); + LogicalIndex zoneDims; + zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + return Indexing(zoneDims); + } + + /** + * \brief Create the topology view and initialize it from the topology. + * \param topo The node containing the topology. + * \return The topology view. + */ + static TopoView view(const conduit::Node &topo) + { + return TopoView(indexing(topo)); + } +}; + /** * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. * \tparam SelectedDimensions An integer whose bits indicate which dimensions are set. * - * \param coordset The coordset node that contains the topology dimensions. - * \param func The function to invoke using the view. + * \param topo The node that contains the topology. + * \param func The function to invoke using the view. */ template -void dispatch_uniform_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), - const conduit::Node &coordset, +void dispatch_uniform_topology(const conduit::Node &topo, FuncType &&func) { - const conduit::Node &n_dims = coordset["dims"]; + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); + const conduit::Node &n_dims = coordset->fetch_existing("dims"); switch(n_dims.dtype().number_of_elements()) { default: case 3: if constexpr(dimension_selected(SelectedDimensions, 3)) { - axom::StackArray zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - zoneDims[1] = n_dims.as_int_accessor()[1] - 1; - zoneDims[2] = n_dims.as_int_accessor()[2] - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_uniform<3>::view(topo); const std::string shape("hex"); func(shape, topoView); } @@ -50,11 +167,7 @@ void dispatch_uniform_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), case 2: if constexpr(dimension_selected(SelectedDimensions, 2)) { - axom::StackArray zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - zoneDims[1] = n_dims.as_int_accessor()[1] - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_uniform<2>::view(topo); const std::string shape("quad"); func(shape, topoView); } @@ -62,10 +175,7 @@ void dispatch_uniform_topology(const conduit::Node &AXOM_UNUSED_PARAM(topo), case 1: if constexpr(dimension_selected(SelectedDimensions, 3)) { - axom::StackArray zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - views::StructuredTopologyView> - topoView(zoneDims); + auto topoView = make_uniform<1>::view(topo); const std::string shape("line"); func(shape, topoView); } From 60bd3cdcff1b27bf77a231062bfc6d9cd9f9b4ea Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 7 Aug 2024 18:49:02 -0700 Subject: [PATCH 154/290] make style --- src/axom/mir/ClipField.hpp | 9 +++-- src/axom/mir/ClipFieldFilterDevice.hpp | 13 ++++--- src/axom/mir/views/StructuredTopologyView.hpp | 3 +- .../views/dispatch_rectilinear_topology.hpp | 37 ++++++++++--------- .../views/dispatch_structured_topology.hpp | 11 +++--- src/axom/mir/views/dispatch_topology.hpp | 3 +- .../mir/views/dispatch_uniform_topology.hpp | 16 ++++---- 7 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index f55dee955f..55af641317 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -166,8 +166,7 @@ inline bool shapeIsSelected(unsigned char color, int selection) * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. */ template -AXOM_HOST_DEVICE -inline T computeWeight(T d0, T d1, T clipValue) +AXOM_HOST_DEVICE inline T computeWeight(T d0, T d1, T clipValue) { constexpr T tiny = 1.e-09; return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / @@ -990,7 +989,8 @@ class ClipField if(n_clip_field_values.dtype().is_float32()) { // Make a view. - clipFieldView = bputils::make_array_view(n_clip_field_values); + clipFieldView = + bputils::make_array_view(n_clip_field_values); } else { @@ -1003,7 +1003,8 @@ class ClipField axom::for_all( n, AXOM_LAMBDA(auto index) { - clipFieldView[index] = static_cast(clipFieldViewSrc[index]); + clipFieldView[index] = + static_cast(clipFieldViewSrc[index]); }); }); } diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp index 4ce2aac0fc..c781df864a 100644 --- a/src/axom/mir/ClipFieldFilterDevice.hpp +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -115,18 +115,19 @@ class ClipFieldFilterDevice axom::mir::views::dispatch_topology( n_topo, [&](const std::string & /*shape*/, auto topologyView) { - using TopologyView = decltype(topologyView); using CoordsetView = decltype(coordsetView); // Don't allow 3D topologies with 2D coordsets. constexpr int RuntimeDimension = -1; - if constexpr ((TopologyView::dimension() == 2) || - (TopologyView::dimension() == 3 && CoordsetView::dimension() == 3) || - (TopologyView::dimension() == RuntimeDimension)) + if constexpr((TopologyView::dimension() == 2) || + (TopologyView::dimension() == 3 && + CoordsetView::dimension() == 3) || + (TopologyView::dimension() == RuntimeDimension)) { - ClipField clipper(topologyView, - coordsetView); + ClipField clipper( + topologyView, + coordsetView); clipper.execute(n_topo, n_coordset, n_fields, diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index e3a61b8b44..a16a81200f 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -40,8 +40,7 @@ class StructuredTopologyView * \brief Constructor */ AXOM_HOST_DEVICE - StructuredTopologyView() : m_indexing() - { } + StructuredTopologyView() : m_indexing() { } /** * \brief Constructor diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index 02b66d9560..02090db779 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -18,14 +18,14 @@ namespace mir { namespace views { - template -struct make_rectilinear {}; +struct make_rectilinear +{ }; /** * \brief Create a 3D structured topology view with normal structured indexing. */ -template<> +template <> struct make_rectilinear<3> { using Indexing = views::StructuredIndexing; @@ -39,16 +39,17 @@ struct make_rectilinear<3> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; zoneDims[1] = - coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; zoneDims[2] = - coordset->fetch_existing(axes[2]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[2]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -66,7 +67,7 @@ struct make_rectilinear<3> /** * \brief Create a 2D structured topology view with normal structured indexing. */ -template<> +template <> struct make_rectilinear<2> { using Indexing = views::StructuredIndexing; @@ -80,14 +81,15 @@ struct make_rectilinear<2> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; zoneDims[1] = - coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -105,7 +107,7 @@ struct make_rectilinear<2> /** * \brief Create a 1D structured topology view with normal structured indexing. */ -template<> +template <> struct make_rectilinear<1> { using Indexing = views::StructuredIndexing; @@ -119,12 +121,13 @@ struct make_rectilinear<1> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -149,10 +152,10 @@ struct make_rectilinear<1> * \param func The function to invoke using the view. */ template -void dispatch_rectilinear_topology(const conduit::Node &topo, - FuncType &&func) +void dispatch_rectilinear_topology(const conduit::Node &topo, FuncType &&func) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); switch(axes.size()) diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index df0f99a221..fb1a025712 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -391,8 +391,7 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) * \note We try to initialize the topoView for each dimension and share the dispatch. */ template -void dispatch_structured_topologies(const conduit::Node &topo, - FuncType &&func) +void dispatch_structured_topologies(const conduit::Node &topo, FuncType &&func) { const std::string offsetsKey("offsets"), stridesKey("strides"); const std::string type = topo.fetch_existing("type").as_string(); @@ -403,8 +402,9 @@ void dispatch_structured_topologies(const conduit::Node &topo, if constexpr(dimension_selected(SelectedDimensions, 3)) { const std::string shape("hex"); - - if(type == "structured" && topo.has_path(offsetsKey) && topo.has_path(stridesKey)) + + if(type == "structured" && topo.has_path(offsetsKey) && + topo.has_path(stridesKey)) { auto topoView = make_strided_structured<3>::view(topo); func(shape, topoView); @@ -427,7 +427,8 @@ void dispatch_structured_topologies(const conduit::Node &topo, if constexpr(dimension_selected(SelectedDimensions, 2)) { const std::string shape("quad"); - if(type == "structured" && topo.has_path(offsetsKey) && topo.has_path(stridesKey)) + if(type == "structured" && topo.has_path(offsetsKey) && + topo.has_path(stridesKey)) { auto topoView = make_strided_structured<2>::view(topo); func(shape, topoView); diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index c7dc26dee8..ac149c4f7a 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -33,8 +33,7 @@ namespace views template -void dispatch_topology(const conduit::Node &topo, - FuncType &&func) +void dispatch_topology(const conduit::Node &topo, FuncType &&func) { const auto type = topo.fetch_existing("type").as_string(); diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 416d6c49af..75ce93743e 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -18,7 +18,6 @@ namespace mir { namespace views { - /** * \brief Base template for uniform topology creation */ @@ -43,7 +42,8 @@ struct make_uniform<3> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; @@ -81,7 +81,8 @@ struct make_uniform<2> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; @@ -118,7 +119,8 @@ struct make_uniform<1> */ static Indexing indexing(const conduit::Node &topo) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; @@ -147,10 +149,10 @@ struct make_uniform<1> * \param func The function to invoke using the view. */ template -void dispatch_uniform_topology(const conduit::Node &topo, - FuncType &&func) +void dispatch_uniform_topology(const conduit::Node &topo, FuncType &&func) { - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); switch(n_dims.dtype().number_of_elements()) From 5c514783577430fb47220a476fea7683dc95f372 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 8 Aug 2024 11:55:52 -0700 Subject: [PATCH 155/290] Separate out options --- src/axom/mir/ClipField.hpp | 246 ++------------------------- src/axom/mir/ClipOptions.hpp | 96 +++++++++++ src/axom/mir/Options.hpp | 184 ++++++++++++++++++++ src/axom/mir/tests/mir_clipfield.cpp | 14 +- 4 files changed, 305 insertions(+), 235 deletions(-) create mode 100644 src/axom/mir/ClipOptions.hpp create mode 100644 src/axom/mir/Options.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 55af641317..8582c7e870 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -15,6 +15,7 @@ #include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/views/view_traits.hpp" +#include "axom/mir/ClipOptions.hpp" #include #include @@ -205,234 +206,6 @@ AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, } // end namespace details -/** - * \brief This class provides a kind of schema over the clipping options, as well - * as default values, and some utilities functions. - */ -template -class ClipOptions -{ -public: - /** - * \brief Constructor - * - * \param nzones The total number of zones in the associated topology. - * \param options The node that contains the clipping options. - */ - ClipOptions(axom::IndexType nzones, const conduit::Node &options) - : m_nzones(nzones) - , m_options(options) - , m_selectedZones() - { } - - /** - * \brief Return a view that contains the list of selected zone ids for the mesh. - * \return A view that contains the list of selected zone ids for the mesh. - * - * \note The data for the view is generated if it has not yet been built. - */ - axom::ArrayView selectedZonesView() - { - if(m_selectedZones.size() == 0) buildSelectedZones(); - return m_selectedZones.view(); - } - - /** - * \brief Invalidate the selected zones array (due to options changing) so we can rebuild it. - */ - void invalidateSelectedZones() { m_selectedZones.clear(); } - - /** - * \brief Return the name of the field used for clipping. - * \return The name of the field used for clipping. - */ - std::string clipField() const - { - return m_options.fetch_existing("clipField").as_string(); - } - - /** - * \brief Return the clip value. - * \return The clip value. - */ - float clipValue() const - { - return m_options.has_child("clipValue") - ? m_options.fetch_existing("clipValue").to_float() - : 0.f; - } - - /** - * \brief Return the name of the new topology to be created. - * \param default_value The name to use if the option is not defined. - * \return The name of the new topology to be created. - */ - std::string topologyName(const std::string &default_value = std::string()) const - { - std::string name(default_value); - if(m_options.has_child("topologyName")) - name = m_options.fetch_existing("topologyName").as_string(); - return name; - } - - /** - * \brief Return the name of the new coordset to be created. - * \param default_value The name to use if the option is not defined. - * \return The name of the new coordset to be created. - */ - std::string coordsetName(const std::string &default_value = std::string()) const - { - std::string name(default_value); - if(m_options.has_child("coordsetName")) - name = m_options.fetch_existing("coordsetName").as_string(); - return name; - } - - /** - * \brief Return the name of the new color field to be created. - * \return The name of the new color field to be created. - */ - std::string colorField() const - { - std::string name("color"); - if(m_options.has_child("colorField")) - name = m_options.fetch_existing("colorField").as_string(); - return name; - } - - /** - * \brief Whether the "inside" of the clipping field is selected. - * \return 1 of the inside clipping is selected, false otherwise. - */ - bool inside() const - { - return m_options.has_path("inside") - ? (m_options.fetch_existing("inside").to_int() > 0) - : true; - } - - /** - * \brief Whether the "outside" of the clipping field is selected. - * \return 1 of the outside clipping is selected, false otherwise. - */ - bool outside() const - { - return m_options.has_path("outside") - ? (m_options.fetch_existing("outside").to_int() > 0) - : false; - } - - /** - * \brief Extract the names of the fields to process (and their output names) from the - * options or \a n_fields if the options do not contain fields. - * - * \param n_fields The Conduit node that contains mesh fields. - * - * \return A map of the fields that will be processed, as well as their output name in the new fields. - */ - std::map fields(const conduit::Node &n_fields) const - { - std::map f; - if(m_options.has_child("fields")) - { - const conduit::Node &n_opt_fields = m_options.fetch_existing("fields"); - for(conduit::index_t i = 0; i < n_opt_fields.number_of_children(); i++) - { - if(n_opt_fields[i].dtype().is_string()) - f[n_opt_fields[i].name()] = n_opt_fields[i].as_string(); - else - f[n_opt_fields[i].name()] = n_opt_fields[i].name(); - } - } - else - { - // No options were specified. Allow all fields with same topology as clipField. - const conduit::Node &n_clipField = n_fields.fetch_existing(clipField()); - const std::string topoName = - n_clipField.fetch_existing("topology").as_string(); - for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) - { - if(topoName == n_fields[i].fetch_existing("topology").as_string()) - f[n_fields[i].name()] = n_fields[i].name(); - } - } - return f; - } - -private: - /** - * \brief The options may contain a "selectedZones" member that is a list of zones - * that will be operated on. If such an array is present, copy and sort it. - * If the zone list is not present, make an array that selects every zone. - * - * \note selectedZones should contain local zone numbers, which in the case of - * strided-structured indexing are the [0..n) zone numbers that exist only - * within the selected window. - */ - void buildSelectedZones() - { - const auto allocatorID = axom::execution_space::allocatorID(); - - if(m_options.has_child("selectedZones")) - { - // Store the zone list in m_selectedZones. - int badValueCount = 0; - views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) { - using loop_policy = - typename axom::execution_space::loop_policy; - using reduce_policy = - typename axom::execution_space::reduce_policy; - - // It probably does not make sense to request more zones than we have in the mesh. - SLIC_ASSERT(zonesView.size() <= m_nzones); - - m_selectedZones = axom::Array(zonesView.size(), - zonesView.size(), - allocatorID); - auto szView = m_selectedZones.view(); - axom::for_all( - szView.size(), - AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); - - // Check that the selected zone values are in range. - const auto nzones = m_nzones; - RAJA::ReduceSum errReduce(0); - axom::for_all( - szView.size(), - AXOM_LAMBDA(auto index) { - const int err = - (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; - errReduce += err; - }); - badValueCount = errReduce.get(); - - // Make sure the selectedZones are sorted. - RAJA::sort(RAJA::make_span(szView.data(), szView.size())); - }); - - if(badValueCount > 0) - { - SLIC_ERROR("Out of range selectedZones values."); - } - } - else - { - // Select all zones. - m_selectedZones = - axom::Array(m_nzones, m_nzones, allocatorID); - auto szView = m_selectedZones.view(); - axom::for_all( - m_nzones, - AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); - } - } - -private: - axom::IndexType m_nzones; // The number of zones in the associated topology. - const conduit::Node &m_options; // A reference to the clipping options node. - axom::Array m_selectedZones; // Storage for a list of selected zone ids. -}; - //------------------------------------------------------------------------------ /** * \brief This class implements a naming policy that uses some hashing functions @@ -1134,11 +907,26 @@ class ClipField sliceIndicesView[start + i] = zoneIndex; }); + // Get the fields that we want to process. + std::map fieldsToProcess; + if(!opts.fields(fieldsToProcess)) + { + // Fields were not present in the options. Select all fields that have the same topology as opts.clipField(). + const std::string clipTopology = n_fields.fetch_existing(opts.clipField() + "/topology").as_string(); + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + if(n_fields[i].fetch_existing("topology").as_string() == clipTopology) + { + fieldsToProcess[n_fields[i].name()] = n_fields[i].name(); + } + } + } + slice.m_indicesView = sliceIndicesView; makeFields(blend, slice, opts.topologyName(n_topo.name()), - opts.fields(n_fields), + fieldsToProcess, n_fields, n_newFields); makeOriginalElements(fragmentData, opts, n_fields, n_newTopo, n_newFields); diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/ClipOptions.hpp new file mode 100644 index 0000000000..7abc50d749 --- /dev/null +++ b/src/axom/mir/ClipOptions.hpp @@ -0,0 +1,96 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_CLIP_OPTIONS_HPP_ +#define AXOM_MIR_CLIP_OPTIONS_HPP_ + +#include "axom/mir/Options.hpp" + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + +/** + * \brief This class provides a kind of schema over the clipping options, as well + * as default values, and some utilities functions. + */ +template +class ClipOptions : public axom::mir::Options +{ +public: + /** + * \brief Constructor + * + * \param nzones The total number of zones in the associated topology. + * \param options The node that contains the clipping options. + */ + ClipOptions(axom::IndexType nzones, const conduit::Node &options) : axom::mir::Options(nzones, options) + { } + + /** + * \brief Return the name of the field used for clipping. + * \return The name of the field used for clipping. + */ + std::string clipField() const + { + return options().fetch_existing("clipField").as_string(); + } + + /** + * \brief Return the clip value. + * \return The clip value. + */ + float clipValue() const + { + return options().has_child("clipValue") + ? options().fetch_existing("clipValue").to_float() + : 0.f; + } + + /** + * \brief Return the name of the new color field to be created. + * \return The name of the new color field to be created. + */ + std::string colorField() const + { + std::string name("color"); + if(options().has_child("colorField")) + name = options().fetch_existing("colorField").as_string(); + return name; + } + + /** + * \brief Whether the "inside" of the clipping field is selected. + * \return 1 of the inside clipping is selected, false otherwise. + */ + bool inside() const + { + return options().has_path("inside") + ? (options().fetch_existing("inside").to_int() > 0) + : true; + } + + /** + * \brief Whether the "outside" of the clipping field is selected. + * \return 1 of the outside clipping is selected, false otherwise. + */ + bool outside() const + { + return options().has_path("outside") + ? (options().fetch_existing("outside").to_int() > 0) + : false; + } +private: + /// Access the base class' options. + const conduit::Node &options() const { return this->m_options; } +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/Options.hpp b/src/axom/mir/Options.hpp new file mode 100644 index 0000000000..963a47eba8 --- /dev/null +++ b/src/axom/mir/Options.hpp @@ -0,0 +1,184 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_OPTIONS_HPP_ +#define AXOM_MIR_OPTIONS_HPP_ + +#include "axom/core.hpp" + +#include + +namespace axom +{ +namespace mir +{ + +/** + * \brief This class provides a kind of schema over options, as well + * as default values, and some utilities functions. + */ +template +class Options +{ +public: + /** + * \brief Constructor + * + * \param nzones The total number of zones in the associated topology. + * \param options The node that contains the clipping options. + */ + Options(axom::IndexType nzones, const conduit::Node &options) + : m_nzones(nzones) + , m_options(options) + , m_selectedZones() + { } + + /** + * \brief Return a view that contains the list of selected zone ids for the mesh. + * \return A view that contains the list of selected zone ids for the mesh. + * + * \note The data for the view is generated if it has not yet been built. + */ + axom::ArrayView selectedZonesView() + { + if(m_selectedZones.size() == 0) + { + buildSelectedZones(); + } + return m_selectedZones.view(); + } + + /** + * \brief Invalidate the selected zones array (due to options changing) so we can rebuild it. + */ + void invalidateSelectedZones() { m_selectedZones.clear(); } + + /** + * \brief Return the name of the new topology to be created. + * \param default_value The name to use if the option is not defined. + * \return The name of the new topology to be created. + */ + std::string topologyName(const std::string &default_value = std::string()) const + { + std::string name(default_value); + if(m_options.has_child("topologyName")) + name = m_options.fetch_existing("topologyName").as_string(); + return name; + } + + /** + * \brief Return the name of the new coordset to be created. + * \param default_value The name to use if the option is not defined. + * \return The name of the new coordset to be created. + */ + std::string coordsetName(const std::string &default_value = std::string()) const + { + std::string name(default_value); + if(m_options.has_child("coordsetName")) + name = m_options.fetch_existing("coordsetName").as_string(); + return name; + } + + /** + * \brief Extract the names of the fields to process (and their output names) from the + * options or \a n_fields if the options do not contain fields. + * + * \param[out] f A map of the fields that will be processed, as well as their output name in the new fields. + * \return True if the fields were present in the options. False otherwise. + */ + bool fields(std::map &f) const + { + bool retval = m_options.has_child("fields"); + f.clear(); + if(retval) + { + const conduit::Node &n_opt_fields = m_options.fetch_existing("fields"); + for(conduit::index_t i = 0; i < n_opt_fields.number_of_children(); i++) + { + if(n_opt_fields[i].dtype().is_string()) + f[n_opt_fields[i].name()] = n_opt_fields[i].as_string(); + else + f[n_opt_fields[i].name()] = n_opt_fields[i].name(); + } + } + return retval; + } + +protected: + /** + * \brief The options may contain a "selectedZones" member that is a list of zones + * that will be operated on. If such an array is present, copy and sort it. + * If the zone list is not present, make an array that selects every zone. + * + * \note selectedZones should contain local zone numbers, which in the case of + * strided-structured indexing are the [0..n) zone numbers that exist only + * within the selected window. + */ + void buildSelectedZones() + { + const auto allocatorID = axom::execution_space::allocatorID(); + + if(m_options.has_child("selectedZones")) + { + // Store the zone list in m_selectedZones. + int badValueCount = 0; + views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) { + using loop_policy = + typename axom::execution_space::loop_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; + + // It probably does not make sense to request more zones than we have in the mesh. + SLIC_ASSERT(zonesView.size() <= m_nzones); + + m_selectedZones = axom::Array(zonesView.size(), + zonesView.size(), + allocatorID); + auto szView = m_selectedZones.view(); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); + + // Check that the selected zone values are in range. + const auto nzones = m_nzones; + RAJA::ReduceSum errReduce(0); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { + const int err = + (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + errReduce += err; + }); + badValueCount = errReduce.get(); + + // Make sure the selectedZones are sorted. + RAJA::sort(RAJA::make_span(szView.data(), szView.size())); + }); + + if(badValueCount > 0) + { + SLIC_ERROR("Out of range selectedZones values."); + } + } + else + { + // Select all zones. + m_selectedZones = + axom::Array(m_nzones, m_nzones, allocatorID); + auto szView = m_selectedZones.view(); + axom::for_all( + m_nzones, + AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); + } + } + +protected: + axom::IndexType m_nzones; // The number of zones in the associated topology. + const conduit::Node &m_options; // A reference to the clipping options node. + axom::Array m_selectedZones; // Storage for a list of selected zone ids. +}; +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index e7e6d69447..3fc60149e8 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -102,21 +102,23 @@ TEST(mir_clipfield, options) n_fields["distance/values"].set(std::vector {0., 1., 2., 3.}); // There are currently no fields in the options. fields should just return the clip field. - auto fields = opts.fields(n_fields); - EXPECT_EQ(fields.size(), 1); - EXPECT_EQ(fields.begin()->first, "distance"); - EXPECT_EQ(fields.begin()->second, "distance"); + std::map fields; + auto have_fields = opts.fields(fields); + EXPECT_FALSE(have_fields); + EXPECT_EQ(fields.size(), 0); // Add an empty fields node so we select NO fields. (void)options["fields"]; - fields = opts.fields(n_fields); + have_fields = opts.fields(fields); + EXPECT_TRUE(have_fields); EXPECT_EQ(fields.size(), 0); // Add some fields options["fields/distance"] = "distance"; options["fields/source"] = "destination"; options["fields/same"] = 1; - fields = opts.fields(n_fields); + have_fields = opts.fields(fields); + EXPECT_TRUE(have_fields); EXPECT_EQ(fields.size(), 3); int i = 0; for(auto it = fields.begin(); it != fields.end(); it++, i++) From b708890f7e7991b8ec86461bfc95a38882949c93 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 8 Aug 2024 12:12:59 -0700 Subject: [PATCH 156/290] Separate some code --- src/axom/mir/BlendGroupBuilder.hpp | 421 +++++++++++++++++++++++++ src/axom/mir/ClipField.hpp | 473 +---------------------------- src/axom/mir/utilities.hpp | 70 +++++ 3 files changed, 494 insertions(+), 470 deletions(-) create mode 100644 src/axom/mir/BlendGroupBuilder.hpp diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp new file mode 100644 index 0000000000..f2083ffe48 --- /dev/null +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -0,0 +1,421 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_BLEND_GROUP_BUILDER_HPP_ +#define AXOM_MIR_BLEND_GROUP_BUILDER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/utilities.hpp" + +namespace axom +{ +namespace mir +{ +namespace clipping +{ + +/** + * \brief This class encapsulates the logic for building blend groups. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam NamingPolicy The naming policy used to create names from node ids. + * + * \note The class only contains views to data so it can be copied into lambdas. + */ +template +class BlendGroupBuilder +{ +public: + using KeyType = typename NamingPolicy::KeyType; + + /** + * \brief This struct holds the views that represent data for blend groups. + */ + struct State + { + // clang-format off + IndexType m_nzones; + + axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. + axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. + axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. + axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. + + axom::ArrayView m_blendNamesView; // Blend group names + axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. + axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. + axom::ArrayView m_blendIdsView; // blend group ids. + axom::ArrayView m_blendCoeffView; // blend group weights. + + axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. + axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. + // clang-format on + }; + + /** + * \brief Access the state views. + * \return A reference to the state. + */ + State &state() { return m_state; } + const State &state() const { return m_state; } + + /** + * \brief Set the number of zones. + * + * \param blendGroupsView The view that holds the number of blend groups for each zone. + * \param blendGroupsLenView The view that holds the size of the blend group data for each zone. + */ + void setBlendGroupSizes(const axom::ArrayView &blendGroupsView, + const axom::ArrayView &blendGroupsLenView) + { + m_state.m_nzones = blendGroupsView.size(); + m_state.m_blendGroupsView = blendGroupsView; + m_state.m_blendGroupsLenView = blendGroupsLenView; + } + + /** + * \brief Compute the total sizes of blend group storage. + * + * \param[out] bgSum The total number of blend groups for all zones. + * \param[out] bgLenSum The total size of blend group data for all zones. + */ + void computeBlendGroupSizes(IndexType &bgSum, IndexType &bgLenSum) + { + using reduce_policy = + typename axom::execution_space::reduce_policy; + RAJA::ReduceSum blendGroups_sum(0); + RAJA::ReduceSum blendGroupLen_sum(0); + const auto localBlendGroupsView = m_state.m_blendGroupsView; + const auto localBlendGroupsLenView = m_state.m_blendGroupsLenView; + axom::for_all( + m_state.m_nzones, + AXOM_LAMBDA(auto zoneIndex) { + blendGroups_sum += localBlendGroupsView[zoneIndex]; + blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; + }); + bgSum = blendGroups_sum.get(); + bgLenSum = blendGroupLen_sum.get(); + } + + /** + * \brief Set the views for the blend group offsets and then fill them using a scan. + * + * \param blendOffsetView The offsets to each blend group for views sized: view[blendGroupSum]. + * \param blendGroupOffsetsView The offsets to each zone's blend groups data. + */ + void setBlendGroupOffsets(const axom::ArrayView &blendOffsetView, + const axom::ArrayView &blendGroupOffsetsView) + { + m_state.m_blendOffsetView = blendOffsetView; + m_state.m_blendGroupOffsetsView = blendGroupOffsetsView; + } + + /** + * brief Compute the blend group offsets that make it easier to store data. + */ + void computeBlendGroupOffsets() + { + axom::exclusive_scan(m_state.m_blendGroupsLenView, + m_state.m_blendOffsetView); + axom::exclusive_scan(m_state.m_blendGroupsView, + m_state.m_blendGroupOffsetsView); + } + + /** + * \brief Set the views that we'll use for blend groups. + */ + void setBlendViews(const axom::ArrayView &blendNames, + const axom::ArrayView &blendGroupSizes, + const axom::ArrayView &blendGroupStart, + const axom::ArrayView &blendIds, + const axom::ArrayView &blendCoeff) + { + m_state.m_blendNamesView = blendNames; + m_state.m_blendGroupSizesView = blendGroupSizes; + m_state.m_blendGroupStartView = blendGroupStart; + m_state.m_blendIdsView = blendIds; + m_state.m_blendCoeffView = blendCoeff; + } + + /** + * \brief Set the unique names and ids views. These are used in mapping blend groups to unique blend groups. + * + * \param uniqueNames A view containing unique, sorted blend group names. + * \param uniqueIndices A view containing the original blend group index for each unique name. + */ + void setUniqueNames(const axom::ArrayView &uniqueNames, + const axom::ArrayView &uniqueIndices) + { + m_state.m_blendUniqueNamesView = uniqueNames; + m_state.m_blendUniqueIndicesView = uniqueIndices; + } + + /** + * \brief Get the blend names view. + * \return The blend names view. + */ + const axom::ArrayView &blendNames() const + { + return m_state.m_blendNamesView; + } + + /** + * \brief This class helps us manage blend group creation and usage for blend groups within a single zone. + */ + class zone_blend_groups + { + public: + /** + * \brief Return the number of blend groups for this zone. + * \return The number of blend groups for this zone. + */ + AXOM_HOST_DEVICE + inline IndexType size() const + { + return m_state->m_blendGroupsView[m_zoneIndex]; + } + + /** + * \brief Set the number of blend groups and total size of the blend groups for a zone. + * + * \param zoneIndex The index of the zone we're initializing. + * \param nBlendGroups The number of blend groups in this zone. + * \param blendGroupSize The size of all of the blend groups in this zone. + */ + AXOM_HOST_DEVICE + inline void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) + { + m_state->m_blendGroupsView[m_zoneIndex] = nBlendGroups; + m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; + } + + /** + * \brief Start creating a new blend group within the allocated space for this zone. + */ + AXOM_HOST_DEVICE + inline void beginGroup() { m_currentDataOffset = m_startOffset; } + + /** + * \brief Add a new blend point in the current blend group. + * + * \param id The node id that will be used for blending. + * \param weight The weight that will be used for blending. + */ + AXOM_HOST_DEVICE + inline void add(IndexType id, float weight) + { + m_state->m_blendIdsView[m_currentDataOffset] = id; + m_state->m_blendCoeffView[m_currentDataOffset] = weight; + m_currentDataOffset++; + } + + /** + * \brief End the current blend group, storing its name, size, etc. + */ + AXOM_HOST_DEVICE + inline void endGroup() + { + IndexType numIds = m_currentDataOffset - m_startOffset; + + // Store where this blendGroup starts in the blendIds,blendCoeff. + m_state->m_blendGroupStartView[m_blendGroupId] = m_startOffset; + + // Save the size for this blend group. + m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; + + // Store "name" of blend group. + KeyType blendName = + NamingPolicy::makeName(m_state->m_blendIdsView.data() + m_startOffset, + numIds); + + m_state->m_blendNamesView[m_blendGroupId] = blendName; +#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) + const float w = weightSum(); + constexpr float EPS = 1.e-5; + if(w < (1. - EPS) || w > (1. + EPS)) + { + SLIC_ERROR(fmt::format("Invalid blend group weights w={}.", w)); + print(std::cout); + } +#endif + m_blendGroupId++; + m_startOffset = m_currentDataOffset; + } + + /** + * \brief Return the sum of the weights for the current blend group. + * \note The blendGroup must have finished construction. + * \return The sum of weights, which should be about 1. + */ + AXOM_HOST_DEVICE + inline float weightSum() const + { + const auto numIds = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; + float w = 0.f; + for(IndexType i = 0; i < numIds; i++) + w += m_state->m_blendCoeffView[start + i]; + return w; + } + + /** + * \brief Return the name of the current blend group. + * \return The name of the current blend group. + */ + AXOM_HOST_DEVICE + inline KeyType name() const + { + return m_state->m_blendNamesView[m_blendGroupId]; + } + + /** + * \brief Return index of the current blend group in the unique blend groups. + * \return The unique index of the current blend group. + */ + AXOM_HOST_DEVICE + inline IndexType uniqueBlendGroupIndex() const + { + return axom::mir::utilities::bsearch(name(), + m_state->m_blendUniqueNamesView); + } + + /** + * \brief Advance to the next blend group. + */ + AXOM_HOST_DEVICE + inline void operator++() { m_blendGroupId++; } + + /** + * \brief Advance to the next blend group. + */ + AXOM_HOST_DEVICE + inline void operator++(int) { m_blendGroupId++; } + +#if !defined(AXOM_DEVICE_CODE) + /** + * \brief Print the current blend group to a stream. + * \param os The stream to which the blend group will print. + */ + void print(std::ostream &os) const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + os << "-\n"; + os << " zoneIndex: " << m_zoneIndex << std::endl; + os << " blendGroupId: " << m_blendGroupId << std::endl; + os << " size: " << n << std::endl; + os << " offset: " << offset << std::endl; + + const IndexType *ids = m_state->m_blendIdsView.data() + offset; + os << " ids: ["; + for(int bi = 0; bi < n; bi++) + { + if(bi > 0) os << ", "; + os << ids[bi]; + } + os << "]"; + os << "\n"; + const float *weights = m_state->m_blendCoeffView.data() + offset; + os << " weights: ["; + for(int bi = 0; bi < n; bi++) + { + if(bi > 0) os << ", "; + os << weights[bi]; + } + os << "]\n"; + os << " name: " << m_state->m_blendNamesView[m_blendGroupId]; + os << "\n"; + } +#endif + + /** + * \brief Return the current blend group's ids. + * \return The current blend group's ids. + * \note This method should not be used if blend groups are still being constructed. + */ + axom::ArrayView ids() const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + return axom::ArrayView(m_state->m_blendIdsView.data() + offset, + n); + } + + /** + * \brief Return the current blend group's ids. + * \return The current blend group's ids. + * \note This method should not be used if blend groups are still being constructed. + */ + axom::ArrayView weights() const + { + const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; + const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; + return axom::ArrayView(m_state->m_blendCoeffsView.data() + offset, + n); + } + + private: + friend class BlendGroupBuilder; + + IndexType m_zoneIndex; // The zone that owns this set of blend groups. + IndexType m_blendGroupId; // The global blend group index within this current zone. + IndexType m_startOffset; // The data offset for the first ids/weights in this blend group. + IndexType m_currentDataOffset; // The current data offset. + State *m_state; // Pointer to the main state. + }; + + /** + * \brief Return a zone_blend_groups object for the current zone so we can add blend groups. + * + * \param zoneIndex The zone whose blend groups we want to edit. + * + * \note This method must be marked const because we can call it from an AXOM_LAMBDA. + * we pass a non-const State reference to the zone_blend_groups that we construct + * so we can write into the blend group data. + */ + AXOM_HOST_DEVICE + zone_blend_groups blendGroupsForZone(IndexType zoneIndex) const + { + zone_blend_groups groups; + // The zone that owns this set of blend groups. + groups.m_zoneIndex = zoneIndex; + + // Global blend group id for the first blend group in this zone. + groups.m_blendGroupId = m_state.m_blendGroupOffsetsView[zoneIndex]; + // Global start + groups.m_startOffset = groups.m_currentDataOffset = + m_state.m_blendOffsetView[zoneIndex]; + + groups.m_state = const_cast(&m_state); + return groups; + } + + /** + * \brief Make a BlendData object from the views in this object. + * + * \return A BlendData object suitable for making new fields and coordsets. + */ + axom::mir::utilities::blueprint::BlendData makeBlendData() const + { + axom::mir::utilities::blueprint::BlendData blend; + + blend.m_selectedIndicesView = + m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices + blend.m_blendGroupSizesView = m_state.m_blendGroupSizesView; + blend.m_blendGroupStartView = m_state.m_blendGroupStartView; + blend.m_blendIdsView = m_state.m_blendIdsView; + blend.m_blendCoeffView = m_state.m_blendCoeffView; + + return blend; + } + +private: + State m_state; +}; + +} // end namespace clipping +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 8582c7e870..9fd5991c06 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -16,6 +16,8 @@ #include "axom/mir/utilities.hpp" #include "axom/mir/views/view_traits.hpp" #include "axom/mir/ClipOptions.hpp" +#include "axom/mir/BlendGroupBuilder.hpp" +#include "axom/mir/utilities.hpp" #include #include @@ -24,34 +26,6 @@ #include #include -// NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers -// so we can write serial versions for when RAJA is not enabled. -namespace axom -{ -template -struct scans -{ - inline void exclusive_scan(const ContiguousMemoryContainer &input, - ContiguousMemoryContainer &output) - { - using loop_policy = typename axom::execution_space::loop_policy; - assert(input.size() == output.size()); - RAJA::exclusive_scan( - RAJA::make_span(input.data(), input.size()), - RAJA::make_span(output.data(), output.size())); - } -}; - -template -inline void exclusive_scan(const ContiguousMemoryContainer &input, - ContiguousMemoryContainer &output) -{ - scans s; - s.exclusive_scan(input, output); -} - -} // end namespace axom - namespace axom { namespace mir @@ -206,447 +180,6 @@ AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, } // end namespace details -//------------------------------------------------------------------------------ -/** - * \brief This class implements a naming policy that uses some hashing functions - * to produce a "name" for an array of ids. - */ -class HashNaming -{ -public: - using KeyType = std::uint64_t; - - /** - * \brief Make a name from an array of ids. - * - * \param ids The array of ids. - * \param numIds The number of ids in the array. - * - * \return The name that describes the array of ids. - * - * \note Different make_name_* functions are used because we can skip most - * sorting for 1,2 element arrays. - */ - AXOM_HOST_DEVICE - inline static KeyType makeName(const IndexType *ids, IndexType numIds) - { - KeyType name {}; - if(numIds == 1) - { - name = axom::mir::utilities::make_name_1(ids[0]); - } - else if(numIds == 2) - { - name = axom::mir::utilities::make_name_2(ids[0], ids[1]); - } - else - { - name = axom::mir::utilities::make_name_n(ids, numIds); - } - return name; - } -}; - -//------------------------------------------------------------------------------ -/** - * \brief This class encapsulates most of the logic for building blend groups. - * - * \tparam ExecSpace The execution space where the algorithm will run. - * \tparam NamingPolicy The naming policy used to create names from node ids. - * - * \note The class only contains views to data so it can be copied into lambdas. - */ -template -class BlendGroupBuilder -{ -public: - using KeyType = typename NamingPolicy::KeyType; - - /** - * \brief This struct holds the views that represent data for blend groups. - */ - struct State - { - // clang-format off - IndexType m_nzones; - - axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. - axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. - axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. - axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. - - axom::ArrayView m_blendNamesView; // Blend group names - axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. - axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. - axom::ArrayView m_blendIdsView; // blend group ids. - axom::ArrayView m_blendCoeffView; // blend group weights. - - axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. - axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. - // clang-format on - }; - - /** - * \brief Access the state views. - * \return A reference to the state. - */ - State &state() { return m_state; } - const State &state() const { return m_state; } - - /** - * \brief Set the number of zones. - * - * \param blendGroupsView The view that holds the number of blend groups for each zone. - * \param blendGroupsLenView The view that holds the size of the blend group data for each zone. - */ - void setBlendGroupSizes(const axom::ArrayView &blendGroupsView, - const axom::ArrayView &blendGroupsLenView) - { - m_state.m_nzones = blendGroupsView.size(); - m_state.m_blendGroupsView = blendGroupsView; - m_state.m_blendGroupsLenView = blendGroupsLenView; - } - - /** - * \brief Compute the total sizes of blend group storage. - * - * \param[out] bgSum The total number of blend groups for all zones. - * \param[out] bgLenSum The total size of blend group data for all zones. - */ - void computeBlendGroupSizes(IndexType &bgSum, IndexType &bgLenSum) - { - using reduce_policy = - typename axom::execution_space::reduce_policy; - RAJA::ReduceSum blendGroups_sum(0); - RAJA::ReduceSum blendGroupLen_sum(0); - const auto localBlendGroupsView = m_state.m_blendGroupsView; - const auto localBlendGroupsLenView = m_state.m_blendGroupsLenView; - axom::for_all( - m_state.m_nzones, - AXOM_LAMBDA(auto zoneIndex) { - blendGroups_sum += localBlendGroupsView[zoneIndex]; - blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; - }); - bgSum = blendGroups_sum.get(); - bgLenSum = blendGroupLen_sum.get(); - } - - /** - * \brief Set the views for the blend group offsets and then fill them using a scan. - * - * \param blendOffsetView The offsets to each blend group for views sized: view[blendGroupSum]. - * \param blendGroupOffsetsView The offsets to each zone's blend groups data. - */ - void setBlendGroupOffsets(const axom::ArrayView &blendOffsetView, - const axom::ArrayView &blendGroupOffsetsView) - { - m_state.m_blendOffsetView = blendOffsetView; - m_state.m_blendGroupOffsetsView = blendGroupOffsetsView; - } - - /** - * brief Compute the blend group offsets that make it easier to store data. - */ - void computeBlendGroupOffsets() - { - axom::exclusive_scan(m_state.m_blendGroupsLenView, - m_state.m_blendOffsetView); - axom::exclusive_scan(m_state.m_blendGroupsView, - m_state.m_blendGroupOffsetsView); - } - - /** - * \brief Set the views that we'll use for blend groups. - */ - void setBlendViews(const axom::ArrayView &blendNames, - const axom::ArrayView &blendGroupSizes, - const axom::ArrayView &blendGroupStart, - const axom::ArrayView &blendIds, - const axom::ArrayView &blendCoeff) - { - m_state.m_blendNamesView = blendNames; - m_state.m_blendGroupSizesView = blendGroupSizes; - m_state.m_blendGroupStartView = blendGroupStart; - m_state.m_blendIdsView = blendIds; - m_state.m_blendCoeffView = blendCoeff; - } - - /** - * \brief Set the unique names and ids views. These are used in mapping blend groups to unique blend groups. - * - * \param uniqueNames A view containing unique, sorted blend group names. - * \param uniqueIndices A view containing the original blend group index for each unique name. - */ - void setUniqueNames(const axom::ArrayView &uniqueNames, - const axom::ArrayView &uniqueIndices) - { - m_state.m_blendUniqueNamesView = uniqueNames; - m_state.m_blendUniqueIndicesView = uniqueIndices; - } - - /** - * \brief Get the blend names view. - * \return The blend names view. - */ - const axom::ArrayView &blendNames() const - { - return m_state.m_blendNamesView; - } - - /** - * \brief This class helps us manage blend group creation and usage for blend groups within a single zone. - */ - class zone_blend_groups - { - public: - /** - * \brief Return the number of blend groups for this zone. - * \return The number of blend groups for this zone. - */ - AXOM_HOST_DEVICE - inline IndexType size() const - { - return m_state->m_blendGroupsView[m_zoneIndex]; - } - - /** - * \brief Set the number of blend groups and total size of the blend groups for a zone. - * - * \param zoneIndex The index of the zone we're initializing. - * \param nBlendGroups The number of blend groups in this zone. - * \param blendGroupSize The size of all of the blend groups in this zone. - */ - AXOM_HOST_DEVICE - inline void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) - { - m_state->m_blendGroupsView[m_zoneIndex] = nBlendGroups; - m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; - } - - /** - * \brief Start creating a new blend group within the allocated space for this zone. - */ - AXOM_HOST_DEVICE - inline void beginGroup() { m_currentDataOffset = m_startOffset; } - - /** - * \brief Add a new blend point in the current blend group. - * - * \param id The node id that will be used for blending. - * \param weight The weight that will be used for blending. - */ - AXOM_HOST_DEVICE - inline void add(IndexType id, float weight) - { - m_state->m_blendIdsView[m_currentDataOffset] = id; - m_state->m_blendCoeffView[m_currentDataOffset] = weight; - m_currentDataOffset++; - } - - /** - * \brief End the current blend group, storing its name, size, etc. - */ - AXOM_HOST_DEVICE - inline void endGroup() - { - IndexType numIds = m_currentDataOffset - m_startOffset; - - // Store where this blendGroup starts in the blendIds,blendCoeff. - m_state->m_blendGroupStartView[m_blendGroupId] = m_startOffset; - - // Save the size for this blend group. - m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; - - // Store "name" of blend group. - KeyType blendName = - NamingPolicy::makeName(m_state->m_blendIdsView.data() + m_startOffset, - numIds); - - m_state->m_blendNamesView[m_blendGroupId] = blendName; -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) - const float w = weightSum(); - constexpr float EPS = 1.e-5; - if(w < (1. - EPS) || w > (1. + EPS)) - { - SLIC_ERROR(fmt::format("Invalid blend group weights w={}.", w)); - print(std::cout); - } -#endif - m_blendGroupId++; - m_startOffset = m_currentDataOffset; - } - - /** - * \brief Return the sum of the weights for the current blend group. - * \note The blendGroup must have finished construction. - * \return The sum of weights, which should be about 1. - */ - AXOM_HOST_DEVICE - inline float weightSum() const - { - const auto numIds = m_state->m_blendGroupSizesView[m_blendGroupId]; - const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; - float w = 0.f; - for(IndexType i = 0; i < numIds; i++) - w += m_state->m_blendCoeffView[start + i]; - return w; - } - - /** - * \brief Return the name of the current blend group. - * \return The name of the current blend group. - */ - AXOM_HOST_DEVICE - inline KeyType name() const - { - return m_state->m_blendNamesView[m_blendGroupId]; - } - - /** - * \brief Return index of the current blend group in the unique blend groups. - * \return The unique index of the current blend group. - */ - AXOM_HOST_DEVICE - inline IndexType uniqueBlendGroupIndex() const - { - return axom::mir::utilities::bsearch(name(), - m_state->m_blendUniqueNamesView); - } - - /** - * \brief Advance to the next blend group. - */ - AXOM_HOST_DEVICE - inline void operator++() { m_blendGroupId++; } - - /** - * \brief Advance to the next blend group. - */ - AXOM_HOST_DEVICE - inline void operator++(int) { m_blendGroupId++; } - -#if !defined(AXOM_DEVICE_CODE) - /** - * \brief Print the current blend group to a stream. - * \param os The stream to which the blend group will print. - */ - void print(std::ostream &os) const - { - const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; - const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; - os << "-\n"; - os << " zoneIndex: " << m_zoneIndex << std::endl; - os << " blendGroupId: " << m_blendGroupId << std::endl; - os << " size: " << n << std::endl; - os << " offset: " << offset << std::endl; - - const IndexType *ids = m_state->m_blendIdsView.data() + offset; - os << " ids: ["; - for(int bi = 0; bi < n; bi++) - { - if(bi > 0) os << ", "; - os << ids[bi]; - } - os << "]"; - os << "\n"; - const float *weights = m_state->m_blendCoeffView.data() + offset; - os << " weights: ["; - for(int bi = 0; bi < n; bi++) - { - if(bi > 0) os << ", "; - os << weights[bi]; - } - os << "]\n"; - os << " name: " << m_state->m_blendNamesView[m_blendGroupId]; - os << "\n"; - } -#endif - - /** - * \brief Return the current blend group's ids. - * \return The current blend group's ids. - * \note This method should not be used if blend groups are still being constructed. - */ - axom::ArrayView ids() const - { - const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; - const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; - return axom::ArrayView(m_state->m_blendIdsView.data() + offset, - n); - } - - /** - * \brief Return the current blend group's ids. - * \return The current blend group's ids. - * \note This method should not be used if blend groups are still being constructed. - */ - axom::ArrayView weights() const - { - const auto n = m_state->m_blendGroupSizesView[m_blendGroupId]; - const auto offset = m_state->m_blendGroupStartView[m_blendGroupId]; - return axom::ArrayView(m_state->m_blendCoeffsView.data() + offset, - n); - } - - private: - friend class BlendGroupBuilder; - - IndexType m_zoneIndex; // The zone that owns this set of blend groups. - IndexType m_blendGroupId; // The global blend group index within this current zone. - IndexType m_startOffset; // The data offset for the first ids/weights in this blend group. - IndexType m_currentDataOffset; // The current data offset. - State *m_state; // Pointer to the main state. - }; - - /** - * \brief Return a zone_blend_groups object for the current zone so we can add blend groups. - * - * \param zoneIndex The zone whose blend groups we want to edit. - * - * \note This method must be marked const because we can call it from an AXOM_LAMBDA. - * we pass a non-const State reference to the zone_blend_groups that we construct - * so we can write into the blend group data. - */ - AXOM_HOST_DEVICE - zone_blend_groups blendGroupsForZone(IndexType zoneIndex) const - { - zone_blend_groups groups; - // The zone that owns this set of blend groups. - groups.m_zoneIndex = zoneIndex; - - // Global blend group id for the first blend group in this zone. - groups.m_blendGroupId = m_state.m_blendGroupOffsetsView[zoneIndex]; - // Global start - groups.m_startOffset = groups.m_currentDataOffset = - m_state.m_blendOffsetView[zoneIndex]; - - groups.m_state = const_cast(&m_state); - return groups; - } - - /** - * \brief Make a BlendData object from the views in this object. - * - * \return A BlendData object suitable for making new fields and coordsets. - */ - axom::mir::utilities::blueprint::BlendData makeBlendData() const - { - axom::mir::utilities::blueprint::BlendData blend; - - blend.m_selectedIndicesView = - m_state.m_blendUniqueIndicesView; // We'll use these to select just the unique indices - blend.m_blendGroupSizesView = m_state.m_blendGroupSizesView; - blend.m_blendGroupStartView = m_state.m_blendGroupStartView; - blend.m_blendIdsView = m_state.m_blendIdsView; - blend.m_blendCoeffView = m_state.m_blendCoeffView; - - return blend; - } - -private: - State m_state; -}; - //------------------------------------------------------------------------------ /** @@ -661,7 +194,7 @@ class BlendGroupBuilder template + typename NamingPolicy = axom::mir::utilities::HashNaming> class ClipField { public: diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 6b43322c3e..b7e9f4838e 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -18,6 +18,35 @@ #include + +// NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers +// so we can write serial versions for when RAJA is not enabled. +namespace axom +{ +template +struct scans +{ + inline void exclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) + { + using loop_policy = typename axom::execution_space::loop_policy; + assert(input.size() == output.size()); + RAJA::exclusive_scan( + RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size())); + } +}; + +template +inline void exclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) +{ + scans s; + s.exclusive_scan(input, output); +} + +} // end namespace axom + /// This header is for device utility functions for MIR. // Q: does this belong in core with a better name? @@ -233,6 +262,47 @@ AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, n * sizeof(ValueType)); } +//------------------------------------------------------------------------------ +/** + * \brief This class implements a naming policy that uses some hashing functions + * to produce a "name" for an array of ids. + */ +class HashNaming +{ +public: + using KeyType = std::uint64_t; + + /** + * \brief Make a name from an array of ids. + * + * \param ids The array of ids. + * \param numIds The number of ids in the array. + * + * \return The name that describes the array of ids. + * + * \note Different make_name_* functions are used because we can skip most + * sorting for 1,2 element arrays. + */ + AXOM_HOST_DEVICE + inline static KeyType makeName(const IndexType *ids, IndexType numIds) + { + KeyType name {}; + if(numIds == 1) + { + name = axom::mir::utilities::make_name_1(ids[0]); + } + else if(numIds == 2) + { + name = axom::mir::utilities::make_name_2(ids[0], ids[1]); + } + else + { + name = axom::mir::utilities::make_name_n(ids, numIds); + } + return name; + } +}; + //------------------------------------------------------------------------------ /** * \brief This function makes a unique array of values from an input list of keys. From 35616f22ecebb0505308ca5e77068d11358c64b0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 8 Aug 2024 12:13:48 -0700 Subject: [PATCH 157/290] make style --- src/axom/mir/ClipField.hpp | 3 ++- src/axom/mir/utilities.hpp | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9fd5991c06..3358ce0892 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -445,7 +445,8 @@ class ClipField if(!opts.fields(fieldsToProcess)) { // Fields were not present in the options. Select all fields that have the same topology as opts.clipField(). - const std::string clipTopology = n_fields.fetch_existing(opts.clipField() + "/topology").as_string(); + const std::string clipTopology = + n_fields.fetch_existing(opts.clipField() + "/topology").as_string(); for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { if(n_fields[i].fetch_existing("topology").as_string() == clipTopology) diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index b7e9f4838e..0f9ccd2bcc 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -18,7 +18,6 @@ #include - // NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers // so we can write serial versions for when RAJA is not enabled. namespace axom From 0207e14d2339bce2a3c292b5d189e285348661da Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 8 Aug 2024 17:36:31 -0700 Subject: [PATCH 158/290] Changed how options work, separated out selected zones. Starting a MIR algorithm --- src/axom/mir/ClipField.hpp | 52 +++--- src/axom/mir/ClipFieldFilter.cpp | 2 +- src/axom/mir/ClipFieldFilterDevice.hpp | 2 +- src/axom/mir/ClipOptions.hpp | 6 +- src/axom/mir/EquiZAlgorithm.cpp | 171 ----------------- src/axom/mir/EquiZAlgorithm.hpp | 193 +++++++++++++++---- src/axom/mir/MIRAlgorithm.cpp | 207 +++++---------------- src/axom/mir/MIRAlgorithm.hpp | 128 +++++-------- src/axom/mir/MIROptions.hpp | 60 ++++++ src/axom/mir/NodeToZoneRelationBuilder.hpp | 38 ++-- src/axom/mir/Options.hpp | 110 +---------- src/axom/mir/SelectedZones.hpp | 121 ++++++++++++ src/axom/mir/tests/mir_clipfield.cpp | 9 +- src/axom/mir/views/MaterialView.hpp | 15 +- 14 files changed, 516 insertions(+), 598 deletions(-) create mode 100644 src/axom/mir/MIROptions.hpp create mode 100644 src/axom/mir/SelectedZones.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 3358ce0892..1168e81d2f 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -18,6 +18,7 @@ #include "axom/mir/ClipOptions.hpp" #include "axom/mir/BlendGroupBuilder.hpp" #include "axom/mir/utilities.hpp" +#include "axom/mir/SelectedZones.hpp" #include #include @@ -236,9 +237,10 @@ class ClipField const conduit::Node &n_options, conduit::Node &n_output) { - ClipOptions opts(0, n_options); + ClipOptions opts(n_options); const std::string clipFieldName = opts.clipField(); + // Get clipField's topo/coordset. const conduit::Node &n_fields = n_input.fetch_existing("fields"); const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); const std::string &topoName = n_clipField["topology"].as_string(); @@ -282,8 +284,9 @@ class ClipField const auto allocatorID = axom::execution_space::allocatorID(); // Make the selected zones and get the size. - ClipOptions opts(m_topologyView.numberOfZones(), n_options); - const auto nzones = opts.selectedZonesView().size(); + ClipOptions opts(n_options); + axom::mir::SelectedZones selectedZones(m_topologyView.numberOfZones(), n_options); + const auto nzones = selectedZones.view().size(); // Get the clip field. Make sure it is double. That lets us make less code down the line. std::string clipFieldName = opts.clipField(); @@ -371,8 +374,8 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, clipFieldView); - computeFragmentSizes(fragmentData, opts); + computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, selectedZones, clipFieldView); + computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); IndexType blendGroupsSize = 0, blendGroupLenSize = 0; @@ -401,7 +404,7 @@ class ClipField blendGroupStart.view(), blendIds.view(), blendCoeff.view()); - makeBlendGroups(clipTableViews, builder, zoneData, opts, clipFieldView); + makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones, clipFieldView); // Make the blend groups unique axom::Array uNames; @@ -418,6 +421,7 @@ class ClipField zoneData, fragmentData, opts, + selectedZones, n_newTopo, n_newCoordset, n_newFields); @@ -430,7 +434,7 @@ class ClipField auto sliceIndicesView = sliceIndices.view(); // Fill in sliceIndicesView. - const auto selectedZonesView = opts.selectedZonesView(); + const auto selectedZonesView = selectedZones.view(); axom::for_all( nzones, AXOM_LAMBDA(auto index) { @@ -463,7 +467,7 @@ class ClipField fieldsToProcess, n_fields, n_newFields); - makeOriginalElements(fragmentData, opts, n_fields, n_newTopo, n_newFields); + makeOriginalElements(fragmentData, opts, selectedZones, n_fields, n_newTopo, n_newFields); } private: @@ -492,7 +496,7 @@ class ClipField /** * \brief Make a bitset that indicates the parts of the selection that are selected. */ - int getSelection(const ClipOptions &opts) const + int getSelection(const ClipOptions &opts) const { int selection = 0; if(opts.inside()) axom::utilities::setBitOn(selection, 0); @@ -546,7 +550,8 @@ class ClipField BlendGroupBuilderType builder, ZoneData zoneData, FragmentData fragmentData, - ClipOptions &opts, + const ClipOptions &opts, + const SelectedZones &selectedZones, const axom::ArrayView &clipFieldView) const { const auto clipValue = static_cast(opts.clipValue()); @@ -556,7 +561,7 @@ class ClipField auto blendGroupsLenView = builder.state().m_blendGroupsLenView; m_topologyView.template for_selected_zones( - opts.selectedZonesView(), + selectedZones.view(), AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); @@ -666,9 +671,9 @@ class ClipField * \param[inout] fragmentData The object that contains data about the zone fragments. */ void computeFragmentSizes(FragmentData &fragmentData, - ClipOptions &opts) const + const SelectedZones &selectedZones) const { - const auto nzones = opts.selectedZonesView().size(); + const auto nzones = selectedZones.view().size(); // Sum the number of fragments. RAJA::ReduceSum fragment_sum(0); @@ -716,14 +721,15 @@ class ClipField void makeBlendGroups(ClipTableViews clipTableViews, BlendGroupBuilderType builder, ZoneData zoneData, - ClipOptions &opts, + const ClipOptions &opts, + const SelectedZones &selectedZones, const axom::ArrayView &clipFieldView) const { const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); m_topologyView.template for_selected_zones( - opts.selectedZonesView(), + selectedZones.view(), AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -827,7 +833,8 @@ class ClipField * \param[in] builder This object holds views to blend group data and helps with building/access. * \param[in] zoneData This object holds views to per-zone data. * \param[in] fragmentData This object holds views to per-fragment data. - * \param[inout] opts Clipping options. + * \param[in] opts Clipping options. + * \param[in] selectedZones The selected zones. * \param[out] n_newTopo The node that will contain the new topology. * \param[out] n_newCoordset The node that will contain the new coordset. * \param[out] n_newFields The node that will contain the new fields. @@ -838,7 +845,8 @@ class ClipField BlendGroupBuilderType builder, ZoneData zoneData, FragmentData fragmentData, - ClipOptions &opts, + const ClipOptions &opts, + const SelectedZones &selectedZones, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const @@ -892,7 +900,7 @@ class ClipField // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. RAJA::ReduceBitOr shapesUsed_reduce(0); m_topologyView.template for_selected_zones( - opts.selectedZonesView(), + selectedZones.view(), AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; @@ -1134,14 +1142,16 @@ class ClipField * * \param[in] fragmentData This object holds views to per-fragment data. * \param[in] opts Clipping options. - * \param[in[ n_fields The node that contains the input mesh's fields. + * \param[in] selectedZones The selected zones. + * \param[in] n_fields The node that contains the input mesh's fields. * \param[out] n_newTopo The node that will contain the new topology. * \param[out] n_newFields The node that will contain the new fields. * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeOriginalElements(FragmentData fragmentData, - ClipOptions &opts, + const ClipOptions &opts, + const SelectedZones &selectedZones, const conduit::Node &n_fields, conduit::Node &n_newTopo, conduit::Node &n_newFields) const @@ -1152,7 +1162,7 @@ class ClipField utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); - const auto selectedZonesView = opts.selectedZonesView(); + const auto selectedZonesView = selectedZones.view(); const auto nzones = selectedZonesView.size(); if(n_fields.has_child("originalElements")) diff --git a/src/axom/mir/ClipFieldFilter.cpp b/src/axom/mir/ClipFieldFilter.cpp index 43394e4778..c25e1d5d8e 100644 --- a/src/axom/mir/ClipFieldFilter.cpp +++ b/src/axom/mir/ClipFieldFilter.cpp @@ -35,7 +35,7 @@ void ClipFieldFilter::execute(const conduit::Node &n_input, const conduit::Node &n_options, conduit::Node &n_output) { - ClipOptions opts(0, n_options); + ClipOptions opts(n_options); const std::string clipFieldName = opts.clipField(); const conduit::Node &n_fields = n_input.fetch_existing("fields"); diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp index c781df864a..6637cbb9d7 100644 --- a/src/axom/mir/ClipFieldFilterDevice.hpp +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -42,7 +42,7 @@ class ClipFieldFilterDevice const conduit::Node &n_options, conduit::Node &n_output) { - ClipOptions opts(0, n_options); + ClipOptions opts(n_options); const std::string clipFieldName = opts.clipField(); const conduit::Node &n_fields = n_input.fetch_existing("fields"); diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/ClipOptions.hpp index 7abc50d749..0af2359b02 100644 --- a/src/axom/mir/ClipOptions.hpp +++ b/src/axom/mir/ClipOptions.hpp @@ -18,17 +18,15 @@ namespace clipping * \brief This class provides a kind of schema over the clipping options, as well * as default values, and some utilities functions. */ -template -class ClipOptions : public axom::mir::Options +class ClipOptions : public axom::mir::Options { public: /** * \brief Constructor * - * \param nzones The total number of zones in the associated topology. * \param options The node that contains the clipping options. */ - ClipOptions(axom::IndexType nzones, const conduit::Node &options) : axom::mir::Options(nzones, options) + ClipOptions(const conduit::Node &options) : axom::mir::Options(options) { } /** diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index 11bbc5cd51..ab4fb995ed 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -3,184 +3,13 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "axom/config.hpp" -#include "axom/core.hpp" #include "axom/mir/EquiZAlgorithm.hpp" -#include "axom/core/ArrayView.hpp" -#include "axom/mir/views/StructuredTopologyView.hpp" -#include "axom/mir/views/dispatch_coordset.hpp" -#include "axom/mir/views/dispatch_topology.hpp" -#include "axom/mir/views/dispatch_material.hpp" -#include "axom/mir/views/dispatch_utilities.hpp" -#include "axom/mir/clipping/ClipTableManager.hpp" -#include "axom/mir/NodeToZoneRelationBuilder.hpp" - -#include - -// RAJA -#if defined(AXOM_USE_RAJA) - #include "RAJA/RAJA.hpp" -#endif - -#if 0 -// clang-format off -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - using seq_exec = axom::SEQ_EXEC; - - #if defined(AXOM_USE_OPENMP) - using omp_exec = axom::OMP_EXEC; - #else - using omp_exec = seq_exec; - #endif - - #if defined(AXOM_USE_CUDA) - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - #else - using cuda_exec = seq_exec; - #endif - - #if defined(AXOM_USE_HIP) - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - #else - using hip_exec = seq_exec; - #endif -#endif -// clang-format on -#endif namespace axom { namespace mir { -void EquiZAlgorithm::execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) -{ -#if 0 - #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) - switch(m_execPolicy) - { - #if defined(AXOM_USE_OPENMP) - case RuntimePolicy::omp: - executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); - break; - #endif - #if defined(AXOM_USE_CUDA) - case RuntimePolicy::cuda: - executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); - break; - #endif - #if defined(AXOM_USE_HIP) - case RuntimePolicy::hip: - executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); - break; - #endif - default: - // Falls through - case RuntimePolicy::seq: - executeImpl(topo, coordset, matset, options, new_topo, new_coordset, new_matset); - break; - } - #endif -#endif -} - -template -void EquiZAlgorithm::executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) -{ - // TODO: migrate data for topo, coordset to appropriate memory space if needed. -#if 0 - views::dispatch_coordset(coordset, [&](auto &coordsetView) - { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) - { - views::dispatch_material(matset, [&](auto &matsetView) - { - EquiZAlgorithm mir; - mir.execute(topoView, coordsetView, matsetView, options); - }); - }); - }); -#endif -#if 0 - if(options.has_path("zones")) - { - const conduit::Node &n_zones = options.fetch_existing("zones"); - -/// NOTE: since each inner dispatch could be a lot of code, should I just make a zones array for the case where zones is not provided? - - // Operate on a list of zones. - views::dispatch_material(matset, [&](auto &matsetView) - { - views::IndexNode_to_ArrayView(n_zones, [&](auto zonesView) - { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) - { - views::dispatch_coordset(coordset, [&](auto &coordsetView) - { - // Create the clipping tables for the topo dimension. - axom::mir::clipping::ClipTableManager clipManager; - clipManager.load(topoView.dimension()); - - const auto matinfo = views::materials(matset); - for(const auto &mat : matinfo) - { - const auto matID = mat.number; - - axom::mir::utilities::NodeToZoneRelationBuilder nz; - conduit::Node rel; - nz.execute(topo, rel); - - views::IndexNode_to_ArrayView_same(rel["zones"], rel["sizes"], rel["offsets"], [&](auto relZonesView, auto relSizesView, auto relOffsetsView) - { - // We need to get views for the various shape types. - using TableView = typename axom::mir::clipping::ClipTableManager::Table::View; - axom::StackArray tables; - tables[ST_TET] = clipManager[ST_TET].view(); - - topoView. template for_selected_zones(zonesView, AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - - }); - }); - } - }); // dispatch_matset - }); // dispatch_coordset - }); // dispatch_topology - }); - } - else - { - // Operate on all zones. - views::dispatch_coordset(coordset, [&](auto &coordsetView) - { - views::dispatch_topology(topo, coordset, [&](const std::string &shape, auto &topoView) - { - views::dispatch_material(matset, [&](auto &matsetView) - { - topoView. template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - - }); - }); - }); - }); - } -#endif -} } // namespace mir } // namespace axom diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index af4190ad04..50d6bc380a 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -5,6 +5,7 @@ #ifndef AXOM_MIR_EQUIZ_ALGORITHM_HPP_ #define AXOM_MIR_EQUIZ_ALGORITHM_HPP_ +#if 0 #include "axom/config.hpp" #include "axom/core.hpp" #include "axom/mir/MIRAlgorithm.hpp" @@ -15,52 +16,180 @@ namespace axom { namespace mir { + +/** + * \brief Populate a new field + */ +template +struct MatsetToField +{ + using MaterialIndex = typename MatsetView::MaterialIndex; + using FloatType = typename MatsetView::FloatType; + + void execute(const MatsetView &matsetView, axom::ArrayView &vfValues) + { + const auto nzones = vfValues.size(); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + FloatType vf{}; + matsetView.zoneContainsMaterial(zoneIndex, mat, vf); + vfValues[zoneIndex] = vf; + }); + } +}; + +// TODO we can + /** * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. */ +template class EquiZAlgorithm : public MIRAlgorithm { public: - using RuntimePolicy = axom::runtime_policy::Policy; - EquiZAlgorithm() = default; + EquiZAlgorithm(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) : + m_topologyView(topoView), m_coordsetView(coordsetView), m_matsetView(matsetView) + { + } + virtual ~EquiZAlgorithm() = default; +protected: + virtual void executeDomain(const conduit::Node &n_topo, + { + namespace bputils = axom::mir::utilities::blueprint; + using FloatType = typename MatsetView::FloatType; + constexpr auto floatTypeID = bputils::cpp2conduit::id; + + const std:string zoneCenteredField("__equiz__zoneMatField"); + const std:string nodeCenteredField("__equiz__nodeMatField"); + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + + // Iterate over the materials + const auto matInfo = materials(n_matset); + conduit::Node n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset; + for(size_t i = 0; i < matInfo.size(); i++) + { + if(i == 0) + { + // The first time through, we can use the supplied views. + this->template iteration( + m_topologyView, m_coordsetView, m_matsetView, + n_topo, n_coordset, n_fields, n_matset, + n_options, + n_newTopo, n_newCoordset, n_newFields, n_newMatset); + } + else + { + // Move the outputs of the last iteration to the inputs of this iteration. + n_InputTopo.move(n_newTopo); + n_InputCoordset.move(n_newCoordset); + n_InputFields.move(n_newFields); + n_InputMatset.move(n_newFields); + + const auto shape = n_newTopo.fetch_existing("elements/shape").as_string(); + if(shape == "mixed") + { + // The data are now an unstructured view, probably a mixed shape view. + // Dispatch to an appropriate topo view + views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) + { + using ICoordSetView = decltype(coordsetView); + views::dispatch_unstructured_mixed_topology(n_InputTopo, [&](auto topoView) + { + using ITopologyView = decltype(topoView); + this->template iteration( + topologyView, coordsetView, matsetView, + n_topo, n_coordset, n_fields, n_matset, + n_options, + n_newTopo, n_newCoordset, n_newFields, n_newMatset); + }); + }); + } + } + } + } + /** - * \brief Set the desired execution policy. - * - * \param policy The execution policy. - * - * \note The input Conduit nodes must have data located in a memory space - * that is compatible with the execution policy. + * \brief Perform one round of material clipping. */ - void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } + template + void iteration(const ITopoView &topoView, + const ICoordsetView &coordsetView, + const IMatsetView &matsetView, + const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_matset, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields, + conduit::Node &n_newMatset) + { + const auto nzones = topoView.numberOfZones(); -protected: - /// Implement the EquiZ algorithm on a single domain. - virtual void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) override; - - /// Implement the EquiZ algorithm on a single domain for a given ExecSpace. - template - void executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset); - - RuntimePolicy m_execPolicy {RuntimePolicy::seq}; -}; + // Make a node to zone relation. + conduit::Node relation; + axom::mir::NodeToZoneRelationBuilder builder; + builder.execute(n_topo, relation); // <----------------------- Should this algorithm take a topo view? + + // Make a shallow copy of the fields that we can modify. + conduit::Node tmpFields; + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + tmpFields[n_fields[i].name()].set_external(n_fields[i]); + } + + // Make a zonal field for the current material's volume fractions. + conduit::Node &zoneMatField = tmpFields[zoneCenteredField]; + zoneMatField["topology"] = n_topo.name(); + zoneMatField["association"] = "element"; + conduit::Node &zoneValues = zoneMatField["values"]; + zoneValues.set_allocator(c2a.getConduitAllocatorID()); + zoneValues.set(conduit::DataType(floatTypeID, nzones)); + + // Populate the zonal volume fraction field from the material view. + MatsetToField m2f; + auto zoneMatFieldView = make_array_view(zoneValues); + m2f.execute(matsetView, currentMat.number, zoneMatFieldView); + + // Recenter the field to nodal using the node to zone relation. + conduit::Node &nodeMatField = tmpFields[nodeCenteredField]; + RecenterField recenter; + recenter.execute(zoneMatField, relation, nodeMatField); + +/** + NOTE - I am a little worried that I've not yet appreciated how the intersections are found + in the EquiZ algorithm and that I'll need to add a new template parameter to ClipField + that lets that behavior be customizable so I can override it here. + */ + + conduit::Node clippedMesh; + conduit::Node &clipTopo = clippedMesh[n_newTopo.path()]; + conduit::Node &clipCoordset = clippedMesh[n_newCoordset.path()]; + conduit::Node &clipFields = clippedMesh["fields"]; + + // Now, clip the topology using the nodal field. + conduit::Node options, clippedMesh; + options["inside"] = 1; + options["outside"] = 1; + options["clipField"] = nodeCenteredField; + options["clipValue"] = 0.5; + ClipField clipper(topoView, coordsetView); + clipper.execute(n_topo, n_coordset, tmpFields, options, + // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. + + + } +} +//------------------------------------------------------------------------------ } // end namespace mir } // end namespace axom - +#endif #endif diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index 34390edae3..0e1182e214 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "axom/mir/MIRAlgorithm.hpp" -#include "axom/core/ArrayView.hpp" +#include "axom/mir/MIROptions.hpp" #include "axom/slic.hpp" #include @@ -14,187 +14,82 @@ namespace axom { namespace mir { -void MIRAlgorithm::execute(const conduit::Node &root, - const conduit::Node &options, - conduit::Node &output) + +void MIRAlgorithm::execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) { - auto domains = conduit::blueprint::mesh::domains(root); + const auto domains = conduit::blueprint::mesh::domains(n_input); if(domains.size() > 1) { // Handle multiple domains for(const auto &dom_ptr : domains) { - const conduit::Node &dom = *dom_ptr; - const std::string topoName = topologyName(dom, options); - const std::string newTopoName = newTopologyName(dom, options); - const std::string newCSName = newCoordsetName(dom, options); - const std::string newMatName = newMatsetName(dom, options); - - conduit::Node &newDomain = output.append(); - const conduit::Node &topologies = dom.fetch_existing("topologies"); - const conduit::Node &topo = topologies.fetch_existing(topoName); - const conduit::Node *cset = - conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); - const conduit::Node &mset = matset(dom, options); + const conduit::Node &n_domain = *dom_ptr; + conduit::Node &n_newDomain = n_output.append(); - conduit::Node &newTopo = newDomain["topologies/" + newTopoName]; - conduit::Node &newCoordset = newDomain["coordsets/" + newCSName]; - conduit::Node &newMatset = newDomain["matsets/" + newMatName]; - copyState(dom, newDomain); - execute(topo, *cset, mset, options, newTopo, newCoordset, newMatset); + executeSetup(n_domain, n_options, n_newDomain); } } else if(domains.size() > 0) { // Handle single domain - const conduit::Node &dom = *domains[0]; - - const std::string topoName = topologyName(dom, options); - const std::string newTopoName = newTopologyName(dom, options); - const std::string newCSName = newCoordsetName(dom, options); - const std::string newMatName = newMatsetName(dom, options); - - const conduit::Node &topologies = dom.fetch_existing("topologies"); - const conduit::Node &topo = topologies.fetch_existing(topoName); - const conduit::Node *cset = - conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); - const conduit::Node &mset = matset(root, options); - - conduit::Node &newTopo = output["topologies/" + newTopoName]; - conduit::Node &newCoordset = output["coordsets/" + newCSName]; - conduit::Node &newMatset = output["matsets/" + newMatName]; - copyState(dom, output); - execute(topo, *cset, mset, options, newTopo, newCoordset, newMatset); + const conduit::Node &n_domain = *domains[0]; + executeSetup(n_domain, n_options, n_output); } } -void MIRAlgorithm::copyState(const conduit::Node &mesh, - conduit::Node &destMesh) const +void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, const conduit::Node &n_options, + conduit::Node &n_newDomain) { - if(mesh.has_path("state")) destMesh["state"].set(mesh["state"]); -} - -std::string MIRAlgorithm::topologyName(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::string topoName; - if(options.has_path("topology")) - topoName = options.fetch_existing("topology").as_string(); - else if(mesh.has_path("topologies")) + MIROptions options(n_options); + + // Get the matset that we'll operate on. + const std::string matset = options.matset(); + + // Which topology is that matset defined on? + const conduit::Node &n_matsets = n_domain.fetch_existing("matsets"); + const conduit::Node &n_matset = n_matsets.fetch_existing(matset); + const conduit::Node *n_topo = + conduit::blueprint::mesh::utils::find_reference_node(n_matset, "topology"); + SLIC_ASSERT(n_topo != nullptr); + + // Which coordset is used by that topology? + const conduit::Node *n_coordset = + conduit::blueprint::mesh::utils::find_reference_node(*n_topo, "coordset"); + SLIC_ASSERT(n_coordset != nullptr); + + // Get the names of the output items. + const std::string newTopoName = options.topologyName(n_topo->name()); + const std::string newCoordsetName = options.coordsetName(n_coordset->name()); + const std::string newMatsetName = options.matsetName(matset); + + // Make some new nodes in the output. + conduit::Node &newTopo = n_newDomain["topologies/" + newTopoName]; + conduit::Node &newCoordset = n_newDomain["coordsets/" + newCoordsetName]; + conduit::Node &newMatset = n_newDomain["matsets/" + newMatsetName]; + + // Execute the algorithm on the domain. + if(n_domain.has_path("state")) + copyState(n_domain["state"], n_newDomain["state"]); + if(n_domain.has_path("fields")) { - const conduit::Node &topologies = mesh.fetch_existing("topologies"); - topoName = topologies[0].name(); + conduit::Node &newFields = n_newDomain["fields"]; + executeDomain(*n_topo, *n_coordset, n_domain["fields"], n_matset, n_options, newTopo, newCoordset, newFields, newMatset); } - return topoName; -} - -std::string MIRAlgorithm::newTopologyName(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::string topoName; - if(options.has_path("new_topology")) - topoName = options.fetch_existing("new_topology").as_string(); - else - topoName = topologyName(mesh, options); - return topoName; -} - -std::string MIRAlgorithm::newCoordsetName(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::string csetName; - if(options.has_path("new_coordset")) - csetName = options.fetch_existing("new_coordset").as_string(); else { - std::string topoName = topologyName(mesh, options); - const conduit::Node &topologies = mesh.fetch_existing("topologies"); - const conduit::Node &topo = topologies.fetch_existing(topoName); - csetName = topo.fetch_existing("coordset").as_string(); - } - - return csetName; -} - -std::string MIRAlgorithm::matsetName(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::string matName; - if(options.has_path("matset")) - matName = options.fetch_existing("matset").as_string(); - else if(mesh.has_path("matsets")) - { - const conduit::Node &matsets = mesh.fetch_existing("matsets"); - matName = matsets[0].name(); + conduit::Node n_fields, newFields; + executeDomain(*n_topo, *n_coordset, n_fields, n_matset, n_options, newTopo, newCoordset, newFields, newMatset); } - return matName; -} - -std::string MIRAlgorithm::newMatsetName(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::string matName; - if(options.has_path("new_matset")) - matName = options.fetch_existing("new_matset").as_string(); - else - matName = matsetName(mesh, options); - return matName; } -const conduit::Node &MIRAlgorithm::topology(const conduit::Node &mesh, - const conduit::Node &options) const +void MIRAlgorithm::copyState(const conduit::Node &srcState, conduit::Node &destState) const { - const std::string topoName = topologyName(mesh, options); - const conduit::Node &topologies = mesh.fetch_existing("topologies"); - return topologies.fetch_existing(topoName); -} - -const conduit::Node &MIRAlgorithm::matset(const conduit::Node &mesh, - const conduit::Node &options) const -{ - const std::string matName = matsetName(mesh, options); -#if 0 - const conduit::Node &matsets = mesh.fetch_existing("matsets"); - for(conduit::index_t i = 0; i < matsets.number_of_children(); i++) - { - const conduit::Node &matset = matsets[i]; - if(matset["topology"].as_string() == topoName) - return matset; - } - // We did not find one. TODO: throw exception. - // return first to eliminate compiler warning. - return matsets[0]; -#else - const conduit::Node &matsets = mesh.fetch_existing("matsets"); - return matsets.fetch_existing(matName); -#endif + for(conduit::index_t i = 0; i < srcState.number_of_children(); i++) + destState[srcState[i].name()].set(srcState[i]); } -std::vector MIRAlgorithm::fieldNames(const conduit::Node &mesh, - const conduit::Node &options) const -{ - std::vector names; - if(options.has_path("fields")) - { - const conduit::Node &fields = options["fields"]; - if(fields.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < fields.number_of_children(); i++) - names.push_back(fields[i].name()); - } - else - { - if(fields.dtype().is_char8_str()) names.push_back(fields.as_string()); - } - } - else if(mesh.has_child("fields")) - { - const conduit::Node &fields = mesh.fetch_existing("fields"); - for(conduit::index_t i = 0; i < fields.number_of_children(); i++) - names.push_back(fields[i].name()); - } - return names; -} } // namespace mir } // namespace axom diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index 83553c30c8..ccb1d00024 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -33,9 +33,9 @@ class MIRAlgorithm root node. Root can either be a mesh domain or a node that contains multiple domains. - \param[in] root The root node that contains either a mesh or list of mesh - domains that contain a topology and matset to be used for MIR. - \param[in] options A node that contains options that help govern MIR execution. + \param[in] n_input The root node that contains either a mesh or list of mesh + domains that contain a topology and matset to be used for MIR. + \param[in] n_options A node that contains options that help govern MIR execution. options: topology: main @@ -57,92 +57,58 @@ class MIRAlgorithm "zones" is a list of zone indices from the topology that need to be reconstructed. If not present then all zones will be considered. "mapping" indicates whether we should include an original_element_numbers field on the new topology to indicate where each new zone came from in the original topology. - \param[out] output A node that will contain the new entities. + \param[out] n_output A node that will contain the new entities. */ - virtual void execute(const conduit::Node &root, - const conduit::Node &options, - conduit::Node &output); + virtual void execute(const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output); protected: /** - \brief Perform material interface reconstruction on a single domain. Derived classes - must implement this method and any device-specific coding gets handled under it. + * \brief Set up the new domain from the old one and invoke executeDomain. + * + * \param n_domain The input domain. + * \param n_options The MIR options. + * \param n_newDomain The output domain. + */ + void executeSetup(const conduit::Node &n_domain, + const conduit::Node &n_options, + conduit::Node &n_newDomain); - \param[in] topo The Conduit node containing the topology that will be used for MIR. - \param[in] coordset The Conduit node containing the topology's coordset. - \param[in] options The Conduit node containing the options that help govern MIR execution. - - \param[out] new_topo A Conduit node that will contain the new topology. - \param[out] new_coordset A Conduit node that will contain the new coordset. - - */ - virtual void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &matset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset, - conduit::Node &new_matset) = 0; - - // Utility methods for derived types. - void copyState(const conduit::Node &mesh, conduit::Node &destMesh) const; - std::string topologyName(const conduit::Node &mesh, - const conduit::Node &options) const; - std::string newTopologyName(const conduit::Node &mesh, - const conduit::Node &options) const; - - std::string newCoordsetName(const conduit::Node &mesh, - const conduit::Node &options) const; - - std::string matsetName(const conduit::Node &mesh, - const conduit::Node &options) const; - std::string newMatsetName(const conduit::Node &mesh, - const conduit::Node &options) const; - - std::vector fieldNames(const conduit::Node &mesh, - const conduit::Node &options) const; - - const conduit::Node &topology(const conduit::Node &input, - const conduit::Node &options) const; - const conduit::Node &matset(const conduit::Node &input, - const conduit::Node &options) const; - - // TODO: method for mapping element field to new topo - // TODO: method for mapping vertex field to new topo -}; - -#if 0 -class ElviraMIRAlgorithm : public MIRAlgorithm -{ -public: - using RuntimePolicy = axom::runtime_policy::Policy; - - ElviraMIRAlgorithm() = default; - virtual ~ElviraMIRAlgorithm() = default; - - void setExecPolicy(RuntimePolicy policy) { m_execPolicy = policy; } + /** + * \brief Perform material interface reconstruction on a single domain. Derived classes + * must implement this method and any device-specific coding gets handled under it. + * + * \param[in] n_topo The Conduit node containing the topology that will be used for MIR. + * \param[in] n_coordset The Conduit node containing the coordset. + * \param[in] n_fields The Conduit node containing the fields. + * \param[in] n_matset The Conduit node containing the matset. + * \param[in] options The Conduit node containing the options that help govern MIR execution. + * + * \param[out] n_newTopo A node that will contain the new clipped topology. + * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. + * \param[out] n_newFields A node that will contain the new fields for the clipped topology. + * \param[out] n_newMatset A Conduit node that will contain the new matset. + * + */ + virtual void executeDomain(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_matset, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields, + conduit::Node &n_newMatset) = 0; -protected: - /// Implement the Elvira MIR algorithm on a single domain. - virtual void execute(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset) override; - - - /// Implement the Elvira MIR algorithm on a single domain for a given ExecSpace. - template - void executeImpl(const conduit::Node &topo, - const conduit::Node &coordset, - const conduit::Node &options, - conduit::Node &new_topo, - conduit::Node &new_coordset); - - RuntimePolicy m_execPolicy{RuntimePolicy::seq}; + /** + * \brief Copy state from the src domain to the destination domain. + * \param srcState The node that contains the state in the source domain. + * \param destState The node that contains the state in the destination domain. + */ + void copyState(const conduit::Node &srcState, conduit::Node &destState) const; }; -#endif } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/MIROptions.hpp b/src/axom/mir/MIROptions.hpp new file mode 100644 index 0000000000..e403f8a814 --- /dev/null +++ b/src/axom/mir/MIROptions.hpp @@ -0,0 +1,60 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_MIROPTIONS_HPP_ +#define AXOM_MIR_MIROPTIONS_HPP_ + +#include "axom/mir/Options.hpp" + +namespace axom +{ +namespace mir +{ + +/** + * \brief This class provides a kind of schema over the MIR options, as well + * as default values, and some utilities functions. + */ +class MIROptions : public axom::mir::Options +{ +public: + /** + * \brief Constructor + * + * \param options The node that contains the clipping options. + */ + MIROptions(const conduit::Node &options) : axom::mir::Options(options) + { } + + /** + * \brief Get the name of the matset on which we'll operate. + * \return The name of the matset. + */ + std::string matset() const + { + return options().fetch_existing("matset").as_string(); + } + + /** + * \brief Return the name of the matset to make in the output. + * \param default_value The name to use if the option is not defined. + * \return The name of the matset to make in the output. + */ + std::string matsetName(const std::string &default_value = std::string()) const + { + std::string name(default_value.empty() ? matset() : default_value); + if(options().has_child("matsetName")) + name = options().fetch_existing("matsetName").as_string(); + return name; + } + +private: + /// Access the base class' options. + const conduit::Node &options() const { return this->m_options; } +}; + +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index b1b0ad067b..15eefcba23 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -6,11 +6,7 @@ #ifndef AXOM_MIR_NODE_TO_ZONE_RELATION_BUILDER_HPP_ #define AXOM_MIR_NODE_TO_ZONE_RELATION_BUILDER_HPP_ -#include "axom/core/memory_management.hpp" -#include "axom/core/execution/execution_space.hpp" -#include "axom/core/execution/for_all.hpp" -#include "axom/core/Array.hpp" -#include "axom/core/ArrayView.hpp" +#include "axom/core.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/views/dispatch_unstructured_topology.hpp" @@ -25,8 +21,7 @@ namespace axom { namespace mir { -namespace utilities -{ + /** * \brief Build an o2m relation that lets us look up the zones for a node. */ @@ -87,23 +82,19 @@ void NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_v axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { - const axom::IndexType different = - (keys_view[i] != keys_view[i - 1]) ? 1 : 0; - const axom::IndexType m = (i >= 1) ? different : 1; - mask_view[i] = m; + mask_view[i] = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; }); // Do a scan on the mask array to build an offset array. axom::Array dest_offsets(n, n, allocatorID); auto dest_offsets_view = dest_offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view, n), - RAJA::make_span(dest_offsets_view, n), - RAJA::operators::plus {}); + axom::exclusive_scan(mask_view, dest_offsets_view); // Build the offsets to each node's zone ids. axom::for_all( offsets_view.size(), AXOM_LAMBDA(axom::IndexType i) { offsets_view[i] = 0; }); + axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { @@ -123,12 +114,17 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, const std::string type = topo.fetch_existing("type").as_string(); const auto allocatorID = axom::execution_space::allocatorID(); + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + + conduit::Node &n_zones = relation["zones"]; conduit::Node &n_sizes = relation["sizes"]; conduit::Node &n_offsets = relation["offsets"]; - n_zones.set_allocator(allocatorID); - n_sizes.set_allocator(allocatorID); - n_offsets.set_allocator(allocatorID); + n_zones.set_allocator(conduitAllocatorID); + n_sizes.set_allocator(conduitAllocatorID); + n_offsets.set_allocator(conduitAllocatorID); const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); @@ -164,15 +160,12 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, // Do a scan on the size array to build an offset array. axom::Array offsets(nzones, nzones, allocatorID); auto offsets_view = offsets.view(); - RAJA::exclusive_scan( - RAJA::make_span(sizes_view, nzones), - RAJA::make_span(offsets_view, nzones), - RAJA::operators::plus {}); + axom::exclusive_scan(sizes_view, offsets_view); sizes.clear(); // Allocate Conduit arrays on the device in a data type that matches the connectivity. conduit::Node n_conn; - n_conn.set_allocator(allocatorID); + n_conn.set_allocator(conduitAllocatorID); n_conn.set(conduit::DataType(intTypeId, connSize)); n_zones.set(conduit::DataType(intTypeId, connSize)); @@ -313,7 +306,6 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, } } -} // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/Options.hpp b/src/axom/mir/Options.hpp index 963a47eba8..545e8fc9f7 100644 --- a/src/axom/mir/Options.hpp +++ b/src/axom/mir/Options.hpp @@ -8,17 +8,19 @@ #include "axom/core.hpp" #include +#include namespace axom { namespace mir { +// IDEA: maybe use inlet for this stuff. + /** * \brief This class provides a kind of schema over options, as well * as default values, and some utilities functions. */ -template class Options { public: @@ -28,36 +30,14 @@ class Options * \param nzones The total number of zones in the associated topology. * \param options The node that contains the clipping options. */ - Options(axom::IndexType nzones, const conduit::Node &options) - : m_nzones(nzones) - , m_options(options) - , m_selectedZones() + Options(const conduit::Node &options) + :m_options(options) { } /** - * \brief Return a view that contains the list of selected zone ids for the mesh. - * \return A view that contains the list of selected zone ids for the mesh. - * - * \note The data for the view is generated if it has not yet been built. - */ - axom::ArrayView selectedZonesView() - { - if(m_selectedZones.size() == 0) - { - buildSelectedZones(); - } - return m_selectedZones.view(); - } - - /** - * \brief Invalidate the selected zones array (due to options changing) so we can rebuild it. - */ - void invalidateSelectedZones() { m_selectedZones.clear(); } - - /** - * \brief Return the name of the new topology to be created. + * \brief Return the name of the topology to make in the output. * \param default_value The name to use if the option is not defined. - * \return The name of the new topology to be created. + * \return The name of the topology to make in the output. */ std::string topologyName(const std::string &default_value = std::string()) const { @@ -68,9 +48,9 @@ class Options } /** - * \brief Return the name of the new coordset to be created. + * \brief Return the name of the coordset to make in the output. * \param default_value The name to use if the option is not defined. - * \return The name of the new coordset to be created. + * \return The name of the coordset to make in the output. */ std::string coordsetName(const std::string &default_value = std::string()) const { @@ -106,77 +86,7 @@ class Options } protected: - /** - * \brief The options may contain a "selectedZones" member that is a list of zones - * that will be operated on. If such an array is present, copy and sort it. - * If the zone list is not present, make an array that selects every zone. - * - * \note selectedZones should contain local zone numbers, which in the case of - * strided-structured indexing are the [0..n) zone numbers that exist only - * within the selected window. - */ - void buildSelectedZones() - { - const auto allocatorID = axom::execution_space::allocatorID(); - - if(m_options.has_child("selectedZones")) - { - // Store the zone list in m_selectedZones. - int badValueCount = 0; - views::IndexNode_to_ArrayView(m_options["selectedZones"], [&](auto zonesView) { - using loop_policy = - typename axom::execution_space::loop_policy; - using reduce_policy = - typename axom::execution_space::reduce_policy; - - // It probably does not make sense to request more zones than we have in the mesh. - SLIC_ASSERT(zonesView.size() <= m_nzones); - - m_selectedZones = axom::Array(zonesView.size(), - zonesView.size(), - allocatorID); - auto szView = m_selectedZones.view(); - axom::for_all( - szView.size(), - AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); - - // Check that the selected zone values are in range. - const auto nzones = m_nzones; - RAJA::ReduceSum errReduce(0); - axom::for_all( - szView.size(), - AXOM_LAMBDA(auto index) { - const int err = - (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; - errReduce += err; - }); - badValueCount = errReduce.get(); - - // Make sure the selectedZones are sorted. - RAJA::sort(RAJA::make_span(szView.data(), szView.size())); - }); - - if(badValueCount > 0) - { - SLIC_ERROR("Out of range selectedZones values."); - } - } - else - { - // Select all zones. - m_selectedZones = - axom::Array(m_nzones, m_nzones, allocatorID); - auto szView = m_selectedZones.view(); - axom::for_all( - m_nzones, - AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); - } - } - -protected: - axom::IndexType m_nzones; // The number of zones in the associated topology. - const conduit::Node &m_options; // A reference to the clipping options node. - axom::Array m_selectedZones; // Storage for a list of selected zone ids. + const conduit::Node &m_options; // A reference to the options node. }; } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp new file mode 100644 index 0000000000..4d4f3d3712 --- /dev/null +++ b/src/axom/mir/SelectedZones.hpp @@ -0,0 +1,121 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_SELECTED_ZONES_HPP_ +#define AXOM_MIR_SELECTED_ZONES_HPP_ + +#include "axom/core.hpp" + +#include + +namespace axom +{ +namespace mir +{ + +/** + * \brief This class provides a kind of schema over options, as well + * as default values, and some utilities functions. + */ +template +class SelectedZones +{ +public: + /** + * \brief Constructor + * + * \param nzones The total number of zones in the associated topology. + * \param options The node that contains the clipping options. + */ + SelectedZones(axom::IndexType nzones, const conduit::Node &options) : m_selectedZones(), + m_selectedZonesView() + { + buildSelectedZones(nzones, options); + } + + /** + * \brief Return a view that contains the list of selected zone ids for the mesh. + * \return A view that contains the list of selected zone ids for the mesh. + */ + const axom::ArrayView &view() const + { + return m_selectedZonesView; + } + +protected: + /** + * \brief The options may contain a "selectedZones" member that is a list of zones + * that will be operated on. If such an array is present, copy and sort it. + * If the zone list is not present, make an array that selects every zone. + * + * \note selectedZones should contain local zone numbers, which in the case of + * strided-structured indexing are the [0..n) zone numbers that exist only + * within the selected window. + */ + void buildSelectedZones(axom::IndexType nzones, const conduit::Node &options) + { + const auto allocatorID = axom::execution_space::allocatorID(); + + if(options.has_child("selectedZones")) + { + // Store the zone list in m_selectedZones. + int badValueCount = 0; + views::IndexNode_to_ArrayView(options["selectedZones"], [&](auto zonesView) { + using loop_policy = + typename axom::execution_space::loop_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; + + // It probably does not make sense to request more zones than we have in the mesh. + SLIC_ASSERT(zonesView.size() <= nzones); + + m_selectedZones = axom::Array(zonesView.size(), + zonesView.size(), + allocatorID); + auto szView = m_selectedZonesView = m_selectedZones.view(); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); + + // Check that the selected zone values are in range. + RAJA::ReduceSum errReduce(0); + axom::for_all( + szView.size(), + AXOM_LAMBDA(auto index) { + const int err = + (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + errReduce += err; + }); + badValueCount = errReduce.get(); + + // Make sure the selectedZones are sorted. + RAJA::sort(RAJA::make_span(szView.data(), szView.size())); + }); + + if(badValueCount > 0) + { + SLIC_ERROR("Out of range selectedZones values."); + } + } + else + { + // Select all zones. + m_selectedZones = + axom::Array(nzones, nzones, allocatorID); + auto szView = m_selectedZonesView = m_selectedZones.view(); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); + } + } + +protected: + axom::Array m_selectedZones; // Storage for a list of selected zone ids. + axom::ArrayView m_selectedZonesView; +}; + +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 3fc60149e8..a5a75fafce 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -62,7 +62,7 @@ TEST(mir_clipfield, options) int nzones = 6; conduit::Node options; - axom::mir::clipping::ClipOptions opts(nzones, options); + axom::mir::clipping::ClipOptions opts(options); options["clipField"] = "distance"; EXPECT_EQ(opts.clipField(), options["clipField"].as_string()); @@ -141,7 +141,8 @@ TEST(mir_clipfield, options) } // There are no "selectedZones" in the options. We should get nzones values from 0 onward. - auto selectedZonesView = opts.selectedZonesView(); + axom::mir::SelectedZones selectedZones(nzones, options); + auto selectedZonesView = selectedZones.view(); EXPECT_EQ(selectedZonesView.size(), 6); EXPECT_EQ(selectedZonesView[0], 0); EXPECT_EQ(selectedZonesView[1], 1); @@ -151,9 +152,9 @@ TEST(mir_clipfield, options) EXPECT_EQ(selectedZonesView[5], 5); // Put some "selectedZones" in the options. - opts.invalidateSelectedZones(); options["selectedZones"].set(std::vector {5, 4, 3}); - selectedZonesView = opts.selectedZonesView(); + axom::mir::SelectedZones selectedZones2(nzones, options); + selectedZonesView = selectedZones2.view(); EXPECT_EQ(selectedZonesView.size(), 3); EXPECT_EQ(selectedZonesView[0], 3); EXPECT_EQ(selectedZonesView[1], 4); diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 29b5636e54..5351357620 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -59,13 +59,14 @@ MaterialInformation materials(const conduit::Node &matset); indices: [1, 4, 6, 3, 2] */ -template +template class UnibufferMaterialView { public: using MaterialIndex = IndexT; using ZoneIndex = IndexT; using IndexType = IndexT; + using FloatType = FloatT; using IDList = StaticArray; using VFList = StaticArray; @@ -175,12 +176,14 @@ class UnibufferMaterialView // NOTE: I'm not sure I 100% get this one. -template +template class MultiBufferMaterialView { public: using MaterialIndex = IndexT; using ZoneIndex = IndexT; + using IndexType = IndexT; + using FloatType = FloatT; using IDList = StaticArray; using VFList = StaticArray; @@ -281,12 +284,14 @@ class MultiBufferMaterialView b: 1 c: 2 */ -template +template class ElementDominantMaterialView { public: using MaterialIndex = IndexT; using ZoneIndex = IndexT; + using IndexType = IndexT; + using FloatType = FloatT; using IDList = StaticArray; using VFList = StaticArray; @@ -385,12 +390,14 @@ class ElementDominantMaterialView */ /// NOTES: This matset type does not seem so GPU friendly since there is some work to do for some of the queries. -template +template class MaterialDominantMaterialView { public: using MaterialIndex = IndexT; using ZoneIndex = IndexT; + using IndexType = IndexT; + using FloatType = FloatT; using IDList = StaticArray; using VFList = StaticArray; From 84d3514fa2a66ed2215e0a1d35d64ba017569fa3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 9 Aug 2024 13:55:52 -0700 Subject: [PATCH 159/290] in progress --- src/axom/mir/CMakeLists.txt | 6 +- src/axom/mir/ClipFieldFilterDevice.hpp | 2 + src/axom/mir/EquiZAlgorithm.hpp | 123 ++++++++++---- .../mir/tests/mir_testing_data_helpers.hpp | 156 ++++++++++++++++++ src/axom/mir/tests/mir_views.cpp | 142 ++++++++++++++++ src/axom/mir/views/MaterialView.hpp | 110 ++++++------ 6 files changed, 448 insertions(+), 91 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index f2b230e56a..20ecbafe94 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -39,7 +39,11 @@ set(mir_headers MIRAlgorithm.hpp RecenterField.hpp utilities.hpp - + NodeToZoneRelationBuilder.hpp + SelectedZones.hpp + MIROptions.hpp + Options.hpp + ClipOptions.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp views/dispatch_rectilinear_topology.hpp diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/ClipFieldFilterDevice.hpp index 6637cbb9d7..06dad0bcce 100644 --- a/src/axom/mir/ClipFieldFilterDevice.hpp +++ b/src/axom/mir/ClipFieldFilterDevice.hpp @@ -100,6 +100,7 @@ class ClipFieldFilterDevice conduit::Node &n_newCoordset, conduit::Node &n_newFields) { +#if 0 // NOTE - there are 2 dispatches here so we can get coordset and topology views. // This instantiates the lambda that creates the ClipField object for // the various view types so we can handle most Blueprint topologies. @@ -138,6 +139,7 @@ class ClipFieldFilterDevice } }); }); +#endif } }; diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 50d6bc380a..8aaddbaa90 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -5,18 +5,20 @@ #ifndef AXOM_MIR_EQUIZ_ALGORITHM_HPP_ #define AXOM_MIR_EQUIZ_ALGORITHM_HPP_ -#if 0 #include "axom/config.hpp" #include "axom/core.hpp" -#include "axom/mir/MIRAlgorithm.hpp" +#include "axom/mir.hpp" #include +#include + +#include namespace axom { namespace mir { - +#if 0 /** * \brief Populate a new field */ @@ -26,7 +28,7 @@ struct MatsetToField using MaterialIndex = typename MatsetView::MaterialIndex; using FloatType = typename MatsetView::FloatType; - void execute(const MatsetView &matsetView, axom::ArrayView &vfValues) + void execute(const MatsetView &matsetView, MaterialIndex mat, axom::ArrayView &vfValues) { const auto nzones = vfValues.size(); axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) @@ -45,7 +47,7 @@ struct MatsetToField * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. */ template -class EquiZAlgorithm : public MIRAlgorithm +class EquiZAlgorithm : public axom::mir::MIRAlgorithm { public: @@ -58,30 +60,37 @@ class EquiZAlgorithm : public MIRAlgorithm protected: virtual void executeDomain(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_matset, + const conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields, + conduit::Node &n_newMatset) { - namespace bputils = axom::mir::utilities::blueprint; - using FloatType = typename MatsetView::FloatType; - constexpr auto floatTypeID = bputils::cpp2conduit::id; - - const std:string zoneCenteredField("__equiz__zoneMatField"); - const std:string nodeCenteredField("__equiz__nodeMatField"); - - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; + conduit::Node n_options_copy(n_options); // Iterate over the materials - const auto matInfo = materials(n_matset); + const auto matInfo = axom::mir::views::materials(n_matset); conduit::Node n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset; for(size_t i = 0; i < matInfo.size(); i++) { if(i == 0) { // The first time through, we can use the supplied views. - this->template iteration( + // clangformat-off + iteration( m_topologyView, m_coordsetView, m_matsetView, + matInfo[i], n_topo, n_coordset, n_fields, n_matset, - n_options, + n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); + // clangformat-on + + // In these iterations, we do not want to pass selectedZones through + // since they are only valid on the current input topology. + n_options_copy.remove("selectedZones"); } else { @@ -96,41 +105,73 @@ class EquiZAlgorithm : public MIRAlgorithm { // The data are now an unstructured view, probably a mixed shape view. // Dispatch to an appropriate topo view + // clangformat-off views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { using ICoordSetView = decltype(coordsetView); - views::dispatch_unstructured_mixed_topology(n_InputTopo, [&](auto topoView) + views::dispatch_unstructured_mixed_topology(n_InputTopo, [&](auto topologyView) { - using ITopologyView = decltype(topoView); - this->template iteration( + using ITopologyView = decltype(topologyView); + + // Assume we made this type out of the first iteration. + axom::mir::views::UnibufferMaterialView matsetView; + matsetView.set( + axom::mir::utilities::blueprint::make_array_view(n_matset["material_ids"]), + axom::mir::utilities::blueprint::make_array_view(n_matset["volume_fractions"]), + axom::mir::utilities::blueprint::make_array_view(n_matset["sizes"]), + axom::mir::utilities::blueprint::make_array_view(n_matset["offsets"]), + axom::mir::utilities::blueprint::make_array_view(n_matset["indices"])); + +// this->template iteration( +// See if the compiler will infer the right args. + iteration( topologyView, coordsetView, matsetView, + matInfo[i], n_topo, n_coordset, n_fields, n_matset, - n_options, + n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); }); }); + // clangformat-on } } + + // TODO: we need to build the new matset. } } /** * \brief Perform one round of material clipping. */ - template + template void iteration(const ITopoView &topoView, const ICoordsetView &coordsetView, const IMatsetView &matsetView, + + const axom::mir::views::Material ¤tMat, + const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, const conduit::Node &n_matset, + const conduit::Node &n_options, + conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields, conduit::Node &n_newMatset) { + namespace bputils = axom::mir::utilities::blueprint; + using FloatType = typename MatsetView::FloatType; + constexpr auto floatTypeID = bputils::cpp2conduit::id; + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + + const std::string zoneCenteredField("__equiz__zoneMatField"); + const std::string nodeCenteredField("__equiz__nodeMatField"); + const auto nzones = topoView.numberOfZones(); // Make a node to zone relation. @@ -160,7 +201,7 @@ class EquiZAlgorithm : public MIRAlgorithm // Recenter the field to nodal using the node to zone relation. conduit::Node &nodeMatField = tmpFields[nodeCenteredField]; - RecenterField recenter; + axom::mir::RecenterField recenter; recenter.execute(zoneMatField, relation, nodeMatField); /** @@ -169,27 +210,39 @@ class EquiZAlgorithm : public MIRAlgorithm that lets that behavior be customizable so I can override it here. */ - conduit::Node clippedMesh; - conduit::Node &clipTopo = clippedMesh[n_newTopo.path()]; - conduit::Node &clipCoordset = clippedMesh[n_newCoordset.path()]; - conduit::Node &clipFields = clippedMesh["fields"]; - // Now, clip the topology using the nodal field. - conduit::Node options, clippedMesh; + conduit::Node options; options["inside"] = 1; options["outside"] = 1; options["clipField"] = nodeCenteredField; options["clipValue"] = 0.5; - ClipField clipper(topoView, coordsetView); - clipper.execute(n_topo, n_coordset, tmpFields, options, + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(n_topo, n_coordset, tmpFields, options, n_newTopo, n_newCoordset, n_newFields); // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. - +#if 1 + conduit::Node mesh; + mesh[n_newTopo.path()].set_external(n_newTopo); + mesh[n_newCoordset.path()].set_external(n_newCoordset); + mesh[n_newFields.path()].set_external(n_newFields); + mesh[n_newMatset.path()].set_external(n_newMatset); + std::stringstream ss; + ss << "debug_equiz_" << currentMat.number; + conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); +#endif + + n_newFields.remove(zoneCenteredField); + n_newFields.remove(nodeCenteredField); } -} -//------------------------------------------------------------------------------ +private: + TopologyView m_topologyView; + CoordsetView m_coordsetView; + MatsetView m_matsetView; +}; +//------------------------------------------------------------------------------ +#endif } // end namespace mir } // end namespace axom -#endif + #endif diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index 9eb30430ca..4bbb5cb689 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -52,6 +52,162 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) add_distance(mesh); } +void make_unibuffer(const std::vector &vfA, + const std::vector &vfB, + const std::vector &vfC, + conduit::Node &matset) +{ + std::vector material_ids; + std::vector volume_fractions; + std::vector sizes(vfA.size(), 0); + std::vector offsets(vfA.size(), 0); + std::vector indices; + + int offset = 0; + for(size_t i = 0; i < vfA.size(); i++) + { + if(vfA[i] > 0.) + { + indices.push_back(static_cast(indices.size())); + material_ids.push_back(0); + volume_fractions.push_back(vfA[i]); + sizes[i]++; + } + if(vfB[i] > 0.) + { + indices.push_back(static_cast(indices.size())); + material_ids.push_back(1); + volume_fractions.push_back(vfB[i]); + sizes[i]++; + } + if(vfC[i] > 0.) + { + indices.push_back(static_cast(indices.size())); + material_ids.push_back(2); + volume_fractions.push_back(vfC[i]); + sizes[i]++; + } + + offsets[i] = offset; + offset += sizes[i]; + } + + matset["material_ids"].set(material_ids); + matset["volume_fractions"].set(volume_fractions); + matset["sizes"].set(sizes); + matset["offsets"].set(offsets); + matset["indices"].set(indices); +} + + +template +void make_matset(const std::string &type, const std::string &topoName, + const Dimensions &dims, conduit::Node &mesh) +{ + constexpr int sampling = 10; + int midx = sampling * dims[0] / 2; + int midy = sampling * dims[1] / 2; + + int ksize = 0; + int nelements = 1; + for(int i = 0; i < dims.size(); i++) + { + nelements *= dims[i]; + ksize = (i == 0) ? 1 : (ksize * dims[i]); + } + std::vector matA(nelements); + std::vector matB(nelements); + std::vector matC(nelements); + + int nk = (dims.size() == 3) ? dims[2] : 1; + for(int k = 0; k < nk; k++) + { + for(int j = 0; j < sampling * dims[1]; j++) + { + const int jele = j / sampling; + for(int i = 0; i < sampling * dims[0]; i++) + { + const int iele = i / sampling; + + bool gt1 = j >= midy; + bool gt2 = j >= ((3./2.)*(i - midx) + midy); + bool gt3 = j >= ((-2./5.)*(i - midx) + midy); + + int index = k * ksize + jele * dims[0] + iele; + + if(gt1 && gt2) + matA[index]++; + else if(!gt1 && !gt3) + matB[index]++; + else + matC[index]++; + } + } + } + + std::vector vfA(nelements); + std::vector vfB(nelements); + std::vector vfC(nelements); + constexpr float s2 = static_cast(sampling * sampling); + for(int k = 0; k < nk; k++) + { + for(int j = 0; j < sampling * dims[1]; j++) + { + const int jele = j / sampling; + for(int i = 0; i < sampling * dims[0]; i++) + { + const int iele = i / sampling; + int index = k * ksize + jele * dims[0] + iele; + + vfA[index] = static_cast(matA[index]) / s2; + vfB[index] = static_cast(matB[index]) / s2; + vfC[index] = static_cast(matC[index]) / s2; + } + } + } + matA.clear(); + matB.clear(); + matC.clear(); + +#if 1 + // Debugging. Add the vfs as fields. + mesh["fields/vfA/topology"] = topoName; + mesh["fields/vfA/association"] = "element"; + mesh["fields/vfA/values"].set(vfA); + + mesh["fields/vfB/topology"] = topoName; + mesh["fields/vfB/association"] = "element"; + mesh["fields/vfB/values"].set(vfB); + + mesh["fields/vfC/topology"] = topoName; + mesh["fields/vfC/association"] = "element"; + mesh["fields/vfC/values"].set(vfC); +#endif + + conduit::Node &matset = mesh["matsets/mat"]; + matset["topology"] = topoName; + matset["material_map/A"] = 0; + matset["material_map/B"] = 1; + matset["material_map/C"] = 2; + + // produce different material types. + if(type == "unibuffer") + { + make_unibuffer(vfA, vfB, vfC, matset); + } + // TODO: write these other cases. + else if(type == "multibuffer") + { + } + else if(type == "element_dominant") + { + } + else if(type == "material_dominant") + { + } +} + + void mixed3d(conduit::Node &mesh) { // clang-format off diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 982879b3e9..58da07ba29 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -12,6 +12,50 @@ #include +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on + +//------------------------------------------------------------------------------ + +// Uncomment to generate baselines +#define AXOM_TESTING_GENERATE_BASELINES + +// Uncomment to save visualization files for debugging (when making baselines) +#define AXOM_TESTING_SAVE_VISUALIZATION + +// Include after seq_exec is defined. +#include "axom/mir/tests/mir_testing_helpers.hpp" + +std::string baselineDirectory() +{ + return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), + "mir_views"); +} +//------------------------------------------------------------------------------ + TEST(mir_views, shape2conduitName) { EXPECT_EQ(axom::mir::views::LineShape::name(), "line"); @@ -124,6 +168,104 @@ TEST(mir_views, strided_structured) }); } +//------------------------------------------------------------------------------ +template +void braid2d_mat_test(const std::string &type, const std::string &mattype, const std::string &name) +{ + using Indexing = axom::mir::views::StructuredIndexing; + using TopoView = axom::mir::views::StructuredTopologyView; + using CoordsetView = axom::mir::views::UniformCoordsetView; + const int allocatorID = axom::execution_space::allocatorID(); + + axom::StackArray dims {10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); +#endif + + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/material_ids"]), + axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/sizes"]), + axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/offsets"]), + axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/indices"])); + + constexpr int MATA = 0; + constexpr int MATB = 1; + constexpr int MATC = 2; + const int matids[] = {0, 36, 40}; + // clang-format off + const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*mats in zone*/ MATB, -1, -1, + /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*mats in zone*/ MATA, MATB, -1, + /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*mats in zone*/ MATA, MATB, MATC}; + // clang-format on + constexpr int nZones = sizeof(matids) / sizeof(int); + + // Get matids into matidsView for device. + axom::Array matidsArray(nZones, nZones, allocatorID); + axom::copy(matidsArray.data(), matids, sizeof(int) * nZones); + auto matidsView = matidsArray.view(); + + // Allocate results array on device. + constexpr int nResults = sizeof(results) / sizeof(int); + axom::Array resultsArrayDevice(nResults, nResults, allocatorID); + auto resultsView = resultsArrayDevice.view(); + + // host-safe tests. + EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); + EXPECT_EQ(matsetView.numberOfMaterials(), 3); + + // Fill in resultsView on the device. + constexpr int nResultsPerZone = nResults / nZones; + axom::for_all(3, AXOM_LAMBDA(auto index) + { + resultsView[nResultsPerZone * index + 0] = matsetView.zoneContainsMaterial(matidsView[index], MATA) ? 1 : 0; + resultsView[nResultsPerZone * index + 1] = matsetView.zoneContainsMaterial(matidsView[index], MATB) ? 1 : 0; + resultsView[nResultsPerZone * index + 2] = matsetView.zoneContainsMaterial(matidsView[index], MATC) ? 1 : 0; + resultsView[nResultsPerZone * index + 3] = matsetView.numberOfMaterials(matidsView[index]); + + MatsetView::IDList ids; + MatsetView::VFList vfs; + matsetView.zoneMaterials(matidsView[index], ids, vfs); + for(int i = 0; i < 3; i++) + { + resultsView[nResultsPerZone * index + 4 + i] = (i < ids.size()) ? ids[i] : -1; + } + }); + // Get containsView data to the host and compare results + std::vector resultsHost(nResults); + axom::copy(resultsHost.data(), resultsView.data(), sizeof(int) * nResults); + for(int i = 0; i < nResults; i++) + { + EXPECT_EQ(results[i], resultsHost[i]); + } +} + +TEST(mir_views, matset_unibuffer) +{ + braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); + +#if defined(AXOM_USE_OPENMP) + braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); +#endif + +#if defined(AXOM_USE_HIP) + braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); +#endif +} + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 5351357620..c7fb294306 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -7,7 +7,7 @@ #define AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ #include "axom/core.hpp" -#include "axom/mir/views/Shapes.hpp" +//#include "axom/mir/views/Shapes.hpp" #include @@ -59,7 +59,7 @@ MaterialInformation materials(const conduit::Node &matset); indices: [1, 4, 6, 3, 2] */ -template +template class UnibufferMaterialView { public: @@ -70,7 +70,7 @@ class UnibufferMaterialView using IDList = StaticArray; using VFList = StaticArray; - constexpr static size_t MaxMaterials = MAXMATERIALS; + constexpr static axom::IndexType MaxMaterials = MAXMATERIALS; void set(const axom::ArrayView &material_ids, const axom::ArrayView &volume_fractions, @@ -86,26 +86,26 @@ class UnibufferMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfZones() const { return m_sizes.size(); } + axom::IndexType numberOfZones() const { return m_sizes.size(); } AXOM_HOST_DEVICE - size_t getNumberOfMaterials(ZoneIndex zi) const + axom::IndexType numberOfMaterials(ZoneIndex zi) const { assert(zi < m_sizes.size()); return m_sizes[zi]; } AXOM_HOST_DEVICE - void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { assert(zi < m_sizes.size()); ids.clear(); vfs.clear(); - const auto sz = m_sizes[zi]; + const auto sz = static_cast(m_sizes[zi]); const auto offset = m_offsets[zi]; - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) { const auto idx = m_indices[offset + i]; @@ -118,9 +118,9 @@ class UnibufferMaterialView bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { assert(zi < m_sizes.size()); - const auto sz = m_sizes[zi]; + const auto sz = static_cast(m_sizes[zi]); const auto offset = m_offsets[zi]; - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) { const auto idx = m_indices[offset + i]; @@ -133,9 +133,9 @@ class UnibufferMaterialView bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const { assert(zi < m_sizes.size()); - const auto sz = m_sizes[zi]; + const auto sz = static_cast(m_sizes[zi]); const auto offset = m_offsets[zi]; - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) { const auto idx = m_indices[offset + i]; @@ -176,7 +176,7 @@ class UnibufferMaterialView // NOTE: I'm not sure I 100% get this one. -template +template class MultiBufferMaterialView { public: @@ -187,7 +187,7 @@ class MultiBufferMaterialView using IDList = StaticArray; using VFList = StaticArray; - constexpr static size_t MaxMaterials = MAXMATERIALS; + constexpr static axom::IndexType MaxMaterials = MAXMATERIALS; void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) @@ -200,19 +200,19 @@ class MultiBufferMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfZones() const + axom::IndexType numberOfZones() const { - size_t nzones = 0; + axom::IndexType nzones = 0; for(int i = 0; i < m_size; i++) nzones = axom::utilities::max(nzones, m_indices[i].size()); return nzones; } AXOM_HOST_DEVICE - size_t getNumberOfMaterials(ZoneIndex zi) const + axom::IndexType numberOfMaterials(ZoneIndex zi) const { - size_t nmats = 0; - for(size_t i = 0; i < m_size; i++) + axom::IndexType nmats = 0; + for(axom::IndexType i = 0; i < m_size; i++) { if(zi < m_indices[i].size()) { @@ -225,12 +225,12 @@ class MultiBufferMaterialView } AXOM_HOST_DEVICE - void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { ids.clear(); vfs.clear(); - for(size_t i = 0; i < m_size; i++) + for(axom::IndexType i = 0; i < m_size; i++) { if(zi < m_indices[i].size()) { @@ -268,7 +268,7 @@ class MultiBufferMaterialView private: axom::StackArray, MAXMATERIALS> m_values {}; axom::StackArray, MAXMATERIALS> m_indices {}; - size_t m_size {0}; + axom::IndexType m_size {0}; }; /** @@ -284,7 +284,7 @@ class MultiBufferMaterialView b: 1 c: 2 */ -template +template class ElementDominantMaterialView { public: @@ -295,7 +295,7 @@ class ElementDominantMaterialView using IDList = StaticArray; using VFList = StaticArray; - constexpr static size_t MaxMaterials = MAXMATERIALS; + constexpr static axom::IndexType MaxMaterials = MAXMATERIALS; void add(const axom::ArrayView &vfs) { @@ -303,26 +303,26 @@ class ElementDominantMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfZones() const + axom::IndexType numberOfZones() const { return (m_volume_fractions.size() > 0) ? m_volume_fractions[0].size() : 0; } AXOM_HOST_DEVICE - size_t getNumberOfMaterials(ZoneIndex zi) const + axom::IndexType numberOfMaterials(ZoneIndex zi) const { - size_t nmats = 0; + axom::IndexType nmats = 0; if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); - for(size_t i = 0; i < m_volume_fractions.size(); i++) + for(axom::IndexType i = 0; i < m_volume_fractions.size(); i++) nmats += m_volume_fractions[i][zi] > 0. ? 1 : 0; } return nmats; } AXOM_HOST_DEVICE - void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { ids.clear(); vfs.clear(); @@ -330,7 +330,7 @@ class ElementDominantMaterialView if(m_volume_fractions.size() > 0) { assert(zi < m_volume_fractions[0].size()); - for(size_t i = 0; i < m_volume_fractions.size(); i++) + for(axom::IndexType i = 0; i < m_volume_fractions.size(); i++) { if(m_volume_fractions[i][zi] > 0) { @@ -390,7 +390,7 @@ class ElementDominantMaterialView */ /// NOTES: This matset type does not seem so GPU friendly since there is some work to do for some of the queries. -template +template class MaterialDominantMaterialView { public: @@ -401,7 +401,7 @@ class MaterialDominantMaterialView using IDList = StaticArray; using VFList = StaticArray; - constexpr static size_t MaxMaterials = MAXMATERIALS; + constexpr static axom::IndexType MaxMaterials = MAXMATERIALS; void add(const axom::ArrayView &ids, const axom::ArrayView &vfs) @@ -414,14 +414,14 @@ class MaterialDominantMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfZones() + axom::IndexType numberOfZones() { if(m_nzones == 0) { - for(size_t mi = 0; mi < m_size; mi++) + for(axom::IndexType mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) m_nzones = axom::utilities::max(m_nzones, m_element_ids[mi][i]); #if 0 // host-only @@ -439,14 +439,14 @@ class MaterialDominantMaterialView } AXOM_HOST_DEVICE - size_t getNumberOfMaterials(ZoneIndex zi) const + axom::IndexType numberOfMaterials(ZoneIndex zi) const { - size_t nmats = 0; - for(size_t mi = 0; mi < m_size; mi++) + axom::IndexType nmats = 0; + for(axom::IndexType mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); #if 1 - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) { if(m_element_ids[mi][i] == zi) { @@ -468,16 +468,16 @@ class MaterialDominantMaterialView } AXOM_HOST_DEVICE - void getZoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const + void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { ids.clear(); vfs.clear(); - for(size_t mi = 0; mi < m_size; mi++) + for(axom::IndexType mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); #if 1 - for(size_t i = 0; i < sz; i++) + for(axom::IndexType i = 0; i < sz; i++) { if(m_element_ids[mi][i] == zi) { @@ -510,7 +510,7 @@ class MaterialDominantMaterialView bool found = false; #if 1 const auto element_ids = m_element_ids[mat]; - for(size_t i = 0; i < element_ids.size(); i++) + for(axom::IndexType i = 0; i < element_ids.size(); i++) { if(element_ids[i] == zi) { @@ -530,7 +530,7 @@ class MaterialDominantMaterialView bool found = false; #if 1 const auto element_ids = m_element_ids[mat]; - for(size_t i = 0; i < element_ids.size(); i++) + for(axom::IndexType i = 0; i < element_ids.size(); i++) { if(element_ids[i] == zi) { @@ -546,8 +546,8 @@ class MaterialDominantMaterialView private: StackArray, MAXMATERIALS> m_element_ids {}; StackArray, MAXMATERIALS> m_volume_fractions {}; - size_t m_size {0}; - size_t m_nzones {0}; + axom::IndexType m_size {0}; + axom::IndexType m_nzones {0}; }; #if 0 @@ -558,7 +558,7 @@ class MaterialDominantMaterialView /** */ template -axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_t nzones) +axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, axom::IndexType nzones) { // Figure out the number of materials per zone. axom::Array matsPerZone(nzones, nzones, axom::getAllocatorID()); @@ -567,7 +567,7 @@ axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_ { matsPerZone_view[i] = 0; }); - for(size_t mi = 0; mi < m_size; mi++) + for(axom::IndexType mi = 0; mi < m_size; mi++) { auto element_ids_view = m_element_ids[mi].view(); axom::forall(0, nzones, AXOM_LAMBDA(int i) @@ -582,7 +582,7 @@ axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, size_ template axom::Array selectZones(const MaterialDominantMaterialView &view, Predicate &&pred) { - const auto nzones = view.getNumberOfZones(); + const auto nzones = view.numberOfZones(); // Figure out the number of materials per zone. axom::Array matsPerZone = makeMatsPerZone(view, nzones); @@ -594,7 +594,7 @@ axom::Array selectZones(const MaterialDominantMaterialView &view, Predicate { num_selected += pred(matsPerZone_view[i]) ? 1 : 0; }); - size_t outsize = num_selected.get(); + axom::IndexType outsize = num_selected.get(); // Make an offset array that records where each thread can write its data. axom::Array offsets(nzones); @@ -648,10 +648,10 @@ axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex ma Then we could write the algorithm so we do RAJA things but we could make a reducer object for the serial non-RAJA case that does nothing. */ - const size_t nzones = view.getNumberOfZones(); + const axom::IndexType nzones = view.numberOfZones(); using REDUCE_POL = typename axom::execution_space::reduce_policy; - RAJA::ReduceSum num_selected(0); + RAJA::ReduceSum num_selected(0); axom::Array zones(nzones); auto zones_view = zones.view(); axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) @@ -660,7 +660,7 @@ axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex ma zones_view[zi] = haveMat; num_selected += haveMat; }); - size_t outsize = num_selected.get(); + axom::IndexType outsize = num_selected.get(); // Make an offset array that records where each thread can write its data. axom::Array offsets(nzones); @@ -694,7 +694,7 @@ axom::Array selectCleanZones(const UnibufferMaterialView &view) { auto zoneIsClean = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) { - return view.getNumberOfMaterials(zi) == 1; + return view.numberOfMaterials(zi) == 1; }; return selectZones(view, 0, zoneIsClean); } @@ -704,7 +704,7 @@ axom::Array selectMixedZones(const UnibufferMaterialView &view) { auto zoneIsMixed = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) { - return view.getNumberOfMaterials(zi) > 1; + return view.numberOfMaterials(zi) > 1; }; return selectZones(view, 0, zoneIsClean); } From 642a218ac1e8a7a01a243feb36ce9572611cce8c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 9 Aug 2024 15:18:35 -0700 Subject: [PATCH 160/290] Fixes for a matset view --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 1 - src/axom/mir/tests/mir_views.cpp | 92 ++++++++++++---------- src/axom/mir/views/MaterialView.hpp | 18 ++--- 3 files changed, 59 insertions(+), 52 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 15eefcba23..312bf9dbca 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -109,7 +109,6 @@ template void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) { - using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; const std::string type = topo.fetch_existing("type").as_string(); const auto allocatorID = axom::execution_space::allocatorID(); diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 58da07ba29..a4d10f81fb 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -41,10 +41,10 @@ //------------------------------------------------------------------------------ // Uncomment to generate baselines -#define AXOM_TESTING_GENERATE_BASELINES +//#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -#define AXOM_TESTING_SAVE_VISUALIZATION +//#define AXOM_TESTING_SAVE_VISUALIZATION // Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -130,7 +130,7 @@ TEST(mir_views, strided_structured) axom::mir::views::dispatch_structured_topology< axom::mir::views::select_dimensions(2)>( hostMesh["topologies/mesh"], - [&](const std::string &shape, auto topoView) { + [&](const std::string &AXOM_UNUSED_PARAM(shape), auto topoView) { // Traverse the zones in the mesh and check the zone ids. topoView.template for_all_zones( AXOM_LAMBDA(auto zoneIndex, const auto &zone) { @@ -169,43 +169,19 @@ TEST(mir_views, strided_structured) } //------------------------------------------------------------------------------ -template -void braid2d_mat_test(const std::string &type, const std::string &mattype, const std::string &name) +// NOTE: pass by value on purpose. +template +void test_matsetview(MatsetView matsetView, int allocatorID) { - using Indexing = axom::mir::views::StructuredIndexing; - using TopoView = axom::mir::views::StructuredTopologyView; - using CoordsetView = axom::mir::views::UniformCoordsetView; - const int allocatorID = axom::execution_space::allocatorID(); - - axom::StackArray dims {10, 10}; - axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; - - // Create the data - conduit::Node hostMesh, deviceMesh; - axom::mir::testing::data::braid(type, dims, hostMesh); - axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); -#if defined(AXOM_TESTING_SAVE_VISUALIZATION) - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig_yaml", "yaml"); -#endif - - using MatsetView = axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set(axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/material_ids"]), - axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), - axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/sizes"]), - axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/offsets"]), - axom::mir::utilities::blueprint::make_array_view(deviceMesh["matsets/mat/indices"])); - constexpr int MATA = 0; constexpr int MATB = 1; constexpr int MATC = 2; const int matids[] = {0, 36, 40}; + // clang-format off - const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*mats in zone*/ MATB, -1, -1, - /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*mats in zone*/ MATA, MATB, -1, - /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*mats in zone*/ MATA, MATB, MATC}; + const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*ids.size*/ 1, /*mats in zone*/ MATB, -1, -1, + /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*ids.size*/ 2, /*mats in zone*/ MATA, MATB, -1, + /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*ids.size*/ 3, /*mats in zone*/ MATA, MATB, MATC}; // clang-format on constexpr int nZones = sizeof(matids) / sizeof(int); @@ -219,10 +195,6 @@ void braid2d_mat_test(const std::string &type, const std::string &mattype, const axom::Array resultsArrayDevice(nResults, nResults, allocatorID); auto resultsView = resultsArrayDevice.view(); - // host-safe tests. - EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); - EXPECT_EQ(matsetView.numberOfMaterials(), 3); - // Fill in resultsView on the device. constexpr int nResultsPerZone = nResults / nZones; axom::for_all(3, AXOM_LAMBDA(auto index) @@ -232,12 +204,13 @@ void braid2d_mat_test(const std::string &type, const std::string &mattype, const resultsView[nResultsPerZone * index + 2] = matsetView.zoneContainsMaterial(matidsView[index], MATC) ? 1 : 0; resultsView[nResultsPerZone * index + 3] = matsetView.numberOfMaterials(matidsView[index]); - MatsetView::IDList ids; - MatsetView::VFList vfs; + typename MatsetView::IDList ids {}; + typename MatsetView::VFList vfs {}; matsetView.zoneMaterials(matidsView[index], ids, vfs); - for(int i = 0; i < 3; i++) + resultsView[nResultsPerZone * index + 4] = ids.size(); + for(axom::IndexType i = 0; i < 3; i++) { - resultsView[nResultsPerZone * index + 4 + i] = (i < ids.size()) ? ids[i] : -1; + resultsView[nResultsPerZone * index + 5 + i] = (i < ids.size()) ? ids[i] : -1; } }); // Get containsView data to the host and compare results @@ -249,6 +222,41 @@ void braid2d_mat_test(const std::string &type, const std::string &mattype, const } } +//------------------------------------------------------------------------------ +template +void braid2d_mat_test(const std::string &type, const std::string &mattype, const std::string &name) +{ + namespace bputils = axom::mir::utilities::blueprint; + const int allocatorID = axom::execution_space::allocatorID(); + + axom::StackArray dims {10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + + if(type == "unibuffer") + { + // clang-format off + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // clang-format on + EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); + test_matsetview(matsetView, allocatorID); + } +} + TEST(mir_views, matset_unibuffer) { braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index c7fb294306..897497408b 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -86,24 +86,24 @@ class UnibufferMaterialView } AXOM_HOST_DEVICE - axom::IndexType numberOfZones() const { return m_sizes.size(); } + inline axom::IndexType numberOfZones() const { return m_sizes.size(); } AXOM_HOST_DEVICE - axom::IndexType numberOfMaterials(ZoneIndex zi) const + inline axom::IndexType numberOfMaterials(ZoneIndex zi) const { - assert(zi < m_sizes.size()); + assert(zi < numberOfZones()); return m_sizes[zi]; } AXOM_HOST_DEVICE void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { - assert(zi < m_sizes.size()); + assert(zi < numberOfZones()); ids.clear(); vfs.clear(); - const auto sz = static_cast(m_sizes[zi]); + const auto sz = numberOfMaterials(zi); const auto offset = m_offsets[zi]; for(axom::IndexType i = 0; i < sz; i++) { @@ -117,8 +117,8 @@ class UnibufferMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { - assert(zi < m_sizes.size()); - const auto sz = static_cast(m_sizes[zi]); + assert(zi < numberOfZones()); + const auto sz = numberOfMaterials(zi); const auto offset = m_offsets[zi]; for(axom::IndexType i = 0; i < sz; i++) { @@ -132,8 +132,8 @@ class UnibufferMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const { - assert(zi < m_sizes.size()); - const auto sz = static_cast(m_sizes[zi]); + assert(zi < numberOfZones()); + const auto sz = numberOfMaterials(zi); const auto offset = m_offsets[zi]; for(axom::IndexType i = 0; i < sz; i++) { From 06e3eb9c8795c33a956c5b4064c759debaa34b26 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 9 Aug 2024 15:20:06 -0700 Subject: [PATCH 161/290] make style --- src/axom/core/StaticArray.hpp | 13 +++--- src/axom/mir/BlendGroupBuilder.hpp | 1 - src/axom/mir/ClipField.hpp | 26 +++++++++-- src/axom/mir/ClipOptions.hpp | 5 +-- src/axom/mir/EquiZAlgorithm.cpp | 5 +-- src/axom/mir/EquiZAlgorithm.hpp | 4 +- src/axom/mir/MIRAlgorithm.cpp | 34 ++++++++++---- src/axom/mir/MIROptions.hpp | 4 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 2 - src/axom/mir/Options.hpp | 5 +-- src/axom/mir/SelectedZones.hpp | 9 ++-- .../mir/tests/mir_testing_data_helpers.hpp | 21 ++++----- src/axom/mir/tests/mir_views.cpp | 45 +++++++++++-------- 13 files changed, 101 insertions(+), 73 deletions(-) diff --git a/src/axom/core/StaticArray.hpp b/src/axom/core/StaticArray.hpp index 6efa38d837..190ad31d19 100644 --- a/src/axom/core/StaticArray.hpp +++ b/src/axom/core/StaticArray.hpp @@ -24,15 +24,18 @@ class StaticArray : public StackArray { public: AXOM_HOST_DEVICE - constexpr size_t capacity() const { return static_cast(N); } + constexpr axom::IndexType capacity() const + { + return static_cast(N); + } AXOM_HOST_DEVICE - size_t size() const { return m_size; } + axom::IndexType size() const { return m_size; } AXOM_HOST_DEVICE void push_back(const T &e) { - if(m_size + 1 < capacity()) StackArray::m_data[m_size++] = e; + if(m_size + 1 <= capacity()) this->m_data[m_size++] = e; } AXOM_HOST_DEVICE @@ -47,11 +50,11 @@ class StaticArray : public StackArray AXOM_HOST_DEVICE void fill(const T &e) { - for(size_t i = 0; i < capacity(); i++) StackArray::m_data[i] = e; + for(size_t i = 0; i < capacity(); i++) this->m_data[i] = e; } private: - size_t m_size {0}; + axom::IndexType m_size {0}; }; } /* namespace axom */ diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index f2083ffe48..e3faed2e28 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -14,7 +14,6 @@ namespace mir { namespace clipping { - /** * \brief This class encapsulates the logic for building blend groups. * diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 1168e81d2f..09ffbf8399 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -285,7 +285,9 @@ class ClipField // Make the selected zones and get the size. ClipOptions opts(n_options); - axom::mir::SelectedZones selectedZones(m_topologyView.numberOfZones(), n_options); + axom::mir::SelectedZones selectedZones( + m_topologyView.numberOfZones(), + n_options); const auto nzones = selectedZones.view().size(); // Get the clip field. Make sure it is double. That lets us make less code down the line. @@ -374,7 +376,13 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, selectedZones, clipFieldView); + computeSizes(clipTableViews, + builder, + zoneData, + fragmentData, + opts, + selectedZones, + clipFieldView); computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); @@ -404,7 +412,12 @@ class ClipField blendGroupStart.view(), blendIds.view(), blendCoeff.view()); - makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones, clipFieldView); + makeBlendGroups(clipTableViews, + builder, + zoneData, + opts, + selectedZones, + clipFieldView); // Make the blend groups unique axom::Array uNames; @@ -467,7 +480,12 @@ class ClipField fieldsToProcess, n_fields, n_newFields); - makeOriginalElements(fragmentData, opts, selectedZones, n_fields, n_newTopo, n_newFields); + makeOriginalElements(fragmentData, + opts, + selectedZones, + n_fields, + n_newTopo, + n_newFields); } private: diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/ClipOptions.hpp index 0af2359b02..072cf0d4d9 100644 --- a/src/axom/mir/ClipOptions.hpp +++ b/src/axom/mir/ClipOptions.hpp @@ -13,7 +13,6 @@ namespace mir { namespace clipping { - /** * \brief This class provides a kind of schema over the clipping options, as well * as default values, and some utilities functions. @@ -26,8 +25,7 @@ class ClipOptions : public axom::mir::Options * * \param options The node that contains the clipping options. */ - ClipOptions(const conduit::Node &options) : axom::mir::Options(options) - { } + ClipOptions(const conduit::Node &options) : axom::mir::Options(options) { } /** * \brief Return the name of the field used for clipping. @@ -82,6 +80,7 @@ class ClipOptions : public axom::mir::Options ? (options().fetch_existing("outside").to_int() > 0) : false; } + private: /// Access the base class' options. const conduit::Node &options() const { return this->m_options; } diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp index ab4fb995ed..7867dd7554 100644 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ b/src/axom/mir/EquiZAlgorithm.cpp @@ -8,8 +8,5 @@ namespace axom { namespace mir -{ - - -} // namespace mir +{ } // namespace mir } // namespace axom diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 8aaddbaa90..28a12a3c6a 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -220,7 +220,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm clipper.execute(n_topo, n_coordset, tmpFields, options, n_newTopo, n_newCoordset, n_newFields); // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. -#if 1 + #if 1 conduit::Node mesh; mesh[n_newTopo.path()].set_external(n_newTopo); mesh[n_newCoordset.path()].set_external(n_newCoordset); @@ -229,7 +229,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::stringstream ss; ss << "debug_equiz_" << currentMat.number; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); -#endif + #endif n_newFields.remove(zoneCenteredField); n_newFields.remove(nodeCenteredField); diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index 0e1182e214..eacc2b5161 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -14,7 +14,6 @@ namespace axom { namespace mir { - void MIRAlgorithm::execute(const conduit::Node &n_input, const conduit::Node &n_options, conduit::Node &n_output) @@ -39,8 +38,9 @@ void MIRAlgorithm::execute(const conduit::Node &n_input, } } -void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, const conduit::Node &n_options, - conduit::Node &n_newDomain) +void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, + const conduit::Node &n_options, + conduit::Node &n_newDomain) { MIROptions options(n_options); @@ -51,12 +51,12 @@ void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, const conduit::No const conduit::Node &n_matsets = n_domain.fetch_existing("matsets"); const conduit::Node &n_matset = n_matsets.fetch_existing(matset); const conduit::Node *n_topo = - conduit::blueprint::mesh::utils::find_reference_node(n_matset, "topology"); + conduit::blueprint::mesh::utils::find_reference_node(n_matset, "topology"); SLIC_ASSERT(n_topo != nullptr); // Which coordset is used by that topology? const conduit::Node *n_coordset = - conduit::blueprint::mesh::utils::find_reference_node(*n_topo, "coordset"); + conduit::blueprint::mesh::utils::find_reference_node(*n_topo, "coordset"); SLIC_ASSERT(n_coordset != nullptr); // Get the names of the output items. @@ -75,21 +75,37 @@ void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, const conduit::No if(n_domain.has_path("fields")) { conduit::Node &newFields = n_newDomain["fields"]; - executeDomain(*n_topo, *n_coordset, n_domain["fields"], n_matset, n_options, newTopo, newCoordset, newFields, newMatset); + executeDomain(*n_topo, + *n_coordset, + n_domain["fields"], + n_matset, + n_options, + newTopo, + newCoordset, + newFields, + newMatset); } else { conduit::Node n_fields, newFields; - executeDomain(*n_topo, *n_coordset, n_fields, n_matset, n_options, newTopo, newCoordset, newFields, newMatset); + executeDomain(*n_topo, + *n_coordset, + n_fields, + n_matset, + n_options, + newTopo, + newCoordset, + newFields, + newMatset); } } -void MIRAlgorithm::copyState(const conduit::Node &srcState, conduit::Node &destState) const +void MIRAlgorithm::copyState(const conduit::Node &srcState, + conduit::Node &destState) const { for(conduit::index_t i = 0; i < srcState.number_of_children(); i++) destState[srcState[i].name()].set(srcState[i]); } - } // namespace mir } // namespace axom diff --git a/src/axom/mir/MIROptions.hpp b/src/axom/mir/MIROptions.hpp index e403f8a814..39aec62504 100644 --- a/src/axom/mir/MIROptions.hpp +++ b/src/axom/mir/MIROptions.hpp @@ -11,7 +11,6 @@ namespace axom { namespace mir { - /** * \brief This class provides a kind of schema over the MIR options, as well * as default values, and some utilities functions. @@ -24,8 +23,7 @@ class MIROptions : public axom::mir::Options * * \param options The node that contains the clipping options. */ - MIROptions(const conduit::Node &options) : axom::mir::Options(options) - { } + MIROptions(const conduit::Node &options) : axom::mir::Options(options) { } /** * \brief Get the name of the matset on which we'll operate. diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 312bf9dbca..99df2cab23 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -21,7 +21,6 @@ namespace axom { namespace mir { - /** * \brief Build an o2m relation that lets us look up the zones for a node. */ @@ -117,7 +116,6 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); - conduit::Node &n_zones = relation["zones"]; conduit::Node &n_sizes = relation["sizes"]; conduit::Node &n_offsets = relation["offsets"]; diff --git a/src/axom/mir/Options.hpp b/src/axom/mir/Options.hpp index 545e8fc9f7..764fbf5d73 100644 --- a/src/axom/mir/Options.hpp +++ b/src/axom/mir/Options.hpp @@ -14,7 +14,6 @@ namespace axom { namespace mir { - // IDEA: maybe use inlet for this stuff. /** @@ -30,9 +29,7 @@ class Options * \param nzones The total number of zones in the associated topology. * \param options The node that contains the clipping options. */ - Options(const conduit::Node &options) - :m_options(options) - { } + Options(const conduit::Node &options) : m_options(options) { } /** * \brief Return the name of the topology to make in the output. diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index 4d4f3d3712..b670b1d325 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -13,7 +13,6 @@ namespace axom { namespace mir { - /** * \brief This class provides a kind of schema over options, as well * as default values, and some utilities functions. @@ -28,8 +27,9 @@ class SelectedZones * \param nzones The total number of zones in the associated topology. * \param options The node that contains the clipping options. */ - SelectedZones(axom::IndexType nzones, const conduit::Node &options) : m_selectedZones(), - m_selectedZonesView() + SelectedZones(axom::IndexType nzones, const conduit::Node &options) + : m_selectedZones() + , m_selectedZonesView() { buildSelectedZones(nzones, options); } @@ -101,8 +101,7 @@ class SelectedZones else { // Select all zones. - m_selectedZones = - axom::Array(nzones, nzones, allocatorID); + m_selectedZones = axom::Array(nzones, nzones, allocatorID); auto szView = m_selectedZonesView = m_selectedZones.view(); axom::for_all( nzones, diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index 4bbb5cb689..1e26240559 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -99,10 +99,11 @@ void make_unibuffer(const std::vector &vfA, matset["indices"].set(indices); } - template -void make_matset(const std::string &type, const std::string &topoName, - const Dimensions &dims, conduit::Node &mesh) +void make_matset(const std::string &type, + const std::string &topoName, + const Dimensions &dims, + conduit::Node &mesh) { constexpr int sampling = 10; int midx = sampling * dims[0] / 2; @@ -130,8 +131,8 @@ void make_matset(const std::string &type, const std::string &topoName, const int iele = i / sampling; bool gt1 = j >= midy; - bool gt2 = j >= ((3./2.)*(i - midx) + midy); - bool gt3 = j >= ((-2./5.)*(i - midx) + midy); + bool gt2 = j >= ((3. / 2.) * (i - midx) + midy); + bool gt3 = j >= ((-2. / 5.) * (i - midx) + midy); int index = k * ksize + jele * dims[0] + iele; @@ -197,17 +198,13 @@ void make_matset(const std::string &type, const std::string &topoName, } // TODO: write these other cases. else if(type == "multibuffer") - { - } + { } else if(type == "element_dominant") - { - } + { } else if(type == "material_dominant") - { - } + { } } - void mixed3d(conduit::Node &mesh) { // clang-format off diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index a4d10f81fb..147cb6d3a7 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -51,8 +51,7 @@ std::string baselineDirectory() { - return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), - "mir_views"); + return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), "mir_views"); } //------------------------------------------------------------------------------ @@ -197,22 +196,28 @@ void test_matsetview(MatsetView matsetView, int allocatorID) // Fill in resultsView on the device. constexpr int nResultsPerZone = nResults / nZones; - axom::for_all(3, AXOM_LAMBDA(auto index) - { - resultsView[nResultsPerZone * index + 0] = matsetView.zoneContainsMaterial(matidsView[index], MATA) ? 1 : 0; - resultsView[nResultsPerZone * index + 1] = matsetView.zoneContainsMaterial(matidsView[index], MATB) ? 1 : 0; - resultsView[nResultsPerZone * index + 2] = matsetView.zoneContainsMaterial(matidsView[index], MATC) ? 1 : 0; - resultsView[nResultsPerZone * index + 3] = matsetView.numberOfMaterials(matidsView[index]); - - typename MatsetView::IDList ids {}; - typename MatsetView::VFList vfs {}; - matsetView.zoneMaterials(matidsView[index], ids, vfs); - resultsView[nResultsPerZone * index + 4] = ids.size(); - for(axom::IndexType i = 0; i < 3; i++) - { - resultsView[nResultsPerZone * index + 5 + i] = (i < ids.size()) ? ids[i] : -1; - } - }); + axom::for_all( + 3, + AXOM_LAMBDA(auto index) { + resultsView[nResultsPerZone * index + 0] = + matsetView.zoneContainsMaterial(matidsView[index], MATA) ? 1 : 0; + resultsView[nResultsPerZone * index + 1] = + matsetView.zoneContainsMaterial(matidsView[index], MATB) ? 1 : 0; + resultsView[nResultsPerZone * index + 2] = + matsetView.zoneContainsMaterial(matidsView[index], MATC) ? 1 : 0; + resultsView[nResultsPerZone * index + 3] = + matsetView.numberOfMaterials(matidsView[index]); + + typename MatsetView::IDList ids {}; + typename MatsetView::VFList vfs {}; + matsetView.zoneMaterials(matidsView[index], ids, vfs); + resultsView[nResultsPerZone * index + 4] = ids.size(); + for(axom::IndexType i = 0; i < 3; i++) + { + resultsView[nResultsPerZone * index + 5 + i] = + (i < ids.size()) ? ids[i] : -1; + } + }); // Get containsView data to the host and compare results std::vector resultsHost(nResults); axom::copy(resultsHost.data(), resultsView.data(), sizeof(int) * nResults); @@ -224,7 +229,9 @@ void test_matsetview(MatsetView matsetView, int allocatorID) //------------------------------------------------------------------------------ template -void braid2d_mat_test(const std::string &type, const std::string &mattype, const std::string &name) +void braid2d_mat_test(const std::string &type, + const std::string &mattype, + const std::string &name) { namespace bputils = axom::mir::utilities::blueprint; const int allocatorID = axom::execution_space::allocatorID(); From db64fac4cbbabf8a8bc1c06fcc2ea06c4889509f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 9 Aug 2024 18:26:35 -0700 Subject: [PATCH 162/290] wrote a test for NodeToZoneRelationBuilder. --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 127 ++++++++--------- src/axom/mir/blueprint_utilities.hpp | 15 +- src/axom/mir/tests/mir_clipfield.cpp | 27 ---- src/axom/mir/tests/mir_testing_helpers.hpp | 27 ++++ src/axom/mir/tests/mir_utilities.cpp | 130 ++++++++++++++++++ src/axom/mir/tests/mir_views.cpp | 49 ++----- .../views/dispatch_rectilinear_topology.hpp | 15 +- 7 files changed, 246 insertions(+), 144 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 99df2cab23..167b83f0b3 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -23,6 +23,8 @@ namespace mir { /** * \brief Build an o2m relation that lets us look up the zones for a node. + * + * \note The zone list for each point is not sorted. */ template class NodeToZoneRelationBuilder @@ -49,68 +51,62 @@ class NodeToZoneRelationBuilder template void buildRelation(const ViewType &nodes_view, ViewType &zones_view, - ViewType &offsets_view) const; + ViewType &offsets_view) const + { + assert(nodes_view.size() == zones_view.size()); + + using loop_policy = typename axom::execution_space::loop_policy; + using value_type = typename ViewType::value_type; + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a copy of the nodes that we'll use as keys. + const auto n = nodes_view.size(); + axom::Array keys(n, n, allocatorID); + auto keys_view = keys.view(); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { keys_view[i] = nodes_view[i]; }); + + // Sort the keys, zones in place. This sorts the zones_view which we want for output. + RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), + RAJA::make_span(zones_view.data(), n)); + + // Make a mask array for where differences occur. + axom::Array mask(n, n, allocatorID); + auto mask_view = mask.view(); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + mask_view[i] = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; + }); + + // Do a scan on the mask array to build an offset array. + axom::Array dest_offsets(n, n, allocatorID); + auto dest_offsets_view = dest_offsets.view(); + axom::exclusive_scan(mask_view, dest_offsets_view); + + // Build the offsets to each node's zone ids. + axom::for_all( + offsets_view.size(), + AXOM_LAMBDA(axom::IndexType i) { offsets_view[i] = 0; }); + + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + if(mask_view[i]) + { + offsets_view[dest_offsets_view[i]] = i; + } + }); + } }; -template -template -void NodeToZoneRelationBuilder::buildRelation(const ViewType &nodes_view, - ViewType &zones_view, - ViewType &offsets_view) const -{ - assert(nodes_view.size() == zones_view.size()); - - using loop_policy = typename axom::execution_space::loop_policy; - const int allocatorID = axom::execution_space::allocatorID(); - - // Make a copy of the nodes that we'll use as keys. - const auto n = nodes_view.size(); - axom::Array keys(n, n, allocatorID); - auto keys_view = keys.view(); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { keys_view[i] = nodes_view[i]; }); - - // Sort the keys, zones in place. This sorts the zones_view which we want for output. - RAJA::sort_pairs(RAJA::make_span(keys_view, n), - RAJA::make_span(zones_view, n)); - - // Make a mask array for where differences occur. - axom::Array mask(n, n, allocatorID); - auto mask_view = mask.view(); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { - mask_view[i] = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; - }); - - // Do a scan on the mask array to build an offset array. - axom::Array dest_offsets(n, n, allocatorID); - auto dest_offsets_view = dest_offsets.view(); - axom::exclusive_scan(mask_view, dest_offsets_view); - - // Build the offsets to each node's zone ids. - axom::for_all( - offsets_view.size(), - AXOM_LAMBDA(axom::IndexType i) { offsets_view[i] = 0; }); - - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { - if(mask_view[i]) - { - offsets_view[dest_offsets_view[i]] = i; - } - }); -} template void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) { - using reduce_policy = typename axom::execution_space::reduce_policy; const std::string type = topo.fetch_existing("type").as_string(); - const auto allocatorID = axom::execution_space::allocatorID(); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. utilities::blueprint::ConduitAllocateThroughAxom c2a; @@ -123,8 +119,8 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, n_sizes.set_allocator(conduitAllocatorID); n_offsets.set_allocator(conduitAllocatorID); - const conduit::Node *coordset = - conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + SLIC_ASSERT(coordset != nullptr); if(type == "unstructured") { @@ -139,7 +135,12 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, if(shape.is_polyhedral()) { - views::dispatch_unstructured_polyhedral_topology(topo, [&](auto topoView) { +#if 0 +// DEBUG THIS LATER + using reduce_policy = typename axom::execution_space::reduce_policy; + const auto allocatorID = axom::execution_space::allocatorID(); + + views::dispatch_unstructured_polyhedral_topology(topo, [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { const auto nzones = topoView.numberOfZones(); axom::Array sizes(nzones, nzones, allocatorID); auto sizes_view = sizes.view(); @@ -189,7 +190,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); + this->template buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. axom::for_all( @@ -201,6 +202,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, }); }); }); +#endif } else if(shape.is_polygonal()) { @@ -238,7 +240,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); + this->template buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. axom::for_all( @@ -274,8 +276,8 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, zonesView[index] = index / nodesPerShape; }); // Make the relation, outputting into the zonesView and offsetsView. - // using ViewType = decltype(connectivityView); - buildRelation(connectivityView, zonesView, offsetsView); + using ViewType = decltype(connectivityView); + this->template buildRelation(connectivityView, zonesView, offsetsView); // Compute sizes from offsets. axom::for_all( @@ -299,7 +301,8 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, mesh); // Recurse using the unstructured mesh. - execute(mesh["newtopo"], relation); + execute(mesh.fetch_existing("topologies/newtopo"), + relation); } } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 0020cfa6ce..420eb4f5a8 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -321,9 +321,8 @@ void to_unstructured(const conduit::Node &topo, n_newsizes.set_allocator(c2a.getConduitAllocatorID()); n_newoffsets.set_allocator(c2a.getConduitAllocatorID()); - views::dispatch_structured_topologies( + axom::mir::views::dispatch_structured_topologies( topo, - coordset, [&](const std::string &shape, auto &topoView) { int ptsPerZone = 2; if(shape == "quad") @@ -341,15 +340,9 @@ void to_unstructured(const conduit::Node &topo, n_newoffsets.set(conduit::DataType::index_t(nzones)); // Make views for the mesh data. - axom::ArrayView connView( - static_cast(n_newconn.data_ptr()), - connSize); - axom::ArrayView sizesView( - static_cast(n_newsizes.data_ptr()), - nzones); - axom::ArrayView offsetsView( - static_cast(n_newoffsets.data_ptr()), - nzones); + auto connView = make_array_view(n_newconn); + auto sizesView = make_array_view(n_newsizes); + auto offsetsView = make_array_view(n_newoffsets); // Fill in the new connectivity. topoView.template for_all_zones( diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index a5a75fafce..78b471d2bb 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -13,32 +13,6 @@ #include #include -// clang-format off -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - using seq_exec = axom::SEQ_EXEC; - - #if defined(AXOM_USE_OPENMP) - using omp_exec = axom::OMP_EXEC; - #else - using omp_exec = seq_exec; - #endif - - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - #else - using cuda_exec = seq_exec; - #endif - - #if defined(AXOM_USE_HIP) - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - #else - using hip_exec = seq_exec; - #endif -#endif -// clang-format on - //------------------------------------------------------------------------------ // Uncomment to generate baselines @@ -47,7 +21,6 @@ // Uncomment to save visualization files for debugging (when making baselines) //#define AXOM_TESTING_SAVE_VISUALIZATION -// Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" std::string baselineDirectory() diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 326b28f977..ae22515cc3 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -13,6 +13,33 @@ #include #include +//------------------------------------------------------------------------------ +// clang-format off +#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) + using seq_exec = axom::SEQ_EXEC; + + #if defined(AXOM_USE_OPENMP) + using omp_exec = axom::OMP_EXEC; + #else + using omp_exec = seq_exec; + #endif + + #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + #else + using cuda_exec = seq_exec; + #endif + + #if defined(AXOM_USE_HIP) + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + #else + using hip_exec = seq_exec; + #endif +#endif +// clang-format on + //------------------------------------------------------------------------------ // Define better names for the execution spaces. This header needs to be included // after the ExecSpace types are defined. diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index e7df190a48..d6423560c3 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -11,6 +11,11 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" +#include "axom/mir/tests/mir_testing_helpers.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +#include + namespace mir = axom::mir; //---------------------------------------------------------------------- @@ -102,6 +107,131 @@ TEST(mir_compute_averages, point_value) //---------------------------------------------------------------------- +template +void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) +{ + namespace bputils = axom::mir::utilities::blueprint; + + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); +#if 0 + // Print the results. + conduit::Node options; + options["num_children_threshold"] = 1000; + options["num_elements_threshold"] = 1000; + hostRelation.to_summary_string_stream(std::cout, options); +#endif + // Expected answers + // clang-format off + const int zones[] = { + 0, + 0, 1, + 1, 2, + 2, + 0, 3, // NOTE: these are sorted here + 0, 1, 3, 4, + 1, 2, 4, 5, + 2, 5, + 3, + 3, 4, + 4, 5, + 5 + }; + const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; + const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; + // clang-format on + + // Compare answers. + const auto zonesView = bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizeof(sizes)/sizeof(int)); + EXPECT_EQ(offsetsView.size(), sizeof(offsets)/sizeof(int)); + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + EXPECT_EQ(sizes[i], sizesView[i]); + EXPECT_EQ(offsets[i], offsetsView[i]); + } + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + // Sort the result so we can compare to the expected answer. + IndexT *begin = zonesView.data() + offsetsView[i]; + IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; + std::sort(begin, end); + + for(int j = 0; j < sizesView[i]; j++) + { + EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + } + } +} + +TEST(mir_utilities, node_to_zone_relation_builder_unstructured) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims{{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + + test_node_to_zone_relation_builder(mesh); +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder(mesh); +#endif +} + +TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims{{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + //mesh.print(); + + test_node_to_zone_relation_builder(mesh); +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder(mesh); +#endif +} + int main(int argc, char* argv[]) { int result = 0; diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 147cb6d3a7..f8133f3437 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -12,32 +12,6 @@ #include -// clang-format off -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - using seq_exec = axom::SEQ_EXEC; - - #if defined(AXOM_USE_OPENMP) - using omp_exec = axom::OMP_EXEC; - #else - using omp_exec = seq_exec; - #endif - - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - #else - using cuda_exec = seq_exec; - #endif - - #if defined(AXOM_USE_HIP) - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - #else - using hip_exec = seq_exec; - #endif -#endif -// clang-format on - //------------------------------------------------------------------------------ // Uncomment to generate baselines @@ -46,7 +20,6 @@ // Uncomment to save visualization files for debugging (when making baselines) //#define AXOM_TESTING_SAVE_VISUALIZATION -// Include after seq_exec is defined. #include "axom/mir/tests/mir_testing_helpers.hpp" std::string baselineDirectory() @@ -175,19 +148,19 @@ void test_matsetview(MatsetView matsetView, int allocatorID) constexpr int MATA = 0; constexpr int MATB = 1; constexpr int MATC = 2; - const int matids[] = {0, 36, 40}; + const int zoneids[] = {0, 36, 40}; // clang-format off const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*ids.size*/ 1, /*mats in zone*/ MATB, -1, -1, /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*ids.size*/ 2, /*mats in zone*/ MATA, MATB, -1, /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*ids.size*/ 3, /*mats in zone*/ MATA, MATB, MATC}; // clang-format on - constexpr int nZones = sizeof(matids) / sizeof(int); + constexpr int nZones = sizeof(zoneids) / sizeof(int); - // Get matids into matidsView for device. - axom::Array matidsArray(nZones, nZones, allocatorID); - axom::copy(matidsArray.data(), matids, sizeof(int) * nZones); - auto matidsView = matidsArray.view(); + // Get zoneids into zoneidsView for device. + axom::Array zoneidsArray(nZones, nZones, allocatorID); + axom::copy(zoneidsArray.data(), zoneids, sizeof(int) * nZones); + auto zoneidsView = zoneidsArray.view(); // Allocate results array on device. constexpr int nResults = sizeof(results) / sizeof(int); @@ -200,17 +173,17 @@ void test_matsetview(MatsetView matsetView, int allocatorID) 3, AXOM_LAMBDA(auto index) { resultsView[nResultsPerZone * index + 0] = - matsetView.zoneContainsMaterial(matidsView[index], MATA) ? 1 : 0; + matsetView.zoneContainsMaterial(zoneidsView[index], MATA) ? 1 : 0; resultsView[nResultsPerZone * index + 1] = - matsetView.zoneContainsMaterial(matidsView[index], MATB) ? 1 : 0; + matsetView.zoneContainsMaterial(zoneidsView[index], MATB) ? 1 : 0; resultsView[nResultsPerZone * index + 2] = - matsetView.zoneContainsMaterial(matidsView[index], MATC) ? 1 : 0; + matsetView.zoneContainsMaterial(zoneidsView[index], MATC) ? 1 : 0; resultsView[nResultsPerZone * index + 3] = - matsetView.numberOfMaterials(matidsView[index]); + matsetView.numberOfMaterials(zoneidsView[index]); typename MatsetView::IDList ids {}; typename MatsetView::VFList vfs {}; - matsetView.zoneMaterials(matidsView[index], ids, vfs); + matsetView.zoneMaterials(zoneidsView[index], ids, vfs); resultsView[nResultsPerZone * index + 4] = ids.size(); for(axom::IndexType i = 0; i < 3; i++) { diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index 02090db779..6dec655815 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -43,13 +43,14 @@ struct make_rectilinear<3> conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; zoneDims[1] = - coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; zoneDims[2] = - coordset->fetch_existing(axes[2]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[2]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -85,11 +86,12 @@ struct make_rectilinear<2> conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; zoneDims[1] = - coordset->fetch_existing(axes[1]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -125,9 +127,10 @@ struct make_rectilinear<1> conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); + const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; zoneDims[0] = - coordset->fetch_existing(axes[0]).dtype().number_of_elements() - 1; + values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } From 284f9e2c7c6eeedf1b26ac0898c9619989645c5a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 9 Aug 2024 18:31:27 -0700 Subject: [PATCH 163/290] make style --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 20 +++++++++------- src/axom/mir/tests/mir_utilities.cpp | 23 +++++++++++-------- .../views/dispatch_rectilinear_topology.hpp | 18 +++++---------- 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 167b83f0b3..ecc8ee41e4 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -58,7 +58,7 @@ class NodeToZoneRelationBuilder using loop_policy = typename axom::execution_space::loop_policy; using value_type = typename ViewType::value_type; const int allocatorID = axom::execution_space::allocatorID(); - + // Make a copy of the nodes that we'll use as keys. const auto n = nodes_view.size(); axom::Array keys(n, n, allocatorID); @@ -77,7 +77,8 @@ class NodeToZoneRelationBuilder axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { - mask_view[i] = (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; + mask_view[i] = + (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; }); // Do a scan on the mask array to build an offset array. @@ -101,7 +102,6 @@ class NodeToZoneRelationBuilder } }; - template void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node &relation) @@ -119,7 +119,8 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, n_sizes.set_allocator(conduitAllocatorID); n_offsets.set_allocator(conduitAllocatorID); - const conduit::Node *coordset = conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); + const conduit::Node *coordset = + conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); SLIC_ASSERT(coordset != nullptr); if(type == "unstructured") @@ -240,7 +241,9 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, zonesView, offsetsView); + this->template buildRelation(connectivityView, + zonesView, + offsetsView); // Compute sizes from offsets. axom::for_all( @@ -277,7 +280,9 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, }); // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, zonesView, offsetsView); + this->template buildRelation(connectivityView, + zonesView, + offsetsView); // Compute sizes from offsets. axom::for_all( @@ -301,8 +306,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, mesh); // Recurse using the unstructured mesh. - execute(mesh.fetch_existing("topologies/newtopo"), - relation); + execute(mesh.fetch_existing("topologies/newtopo"), relation); } } diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index d6423560c3..15203efe40 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -153,11 +153,14 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) // clang-format on // Compare answers. - const auto zonesView = bputils::make_array_view(hostRelation["zones"]); - const auto sizesView = bputils::make_array_view(hostRelation["sizes"]); - const auto offsetsView = bputils::make_array_view(hostRelation["offsets"]); - EXPECT_EQ(sizesView.size(), sizeof(sizes)/sizeof(int)); - EXPECT_EQ(offsetsView.size(), sizeof(offsets)/sizeof(int)); + const auto zonesView = + bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = + bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = + bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizeof(sizes) / sizeof(int)); + EXPECT_EQ(offsetsView.size(), sizeof(offsets) / sizeof(int)); for(axom::IndexType i = 0; i < sizesView.size(); i++) { EXPECT_EQ(sizes[i], sizesView[i]); @@ -179,7 +182,7 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) TEST(mir_utilities, node_to_zone_relation_builder_unstructured) { - /* + /* 8---9--10--11 | | | | 4---5---6---7 @@ -187,7 +190,7 @@ TEST(mir_utilities, node_to_zone_relation_builder_unstructured) 0---1---2---3 */ conduit::Node mesh; - axom::StackArray dims{{4, 3}}; + axom::StackArray dims {{4, 3}}; axom::mir::testing::data::braid("quads", dims, mesh); test_node_to_zone_relation_builder(mesh); @@ -206,7 +209,7 @@ TEST(mir_utilities, node_to_zone_relation_builder_unstructured) TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) { - /* + /* 8---9--10--11 | | | | 4---5---6---7 @@ -214,7 +217,7 @@ TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) 0---1---2---3 */ conduit::Node mesh; - axom::StackArray dims{{4, 3}}; + axom::StackArray dims {{4, 3}}; axom::mir::testing::data::braid("rectilinear", dims, mesh); //mesh.print(); @@ -232,7 +235,7 @@ TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) #endif } -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index 6dec655815..deab009c31 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -45,12 +45,9 @@ struct make_rectilinear<3> const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; - zoneDims[0] = - values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = - values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; - zoneDims[2] = - values.fetch_existing(axes[2]).dtype().number_of_elements() - 1; + zoneDims[0] = values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + zoneDims[2] = values.fetch_existing(axes[2]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -88,10 +85,8 @@ struct make_rectilinear<2> const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; - zoneDims[0] = - values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; - zoneDims[1] = - values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; + zoneDims[0] = values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[1] = values.fetch_existing(axes[1]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } @@ -129,8 +124,7 @@ struct make_rectilinear<1> const auto axes = conduit::blueprint::mesh::utils::coordset::axes(*coordset); const conduit::Node &values = coordset->fetch_existing("values"); LogicalIndex zoneDims; - zoneDims[0] = - values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; + zoneDims[0] = values.fetch_existing(axes[0]).dtype().number_of_elements() - 1; return Indexing(zoneDims); } From e143dc8774de034f9f455da1f6941e6e29a1ae5d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 11:17:30 -0700 Subject: [PATCH 164/290] Fix for NodetoZoneRelationBuilder tests --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 3 - src/axom/mir/tests/mir_utilities.cpp | 179 +++++++++++++++--- .../UnstructuredTopologyPolyhedralView.hpp | 25 +-- 3 files changed, 161 insertions(+), 46 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index ecc8ee41e4..f350545752 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -136,8 +136,6 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, if(shape.is_polyhedral()) { -#if 0 -// DEBUG THIS LATER using reduce_policy = typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); @@ -203,7 +201,6 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, }); }); }); -#endif } else if(shape.is_polygonal()) { diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 15203efe40..474440fc3c 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -18,6 +18,14 @@ namespace mir = axom::mir; +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 1000; + options["num_elements_threshold"] = 1000; + n.to_summary_string_stream(std::cout, options); +} + //---------------------------------------------------------------------- TEST(mir_shape_tests, shape_dimesionality) @@ -107,11 +115,44 @@ TEST(mir_compute_averages, point_value) //---------------------------------------------------------------------- -template -void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) +template +void compareRelation(const conduit::Node &hostRelation, + const axom::ArrayView &zones, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) { namespace bputils = axom::mir::utilities::blueprint; + const auto zonesView = + bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = + bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = + bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizes.size()); + EXPECT_EQ(offsetsView.size(), offsets.size()); + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + EXPECT_EQ(sizes[i], sizesView[i]); + EXPECT_EQ(offsets[i], offsetsView[i]); + } + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + // Sort the result so we can compare to the expected answer. + IndexT *begin = zonesView.data() + offsetsView[i]; + IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; + std::sort(begin, end); + + for(int j = 0; j < sizesView[i]; j++) + { + EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + } + } +} + +template +void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) +{ // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); @@ -127,10 +168,7 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); #if 0 // Print the results. - conduit::Node options; - options["num_children_threshold"] = 1000; - options["num_elements_threshold"] = 1000; - hostRelation.to_summary_string_stream(std::cout, options); + printNode(hostRelation); #endif // Expected answers // clang-format off @@ -153,31 +191,10 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) // clang-format on // Compare answers. - const auto zonesView = - bputils::make_array_view(hostRelation["zones"]); - const auto sizesView = - bputils::make_array_view(hostRelation["sizes"]); - const auto offsetsView = - bputils::make_array_view(hostRelation["offsets"]); - EXPECT_EQ(sizesView.size(), sizeof(sizes) / sizeof(int)); - EXPECT_EQ(offsetsView.size(), sizeof(offsets) / sizeof(int)); - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - EXPECT_EQ(sizes[i], sizesView[i]); - EXPECT_EQ(offsets[i], offsetsView[i]); - } - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - // Sort the result so we can compare to the expected answer. - IndexT *begin = zonesView.data() + offsetsView[i]; - IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; - std::sort(begin, end); - - for(int j = 0; j < sizesView[i]; j++) - { - EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); - } - } + compareRelation(hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); } TEST(mir_utilities, node_to_zone_relation_builder_unstructured) @@ -235,6 +252,106 @@ TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) #endif } +template +void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh) +{ + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); +#if 0 + // Print the results. + printNode(hostRelation); +#endif + // Expected answers + // clang-format off + const int zones[] = { + /*node 0*/ 0, + /*node 1*/ 0, 1, + /*node 2*/ 1, + /*node 3*/ 0, 2, + /*node 4*/ 0, 1, 2, 3, + /*node 5*/ 1, 3, + /*node 6*/ 2, + /*node 7*/ 2, 3, + /*node 8*/ 3, + + /*node 9*/ 0, 4, + /*node 10*/ 0, 1, 4, 5, + /*node 11*/ 1, 5, + /*node 12*/ 0, 2, 4, 6, + /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, + /*node 14*/ 1, 3, 5, 7, + /*node 15*/ 2, 6, + /*node 16*/ 2, 3, 6, 7, + /*node 17*/ 3, 7, + + /*node 18*/ 4, + /*node 19*/ 4, 5, + /*node 20*/ 5, + /*node 21*/ 4, 6, + /*node 22*/ 4, 5, 6, 7, + /*node 23*/ 5, 7, + /*node 24*/ 6, + /*node 25*/ 6, 7, + /*node 26*/ 7 + }; + const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, + 2, 4, 2, 4, 8, 4, 2, 4, 2, + 1, 2, 1, 2, 4, 2, 1, 2, 1 + }; + const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, + 16, 18, 22, 24, 28, 36, 40, 42, 46, + 48, 49, 51, 52, 54, 58, 60, 61, 63}; + // clang-format on + + // Compare answers. + compareRelation(hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); +} + +TEST(mir_utilities, node_to_zone_relation_builder_polyhedral) +{ + conduit::Node mesh; + conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); + // Make sure all the types are the same. + conduit::blueprint::mesh::utils::convert(mesh, conduit::DataType::int32(), + std::vector{{ + "topologies/mesh/elements/connectivity", + "topologies/mesh/elements/sizes", + "topologies/mesh/elements/offsets", + "topologies/mesh/subelements/connectivity", + "topologies/mesh/subelements/sizes", + "topologies/mesh/subelements/offsets" + }}); + //printNode(mesh); + + test_node_to_zone_relation_builder_polyhedral(mesh); + +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif +} + int main(int argc, char *argv[]) { int result = 0; diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 6538adcbef..18c837ec16 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -125,7 +125,7 @@ class UnstructuredTopologyPolyhedralView const auto faceIds = getFace(f); for(axom::IndexType i = 0; i < faceIds.size(); i++) { - if(!find(m_ids, nnodes)) + if(!find(m_ids.m_data, nnodes, faceIds[i])) { if(nnodes < MaximumNumberOfIds) m_ids[nnodes++] = faceIds[i]; @@ -137,7 +137,7 @@ class UnstructuredTopologyPolyhedralView } } } - return axom::ArrayView(m_ids.m_data, nnodes); + return ConnectivityView(m_ids.m_data, nnodes); } AXOM_HOST_DEVICE ConnectivityView getFace(int faceIndex) const @@ -153,12 +153,13 @@ class UnstructuredTopologyPolyhedralView } private: - AXOM_HOST_DEVICE bool find(const ConnectivityView *arr, - axom::IndexType n, - ConnectivityView value) const + AXOM_HOST_DEVICE bool find(const ConnectivityType *arr, axom::IndexType n, ConnectivityType value) const { bool found = false; - for(axom::IndexType i = 0; i < n && !found; i++) found = arr[i] == value; + for(axom::IndexType i = 0; i < n && !found; i++) + { + found = arr[i] == value; + } return found; } @@ -171,12 +172,12 @@ class UnstructuredTopologyPolyhedralView using ShapeType = PolyhedronShape; UnstructuredTopologyPolyhedralView( - const axom::ArrayView &subelement_conn, - const axom::ArrayView &subelement_sizes, - const axom::ArrayView &subelement_offsets, - const axom::ArrayView &element_conn, - const axom::ArrayView &element_sizes, - const axom::ArrayView &element_offsets) + const ConnectivityView &subelement_conn, + const ConnectivityView &subelement_sizes, + const ConnectivityView &subelement_offsets, + const ConnectivityView &element_conn, + const ConnectivityView &element_sizes, + const ConnectivityView &element_offsets) : m_data(subelement_conn, subelement_sizes, subelement_offsets, From 52ec6fe12b6bc5bb75e7e5a4b837c6826a716c0d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 11:22:32 -0700 Subject: [PATCH 165/290] Changed namespace --- src/axom/mir/EquiZAlgorithm.hpp | 2 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 6 ++++++ src/axom/mir/RecenterField.hpp | 6 ++++++ src/axom/mir/tests/mir_utilities.cpp | 4 ++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 28a12a3c6a..26d2f5576a 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -176,7 +176,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make a node to zone relation. conduit::Node relation; - axom::mir::NodeToZoneRelationBuilder builder; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder builder; builder.execute(n_topo, relation); // <----------------------- Should this algorithm take a topo view? // Make a shallow copy of the fields that we can modify. diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index f350545752..f0f03f484f 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -21,6 +21,10 @@ namespace axom { namespace mir { +namespace utilities +{ +namespace blueprint +{ /** * \brief Build an o2m relation that lets us look up the zones for a node. * @@ -307,6 +311,8 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, } } +} // end namespace blueprint +} // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index f3d274676a..8bcd04f868 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -17,6 +17,10 @@ namespace axom { namespace mir { +namespace utilities +{ +namespace blueprint +{ /** * \brief Convert a field with one association type to a field of another association type using an o2mrelation. * @@ -125,6 +129,8 @@ void RecenterField::recenterSingleComponent( }); } +} // end namespace blueprint +} // end namespace utilities } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 474440fc3c..3a7461e35a 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -160,7 +160,7 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) // Run the algorithm on the device conduit::Node deviceRelation; - axom::mir::NodeToZoneRelationBuilder n2z; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; n2z.execute(deviceTopo, deviceRelation); // device -> host @@ -262,7 +262,7 @@ void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh // Run the algorithm on the device conduit::Node deviceRelation; - axom::mir::NodeToZoneRelationBuilder n2z; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; n2z.execute(deviceTopo, deviceRelation); // device -> host From c29471700433e30d240986856111cf710fa16ac3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 11:26:20 -0700 Subject: [PATCH 166/290] moved some tests --- .../mir/tests/mir_blueprint_utilities.cpp | 283 ++++++++++++++++-- src/axom/mir/tests/mir_utilities.cpp | 252 ---------------- 2 files changed, 253 insertions(+), 282 deletions(-) diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 4a33af54e3..a7dab0843a 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -6,41 +6,25 @@ #include "gtest/gtest.h" #include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/mir.hpp" -#include "axom/mir/blueprint_utilities.hpp" +//#include "axom/mir/blueprint_utilities.hpp" -#include - -// RAJA -#if defined(AXOM_USE_RAJA) - #include "RAJA/RAJA.hpp" -#endif +#include "axom/mir/tests/mir_testing_helpers.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" -// clang-format off -#if defined (AXOM_USE_RAJA) && defined (AXOM_USE_UMPIRE) - using seq_exec = axom::SEQ_EXEC; - - #if defined(AXOM_USE_OPENMP) - using omp_exec = axom::OMP_EXEC; - #else - using omp_exec = seq_exec; - #endif +#include +#include - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - #else - using cuda_exec = seq_exec; - #endif +namespace mir = axom::mir; - #if defined(AXOM_USE_HIP) - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - #else - using hip_exec = seq_exec; - #endif -#endif -// clang-format on +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 1000; + options["num_elements_threshold"] = 1000; + n.to_summary_string_stream(std::cout, options); +} template void test_conduit_allocate() @@ -135,6 +119,245 @@ TEST(mir_blueprint_utilities, to_unstructured) // TODO: to_unstructured } +//---------------------------------------------------------------------- + +template +void compareRelation(const conduit::Node &hostRelation, + const axom::ArrayView &zones, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) +{ + namespace bputils = axom::mir::utilities::blueprint; + + const auto zonesView = + bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = + bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = + bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizes.size()); + EXPECT_EQ(offsetsView.size(), offsets.size()); + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + EXPECT_EQ(sizes[i], sizesView[i]); + EXPECT_EQ(offsets[i], offsetsView[i]); + } + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + // Sort the result so we can compare to the expected answer. + IndexT *begin = zonesView.data() + offsetsView[i]; + IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; + std::sort(begin, end); + + for(int j = 0; j < sizesView[i]; j++) + { + EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + } + } +} + +template +void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) +{ + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); +#if 0 + // Print the results. + printNode(hostRelation); +#endif + // Expected answers + // clang-format off + const int zones[] = { + 0, + 0, 1, + 1, 2, + 2, + 0, 3, // NOTE: these are sorted here + 0, 1, 3, 4, + 1, 2, 4, 5, + 2, 5, + 3, + 3, 4, + 4, 5, + 5 + }; + const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; + const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; + // clang-format on + + // Compare answers. + compareRelation(hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); +} + +TEST(mir_blueprint_utilities, node_to_zone_relation_builder_unstructured) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + + test_node_to_zone_relation_builder(mesh); +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder(mesh); +#endif +} + +TEST(mir_blueprint_utilities, node_to_zone_relation_builder_rectilinear) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + //mesh.print(); + + test_node_to_zone_relation_builder(mesh); +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder(mesh); +#endif +} + +template +void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh) +{ + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); +#if 0 + // Print the results. + printNode(hostRelation); +#endif + // Expected answers + // clang-format off + const int zones[] = { + /*node 0*/ 0, + /*node 1*/ 0, 1, + /*node 2*/ 1, + /*node 3*/ 0, 2, + /*node 4*/ 0, 1, 2, 3, + /*node 5*/ 1, 3, + /*node 6*/ 2, + /*node 7*/ 2, 3, + /*node 8*/ 3, + + /*node 9*/ 0, 4, + /*node 10*/ 0, 1, 4, 5, + /*node 11*/ 1, 5, + /*node 12*/ 0, 2, 4, 6, + /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, + /*node 14*/ 1, 3, 5, 7, + /*node 15*/ 2, 6, + /*node 16*/ 2, 3, 6, 7, + /*node 17*/ 3, 7, + + /*node 18*/ 4, + /*node 19*/ 4, 5, + /*node 20*/ 5, + /*node 21*/ 4, 6, + /*node 22*/ 4, 5, 6, 7, + /*node 23*/ 5, 7, + /*node 24*/ 6, + /*node 25*/ 6, 7, + /*node 26*/ 7 + }; + const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, + 2, 4, 2, 4, 8, 4, 2, 4, 2, + 1, 2, 1, 2, 4, 2, 1, 2, 1 + }; + const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, + 16, 18, 22, 24, 28, 36, 40, 42, 46, + 48, 49, 51, 52, 54, 58, 60, 61, 63}; + // clang-format on + + // Compare answers. + compareRelation(hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); +} + +TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) +{ + conduit::Node mesh; + conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); + // Make sure all the types are the same. + conduit::blueprint::mesh::utils::convert(mesh, conduit::DataType::int32(), + std::vector{{ + "topologies/mesh/elements/connectivity", + "topologies/mesh/elements/sizes", + "topologies/mesh/elements/offsets", + "topologies/mesh/subelements/connectivity", + "topologies/mesh/subelements/sizes", + "topologies/mesh/subelements/offsets" + }}); + //printNode(mesh); + + test_node_to_zone_relation_builder_polyhedral(mesh); + +#if defined(AXOM_USE_OPENMP) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_node_to_zone_relation_builder_polyhedral(mesh); +#endif +} + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp index 3a7461e35a..03d94b707b 100644 --- a/src/axom/mir/tests/mir_utilities.cpp +++ b/src/axom/mir/tests/mir_utilities.cpp @@ -11,21 +11,8 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" -#include "axom/mir/tests/mir_testing_helpers.hpp" -#include "axom/mir/tests/mir_testing_data_helpers.hpp" - -#include - namespace mir = axom::mir; -void printNode(const conduit::Node &n) -{ - conduit::Node options; - options["num_children_threshold"] = 1000; - options["num_elements_threshold"] = 1000; - n.to_summary_string_stream(std::cout, options); -} - //---------------------------------------------------------------------- TEST(mir_shape_tests, shape_dimesionality) @@ -113,245 +100,6 @@ TEST(mir_compute_averages, point_value) EXPECT_DOUBLE_EQ(centroid[2], 0.5); } -//---------------------------------------------------------------------- - -template -void compareRelation(const conduit::Node &hostRelation, - const axom::ArrayView &zones, - const axom::ArrayView &sizes, - const axom::ArrayView &offsets) -{ - namespace bputils = axom::mir::utilities::blueprint; - - const auto zonesView = - bputils::make_array_view(hostRelation["zones"]); - const auto sizesView = - bputils::make_array_view(hostRelation["sizes"]); - const auto offsetsView = - bputils::make_array_view(hostRelation["offsets"]); - EXPECT_EQ(sizesView.size(), sizes.size()); - EXPECT_EQ(offsetsView.size(), offsets.size()); - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - EXPECT_EQ(sizes[i], sizesView[i]); - EXPECT_EQ(offsets[i], offsetsView[i]); - } - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - // Sort the result so we can compare to the expected answer. - IndexT *begin = zonesView.data() + offsetsView[i]; - IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; - std::sort(begin, end); - - for(int j = 0; j < sizesView[i]; j++) - { - EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); - } - } -} - -template -void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) -{ - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceRelation); - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); -#if 0 - // Print the results. - printNode(hostRelation); -#endif - // Expected answers - // clang-format off - const int zones[] = { - 0, - 0, 1, - 1, 2, - 2, - 0, 3, // NOTE: these are sorted here - 0, 1, 3, 4, - 1, 2, 4, 5, - 2, 5, - 3, - 3, 4, - 4, 5, - 5 - }; - const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; - const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; - // clang-format on - - // Compare answers. - compareRelation(hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); -} - -TEST(mir_utilities, node_to_zone_relation_builder_unstructured) -{ - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - - test_node_to_zone_relation_builder(mesh); -#if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder(mesh); -#endif - -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_node_to_zone_relation_builder(mesh); -#endif - -#if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder(mesh); -#endif -} - -TEST(mir_utilities, node_to_zone_relation_builder_rectilinear) -{ - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("rectilinear", dims, mesh); - //mesh.print(); - - test_node_to_zone_relation_builder(mesh); -#if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder(mesh); -#endif - -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_node_to_zone_relation_builder(mesh); -#endif - -#if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder(mesh); -#endif -} - -template -void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh) -{ - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceRelation); - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); -#if 0 - // Print the results. - printNode(hostRelation); -#endif - // Expected answers - // clang-format off - const int zones[] = { - /*node 0*/ 0, - /*node 1*/ 0, 1, - /*node 2*/ 1, - /*node 3*/ 0, 2, - /*node 4*/ 0, 1, 2, 3, - /*node 5*/ 1, 3, - /*node 6*/ 2, - /*node 7*/ 2, 3, - /*node 8*/ 3, - - /*node 9*/ 0, 4, - /*node 10*/ 0, 1, 4, 5, - /*node 11*/ 1, 5, - /*node 12*/ 0, 2, 4, 6, - /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, - /*node 14*/ 1, 3, 5, 7, - /*node 15*/ 2, 6, - /*node 16*/ 2, 3, 6, 7, - /*node 17*/ 3, 7, - - /*node 18*/ 4, - /*node 19*/ 4, 5, - /*node 20*/ 5, - /*node 21*/ 4, 6, - /*node 22*/ 4, 5, 6, 7, - /*node 23*/ 5, 7, - /*node 24*/ 6, - /*node 25*/ 6, 7, - /*node 26*/ 7 - }; - const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, - 2, 4, 2, 4, 8, 4, 2, 4, 2, - 1, 2, 1, 2, 4, 2, 1, 2, 1 - }; - const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, - 16, 18, 22, 24, 28, 36, 40, 42, 46, - 48, 49, 51, 52, 54, 58, 60, 61, 63}; - // clang-format on - - // Compare answers. - compareRelation(hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); -} - -TEST(mir_utilities, node_to_zone_relation_builder_polyhedral) -{ - conduit::Node mesh; - conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); - // Make sure all the types are the same. - conduit::blueprint::mesh::utils::convert(mesh, conduit::DataType::int32(), - std::vector{{ - "topologies/mesh/elements/connectivity", - "topologies/mesh/elements/sizes", - "topologies/mesh/elements/offsets", - "topologies/mesh/subelements/connectivity", - "topologies/mesh/subelements/sizes", - "topologies/mesh/subelements/offsets" - }}); - //printNode(mesh); - - test_node_to_zone_relation_builder_polyhedral(mesh); - -#if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder_polyhedral(mesh); -#endif - -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_node_to_zone_relation_builder_polyhedral(mesh); -#endif - -#if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder_polyhedral(mesh); -#endif -} - int main(int argc, char *argv[]) { int result = 0; From 47c55bff38125f8de3edc2f65156eff95803f594 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 12:03:40 -0700 Subject: [PATCH 167/290] Added test for RecenterField --- src/axom/mir/FieldBlender.hpp | 2 +- src/axom/mir/RecenterField.hpp | 14 ++-- .../mir/tests/mir_blueprint_utilities.cpp | 71 +++++++++++++++++++ src/axom/mir/utilities.hpp | 8 +-- 4 files changed, 83 insertions(+), 12 deletions(-) diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index dae3defb46..26a6b3a6bc 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -152,7 +152,7 @@ class FieldBlender [&](auto compView, auto outView) { using value_type = typename decltype(compView)::value_type; using accum_type = - typename axom::mir::utilities::accumulation_traits::type; + typename axom::mir::utilities::accumulation_traits::value_type; IndexingPolicy deviceIndexing(m_indexing); const BlendData deviceBlend(blend); diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 8bcd04f868..564e9f9803 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -72,12 +72,12 @@ void RecenterField::execute(const conduit::Node &field, for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { const conduit::Node &n_comp = n_values[c]; - recenterSingleComponent(relation, outField["values"][n_comp.name()], n_comp); + recenterSingleComponent(n_comp, relation, outField["values"][n_comp.name()]); } } else { - recenterSingleComponent(relation, outField["values"], n_values); + recenterSingleComponent(n_values, relation, outField["values"]); } } @@ -104,7 +104,7 @@ void RecenterField::recenterSingleComponent( // Allocate Conduit data through Axom. utilities::blueprint::ConduitAllocateThroughAxom c2a; n_out.set_allocator(c2a.getConduitAllocatorID()); - n_out.set(n_comp.dtype().id(), relSize); + n_out.set(conduit::DataType(n_comp.dtype().id(), relSize)); views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) { using Precision = typename decltype(compView)::value_type; @@ -112,12 +112,12 @@ void RecenterField::recenterSingleComponent( typename axom::mir::utilities::accumulation_traits::value_type; axom::for_all( relSize, - AXOM_LAMBDA(int relIndex) { - const auto n = sizesView[relIndex]; + AXOM_LAMBDA(auto relIndex) { + const auto n = static_cast(sizesView[relIndex]); const auto offset = offsetsView[relIndex]; - AccumType sum = 0; - for(int i = 0; i < n; i++) + AccumType sum {}; + for(axom::IndexType i = 0; i < n; i++) { const auto id = relView[offset + i]; sum += static_cast(compView[id]); diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index a7dab0843a..14d7dcffba 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -359,7 +359,78 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) } //------------------------------------------------------------------------------ +template +void test_recenter_field(const conduit::Node &hostMesh) +{ + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + + // Make a node to zone relation on the device. + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceRelation); + + // Recenter a field zonal->nodal on the device + axom::mir::utilities::blueprint::RecenterField r; + r.execute(deviceMesh["fields/easy_zonal"], deviceRelation, deviceMesh["fields/z2n"]); + + // Recenter a field nodal->zonal on the device. (The elements are an o2m relation) + r.execute(deviceMesh["fields/z2n"], deviceMesh["topologies/mesh/elements"], deviceMesh["fields/n2z"]); + // device -> host + conduit::Node hostResultMesh; + axom::mir::utilities::blueprint::copy(hostResultMesh, deviceMesh); +#if 0 + // Print the results. + printNode(hostResultMesh); +#endif + const float n2z_result[] = {1., 2., 4., 5., 4., 5., 7., 8., 7., 8., 10., 11.}; + for(size_t i = 0; i < (sizeof(n2z_result) / sizeof(float)); i++) + { + EXPECT_EQ(n2z_result[i], hostResultMesh["fields/z2n/values"].as_float_accessor()[i]); + } + const float z2n_result[] = {3., 4.5, 6., 6., 7.5, 9.}; + for(size_t i = 0; i < (sizeof(z2n_result) / sizeof(float)); i++) + { + EXPECT_EQ(z2n_result[i], hostResultMesh["fields/n2z/values"].as_float_accessor()[i]); + } +} + +TEST(mir_blueprint_utilities, recenterfield) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + mesh["topologies/mesh/elements/sizes"].set(std::vector{{4, 4, 4, 4, 4, 4}}); + mesh["topologies/mesh/elements/offsets"].set(std::vector{{0, 4, 8, 12, 16, 20}}); + mesh["fields/easy_zonal/topology"] = "mesh"; + mesh["fields/easy_zonal/association"] = "element"; + mesh["fields/easy_zonal/values"].set(std::vector{{1,3,5,7,9,11}}); + + test_recenter_field(mesh); +#if defined(AXOM_USE_OPENMP) + test_recenter_field(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_recenter_field(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_recenter_field(mesh); +#endif +} + +//------------------------------------------------------------------------------ int main(int argc, char *argv[]) { int result = 0; diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 0f9ccd2bcc..3dcc48057a 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -67,25 +67,25 @@ namespace utilities template struct accumulation_traits { - using type = float; + using value_type = float; }; template <> struct accumulation_traits { - using type = double; + using value_type = double; }; template <> struct accumulation_traits { - using type = double; + using value_type = double; }; template <> struct accumulation_traits { - using type = double; + using value_type = double; }; //------------------------------------------------------------------------------ From 3d12af09e3e2db773f45910db0740d1e671c8577 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 12:04:57 -0700 Subject: [PATCH 168/290] make style --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 134 ++++++++++-------- .../mir/tests/mir_blueprint_utilities.cpp | 58 ++++---- .../UnstructuredTopologyPolyhedralView.hpp | 17 +-- 3 files changed, 114 insertions(+), 95 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index f0f03f484f..5916fa5676 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -140,71 +140,79 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, if(shape.is_polyhedral()) { - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; const auto allocatorID = axom::execution_space::allocatorID(); - views::dispatch_unstructured_polyhedral_topology(topo, [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { - const auto nzones = topoView.numberOfZones(); - axom::Array sizes(nzones, nzones, allocatorID); - auto sizes_view = sizes.view(); - - // Run through the topology once to do a count of each zone's unique node ids. - RAJA::ReduceSum count(0); - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); - const auto connSize = count.get(); - - // Do a scan on the size array to build an offset array. - axom::Array offsets(nzones, nzones, allocatorID); - auto offsets_view = offsets.view(); - axom::exclusive_scan(sizes_view, offsets_view); - sizes.clear(); - - // Allocate Conduit arrays on the device in a data type that matches the connectivity. - conduit::Node n_conn; - n_conn.set_allocator(conduitAllocatorID); - n_conn.set(conduit::DataType(intTypeId, connSize)); - - n_zones.set(conduit::DataType(intTypeId, connSize)); - n_sizes.set(conduit::DataType(intTypeId, nnodes)); - n_offsets.set(conduit::DataType(intTypeId, nnodes)); - - views::IndexNode_to_ArrayView_same( - n_conn, - n_zones, - n_sizes, - n_offsets, - [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { - // Run through the data one more time to build the nodes and zones arrays. - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } - }); - - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, zonesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); - }); - }); + views::dispatch_unstructured_polyhedral_topology( + topo, + [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { + const auto nzones = topoView.numberOfZones(); + axom::Array sizes(nzones, nzones, allocatorID); + auto sizes_view = sizes.view(); + + // Run through the topology once to do a count of each zone's unique node ids. + RAJA::ReduceSum count(0); + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); + const auto connSize = count.get(); + + // Do a scan on the size array to build an offset array. + axom::Array offsets(nzones, nzones, allocatorID); + auto offsets_view = offsets.view(); + axom::exclusive_scan(sizes_view, offsets_view); + sizes.clear(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + conduit::Node n_conn; + n_conn.set_allocator(conduitAllocatorID); + n_conn.set(conduit::DataType(intTypeId, connSize)); + + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same( + n_conn, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, + auto zonesView, + auto sizesView, + auto offsetsView) { + // Run through the data one more time to build the nodes and zones arrays. + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); + + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + this->template buildRelation(connectivityView, + zonesView, + offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); + }); + }); } else if(shape.is_polygonal()) { diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 14d7dcffba..8950304a24 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -197,10 +197,11 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) // clang-format on // Compare answers. - compareRelation(hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); + compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); } TEST(mir_blueprint_utilities, node_to_zone_relation_builder_unstructured) @@ -321,10 +322,11 @@ void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh // clang-format on // Compare answers. - compareRelation(hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); + compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); } TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) @@ -332,15 +334,15 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) conduit::Node mesh; conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); // Make sure all the types are the same. - conduit::blueprint::mesh::utils::convert(mesh, conduit::DataType::int32(), - std::vector{{ - "topologies/mesh/elements/connectivity", - "topologies/mesh/elements/sizes", - "topologies/mesh/elements/offsets", - "topologies/mesh/subelements/connectivity", - "topologies/mesh/subelements/sizes", - "topologies/mesh/subelements/offsets" - }}); + conduit::blueprint::mesh::utils::convert( + mesh, + conduit::DataType::int32(), + std::vector {{"topologies/mesh/elements/connectivity", + "topologies/mesh/elements/sizes", + "topologies/mesh/elements/offsets", + "topologies/mesh/subelements/connectivity", + "topologies/mesh/subelements/sizes", + "topologies/mesh/subelements/offsets"}}); //printNode(mesh); test_node_to_zone_relation_builder_polyhedral(mesh); @@ -374,10 +376,14 @@ void test_recenter_field(const conduit::Node &hostMesh) // Recenter a field zonal->nodal on the device axom::mir::utilities::blueprint::RecenterField r; - r.execute(deviceMesh["fields/easy_zonal"], deviceRelation, deviceMesh["fields/z2n"]); + r.execute(deviceMesh["fields/easy_zonal"], + deviceRelation, + deviceMesh["fields/z2n"]); // Recenter a field nodal->zonal on the device. (The elements are an o2m relation) - r.execute(deviceMesh["fields/z2n"], deviceMesh["topologies/mesh/elements"], deviceMesh["fields/n2z"]); + r.execute(deviceMesh["fields/z2n"], + deviceMesh["topologies/mesh/elements"], + deviceMesh["fields/n2z"]); // device -> host conduit::Node hostResultMesh; @@ -389,12 +395,14 @@ void test_recenter_field(const conduit::Node &hostMesh) const float n2z_result[] = {1., 2., 4., 5., 4., 5., 7., 8., 7., 8., 10., 11.}; for(size_t i = 0; i < (sizeof(n2z_result) / sizeof(float)); i++) { - EXPECT_EQ(n2z_result[i], hostResultMesh["fields/z2n/values"].as_float_accessor()[i]); + EXPECT_EQ(n2z_result[i], + hostResultMesh["fields/z2n/values"].as_float_accessor()[i]); } const float z2n_result[] = {3., 4.5, 6., 6., 7.5, 9.}; for(size_t i = 0; i < (sizeof(z2n_result) / sizeof(float)); i++) { - EXPECT_EQ(z2n_result[i], hostResultMesh["fields/n2z/values"].as_float_accessor()[i]); + EXPECT_EQ(z2n_result[i], + hostResultMesh["fields/n2z/values"].as_float_accessor()[i]); } } @@ -410,11 +418,13 @@ TEST(mir_blueprint_utilities, recenterfield) conduit::Node mesh; axom::StackArray dims {{4, 3}}; axom::mir::testing::data::braid("quads", dims, mesh); - mesh["topologies/mesh/elements/sizes"].set(std::vector{{4, 4, 4, 4, 4, 4}}); - mesh["topologies/mesh/elements/offsets"].set(std::vector{{0, 4, 8, 12, 16, 20}}); + mesh["topologies/mesh/elements/sizes"].set( + std::vector {{4, 4, 4, 4, 4, 4}}); + mesh["topologies/mesh/elements/offsets"].set( + std::vector {{0, 4, 8, 12, 16, 20}}); mesh["fields/easy_zonal/topology"] = "mesh"; mesh["fields/easy_zonal/association"] = "element"; - mesh["fields/easy_zonal/values"].set(std::vector{{1,3,5,7,9,11}}); + mesh["fields/easy_zonal/values"].set(std::vector {{1, 3, 5, 7, 9, 11}}); test_recenter_field(mesh); #if defined(AXOM_USE_OPENMP) diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 18c837ec16..738a3dfe8e 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -153,7 +153,9 @@ class UnstructuredTopologyPolyhedralView } private: - AXOM_HOST_DEVICE bool find(const ConnectivityType *arr, axom::IndexType n, ConnectivityType value) const + AXOM_HOST_DEVICE bool find(const ConnectivityType *arr, + axom::IndexType n, + ConnectivityType value) const { bool found = false; for(axom::IndexType i = 0; i < n && !found; i++) @@ -171,13 +173,12 @@ class UnstructuredTopologyPolyhedralView using ShapeType = PolyhedronShape; - UnstructuredTopologyPolyhedralView( - const ConnectivityView &subelement_conn, - const ConnectivityView &subelement_sizes, - const ConnectivityView &subelement_offsets, - const ConnectivityView &element_conn, - const ConnectivityView &element_sizes, - const ConnectivityView &element_offsets) + UnstructuredTopologyPolyhedralView(const ConnectivityView &subelement_conn, + const ConnectivityView &subelement_sizes, + const ConnectivityView &subelement_offsets, + const ConnectivityView &element_conn, + const ConnectivityView &element_sizes, + const ConnectivityView &element_offsets) : m_data(subelement_conn, subelement_sizes, subelement_offsets, From 087b33b28b351e716f23013ca2c96f6801bf5b36 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 17:36:27 -0700 Subject: [PATCH 169/290] Working on a MIR algorithm --- src/axom/mir/ClipField.hpp | 9 +- src/axom/mir/ClipOptions.hpp | 12 + src/axom/mir/EquiZAlgorithm.hpp | 220 +++++++++++++++--- src/axom/mir/MIRAlgorithm.cpp | 4 +- src/axom/mir/tests/CMakeLists.txt | 1 + .../mir/tests/mir_blueprint_utilities.cpp | 8 - src/axom/mir/tests/mir_equiz.cpp | 152 ++++++++++++ src/axom/mir/tests/mir_testing_helpers.hpp | 8 + src/axom/mir/views/MaterialView.cpp | 2 +- src/axom/mir/views/MaterialView.hpp | 7 + src/axom/mir/views/dispatch_coordset.hpp | 122 ++++++---- 11 files changed, 456 insertions(+), 89 deletions(-) create mode 100644 src/axom/mir/tests/mir_equiz.cpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 09ffbf8399..4b84e5a226 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1182,15 +1182,16 @@ class ClipField const auto selectedZonesView = selectedZones.view(); const auto nzones = selectedZonesView.size(); + const std::string originalElements(opts.originalElementsField()); - if(n_fields.has_child("originalElements")) + if(n_fields.has_child(originalElements)) { // originalElements already exists. We need to map it forward. - const conduit::Node &n_orig = n_fields["originalElements"]; + const conduit::Node &n_orig = n_fields[originalElements]; const conduit::Node &n_orig_values = n_orig["values"]; views::IndexNode_to_ArrayView(n_orig_values, [&](auto origValuesView) { using value_type = typename decltype(origValuesView)::value_type; - conduit::Node &n_origElem = n_newFields["originalElements"]; + conduit::Node &n_origElem = n_newFields[originalElements]; n_origElem["association"] = "element"; n_origElem["topology"] = opts.topologyName(n_newTopo.name()); conduit::Node &n_values = n_origElem["values"]; @@ -1212,7 +1213,7 @@ class ClipField else { // Make a new node and populate originalElement. - conduit::Node &n_orig = n_newFields["originalElements"]; + conduit::Node &n_orig = n_newFields[originalElements]; n_orig["association"] = "element"; n_orig["topology"] = opts.topologyName(n_newTopo.name()); conduit::Node &n_values = n_orig["values"]; diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/ClipOptions.hpp index 072cf0d4d9..03945551a8 100644 --- a/src/axom/mir/ClipOptions.hpp +++ b/src/axom/mir/ClipOptions.hpp @@ -59,6 +59,18 @@ class ClipOptions : public axom::mir::Options return name; } + /** + * \brief Return the name of the new original elements field to be created. + * \return The name of the new original elements to be created. + */ + std::string originalElementsField() const + { + std::string name("originalElements"); + if(options().has_child("originalElementsField")) + name = options().fetch_existing("originalElementsField").as_string(); + return name; + } + /** * \brief Whether the "inside" of the clipping field is selected. * \return 1 of the inside clipping is selected, false otherwise. diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 26d2f5576a..fe5a12f84c 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -14,13 +14,17 @@ #include +#if defined(AXOM_USE_RAJA) + #include +#endif + namespace axom { namespace mir { -#if 0 + /** - * \brief Populate a new field + * \brief Populate values for a new field based on a material's volume fraction. */ template struct MatsetToField @@ -39,9 +43,7 @@ struct MatsetToField }); } }; - -// TODO we can - +#if 0 /** * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. @@ -50,7 +52,7 @@ template (n_options_copy, n_options); // Iterate over the materials const auto matInfo = axom::mir::views::materials(n_matset); @@ -80,7 +86,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { // The first time through, we can use the supplied views. // clangformat-off - iteration( + iteration( m_topologyView, m_coordsetView, m_matsetView, matInfo[i], n_topo, n_coordset, n_fields, n_matset, @@ -88,8 +94,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newTopo, n_newCoordset, n_newFields, n_newMatset); // clangformat-on - // In these iterations, we do not want to pass selectedZones through - // since they are only valid on the current input topology. + // In later iterations, we do not want to pass selectedZones through + // since they are only valid on the current input topology. Also, if they + // were passed then the new topology only has those selected zones. n_options_copy.remove("selectedZones"); } else @@ -98,7 +105,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputTopo.move(n_newTopo); n_InputCoordset.move(n_newCoordset); n_InputFields.move(n_newFields); - n_InputMatset.move(n_newFields); + n_InputMatset.move(n_newMatset); const auto shape = n_newTopo.fetch_existing("elements/shape").as_string(); if(shape == "mixed") @@ -108,23 +115,24 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // clangformat-off views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { - using ICoordSetView = decltype(coordsetView); + using ICoordsetView = decltype(coordsetView); views::dispatch_unstructured_mixed_topology(n_InputTopo, [&](auto topologyView) { using ITopologyView = decltype(topologyView); // Assume we made this type out of the first iteration. - axom::mir::views::UnibufferMaterialView matsetView; + using IMatsetView = axom::mir::views::UnibufferMaterialView; + IMatsetView matsetView; matsetView.set( - axom::mir::utilities::blueprint::make_array_view(n_matset["material_ids"]), - axom::mir::utilities::blueprint::make_array_view(n_matset["volume_fractions"]), - axom::mir::utilities::blueprint::make_array_view(n_matset["sizes"]), - axom::mir::utilities::blueprint::make_array_view(n_matset["offsets"]), - axom::mir::utilities::blueprint::make_array_view(n_matset["indices"])); - + bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); + // this->template iteration( // See if the compiler will infer the right args. - iteration( + iteration( topologyView, coordsetView, matsetView, matInfo[i], n_topo, n_coordset, n_fields, n_matset, @@ -142,9 +150,13 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm /** * \brief Perform one round of material clipping. + * + * \tparam ITopologyView The topology view type for the intermediate topology. + * \tparam ICoordsetView The topology view type for the intermediate coordset. + * \tparam IMatsetView The topology view type for the intermediate matset. */ template - void iteration(const ITopoView &topoView, + void iteration(const ITopologyView &topoView, const ICoordsetView &coordsetView, const IMatsetView &matsetView, @@ -167,17 +179,20 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm constexpr auto floatTypeID = bputils::cpp2conduit::id; // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; + bputils::ConduitAllocateThroughAxom c2a; const std::string zoneCenteredField("__equiz__zoneMatField"); const std::string nodeCenteredField("__equiz__nodeMatField"); + const std::string colorField("__equiz__colors"); const auto nzones = topoView.numberOfZones(); // Make a node to zone relation. conduit::Node relation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder builder; - builder.execute(n_topo, relation); // <----------------------- Should this algorithm take a topo view? + { + bputils::NodeToZoneRelationBuilder rb; + rb.execute(n_topo, relation); // <----------------------- Should this algorithm take a topo view? + } // Make a shallow copy of the fields that we can modify. conduit::Node tmpFields; @@ -195,14 +210,18 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm zoneValues.set(conduit::DataType(floatTypeID, nzones)); // Populate the zonal volume fraction field from the material view. - MatsetToField m2f; - auto zoneMatFieldView = make_array_view(zoneValues); - m2f.execute(matsetView, currentMat.number, zoneMatFieldView); + auto zoneMatFieldView = bputils::make_array_view(zoneValues); + { + MatsetToField m2f; + m2f.execute(matsetView, currentMat.number, zoneMatFieldView); + } // Recenter the field to nodal using the node to zone relation. conduit::Node &nodeMatField = tmpFields[nodeCenteredField]; - axom::mir::RecenterField recenter; - recenter.execute(zoneMatField, relation, nodeMatField); + { + bputils::RecenterField recenter; + recenter.execute(zoneMatField, relation, nodeMatField); + } /** NOTE - I am a little worried that I've not yet appreciated how the intersections are found @@ -216,9 +235,17 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm options["outside"] = 1; options["clipField"] = nodeCenteredField; options["clipValue"] = 0.5; - axom::mir::clipping::ClipField clipper(topoView, coordsetView); - clipper.execute(n_topo, n_coordset, tmpFields, options, n_newTopo, n_newCoordset, n_newFields); - // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. + options["colorField"] = colorField; + if(n_options.has_child("selectedZones")) + { + // Pass selectedZones along in the clip options, if present. + options["selectedZones"].set_external(n_options["selectedZones"]); + } + { + axom::mir::clipping::ClipField clipper(topoView, coordsetView); + clipper.execute(n_topo, n_coordset, tmpFields, options, n_newTopo, n_newCoordset, n_newFields); + // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. + } #if 1 conduit::Node mesh; @@ -233,14 +260,141 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newFields.remove(zoneCenteredField); n_newFields.remove(nodeCenteredField); + + // Make a new matset. + auto colorView = axom::mir::utilities::blueprint::make_array_view(n_newFields[colorField +"/values"]); + auto origElemView = axom::mir::utilities::blueprint::make_array_view(n_newFields["originalElements/values"]); + makeNewMatset(matsetView, currentMat, colorView, origElemView, n_matset, n_newMatset); + + n_newFields.remove("originalElements"); + } + + template + void makeNewMatset(// by value + const IMatsetView currentMatsetView, + const axom::mir::views::Material currentMat, + const axom::ArrayView colorView, + const axom::ArrayView originalElementsView, + // by reference + const conduit::Node &n_matset, + conduit::Node &n_newMatset) + { + namespace bputils = axom::mir::utilities::blueprint; + using reduce_policy = typename axom::execution_space::reduce_policy; + using IntType = typename IMatsetView::IndexType; + using FloatType = typename IMatsetView::FloatType; + const int intTypeID = bputils::cpp2conduit::id; + const int floatTypeID = bputils::cpp2conduit::id; + + // Copy the material map to the new matset. + if(n_matset.has_child("material_map")) + { + n_newMatset.set(n_matset.fetch_existing("material_map")); + } + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + + // Make sizes, offsets for the new matset. + const auto nzones = colorView.size(); + conduit::Node &n_sizes = n_newMatset["sizes"]; + conduit::Node &n_offsets = n_newMatset["offsets"]; + n_sizes.set_allocator(c2a.getConduitAllocatorID()); + n_offsets.set_allocator(c2a.getConduitAllocatorID()); + n_sizes.set(conduit::DataType(intTypeID, nzones)); + n_offsets.set(conduit::DataType(intTypeID, nzones)); + + // Count how many slots are needed for the new material. Make sizes. + RAJA::ReduceSum sum(0); + auto sizesView = axom::mir::utilities::blueprint::make_array_view(n_sizes); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + axom::IndexType nmatsThisZone = 0; + if(colorView[zoneIndex] == COLOR0) + { + nmatsThisZone = 1; + } + else + { + // These were the unselected parts of the zone after clipping. + // When we make the new material, this set of zones remove the current material. + nmatsThisZone = currentMatsetView.numberOfMaterials(zoneIndex) - 1; + } + sizesView[zoneIndex] = nmatsThisZone; + sum += nmatsThisZone; + }); + + // Make offsets. + auto offsetsView = axom::mir::utilities::blueprint::make_array_view(n_offsets); + axom::exclusive_scan(sizesView, offsetsView); + + // Make new matset data. + const auto arraySize = sum.get(); + conduit::Node &n_material_ids = n_newMatset["material_ids"]; + conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; + conduit::Node &n_indices = n_newMatset["indices"]; + n_material_ids.set_allocator(c2a.getConduitAllocatorID()); + n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); + n_indices.set_allocator(c2a.getConduitAllocatorID()); + n_material_ids.set(conduit::DataType(intTypeID, arraySize)); + n_volume_fractions.set(conduit::DataType(floatTypeID, arraySize)); + n_indices.set(conduit::DataType(intTypeID, arraySize)); + + auto matidsView = axom::mir::utilities::blueprint::make_array_view(n_material_ids); + auto vfView = axom::mir::utilities::blueprint::make_array_view(n_volume_fractions); + auto indicesView = axom::mir::utilities::blueprint::make_array_view(n_material_ids); + + // Fill in the indices used. + axom::for_all(arraySize, AXOM_LAMBDA(auto index) + { + indicesView[index] = index; + }); + + // Fill in the material data. + const int currentMatNumber = currentMat.number; + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + auto offset = offsetsView[zoneIndex]; + if(colorView[zoneIndex] == COLOR0) + { + matidsView[offset] = currentMatNumber; + vfView[offset] = 1; + } + else + { + // Get the material ids and volume fractions from the original material's zone. + const auto origIndex = originalElementsView[zoneIndex]; + typename IMatsetView::IDList ids {}; + typename IMatsetView::VFList vfs {}; + currentMatsetView.zoneMaterials(origIndex, ids, vfs); + + // Total up the materials, excluding the current material. + FloatType vfTotal {}; + for(axom::IndexType i = 0; i < ids.size(); i++) + { + vfTotal += (ids[i] != currentMatNumber) ? vfs[i] : 0; + } + + // Fill in the new materials. + for(axom::IndexType i = 0; i < ids.size(); i++) + { + if(ids[i] != currentMatNumber) + { + matidsView[offset] = ids[i]; + vfView[offset] = vfs[i] / vfTotal; + offset++; + } + } + } + }); } private: TopologyView m_topologyView; CoordsetView m_coordsetView; MatsetView m_matsetView; +#endif }; -//------------------------------------------------------------------------------ #endif } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index eacc2b5161..dccbbc49ec 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -65,9 +65,11 @@ void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, const std::string newMatsetName = options.matsetName(matset); // Make some new nodes in the output. - conduit::Node &newTopo = n_newDomain["topologies/" + newTopoName]; conduit::Node &newCoordset = n_newDomain["coordsets/" + newCoordsetName]; + conduit::Node &newTopo = n_newDomain["topologies/" + newTopoName]; + newTopo["coordset"] = newCoordsetName; conduit::Node &newMatset = n_newDomain["matsets/" + newMatsetName]; + newMatset["topology"] = newTopoName; // Execute the algorithm on the domain. if(n_domain.has_path("state")) diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 1457137920..123d96c45e 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -20,6 +20,7 @@ set(gtest_mir_tests mir_views_indexing.cpp mir_cell_generator.cpp mir_views.cpp + mir_equiz.cpp ) set(mir_tests_depends_on diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 8950304a24..963df01e81 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -18,14 +18,6 @@ namespace mir = axom::mir; -void printNode(const conduit::Node &n) -{ - conduit::Node options; - options["num_children_threshold"] = 1000; - options["num_elements_threshold"] = 1000; - n.to_summary_string_stream(std::cout, options); -} - template void test_conduit_allocate() { diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp new file mode 100644 index 0000000000..520d0771ff --- /dev/null +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/primal.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +//------------------------------------------------------------------------------ + +// Uncomment to generate baselines +//#define AXOM_TESTING_GENERATE_BASELINES + +// Uncomment to save visualization files for debugging (when making baselines) +//#define AXOM_TESTING_SAVE_VISUALIZATION + +#include "axom/mir/tests/mir_testing_helpers.hpp" + +std::string baselineDirectory() +{ + return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), "mir_equiz"); +} + +//------------------------------------------------------------------------------ +TEST(mir_equiz, miralgorithm) +{ + axom::mir::MIRAlgorithm *m = nullptr; + EXPECT_EQ(m, nullptr); +} + +//------------------------------------------------------------------------------ +TEST(mir_equiz, materialinformation) +{ + conduit::Node matset; + matset["material_map/a"] = 1; + matset["material_map/b"] = 2; + matset["material_map/c"] = 0; + + auto mi = axom::mir::views::materials(matset); + EXPECT_EQ(mi.size(), 3); + EXPECT_EQ(mi[0].number, 1); + EXPECT_EQ(mi[0].name, "a"); + + EXPECT_EQ(mi[1].number, 2); + EXPECT_EQ(mi[1].name, "b"); + + EXPECT_EQ(mi[2].number, 0); + EXPECT_EQ(mi[2].name, "c"); +} + +#if 0 +//------------------------------------------------------------------------------ +template +void braid2d_mat_test(const std::string &type, + const std::string &mattype, + const std::string &name) +{ + namespace bputils = axom::mir::utilities::blueprint; + const int allocatorID = axom::execution_space::allocatorID(); + + axom::StackArray dims {10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + + // Make views. + auto coordsetView = axom::mir::views::make_uniform_coordset<2>(deviceMesh["coordsets/coords"]); + auto topologyView = axom::mir::views::make_uniform<2>::view(deviceMesh["topologies/mesh"]); + using CoordsetView = decltype(coordsetView); + using TopologyView = decltype(topologyView); + + conduit::Node deviceMIRMesh; + + if(mattype == "unibuffer") + { + // clang-format off + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // clang-format on + + using MIR = axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + conduit::Node options, deviceMIRMesh; + options["matset"] = "mat"; + m.execute(deviceMesh, options, deviceMIRMesh); + } + + // device->host + conduit::Node hostMIRMesh; + axom::mir::utilities::blueprint::copy(hostMIRMesh, deviceMIRMesh); + +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + printNode(hostMIRMesh); + conduit::relay::io::blueprint::save_mesh(hostMIRMesh, name, "hdf5"); +#endif + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostMIRMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh)); +#endif + } +} + +TEST(mir_equiz, equiz_uniform_unibuffer) +{ + braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); + +#if defined(AXOM_USE_OPENMP) + braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +#endif + +#if defined(AXOM_USE_HIP) + braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +#endif +} + +//------------------------------------------------------------------------------ +#endif +int main(int argc, char *argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index ae22515cc3..1ee1cba180 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -234,4 +234,12 @@ bool compareBaseline(const std::vector &baselinePaths, return success; } +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); +} + #endif diff --git a/src/axom/mir/views/MaterialView.cpp b/src/axom/mir/views/MaterialView.cpp index 85a984e34e..2ea3a03461 100644 --- a/src/axom/mir/views/MaterialView.cpp +++ b/src/axom/mir/views/MaterialView.cpp @@ -19,7 +19,7 @@ MaterialInformation materials(const conduit::Node &matset) const conduit::Node &mm = matset["material_map"]; for(conduit::index_t i = 0; i < mm.number_of_children(); i++) { - info.push_back(Material {static_cast(i), mm[i].name()}); + info.push_back(Material {mm[i].to_int(), mm[i].name()}); } } return info; diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 897497408b..0fa78cca35 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -34,6 +34,13 @@ struct Material using MaterialInformation = std::vector; +/** + * \brief Return a vector of Material from a matset (this is the material_map) + * + * \param matset The Conduit node that contains the matset. + * + * \return A vector of Material that contains the materials in the material_map. + */ MaterialInformation materials(const conduit::Node &matset); //--------------------------------------------------------------------------- diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 3a64ea17eb..962fe90bc0 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -39,16 +39,11 @@ struct make_rectilinear_coordset */ static CoordsetView view(const conduit::Node &coordset) { + namespace bputils = axom::mir::utilities::blueprint; const conduit::Node &values = coordset.fetch_existing("values"); - axom::ArrayView xView( - static_cast(const_cast(values[0].data_ptr())), - values[0].dtype().number_of_elements()); - axom::ArrayView yView( - static_cast(const_cast(values[1].data_ptr())), - values[1].dtype().number_of_elements()); - axom::ArrayView zView( - static_cast(const_cast(values[2].data_ptr())), - values[2].dtype().number_of_elements()); + auto xView = bputils::make_array_view(values[0]); + auto yView = bputils::make_array_view(values[1]); + auto zView = bputils::make_array_view(values[2]); return CoordsetView(xView, yView, zView); } }; @@ -68,17 +63,83 @@ struct make_rectilinear_coordset */ static CoordsetView view(const conduit::Node &coordset) { + namespace bputils = axom::mir::utilities::blueprint; const conduit::Node &values = coordset.fetch_existing("values"); - axom::ArrayView xView( - static_cast(const_cast(values[0].data_ptr())), - values[0].dtype().number_of_elements()); - axom::ArrayView yView( - static_cast(const_cast(values[1].data_ptr())), - values[1].dtype().number_of_elements()); + auto xView = bputils::make_array_view(values[0]); + auto yView = bputils::make_array_view(values[1]); return CoordsetView(xView, yView); } }; +/** + * \brief Base template for creating a rectilinear coordset view. + */ +template +struct make_uniform_coordset +{ }; + +/** + * \brief Partial specialization for creating 3D uniform coordset view. + */ +template <> +struct make_uniform_coordset<3> +{ + using CoordsetView = axom::mir::views::UniformCoordsetView; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + const std::string keys[] = {"i", "j", "k"}; + const conduit::Node &n_dims = coordset["dims"]; + axom::StackArray dims; + axom::StackArray origin {0., 0., 0.}, spacing {1., 1., 1.}; + for(int i = 0; i < 3; i++) + { + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); + if(coordset.has_child("origin")) + origin[i] = coordset["origin"][keys[i]].to_double(); + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"][keys[i]].to_double(); + } + return CoordsetView(dims, origin, spacing); + } +}; + +/** + * \brief Partial specialization for creating 2D rectilinear coordset view. + */ +template <> +struct make_uniform_coordset<2> +{ + using CoordsetView = axom::mir::views::UniformCoordsetView; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + const std::string keys[] = {"i", "j"}; + const conduit::Node &n_dims = coordset["dims"]; + axom::StackArray dims; + axom::StackArray origin {0., 0.}, spacing {1., 1.}; + for(int i = 0; i < 2; i++) + { + dims[i] = n_dims.fetch_existing(keys[i]).to_int(); + if(coordset.has_child("origin")) + origin[i] = coordset["origin"][keys[i]].to_double(); + if(coordset.has_child("spacing")) + spacing[i] = coordset["spacing"][keys[i]].to_double(); + } + return CoordsetView(dims, origin, spacing); + } +}; + /** * \brief Dispatch an uniform coordset to a function. * @@ -91,40 +152,17 @@ struct make_rectilinear_coordset template void dispatch_uniform_coordset(const conduit::Node &coordset, FuncType &&func) { - const std::string keys[] = {"i", "j", "k"}; const conduit::Node &n_dims = coordset["dims"]; const conduit::index_t ndims = n_dims.number_of_children(); if(ndims == 2) { - axom::StackArray dims; - axom::StackArray origin {0., 0.}, spacing {1., 1.}; - for(int i = 0; i < ndims; i++) - { - dims[i] = n_dims.fetch_existing(keys[i]).to_int(); - if(coordset.has_child("origin")) - origin[i] = coordset["origin"][i].to_double(); - if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][i].to_double(); - } - - UniformCoordsetView coordView(dims, origin, spacing); - func(coordView); + auto coordsetView = make_uniform_coordset<2>::view(coordset); + func(coordsetView); } else if(ndims == 3) { - axom::StackArray dims; - axom::StackArray origin {0., 0., 0.}, spacing {1., 1., 1.}; - for(int i = 0; i < ndims; i++) - { - dims[i] = n_dims.fetch_existing(keys[i]).to_int(); - if(coordset.has_child("origin")) - origin[i] = coordset["origin"][i].to_double(); - if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][i].to_double(); - } - - UniformCoordsetView coordView(dims, origin, spacing); - func(coordView); + auto coordsetView = make_uniform_coordset<3>::view(coordset); + func(coordsetView); } } From ebe2c465ef47e9f89fb0cdd711fece0ff0c7b79c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 12 Aug 2024 18:30:00 -0700 Subject: [PATCH 170/290] Debugging MIR --- src/axom/mir/EquiZAlgorithm.hpp | 61 ++++++++++++++++--- src/axom/mir/MIRAlgorithm.hpp | 2 +- src/axom/mir/tests/mir_equiz.cpp | 20 ++++-- src/axom/mir/views/MaterialView.hpp | 4 +- src/axom/mir/views/dispatch_coordset.hpp | 8 +-- .../mir/views/dispatch_uniform_topology.hpp | 12 ++-- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index fe5a12f84c..7efb08818c 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -9,6 +9,12 @@ #include "axom/core.hpp" #include "axom/mir.hpp" +// Include these directly for now. +#include "axom/mir/views/MaterialView.hpp" +#include "axom/mir/MIRAlgorithm.hpp" +#include "axom/mir/RecenterField.hpp" +#include "axom/mir/NodeToZoneRelationBuilder.hpp" + #include #include @@ -43,7 +49,7 @@ struct MatsetToField }); } }; -#if 0 + /** * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. @@ -52,15 +58,37 @@ template (n_matset["offsets"]), bputils::make_array_view(n_matset["indices"])); -// this->template iteration( -// See if the compiler will infer the right args. + // iteration( topologyView, coordsetView, matsetView, matInfo[i], - n_topo, n_coordset, n_fields, n_matset, + n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset, n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); }); }); // clangformat-on } + else + { + // NOTE: what if the topo output was all clean after the 1st round? + } } // TODO: we need to build the new matset. @@ -253,6 +284,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mesh[n_newCoordset.path()].set_external(n_newCoordset); mesh[n_newFields.path()].set_external(n_newFields); mesh[n_newMatset.path()].set_external(n_newMatset); + + mesh.print(); + std::stringstream ss; ss << "debug_equiz_" << currentMat.number; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); @@ -269,6 +303,16 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newFields.remove("originalElements"); } + /** + * \brief Make a new matset for the output dataset. + * + * \param curentMatsetView The view for the current matset. + * \param currentMat The current material information. + * \param colorView A view for the color field, which says how the dataset was clipped. + * \param originalElementsView A view for the field that contains the original element number. + * \param n_matset The input matset (pre-MIR). + * \param n_newMatset The output matset. + */ template void makeNewMatset(// by value const IMatsetView currentMatsetView, @@ -393,9 +437,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm TopologyView m_topologyView; CoordsetView m_coordsetView; MatsetView m_matsetView; -#endif }; -#endif + } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index ccb1d00024..d265d3f658 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -84,7 +84,7 @@ class MIRAlgorithm * \param[in] n_coordset The Conduit node containing the coordset. * \param[in] n_fields The Conduit node containing the fields. * \param[in] n_matset The Conduit node containing the matset. - * \param[in] options The Conduit node containing the options that help govern MIR execution. + * \param[in] n_options The Conduit node containing the options that help govern MIR execution. * * \param[out] n_newTopo A node that will contain the new clipped topology. * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 520d0771ff..00976000d8 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -12,6 +12,8 @@ //------------------------------------------------------------------------------ +#define DEBUGGING_TEST_CASES + // Uncomment to generate baselines //#define AXOM_TESTING_GENERATE_BASELINES @@ -52,7 +54,6 @@ TEST(mir_equiz, materialinformation) EXPECT_EQ(mi[2].name, "c"); } -#if 0 //------------------------------------------------------------------------------ template void braid2d_mat_test(const std::string &type, @@ -60,7 +61,6 @@ void braid2d_mat_test(const std::string &type, const std::string &name) { namespace bputils = axom::mir::utilities::blueprint; - const int allocatorID = axom::execution_space::allocatorID(); axom::StackArray dims {10, 10}; axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; @@ -75,7 +75,7 @@ void braid2d_mat_test(const std::string &type, #endif // Make views. - auto coordsetView = axom::mir::views::make_uniform_coordset<2>(deviceMesh["coordsets/coords"]); + auto coordsetView = axom::mir::views::make_uniform_coordset<2>::view(deviceMesh["coordsets/coords"]); auto topologyView = axom::mir::views::make_uniform<2>::view(deviceMesh["topologies/mesh"]); using CoordsetView = decltype(coordsetView); using TopologyView = decltype(topologyView); @@ -139,14 +139,26 @@ TEST(mir_equiz, equiz_uniform_unibuffer) } //------------------------------------------------------------------------------ +#if defined(DEBUGGING_TEST_CASES) +void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) +{ + std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; + // This is on purpose. + while(1) + ; +} #endif +//------------------------------------------------------------------------------ + int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); axom::slic::SimpleLogger logger; // create & initialize test logger, - +#if defined(DEBUGGING_TEST_CASES) + conduit::utils::set_error_handler(conduit_debug_err_handler); +#endif result = RUN_ALL_TESTS(); return result; } diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 0fa78cca35..39c3a45656 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -28,8 +28,8 @@ namespace views */ struct Material { - int number; - std::string name; + int number {}; + std::string name {}; }; using MaterialInformation = std::vector; diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 962fe90bc0..083d980ee3 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -101,9 +101,9 @@ struct make_uniform_coordset<3> { dims[i] = n_dims.fetch_existing(keys[i]).to_int(); if(coordset.has_child("origin")) - origin[i] = coordset["origin"][keys[i]].to_double(); + origin[i] = coordset["origin"][i].to_double(); if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][keys[i]].to_double(); + spacing[i] = coordset["spacing"][i].to_double(); } return CoordsetView(dims, origin, spacing); } @@ -132,9 +132,9 @@ struct make_uniform_coordset<2> { dims[i] = n_dims.fetch_existing(keys[i]).to_int(); if(coordset.has_child("origin")) - origin[i] = coordset["origin"][keys[i]].to_double(); + origin[i] = coordset["origin"][i].to_double(); if(coordset.has_child("spacing")) - spacing[i] = coordset["spacing"][keys[i]].to_double(); + spacing[i] = coordset["spacing"][i].to_double(); } return CoordsetView(dims, origin, spacing); } diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 75ce93743e..28dfe25396 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -47,9 +47,9 @@ struct make_uniform<3> SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - zoneDims[1] = n_dims.as_int_accessor()[1] - 1; - zoneDims[2] = n_dims.as_int_accessor()[2] - 1; + zoneDims[0] = n_dims[0].to_index_t() - 1; + zoneDims[1] = n_dims[1].to_index_t() - 1; + zoneDims[2] = n_dims[2].to_index_t() - 1; return Indexing(zoneDims); } @@ -86,8 +86,8 @@ struct make_uniform<2> SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; - zoneDims[1] = n_dims.as_int_accessor()[1] - 1; + zoneDims[0] = n_dims[0].to_index_t() - 1; + zoneDims[1] = n_dims[1].to_index_t() - 1; return Indexing(zoneDims); } @@ -124,7 +124,7 @@ struct make_uniform<1> SLIC_ASSERT(coordset != nullptr); const conduit::Node &n_dims = coordset->fetch_existing("dims"); LogicalIndex zoneDims; - zoneDims[0] = n_dims.as_int_accessor()[0] - 1; + zoneDims[0] = n_dims[0].to_index_t() - 1; return Indexing(zoneDims); } From 4f8e3592a6ef07a29d08aeeac11dbd7235d39872 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 13 Aug 2024 17:01:11 -0700 Subject: [PATCH 171/290] MIR improvements. Started some new helper classes that can replace part of the clip algorithm. --- src/axom/mir/EquiZAlgorithm.hpp | 389 ++++++++++++++++-- src/axom/mir/NodeToZoneRelationBuilder.hpp | 17 +- .../mir/tests/mir_blueprint_utilities.cpp | 15 +- 3 files changed, 374 insertions(+), 47 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 7efb08818c..e7c75813c6 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -50,6 +50,273 @@ struct MatsetToField } }; +#if 0 + +template +struct RelationView +{ + axom::ArrayView m_zonesView; + axom::ArrayView m_sizesView; + axom::ArrayView m_offsetsView; +}; + +template +class MatsetIntersector +{ +public: + using ConnectivityType = ConnectivityT; + using MatsetView = MatsetViewType; + using RelationView = RelationViewType; + + MatsetIntersector(const MatsetView &mv, const RelationView &rv) : m_view(mv, rv) + { } + + /** + * \brief This is a view class for MatsetIntersector that can be used in device code. + */ + struct View + { + using VFList = typename MatsetView::VFList; + constexpr ConnectivityType NULL_MAT = -1; + + View() : m_matsetView(), m_relationView() + { + } + + View(const MatsetView &mv, const RelationView &rv) : m_matsetView(mv), m_relationView(rv) + { } + + AXOM_HOST_DEVICE + axom::IndexType determineClipCase(const ZoneType &zone) const + { + VFList vf1, vf2; + getVolumeFractionsAtNodes(mat1, mat2, zone.getIds(), vf1, vf2); + + size_t clipcase = 0; + for(IndexType i = 0; i < zone.numberOfNodes(); i++) + { + clipcase |= (vf1[i] > vf2[i]) ? (1 << i) : 0; + } + return clipcase; + } + + /** + * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. + * + * \param id0 The mesh node at the start of the edge. + * \param id1 The mesh node at the end of the edge. + */ + AXOM_HOST_DEVICE + float computeWeight(ConnectivityType id0, ConnectivityType id1) const + { + // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. + ConnectivityType ids[2] = {id0, id1}; + ConnectivityView idsView(ids, 2); + VFList vf1, vf2; + getVolumeFractionsAtNodes(mat1, mat2, ids, vf1, vf2); + + vf1[0] = axom::utilities::max(vf1[0], 0.f); + vf1[1] = axom::utilities::max(vf1[1], 0.f); + vf2[0] = axom::utilities::max(vf2[0], 0.f); + vf2[1] = axom::utilities::max(vf2[1], 0.f); + + float numerator = vf2[0] - vf1[0]; + float demoninator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; + + float t = 0.f; + if(denominator != 0.f) + { + t = numerator / denominator; + } + t = axom::utilities::clampVal(ret, 0.f, 1.f); + + return t; + } + + /** + * \brief Return node-averaged volume fractions for the specified material at the given node ids. + */ + AXOM_HOST_DEVICE + void getVolumeFractionAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const + { + for(axom::IndexType i = 0; i < nodeIds.size(); i++) + { + const auto nid = nodeIds[i]; + + // Use the relation to average VFs for this material to the node. + const auto size = m_relationView.m_sizesView[nid]; + const auto offset = m_relationView.m_offsetsView[nid]; + float vfSum{}; + for(axom::IndexType j = 0; j < size; j++) + { + const auto zoneIndex = m_relationView.m_zonesView[offset + j]; + float vf{}; + m_matsetView.zoneContainsMaterial(zoneIndex, materialNumber, vf); + vfSum += vf; + } + + vfs.push_back(vfSum / static_cast(size)); + } + } + + AXOM_HOST_DEVICE + void getVolumeFractionsAtNodes(int mat1, int mat2, const ConnectivityView &nodeIds, VFList &vf1, VFList &vf2) const + { + if(mat1 == NULL_MAT) + { + for(axom::IndexType i = 0; i < nodeIds.size(); i++) + vf1.push_back(-1); + } + else + { + getVolumeFractionAtNodes(mat1, nodeIds, vf1); + } + + if(mat2 == NULL_MAT) + { + for(axom::IndexType i = 0; i < nodeIds.size(); i++) + vf1.push_back(-1); + } + else + { + getVolumeFractionAtNodes(mat2, nodeIds, vf2); + } + } + + MatsetView m_matsetView {}; + RelationView m_relationView {}; + int m_dominantMaterial {0}; + int m_currentMaterial {0}; + }; + + /** + * \brief Initialize the object from options. + * \param n_options The node that contains the options. + * \param n_fields The node that contains fields. + * \param allocatorID The allocator ID to use when allocating memory. + */ + void initialize(const conduit::Node &n_options, const conduit::Node &AXOM_UNUSED_PARAM(n_fields), int AXOM_UNUSED_PARAM(allocatorID)) + { + // We pass in the current material from the MIR algorithm via the options. + if(n_options.has_path("currentMaterial")) + m_view.m_currentMaterial = n_options["currentMaterial"].to_int(); + } + + View m_view {}; +}; + + +template +class FieldIntersector +{ +public: + using ClipFieldType = float; + using ConnectivityView = axom::ArrayView; + + /** + * \brief This is a view class for FieldIntersector that can be used in device code. + */ + struct View + { + /** + * \brief Given a zone index and the node ids that comprise the zone, return + * the appropriate clip case, taking into account the clip field and + * clip value. + * + * \param zoneIndex The zone index. + * \param nodeIds A view containing node ids for the zone. + */ + AXOM_HOST_DEVICE + axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIds) const + { + size_t clipcase = 0; + for(IndexType i = 0; i < nodeIds.size(); i++) + { + const auto id = nodeIds[i]; + const auto value = m_clipFieldView[id] - m_clipValue; + clipcase |= (value > 0) ? (1 << i) : 0; + } + return clipcase; + } + + /** + * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. + * + * \param id0 The mesh node at the start of the edge. + * \param id1 The mesh node at the end of the edge. + */ + AXOM_HOST_DEVICE + ClipFieldType computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const + { + const ClipFieldType d0 = m_clipFieldView[id0]; + const ClipFieldType d1 = m_clipFieldView[id1]; + constexpr ClipFieldType tiny = 1.e-09; + return axom::utilities::clampVal(axom::utilities::abs(m_clipValue - d0) / + (axom::utilities::abs(d1 - d0) + tiny), + ClipFieldType(0), + ClipFieldType(1)); + } + + axom::ArrayView m_clipFieldView {}; + ClipFieldType m_clipValue {}; + }; + + /** + * \brief Initialize the object from options. + * \param n_options The node that contains the options. + * \param n_fields The node that contains fields. + * \param allocatorID The allocator ID to use when allocating memory. + */ + void initialize(const conduit::Node &n_options, const conduit::Node &n_fields, int allocatorID) + { + // Get the clip field and value. + ClipOptions opts(n_options); + std::string clipFieldName = opts.clipField(); + m_view.m_clipValue = opts.clipValue(); + + // Make sure the clipField is the right data type and store access to it in the view. + const conduit + const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); + const conduit::Node &n_clip_field_values = n_clip_field["values"]; + SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); + if(n_clip_field_values.dtype().is_float32()) + { + // Make a view. + m_view.m_clipFieldView = + bputils::make_array_view(n_clip_field_values); + } + else + { + // Convert to ClipFieldType. + const IndexType n = + static_cast(n_clip_field_values.dtype().number_of_elements()); + m_clipFieldData = axom::Array(n, n, allocatorID); + auto clipFieldView = clipFieldData.view(); + m_view.m_clipFieldView = clipFieldView; + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { + axom::for_all( + n, + AXOM_LAMBDA(auto index) { + clipFieldView[index] = + static_cast(clipFieldViewSrc[index]); + }); + }); + } + } + + View view() const + { + return m_view; + } + +private: + axom::Array m_clipFieldData {}; + View m_view {}; +} + + +#endif + /** * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. @@ -74,6 +341,14 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm virtual ~EquiZAlgorithm() = default; protected: +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); +} + /** * \brief Perform material interface reconstruction on a single domain. * @@ -125,7 +400,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // In later iterations, we do not want to pass selectedZones through // since they are only valid on the current input topology. Also, if they // were passed then the new topology only has those selected zones. - n_options_copy.remove("selectedZones"); + if(n_options_copy.has_child("selectedZones")) + { + n_options_copy.remove("selectedZones"); + } } else { @@ -135,7 +413,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputFields.move(n_newFields); n_InputMatset.move(n_newMatset); - const auto shape = n_newTopo.fetch_existing("elements/shape").as_string(); + const auto shape = n_InputTopo.fetch_existing("elements/shape").as_string(); if(shape == "mixed") { // The data are now an unstructured view, probably a mixed shape view. @@ -148,23 +426,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { using ITopologyView = decltype(topologyView); - // Assume we made this type out of the first iteration. + // The output of the first iteration was a unibuffer matset. using IMatsetView = axom::mir::views::UnibufferMaterialView; IMatsetView matsetView; matsetView.set( - bputils::make_array_view(n_matset["material_ids"]), - bputils::make_array_view(n_matset["volume_fractions"]), - bputils::make_array_view(n_matset["sizes"]), - bputils::make_array_view(n_matset["offsets"]), - bputils::make_array_view(n_matset["indices"])); + bputils::make_array_view(n_InputMatset["material_ids"]), + bputils::make_array_view(n_InputMatset["volume_fractions"]), + bputils::make_array_view(n_InputMatset["sizes"]), + bputils::make_array_view(n_InputMatset["offsets"]), + bputils::make_array_view(n_InputMatset["indices"])); - // + // Do the next iteration. + // clangformat-off iteration( topologyView, coordsetView, matsetView, matInfo[i], n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset, n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); + // clangformat-on }); }); // clangformat-on @@ -206,8 +486,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newMatset) { namespace bputils = axom::mir::utilities::blueprint; + namespace bpmeshutils = conduit::blueprint::mesh::utils; using FloatType = typename MatsetView::FloatType; constexpr auto floatTypeID = bputils::cpp2conduit::id; +std::cout << "------------------------ start of iteration --------------------------------\n"; // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. bputils::ConduitAllocateThroughAxom c2a; @@ -222,7 +504,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node relation; { bputils::NodeToZoneRelationBuilder rb; - rb.execute(n_topo, relation); // <----------------------- Should this algorithm take a topo view? + rb.execute(n_topo, n_coordset, relation); +std::cout << "\nnodeToZoneRelation = \n"; +printNode(relation); +std::cout.flush(); } // Make a shallow copy of the fields that we can modify. @@ -245,6 +530,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { MatsetToField m2f; m2f.execute(matsetView, currentMat.number, zoneMatFieldView); + +std::cout << "\nzoneMatField = \n"; +printNode(zoneMatField); +std::cout.flush(); } // Recenter the field to nodal using the node to zone relation. @@ -252,6 +541,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { bputils::RecenterField recenter; recenter.execute(zoneMatField, relation, nodeMatField); +std::cout << "\nnodeMatField = \n"; +printNode(nodeMatField); +std::cout.flush(); } /** @@ -270,7 +562,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm if(n_options.has_child("selectedZones")) { // Pass selectedZones along in the clip options, if present. - options["selectedZones"].set_external(n_options["selectedZones"]); + options["selectedZones"].set_external(n_options.fetch_existing("selectedZones")); } { axom::mir::clipping::ClipField clipper(topoView, coordsetView); @@ -278,28 +570,41 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. } - #if 1 + n_newFields.remove(zoneCenteredField); + n_newFields.remove(nodeCenteredField); + +#if 1 + // Print clip results. conduit::Node mesh; mesh[n_newTopo.path()].set_external(n_newTopo); mesh[n_newCoordset.path()].set_external(n_newCoordset); mesh[n_newFields.path()].set_external(n_newFields); - mesh[n_newMatset.path()].set_external(n_newMatset); +// printNode(mesh); - mesh.print(); + // Print old matset. +// printNode(n_matset); - std::stringstream ss; - ss << "debug_equiz_" << currentMat.number; - conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); - #endif + // Print old matset. +// printNode(n_newMatset); - n_newFields.remove(zoneCenteredField); - n_newFields.remove(nodeCenteredField); +#endif // Make a new matset. - auto colorView = axom::mir::utilities::blueprint::make_array_view(n_newFields[colorField +"/values"]); - auto origElemView = axom::mir::utilities::blueprint::make_array_view(n_newFields["originalElements/values"]); + auto colorView = bputils::make_array_view(n_newFields[colorField +"/values"]); + auto origElemView = bputils::make_array_view(n_newFields["originalElements/values"]); makeNewMatset(matsetView, currentMat, colorView, origElemView, n_matset, n_newMatset); +#if 1 + // Print/save mesh with new matset. + mesh[n_newMatset.path()].set_external(n_newMatset); + printNode(mesh); +std::cout << "------------------------ end of iteration --------------------------------\n"; +std::cout.flush(); + std::stringstream ss; + ss << "debug_equiz_" << currentMat.number; + conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); +#endif + n_newFields.remove("originalElements"); } @@ -333,7 +638,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Copy the material map to the new matset. if(n_matset.has_child("material_map")) { - n_newMatset.set(n_matset.fetch_existing("material_map")); + bputils::copy(n_newMatset["material_map"], n_matset.fetch_existing("material_map")); + } + if(n_matset.has_child("topology")) + { + n_newMatset["topology"].set(n_matset.fetch_existing("topology")); } // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. @@ -350,7 +659,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Count how many slots are needed for the new material. Make sizes. RAJA::ReduceSum sum(0); - auto sizesView = axom::mir::utilities::blueprint::make_array_view(n_sizes); + auto sizesView = bputils::make_array_view(n_sizes); +std::cout << "makeNewMatset: nzones=" << nzones << std::endl; + const int currentMatNumber = currentMat.number; axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) { axom::IndexType nmatsThisZone = 0; @@ -362,18 +673,29 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { // These were the unselected parts of the zone after clipping. // When we make the new material, this set of zones remove the current material. - nmatsThisZone = currentMatsetView.numberOfMaterials(zoneIndex) - 1; + const auto origIndex = originalElementsView[zoneIndex]; + nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); + + // If the original zone was mixed and it contains the current material, + // subtract 1 since we will not include the current material. +// if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) +// { +// nmatsThisZone--; +// } } +std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] << ", nmats=" << nmatsThisZone << std::endl; + sizesView[zoneIndex] = nmatsThisZone; sum += nmatsThisZone; }); // Make offsets. - auto offsetsView = axom::mir::utilities::blueprint::make_array_view(n_offsets); + auto offsetsView = bputils::make_array_view(n_offsets); axom::exclusive_scan(sizesView, offsetsView); // Make new matset data. const auto arraySize = sum.get(); +std::cout << "arraySize=" << arraySize << std::endl; conduit::Node &n_material_ids = n_newMatset["material_ids"]; conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; conduit::Node &n_indices = n_newMatset["indices"]; @@ -384,9 +706,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_volume_fractions.set(conduit::DataType(floatTypeID, arraySize)); n_indices.set(conduit::DataType(intTypeID, arraySize)); - auto matidsView = axom::mir::utilities::blueprint::make_array_view(n_material_ids); - auto vfView = axom::mir::utilities::blueprint::make_array_view(n_volume_fractions); - auto indicesView = axom::mir::utilities::blueprint::make_array_view(n_material_ids); + auto matidsView = bputils::make_array_view(n_material_ids); + auto vfView = bputils::make_array_view(n_volume_fractions); + auto indicesView = bputils::make_array_view(n_indices); // Fill in the indices used. axom::for_all(arraySize, AXOM_LAMBDA(auto index) @@ -395,12 +717,12 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm }); // Fill in the material data. - const int currentMatNumber = currentMat.number; axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) { auto offset = offsetsView[zoneIndex]; if(colorView[zoneIndex] == COLOR0) { +std::cout << "\tzone " << zoneIndex << ": offset=" << offset << ", id=" << currentMatNumber << ", vf=1\n"; matidsView[offset] = currentMatNumber; vfView[offset] = 1; } @@ -416,16 +738,19 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm FloatType vfTotal {}; for(axom::IndexType i = 0; i < ids.size(); i++) { - vfTotal += (ids[i] != currentMatNumber) ? vfs[i] : 0; + vfTotal += vfs[i];//(ids[i] != currentMatNumber) ? vfs[i] : 0; } // Fill in the new materials. for(axom::IndexType i = 0; i < ids.size(); i++) { - if(ids[i] != currentMatNumber) +// if(ids[i] != currentMatNumber) { matidsView[offset] = ids[i]; vfView[offset] = vfs[i] / vfTotal; + +std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << "\n"; + offset++; } } diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 5916fa5676..14cbd69b6e 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -38,9 +38,10 @@ class NodeToZoneRelationBuilder * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. * * \param topo The topology for which we're building the O2M relation. + * \param coordset The topology's coordset. * \param[out] The node that will contain the O2M relation. */ - void execute(const conduit::Node &topo, conduit::Node &relation); + void execute(const conduit::Node &topo, const conduit::Node &coordset, conduit::Node &relation); private: /** @@ -108,6 +109,7 @@ class NodeToZoneRelationBuilder template void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, + const conduit::Node &coordset, conduit::Node &relation) { const std::string type = topo.fetch_existing("type").as_string(); @@ -123,20 +125,17 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, n_sizes.set_allocator(conduitAllocatorID); n_offsets.set_allocator(conduitAllocatorID); - const conduit::Node *coordset = - conduit::blueprint::mesh::utils::find_reference_node(topo, "coordset"); - SLIC_ASSERT(coordset != nullptr); - if(type == "unstructured") { conduit::blueprint::mesh::utils::ShapeType shape(topo); const conduit::Node &n_connectivity = topo["elements/connectivity"]; + const std::string shapeType = topo["elements/shape"].as_string(); const auto intTypeId = n_connectivity.dtype().id(); const auto connSize = n_connectivity.dtype().number_of_elements(); // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. const auto nnodes = - conduit::blueprint::mesh::utils::coordset::length(*coordset); + conduit::blueprint::mesh::utils::coordset::length(coordset); if(shape.is_polyhedral()) { @@ -214,7 +213,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, }); }); } - else if(shape.is_polygonal()) + else if(shape.is_polygonal() || shapeType == "mixed") { const conduit::Node &n_topo_sizes = topo["elements/sizes"]; const conduit::Node &n_topo_offsets = topo["elements/offsets"]; @@ -310,12 +309,12 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, conduit::Node mesh; axom::mir::utilities::blueprint::to_unstructured(topo, - *coordset, + coordset, "newtopo", mesh); // Recurse using the unstructured mesh. - execute(mesh.fetch_existing("topologies/newtopo"), relation); + execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); } } diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 963df01e81..5dee62d076 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -154,12 +154,13 @@ void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; // Run the algorithm on the device conduit::Node deviceRelation; axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceRelation); + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); // device -> host conduit::Node hostRelation; @@ -257,12 +258,13 @@ void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; // Run the algorithm on the device conduit::Node deviceRelation; axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceRelation); + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); // device -> host conduit::Node hostRelation; @@ -359,12 +361,13 @@ void test_recenter_field(const conduit::Node &hostMesh) // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; // Make a node to zone relation on the device. conduit::Node deviceRelation; axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceRelation); + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); // Recenter a field zonal->nodal on the device axom::mir::utilities::blueprint::RecenterField r; From 09dfb43d1da74b68c67710cf786daced19131561 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 13 Aug 2024 17:57:04 -0700 Subject: [PATCH 172/290] Abstract out Intersector code from ClipField so I can template on an Intersector --- src/axom/mir/ClipField.hpp | 224 +++++++++++------- src/axom/mir/EquiZAlgorithm.hpp | 119 +--------- src/axom/mir/tests/mir_testing_helpers.hpp | 5 + .../UnstructuredTopologyPolyhedralView.hpp | 8 +- 4 files changed, 150 insertions(+), 206 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 4b84e5a226..c91780ec63 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -132,57 +132,128 @@ inline bool shapeIsSelected(unsigned char color, int selection) (color1Selected(selection) && color == COLOR1); } -/** - * \brief Compute a parametric value for where clipValues occurs in [d0,d1], clamped to [0,1]. - * - * \param d0 The first data value. - * \param d1 The second data value. - * \param clipValue The data value we're looking for. - * - * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. - */ -template -AXOM_HOST_DEVICE inline T computeWeight(T d0, T d1, T clipValue) -{ - constexpr T tiny = 1.e-09; - return axom::utilities::clampVal(axom::utilities::abs(clipValue - d0) / - (axom::utilities::abs(d1 - d0) + tiny), - T(0), - T(1)); -} +} // end namespace details -// TODO: Could we make ZoneType be a concept? +//------------------------------------------------------------------------------ /** - * \brief Use the ids for the provided zone and the values from the array view to determine a clip case. - * - * \tparam ZoneType A class that implements the Shape interface. - * \tparam DataType The data type of the clip data. - * - * \param[in] zone The zone being clipped. - * \param[in] view The view that can access the clipping distance field. - * \param[in] clipValue The value used for clipping. - * - * \return The index of the clipping case. + * \brief This class helps ClipField determine intersection cases and weights + * using a field designated by the options. */ -template -AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, - const axom::ArrayView &view, - DataType clipValue) +template +class FieldIntersector { - size_t clipcase = 0; - for(IndexType i = 0; i < zone.numberOfNodes(); i++) +public: + using ClipFieldType = float; + using ConnectivityView = axom::ArrayView; + + /** + * \brief This is a view class for FieldIntersector that can be used in device code. + */ + struct View + { + /** + * \brief Given a zone index and the node ids that comprise the zone, return + * the appropriate clip case, taking into account the clip field and + * clip value. + * + * \param zoneIndex The zone index. + * \param nodeIds A view containing node ids for the zone. + */ + AXOM_HOST_DEVICE + axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIds) const + { + size_t clipcase = 0; + for(IndexType i = 0; i < nodeIds.size(); i++) + { + const auto id = nodeIds[i]; + const auto value = m_clipFieldView[id] - m_clipValue; + clipcase |= (value > 0) ? (1 << i) : 0; + } + return clipcase; + } + + /** + * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. + * + * \param id0 The mesh node at the start of the edge. + * \param id1 The mesh node at the end of the edge. + * + * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. + */ + AXOM_HOST_DEVICE + ClipFieldType computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const + { + const ClipFieldType d0 = m_clipFieldView[id0]; + const ClipFieldType d1 = m_clipFieldView[id1]; + constexpr ClipFieldType tiny = 1.e-09; + return axom::utilities::clampVal(axom::utilities::abs(m_clipValue - d0) / + (axom::utilities::abs(d1 - d0) + tiny), + ClipFieldType(0), + ClipFieldType(1)); + } + + axom::ArrayView m_clipFieldView {}; + ClipFieldType m_clipValue {}; + }; + + /** + * \brief Initialize the object from options. + * \param n_options The node that contains the options. + * \param n_fields The node that contains fields. + */ + void initialize(const conduit::Node &n_options, const conduit::Node &n_fields) + { + namespace bputils = axom::mir::utilities::blueprint; + const int allocatorID = axom::execution_space::allocatorID(); + + // Get the clip field and value. + ClipOptions opts(n_options); + std::string clipFieldName = opts.clipField(); + m_view.m_clipValue = opts.clipValue(); + + // Make sure the clipField is the right data type and store access to it in the view. + const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); + const conduit::Node &n_clip_field_values = n_clip_field["values"]; + SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); + if(n_clip_field_values.dtype().is_float32()) + { + // Make a view. + m_view.m_clipFieldView = + bputils::make_array_view(n_clip_field_values); + } + else + { + // Convert to ClipFieldType. + const IndexType n = + static_cast(n_clip_field_values.dtype().number_of_elements()); + m_clipFieldData = axom::Array(n, n, allocatorID); + auto clipFieldView = m_view.m_clipFieldView = m_clipFieldData.view(); + views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { + axom::for_all( + n, + AXOM_LAMBDA(auto index) { + clipFieldView[index] = + static_cast(clipFieldViewSrc[index]); + }); + }); + } + } + + /** + * \brief Return a new instance of the view. + * \return A new instance of the view. + */ + View view() const { - const auto id = zone.getId(i); - const auto value = view[id] - clipValue; - clipcase |= (value > 0) ? (1 << i) : 0; + return m_view; } - return clipcase; -} -} // end namespace details +private: + axom::Array m_clipFieldData {}; + View m_view {}; +}; //------------------------------------------------------------------------------ - /** * \accelerated * \brief This class clips a topology using a field and puts the new topology into a new Conduit node. @@ -190,11 +261,13 @@ AXOM_HOST_DEVICE size_t clip_case(const ZoneType &zone, * \tparam ExecSpace The execution space where the compute-heavy kernels run. * \tparam TopologyView The topology view that can operate on the Blueprint topology. * \tparam CoordsetView The coordset view that can operate on the Blueprint coordset. + * \tparam IntersectPolicy The intersector policy that can helps with cases and weights. * \tparam NamingPolicy The policy for making names from arrays of ids. */ template , typename NamingPolicy = axom::mir::utilities::HashNaming> class ClipField { @@ -202,6 +275,7 @@ class ClipField using BlendData = axom::mir::utilities::blueprint::BlendData; using SliceData = axom::mir::utilities::blueprint::SliceData; using ClipTableViews = axom::StackArray; + using Intersector = IntersectPolicy; using BitSet = std::uint64_t; using KeyType = typename NamingPolicy::KeyType; @@ -218,9 +292,10 @@ class ClipField * \param coordsetView A coordset view suitable for the supplied coordset. * */ - ClipField(const TopologyView &topoView, const CoordsetView &coordsetView) + ClipField(const TopologyView &topoView, const CoordsetView &coordsetView, const Intersector &intersector = Intersector()) : m_topologyView(topoView) , m_coordsetView(coordsetView) + , m_intersector(intersector) , m_clipTables() { } @@ -290,35 +365,8 @@ class ClipField n_options); const auto nzones = selectedZones.view().size(); - // Get the clip field. Make sure it is double. That lets us make less code down the line. - std::string clipFieldName = opts.clipField(); - const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); - const conduit::Node &n_clip_field_values = n_clip_field["values"]; - SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - axom::Array clipFieldData; - axom::ArrayView clipFieldView; - if(n_clip_field_values.dtype().is_float32()) - { - // Make a view. - clipFieldView = - bputils::make_array_view(n_clip_field_values); - } - else - { - // Convert to double. - const IndexType n = - static_cast(n_clip_field_values.dtype().number_of_elements()); - clipFieldData = axom::Array(n, n, allocatorID); - clipFieldView = clipFieldData.view(); - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { - axom::for_all( - n, - AXOM_LAMBDA(auto index) { - clipFieldView[index] = - static_cast(clipFieldViewSrc[index]); - }); - }); - } + // Give the intersector a chance to further initialize. + m_intersector.initialize(n_options, n_fields); // Load clip table data and make views. m_clipTables.load(m_topologyView.dimension()); @@ -381,8 +429,7 @@ class ClipField zoneData, fragmentData, opts, - selectedZones, - clipFieldView); + selectedZones); computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); @@ -416,8 +463,7 @@ class ClipField builder, zoneData, opts, - selectedZones, - clipFieldView); + selectedZones); // Make the blend groups unique axom::Array uNames; @@ -560,7 +606,6 @@ class ClipField * \param[in] zoneData This object holds views to per-zone data. * \param[in] fragmentData This object holds views to per-fragment data. * \param[inout] opts Clipping options. - * \param[in] clipFieldView The view that contains clipping field values. * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ @@ -569,20 +614,19 @@ class ClipField ZoneData zoneData, FragmentData fragmentData, const ClipOptions &opts, - const SelectedZones &selectedZones, - const axom::ArrayView &clipFieldView) const + const SelectedZones &selectedZones) const { - const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); auto blendGroupsView = builder.state().m_blendGroupsView; auto blendGroupsLenView = builder.state().m_blendGroupsLenView; + const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. - const auto clipcase = details::clip_case(zone, clipFieldView, clipValue); + const auto clipcase = deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); zoneData.m_clipCasesView[szIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. @@ -732,7 +776,6 @@ class ClipField * \param[in] builder This object holds views to blend group data and helps with building/access. * \param[in] zoneData This object holds views to per-zone data. * \param[inout] opts Clipping options. - * \param[in] clipFieldView The view that contains clipping field values. * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ @@ -740,15 +783,15 @@ class ClipField BlendGroupBuilderType builder, ZoneData zoneData, const ClipOptions &opts, - const SelectedZones &selectedZones, - const axom::ArrayView &clipFieldView) const + const SelectedZones &selectedZones) const { const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); + const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -796,9 +839,7 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const auto t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); + const auto t = deviceIntersector.computeWeight(zoneIndex, id0, id1); groups.add(id0, one_over_n * (1.f - t)); groups.add(id1, one_over_n * t); @@ -831,9 +872,7 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const auto t = details::computeWeight(clipFieldView[id0], - clipFieldView[id1], - clipValue); + const auto t = deviceIntersector.computeWeight(zoneIndex, id0, id1); groups.beginGroup(); groups.add(id0, 1.f - t); @@ -1276,6 +1315,7 @@ class ClipField private: TopologyView m_topologyView; CoordsetView m_coordsetView; + Intersector m_intersector; axom::mir::clipping::ClipTableManager m_clipTables; }; diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index e7c75813c6..ce2b837da3 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -205,116 +205,6 @@ class MatsetIntersector View m_view {}; }; - -template -class FieldIntersector -{ -public: - using ClipFieldType = float; - using ConnectivityView = axom::ArrayView; - - /** - * \brief This is a view class for FieldIntersector that can be used in device code. - */ - struct View - { - /** - * \brief Given a zone index and the node ids that comprise the zone, return - * the appropriate clip case, taking into account the clip field and - * clip value. - * - * \param zoneIndex The zone index. - * \param nodeIds A view containing node ids for the zone. - */ - AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIds) const - { - size_t clipcase = 0; - for(IndexType i = 0; i < nodeIds.size(); i++) - { - const auto id = nodeIds[i]; - const auto value = m_clipFieldView[id] - m_clipValue; - clipcase |= (value > 0) ? (1 << i) : 0; - } - return clipcase; - } - - /** - * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. - * - * \param id0 The mesh node at the start of the edge. - * \param id1 The mesh node at the end of the edge. - */ - AXOM_HOST_DEVICE - ClipFieldType computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const - { - const ClipFieldType d0 = m_clipFieldView[id0]; - const ClipFieldType d1 = m_clipFieldView[id1]; - constexpr ClipFieldType tiny = 1.e-09; - return axom::utilities::clampVal(axom::utilities::abs(m_clipValue - d0) / - (axom::utilities::abs(d1 - d0) + tiny), - ClipFieldType(0), - ClipFieldType(1)); - } - - axom::ArrayView m_clipFieldView {}; - ClipFieldType m_clipValue {}; - }; - - /** - * \brief Initialize the object from options. - * \param n_options The node that contains the options. - * \param n_fields The node that contains fields. - * \param allocatorID The allocator ID to use when allocating memory. - */ - void initialize(const conduit::Node &n_options, const conduit::Node &n_fields, int allocatorID) - { - // Get the clip field and value. - ClipOptions opts(n_options); - std::string clipFieldName = opts.clipField(); - m_view.m_clipValue = opts.clipValue(); - - // Make sure the clipField is the right data type and store access to it in the view. - const conduit - const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); - const conduit::Node &n_clip_field_values = n_clip_field["values"]; - SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - if(n_clip_field_values.dtype().is_float32()) - { - // Make a view. - m_view.m_clipFieldView = - bputils::make_array_view(n_clip_field_values); - } - else - { - // Convert to ClipFieldType. - const IndexType n = - static_cast(n_clip_field_values.dtype().number_of_elements()); - m_clipFieldData = axom::Array(n, n, allocatorID); - auto clipFieldView = clipFieldData.view(); - m_view.m_clipFieldView = clipFieldView; - views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { - axom::for_all( - n, - AXOM_LAMBDA(auto index) { - clipFieldView[index] = - static_cast(clipFieldViewSrc[index]); - }); - }); - } - } - - View view() const - { - return m_view; - } - -private: - axom::Array m_clipFieldData {}; - View m_view {}; -} - - #endif /** @@ -683,7 +573,9 @@ std::cout << "makeNewMatset: nzones=" << nzones << std::endl; // nmatsThisZone--; // } } +#if !defined(AXOM_DEVICE_CODE) std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] << ", nmats=" << nmatsThisZone << std::endl; +#endif sizesView[zoneIndex] = nmatsThisZone; sum += nmatsThisZone; @@ -722,7 +614,9 @@ std::cout << "arraySize=" << arraySize << std::endl; auto offset = offsetsView[zoneIndex]; if(colorView[zoneIndex] == COLOR0) { +#if !defined(AXOM_DEVICE_CODE) std::cout << "\tzone " << zoneIndex << ": offset=" << offset << ", id=" << currentMatNumber << ", vf=1\n"; +#endif matidsView[offset] = currentMatNumber; vfView[offset] = 1; } @@ -749,8 +643,9 @@ std::cout << "\tzone " << zoneIndex << ": offset=" << offset << ", id=" << curre matidsView[offset] = ids[i]; vfView[offset] = vfs[i] / vfTotal; -std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << "\n"; - +#if !defined(AXOM_DEVICE_CODE) +//std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << "\n"; +#endif offset++; } } diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 1ee1cba180..ae1d854104 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -216,6 +216,11 @@ bool compareBaseline(const std::vector &baselinePaths, if(!success) { info.print(); +#if 1 + std::string errFile(filename + "_err"); + conduit::relay::io::blueprint::save_mesh(current, errFile, "hdf5"); + conduit::relay::io::blueprint::save_mesh(current, errFile + "_yaml", "yaml"); +#endif } // We found a baseline so we can exit break; diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 738a3dfe8e..36fa71771b 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -104,11 +104,13 @@ class UnstructuredTopologyPolyhedralView const auto faceIds = getFace(f); for(axom::IndexType i = 0; i < faceIds.size(); i++) { - if(nnodes < MaximumNumberOfIds) + if(nnodes + 1 <= MaximumNumberOfIds) m_ids[nnodes++] = faceIds[i]; else { +#if !defined(AXOM_DEVICE_CODE) SLIC_ERROR("m_ids is not large enough to hold all node ids."); +#endif break; } } @@ -127,11 +129,13 @@ class UnstructuredTopologyPolyhedralView { if(!find(m_ids.m_data, nnodes, faceIds[i])) { - if(nnodes < MaximumNumberOfIds) + if(nnodes + 1 <= MaximumNumberOfIds) m_ids[nnodes++] = faceIds[i]; else { +#if !defined(AXOM_DEVICE_CODE) SLIC_ERROR("m_ids is not large enough to hold all node ids."); +#endif break; } } From c8d84c6f5ee36c1487aa6c02a981b570dfd9fc0e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 13 Aug 2024 17:58:41 -0700 Subject: [PATCH 173/290] make style --- src/axom/mir/ClipField.hpp | 46 ++- src/axom/mir/EquiZAlgorithm.hpp | 348 ++++++++++++--------- src/axom/mir/NodeToZoneRelationBuilder.hpp | 4 +- src/axom/mir/tests/mir_equiz.cpp | 11 +- src/axom/mir/tests/mir_testing_helpers.hpp | 4 +- 5 files changed, 231 insertions(+), 182 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index c91780ec63..9ad41f25a0 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -160,7 +160,8 @@ class FieldIntersector * \param nodeIds A view containing node ids for the zone. */ AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIds) const + axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ConnectivityView &nodeIds) const { size_t clipcase = 0; for(IndexType i = 0; i < nodeIds.size(); i++) @@ -181,15 +182,17 @@ class FieldIntersector * \return A parametric position t [0,1] where we locate \a clipValues in [d0,d1]. */ AXOM_HOST_DEVICE - ClipFieldType computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const + ClipFieldType computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + ConnectivityType id0, + ConnectivityType id1) const { const ClipFieldType d0 = m_clipFieldView[id0]; const ClipFieldType d1 = m_clipFieldView[id1]; constexpr ClipFieldType tiny = 1.e-09; return axom::utilities::clampVal(axom::utilities::abs(m_clipValue - d0) / - (axom::utilities::abs(d1 - d0) + tiny), - ClipFieldType(0), - ClipFieldType(1)); + (axom::utilities::abs(d1 - d0) + tiny), + ClipFieldType(0), + ClipFieldType(1)); } axom::ArrayView m_clipFieldView {}; @@ -243,10 +246,7 @@ class FieldIntersector * \brief Return a new instance of the view. * \return A new instance of the view. */ - View view() const - { - return m_view; - } + View view() const { return m_view; } private: axom::Array m_clipFieldData {}; @@ -267,7 +267,8 @@ class FieldIntersector template , + typename IntersectPolicy = axom::mir::clipping:: + FieldIntersector, typename NamingPolicy = axom::mir::utilities::HashNaming> class ClipField { @@ -292,7 +293,9 @@ class ClipField * \param coordsetView A coordset view suitable for the supplied coordset. * */ - ClipField(const TopologyView &topoView, const CoordsetView &coordsetView, const Intersector &intersector = Intersector()) + ClipField(const TopologyView &topoView, + const CoordsetView &coordsetView, + const Intersector &intersector = Intersector()) : m_topologyView(topoView) , m_coordsetView(coordsetView) , m_intersector(intersector) @@ -424,12 +427,7 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, - builder, - zoneData, - fragmentData, - opts, - selectedZones); + computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, selectedZones); computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); @@ -459,11 +457,7 @@ class ClipField blendGroupStart.view(), blendIds.view(), blendCoeff.view()); - makeBlendGroups(clipTableViews, - builder, - zoneData, - opts, - selectedZones); + makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones); // Make the blend groups unique axom::Array uNames; @@ -626,7 +620,8 @@ class ClipField selectedZones.view(), AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { // Get the clip case for the current zone. - const auto clipcase = deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); + const auto clipcase = + deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); zoneData.m_clipCasesView[szIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. @@ -839,7 +834,8 @@ class ClipField const auto id1 = zone.getId(edge[1]); // Figure out the blend for edge. - const auto t = deviceIntersector.computeWeight(zoneIndex, id0, id1); + const auto t = + deviceIntersector.computeWeight(zoneIndex, id0, id1); groups.add(id0, one_over_n * (1.f - t)); groups.add(id1, one_over_n * t); @@ -1315,7 +1311,7 @@ class ClipField private: TopologyView m_topologyView; CoordsetView m_coordsetView; - Intersector m_intersector; + Intersector m_intersector; axom::mir::clipping::ClipTableManager m_clipTables; }; diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index ce2b837da3..64f8809d19 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -28,7 +28,6 @@ namespace axom { namespace mir { - /** * \brief Populate values for a new field based on a material's volume fraction. */ @@ -37,16 +36,19 @@ struct MatsetToField { using MaterialIndex = typename MatsetView::MaterialIndex; using FloatType = typename MatsetView::FloatType; - - void execute(const MatsetView &matsetView, MaterialIndex mat, axom::ArrayView &vfValues) + + void execute(const MatsetView &matsetView, + MaterialIndex mat, + axom::ArrayView &vfValues) { const auto nzones = vfValues.size(); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - FloatType vf{}; - matsetView.zoneContainsMaterial(zoneIndex, mat, vf); - vfValues[zoneIndex] = vf; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + FloatType vf {}; + matsetView.zoneContainsMaterial(zoneIndex, mat, vf); + vfValues[zoneIndex] = vf; + }); } }; @@ -222,24 +224,27 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \param coordsetView The coordset view to use for the input data. * \param matsetView The matset view to use for the input data. */ - EquiZAlgorithm(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) : - m_topologyView(topoView), m_coordsetView(coordsetView), m_matsetView(matsetView) - { - } + EquiZAlgorithm(const TopologyView &topoView, + const CoordsetView &coordsetView, + const MatsetView &matsetView) + : m_topologyView(topoView) + , m_coordsetView(coordsetView) + , m_matsetView(matsetView) + { } /// Destructor virtual ~EquiZAlgorithm() = default; protected: -void printNode(const conduit::Node &n) -{ - conduit::Node options; - options["num_children_threshold"] = 10000; - options["num_elements_threshold"] = 10000; - n.to_summary_string_stream(std::cout, options); -} + void printNode(const conduit::Node &n) + { + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); + } - /** + /** * \brief Perform material interface reconstruction on a single domain. * * \param[in] n_topo The Conduit node containing the topology that will be used for MIR. @@ -270,7 +275,7 @@ void printNode(const conduit::Node &n) conduit::Node n_options_copy; bputils::copy(n_options_copy, n_options); - // Iterate over the materials + // Iterate over the materials const auto matInfo = axom::mir::views::materials(n_matset); conduit::Node n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset; for(size_t i = 0; i < matInfo.size(); i++) @@ -279,12 +284,19 @@ void printNode(const conduit::Node &n) { // The first time through, we can use the supplied views. // clangformat-off - iteration( - m_topologyView, m_coordsetView, m_matsetView, - matInfo[i], - n_topo, n_coordset, n_fields, n_matset, - n_options_copy, - n_newTopo, n_newCoordset, n_newFields, n_newMatset); + iteration(m_topologyView, + m_coordsetView, + m_matsetView, + matInfo[i], + n_topo, + n_coordset, + n_fields, + n_matset, + n_options_copy, + n_newTopo, + n_newCoordset, + n_newFields, + n_newMatset); // clangformat-on // In later iterations, we do not want to pass selectedZones through @@ -303,39 +315,50 @@ void printNode(const conduit::Node &n) n_InputFields.move(n_newFields); n_InputMatset.move(n_newMatset); - const auto shape = n_InputTopo.fetch_existing("elements/shape").as_string(); + const auto shape = + n_InputTopo.fetch_existing("elements/shape").as_string(); if(shape == "mixed") { // The data are now an unstructured view, probably a mixed shape view. // Dispatch to an appropriate topo view // clangformat-off - views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) - { + views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { using ICoordsetView = decltype(coordsetView); - views::dispatch_unstructured_mixed_topology(n_InputTopo, [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) - { - using ITopologyView = decltype(topologyView); - - // The output of the first iteration was a unibuffer matset. - using IMatsetView = axom::mir::views::UnibufferMaterialView; - IMatsetView matsetView; - matsetView.set( - bputils::make_array_view(n_InputMatset["material_ids"]), - bputils::make_array_view(n_InputMatset["volume_fractions"]), - bputils::make_array_view(n_InputMatset["sizes"]), - bputils::make_array_view(n_InputMatset["offsets"]), - bputils::make_array_view(n_InputMatset["indices"])); - - // Do the next iteration. - // clangformat-off - iteration( - topologyView, coordsetView, matsetView, - matInfo[i], - n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset, - n_options_copy, - n_newTopo, n_newCoordset, n_newFields, n_newMatset); - // clangformat-on - }); + views::dispatch_unstructured_mixed_topology( + n_InputTopo, + [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { + using ITopologyView = decltype(topologyView); + + // The output of the first iteration was a unibuffer matset. + using IMatsetView = + axom::mir::views::UnibufferMaterialView; + IMatsetView matsetView; + matsetView.set( + bputils::make_array_view(n_InputMatset["material_ids"]), + bputils::make_array_view( + n_InputMatset["volume_fractions"]), + bputils::make_array_view(n_InputMatset["sizes"]), + bputils::make_array_view(n_InputMatset["offsets"]), + bputils::make_array_view(n_InputMatset["indices"])); + + // Do the next iteration. + // clangformat-off + iteration( + topologyView, + coordsetView, + matsetView, + matInfo[i], + n_InputTopo, + n_InputCoordset, + n_InputFields, + n_InputMatset, + n_options_copy, + n_newTopo, + n_newCoordset, + n_newFields, + n_newMatset); + // clangformat-on + }); }); // clangformat-on } @@ -379,7 +402,8 @@ void printNode(const conduit::Node &n) namespace bpmeshutils = conduit::blueprint::mesh::utils; using FloatType = typename MatsetView::FloatType; constexpr auto floatTypeID = bputils::cpp2conduit::id; -std::cout << "------------------------ start of iteration --------------------------------\n"; + std::cout << "------------------------ start of iteration " + "--------------------------------\n"; // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. bputils::ConduitAllocateThroughAxom c2a; @@ -395,9 +419,9 @@ std::cout << "------------------------ start of iteration ---------------------- { bputils::NodeToZoneRelationBuilder rb; rb.execute(n_topo, n_coordset, relation); -std::cout << "\nnodeToZoneRelation = \n"; -printNode(relation); -std::cout.flush(); + std::cout << "\nnodeToZoneRelation = \n"; + printNode(relation); + std::cout.flush(); } // Make a shallow copy of the fields that we can modify. @@ -421,9 +445,9 @@ std::cout.flush(); MatsetToField m2f; m2f.execute(matsetView, currentMat.number, zoneMatFieldView); -std::cout << "\nzoneMatField = \n"; -printNode(zoneMatField); -std::cout.flush(); + std::cout << "\nzoneMatField = \n"; + printNode(zoneMatField); + std::cout.flush(); } // Recenter the field to nodal using the node to zone relation. @@ -431,12 +455,12 @@ std::cout.flush(); { bputils::RecenterField recenter; recenter.execute(zoneMatField, relation, nodeMatField); -std::cout << "\nnodeMatField = \n"; -printNode(nodeMatField); -std::cout.flush(); + std::cout << "\nnodeMatField = \n"; + printNode(nodeMatField); + std::cout.flush(); } -/** + /** NOTE - I am a little worried that I've not yet appreciated how the intersections are found in the EquiZ algorithm and that I'll need to add a new template parameter to ClipField that lets that behavior be customizable so I can override it here. @@ -452,11 +476,20 @@ std::cout.flush(); if(n_options.has_child("selectedZones")) { // Pass selectedZones along in the clip options, if present. - options["selectedZones"].set_external(n_options.fetch_existing("selectedZones")); + options["selectedZones"].set_external( + n_options.fetch_existing("selectedZones")); } { - axom::mir::clipping::ClipField clipper(topoView, coordsetView); - clipper.execute(n_topo, n_coordset, tmpFields, options, n_newTopo, n_newCoordset, n_newFields); + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(n_topo, + n_coordset, + tmpFields, + options, + n_newTopo, + n_newCoordset, + n_newFields); // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. } @@ -469,27 +502,35 @@ std::cout.flush(); mesh[n_newTopo.path()].set_external(n_newTopo); mesh[n_newCoordset.path()].set_external(n_newCoordset); mesh[n_newFields.path()].set_external(n_newFields); -// printNode(mesh); + // printNode(mesh); - // Print old matset. -// printNode(n_matset); + // Print old matset. + // printNode(n_matset); - // Print old matset. -// printNode(n_newMatset); + // Print old matset. + // printNode(n_newMatset); #endif // Make a new matset. - auto colorView = bputils::make_array_view(n_newFields[colorField +"/values"]); - auto origElemView = bputils::make_array_view(n_newFields["originalElements/values"]); - makeNewMatset(matsetView, currentMat, colorView, origElemView, n_matset, n_newMatset); + auto colorView = + bputils::make_array_view(n_newFields[colorField + "/values"]); + auto origElemView = + bputils::make_array_view(n_newFields["originalElements/values"]); + makeNewMatset(matsetView, + currentMat, + colorView, + origElemView, + n_matset, + n_newMatset); #if 1 // Print/save mesh with new matset. mesh[n_newMatset.path()].set_external(n_newMatset); printNode(mesh); -std::cout << "------------------------ end of iteration --------------------------------\n"; -std::cout.flush(); + std::cout << "------------------------ end of iteration " + "--------------------------------\n"; + std::cout.flush(); std::stringstream ss; ss << "debug_equiz_" << currentMat.number; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); @@ -509,17 +550,18 @@ std::cout.flush(); * \param n_newMatset The output matset. */ template - void makeNewMatset(// by value - const IMatsetView currentMatsetView, - const axom::mir::views::Material currentMat, - const axom::ArrayView colorView, - const axom::ArrayView originalElementsView, - // by reference - const conduit::Node &n_matset, - conduit::Node &n_newMatset) + void makeNewMatset( // by value + const IMatsetView currentMatsetView, + const axom::mir::views::Material currentMat, + const axom::ArrayView colorView, + const axom::ArrayView originalElementsView, + // by reference + const conduit::Node &n_matset, + conduit::Node &n_newMatset) { namespace bputils = axom::mir::utilities::blueprint; - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; using IntType = typename IMatsetView::IndexType; using FloatType = typename IMatsetView::FloatType; const int intTypeID = bputils::cpp2conduit::id; @@ -528,7 +570,8 @@ std::cout.flush(); // Copy the material map to the new matset. if(n_matset.has_child("material_map")) { - bputils::copy(n_newMatset["material_map"], n_matset.fetch_existing("material_map")); + bputils::copy(n_newMatset["material_map"], + n_matset.fetch_existing("material_map")); } if(n_matset.has_child("topology")) { @@ -550,36 +593,38 @@ std::cout.flush(); // Count how many slots are needed for the new material. Make sizes. RAJA::ReduceSum sum(0); auto sizesView = bputils::make_array_view(n_sizes); -std::cout << "makeNewMatset: nzones=" << nzones << std::endl; + std::cout << "makeNewMatset: nzones=" << nzones << std::endl; const int currentMatNumber = currentMat.number; - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - axom::IndexType nmatsThisZone = 0; - if(colorView[zoneIndex] == COLOR0) - { - nmatsThisZone = 1; - } - else - { - // These were the unselected parts of the zone after clipping. - // When we make the new material, this set of zones remove the current material. - const auto origIndex = originalElementsView[zoneIndex]; - nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); - - // If the original zone was mixed and it contains the current material, - // subtract 1 since we will not include the current material. -// if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) -// { -// nmatsThisZone--; -// } - } + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + axom::IndexType nmatsThisZone = 0; + if(colorView[zoneIndex] == COLOR0) + { + nmatsThisZone = 1; + } + else + { + // These were the unselected parts of the zone after clipping. + // When we make the new material, this set of zones remove the current material. + const auto origIndex = originalElementsView[zoneIndex]; + nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); + + // If the original zone was mixed and it contains the current material, + // subtract 1 since we will not include the current material. + // if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) + // { + // nmatsThisZone--; + // } + } #if !defined(AXOM_DEVICE_CODE) -std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] << ", nmats=" << nmatsThisZone << std::endl; + std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] + << ", nmats=" << nmatsThisZone << std::endl; #endif - sizesView[zoneIndex] = nmatsThisZone; - sum += nmatsThisZone; - }); + sizesView[zoneIndex] = nmatsThisZone; + sum += nmatsThisZone; + }); // Make offsets. auto offsetsView = bputils::make_array_view(n_offsets); @@ -587,7 +632,7 @@ std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] << ", // Make new matset data. const auto arraySize = sum.get(); -std::cout << "arraySize=" << arraySize << std::endl; + std::cout << "arraySize=" << arraySize << std::endl; conduit::Node &n_material_ids = n_newMatset["material_ids"]; conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; conduit::Node &n_indices = n_newMatset["indices"]; @@ -603,54 +648,55 @@ std::cout << "arraySize=" << arraySize << std::endl; auto indicesView = bputils::make_array_view(n_indices); // Fill in the indices used. - axom::for_all(arraySize, AXOM_LAMBDA(auto index) - { - indicesView[index] = index; - }); + axom::for_all( + arraySize, + AXOM_LAMBDA(auto index) { indicesView[index] = index; }); // Fill in the material data. - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - auto offset = offsetsView[zoneIndex]; - if(colorView[zoneIndex] == COLOR0) - { + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + auto offset = offsetsView[zoneIndex]; + if(colorView[zoneIndex] == COLOR0) + { #if !defined(AXOM_DEVICE_CODE) -std::cout << "\tzone " << zoneIndex << ": offset=" << offset << ", id=" << currentMatNumber << ", vf=1\n"; + std::cout << "\tzone " << zoneIndex << ": offset=" << offset + << ", id=" << currentMatNumber << ", vf=1\n"; #endif - matidsView[offset] = currentMatNumber; - vfView[offset] = 1; - } - else - { - // Get the material ids and volume fractions from the original material's zone. - const auto origIndex = originalElementsView[zoneIndex]; - typename IMatsetView::IDList ids {}; - typename IMatsetView::VFList vfs {}; - currentMatsetView.zoneMaterials(origIndex, ids, vfs); - - // Total up the materials, excluding the current material. - FloatType vfTotal {}; - for(axom::IndexType i = 0; i < ids.size(); i++) - { - vfTotal += vfs[i];//(ids[i] != currentMatNumber) ? vfs[i] : 0; + matidsView[offset] = currentMatNumber; + vfView[offset] = 1; } - - // Fill in the new materials. - for(axom::IndexType i = 0; i < ids.size(); i++) + else { -// if(ids[i] != currentMatNumber) + // Get the material ids and volume fractions from the original material's zone. + const auto origIndex = originalElementsView[zoneIndex]; + typename IMatsetView::IDList ids {}; + typename IMatsetView::VFList vfs {}; + currentMatsetView.zoneMaterials(origIndex, ids, vfs); + + // Total up the materials, excluding the current material. + FloatType vfTotal {}; + for(axom::IndexType i = 0; i < ids.size(); i++) { - matidsView[offset] = ids[i]; - vfView[offset] = vfs[i] / vfTotal; + vfTotal += vfs[i]; //(ids[i] != currentMatNumber) ? vfs[i] : 0; + } + + // Fill in the new materials. + for(axom::IndexType i = 0; i < ids.size(); i++) + { + // if(ids[i] != currentMatNumber) + { + matidsView[offset] = ids[i]; + vfView[offset] = vfs[i] / vfTotal; #if !defined(AXOM_DEVICE_CODE) //std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << "\n"; #endif - offset++; + offset++; + } } } - } - }); + }); } private: diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 14cbd69b6e..7229fb5f86 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -41,7 +41,9 @@ class NodeToZoneRelationBuilder * \param coordset The topology's coordset. * \param[out] The node that will contain the O2M relation. */ - void execute(const conduit::Node &topo, const conduit::Node &coordset, conduit::Node &relation); + void execute(const conduit::Node &topo, + const conduit::Node &coordset, + conduit::Node &relation); private: /** diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 00976000d8..533aa14e6a 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -75,8 +75,10 @@ void braid2d_mat_test(const std::string &type, #endif // Make views. - auto coordsetView = axom::mir::views::make_uniform_coordset<2>::view(deviceMesh["coordsets/coords"]); - auto topologyView = axom::mir::views::make_uniform<2>::view(deviceMesh["topologies/mesh"]); + auto coordsetView = axom::mir::views::make_uniform_coordset<2>::view( + deviceMesh["coordsets/coords"]); + auto topologyView = + axom::mir::views::make_uniform<2>::view(deviceMesh["topologies/mesh"]); using CoordsetView = decltype(coordsetView); using TopologyView = decltype(topologyView); @@ -93,8 +95,9 @@ void braid2d_mat_test(const std::string &type, bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), bputils::make_array_view(deviceMesh["matsets/mat/indices"])); // clang-format on - - using MIR = axom::mir::EquiZAlgorithm; + + using MIR = + axom::mir::EquiZAlgorithm; MIR m(topologyView, coordsetView, matsetView); conduit::Node options, deviceMIRMesh; options["matset"] = "mat"; diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index ae1d854104..2b72781b3b 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -219,7 +219,9 @@ bool compareBaseline(const std::vector &baselinePaths, #if 1 std::string errFile(filename + "_err"); conduit::relay::io::blueprint::save_mesh(current, errFile, "hdf5"); - conduit::relay::io::blueprint::save_mesh(current, errFile + "_yaml", "yaml"); + conduit::relay::io::blueprint::save_mesh(current, + errFile + "_yaml", + "yaml"); #endif } // We found a baseline so we can exit From b71a743be613c8b33340f7d72c52ddb08e0cbc67 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 14 Aug 2024 14:35:56 -0700 Subject: [PATCH 174/290] MIR improvements --- src/axom/mir/ClipField.hpp | 36 ++- src/axom/mir/EquiZAlgorithm.hpp | 359 +++++++++++++++------------- src/axom/mir/views/MaterialView.hpp | 68 ++++++ 3 files changed, 290 insertions(+), 173 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9ad41f25a0..7a229c7c89 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -163,7 +163,7 @@ class FieldIntersector axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIds) const { - size_t clipcase = 0; + axom::IndexType clipcase = 0; for(IndexType i = 0; i < nodeIds.size(); i++) { const auto id = nodeIds[i]; @@ -242,6 +242,21 @@ class FieldIntersector } } + /** + * \brief Determine the name of the topology on which to operate. + * \param n_input The input mesh node. + * \param n_options The clipping options. + * \return The name of the toplogy on which to operate. + */ + std::string getTopologyName(const conduit::Node &n_input, const conduit::Node &n_options) const + { + // Get the clipField's topo name. + ClipOptions opts(n_options); + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + const conduit::Node &n_clipField = n_fields.fetch_existing(opts.clipField()); + return n_clipField["topology"].as_string(); + } + /** * \brief Return a new instance of the view. * \return A new instance of the view. @@ -315,18 +330,15 @@ class ClipField const conduit::Node &n_options, conduit::Node &n_output) { + // Get the topo/coordset names in the input. ClipOptions opts(n_options); - const std::string clipFieldName = opts.clipField(); - - // Get clipField's topo/coordset. - const conduit::Node &n_fields = n_input.fetch_existing("fields"); - const conduit::Node &n_clipField = n_fields.fetch_existing(clipFieldName); - const std::string &topoName = n_clipField["topology"].as_string(); + const std::string topoName = m_intersector.getTopologyName(n_input, n_options); const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); - const std::string &coordsetName = n_topo["coordset"].as_string(); + const std::string coordsetName = n_topo["coordset"].as_string(); const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); + const conduit::Node &n_fields = n_input.fetch_existing("fields"); execute(n_topo, n_coordset, @@ -501,13 +513,13 @@ class ClipField std::map fieldsToProcess; if(!opts.fields(fieldsToProcess)) { - // Fields were not present in the options. Select all fields that have the same topology as opts.clipField(). - const std::string clipTopology = - n_fields.fetch_existing(opts.clipField() + "/topology").as_string(); +std::cout << " fields to process:\n"; + // Fields were not present in the options. Select all fields that have the same topology as n_topo. for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { - if(n_fields[i].fetch_existing("topology").as_string() == clipTopology) + if(n_fields[i].fetch_existing("topology").as_string() == n_topo.name()) { +std::cout << " " << n_fields[i].name() << std::endl; fieldsToProcess[n_fields[i].name()] = n_fields[i].name(); } } diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 64f8809d19..3c0f7ab2bc 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -52,21 +52,25 @@ struct MatsetToField } }; -#if 0 +#if 1 template struct RelationView { - axom::ArrayView m_zonesView; - axom::ArrayView m_sizesView; - axom::ArrayView m_offsetsView; + axom::ArrayView m_zonesView {}; + axom::ArrayView m_sizesView {}; + axom::ArrayView m_offsetsView {}; }; -template +/** + * \tparam MAXELEMENTS the max number of nodes in any zone we'll encounter. + */ +template class MatsetIntersector { public: using ConnectivityType = ConnectivityT; + using ConnectivityView = axom::ArrayView; using MatsetView = MatsetViewType; using RelationView = RelationViewType; @@ -78,27 +82,49 @@ class MatsetIntersector */ struct View { - using VFList = typename MatsetView::VFList; - constexpr ConnectivityType NULL_MAT = -1; + using FloatType = typename MatsetView::FloatType; + using VFList = StaticArray; +// constexpr static ConnectivityType NULL_MAT = -1; - View() : m_matsetView(), m_relationView() - { - } + View() = default; View(const MatsetView &mv, const RelationView &rv) : m_matsetView(mv), m_relationView(rv) { } AXOM_HOST_DEVICE - axom::IndexType determineClipCase(const ZoneType &zone) const + axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIds) const { VFList vf1, vf2; - getVolumeFractionsAtNodes(mat1, mat2, zone.getIds(), vf1, vf2); + const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); + getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIds, vf1, vf2); - size_t clipcase = 0; - for(IndexType i = 0; i < zone.numberOfNodes(); i++) + axom::IndexType clipcase = 0; + const auto n = nodeIds.size(); + for(IndexType i = 0; i < n; i++) + { + clipcase |= (vf2[i] > vf1[i]) ? (1 << i) : 0; + } +#if 1 +std::cout << "clipcase: zoneIndex=" << zoneIndex + << ", dominantMaterial=" << dominantMaterial + << ", m_currentMaterial=" << m_currentMaterial + << ", nodeIds={"; + for(IndexType i = 0; i < n; i++) { - clipcase |= (vf1[i] > vf2[i]) ? (1 << i) : 0; + std::cout << nodeIds[i] << ", "; } +std::cout << "}, vf1={"; + for(IndexType i = 0; i < vf1.size(); i++) + { + std::cout << vf1[i] << ", "; + } +std::cout << "}, vf2={"; + for(IndexType i = 0; i < vf2.size(); i++) + { + std::cout << vf2[i] << ", "; + } +std::cout << "}, clipcase=" << clipcase << std::endl; +#endif return clipcase; } @@ -109,28 +135,29 @@ class MatsetIntersector * \param id1 The mesh node at the end of the edge. */ AXOM_HOST_DEVICE - float computeWeight(ConnectivityType id0, ConnectivityType id1) const + float computeWeight(axom::IndexType zoneIndex, ConnectivityType id0, ConnectivityType id1) const { // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. - ConnectivityType ids[2] = {id0, id1}; - ConnectivityView idsView(ids, 2); + ConnectivityType nodeIds[2] = {id0, id1}; + ConnectivityView nodeIdsView(nodeIds, 2); VFList vf1, vf2; - getVolumeFractionsAtNodes(mat1, mat2, ids, vf1, vf2); + const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); + getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIdsView, vf1, vf2); - vf1[0] = axom::utilities::max(vf1[0], 0.f); - vf1[1] = axom::utilities::max(vf1[1], 0.f); - vf2[0] = axom::utilities::max(vf2[0], 0.f); - vf2[1] = axom::utilities::max(vf2[1], 0.f); + //vf1[0] = axom::utilities::max(vf1[0], 0.f); + //vf1[1] = axom::utilities::max(vf1[1], 0.f); + //vf2[0] = axom::utilities::max(vf2[0], 0.f); + //vf2[1] = axom::utilities::max(vf2[1], 0.f); float numerator = vf2[0] - vf1[0]; - float demoninator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; + float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; float t = 0.f; if(denominator != 0.f) { t = numerator / denominator; } - t = axom::utilities::clampVal(ret, 0.f, 1.f); + t = axom::utilities::clampVal(t, 0.f, 1.f); return t; } @@ -141,30 +168,40 @@ class MatsetIntersector AXOM_HOST_DEVICE void getVolumeFractionAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const { +std::cout << "{\ngetVolumeFractionAtNodes: mat=" << materialNumber << std::endl; for(axom::IndexType i = 0; i < nodeIds.size(); i++) { const auto nid = nodeIds[i]; // Use the relation to average VFs for this material to the node. - const auto size = m_relationView.m_sizesView[nid]; + auto size = m_relationView.m_sizesView[nid]; + using SizeType = decltype(size); const auto offset = m_relationView.m_offsetsView[nid]; +std::cout << "\tnid=" << nid << ", size=" << size << ", offset=" << offset << std::endl; float vfSum{}; - for(axom::IndexType j = 0; j < size; j++) + for(SizeType j = 0; j < size; j++) { const auto zoneIndex = m_relationView.m_zonesView[offset + j]; float vf{}; m_matsetView.zoneContainsMaterial(zoneIndex, materialNumber, vf); vfSum += vf; +std::cout <<"\t\tzoneIndex=" << zoneIndex << ", vf=" << vf << ", vfSum=" << vfSum << std::endl; } - - vfs.push_back(vfSum / static_cast(size)); + const float nodeVF = vfSum / static_cast(size); +std::cout << "\tnodeVF=" << nodeVF << std::endl; + vfs.push_back(nodeVF); } +std::cout << "}" << std::endl; } AXOM_HOST_DEVICE void getVolumeFractionsAtNodes(int mat1, int mat2, const ConnectivityView &nodeIds, VFList &vf1, VFList &vf2) const { - if(mat1 == NULL_MAT) +#if 1 + getVolumeFractionAtNodes(mat1, nodeIds, vf1); + getVolumeFractionAtNodes(mat2, nodeIds, vf2); +#else + if(mat1 == NULL_MAT) // Q: do we need this? { for(axom::IndexType i = 0; i < nodeIds.size(); i++) vf1.push_back(-1); @@ -183,11 +220,11 @@ class MatsetIntersector { getVolumeFractionAtNodes(mat2, nodeIds, vf2); } +#endif } MatsetView m_matsetView {}; RelationView m_relationView {}; - int m_dominantMaterial {0}; int m_currentMaterial {0}; }; @@ -195,15 +232,33 @@ class MatsetIntersector * \brief Initialize the object from options. * \param n_options The node that contains the options. * \param n_fields The node that contains fields. - * \param allocatorID The allocator ID to use when allocating memory. */ - void initialize(const conduit::Node &n_options, const conduit::Node &AXOM_UNUSED_PARAM(n_fields), int AXOM_UNUSED_PARAM(allocatorID)) + void initialize(const conduit::Node &n_options, const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) { // We pass in the current material from the MIR algorithm via the options. if(n_options.has_path("currentMaterial")) m_view.m_currentMaterial = n_options["currentMaterial"].to_int(); } + /** + * \brief Determine the name of the topology on which to operate. + * \param n_input The input mesh node. + * \param n_options The clipping options. + * \return The name of the toplogy on which to operate. + */ + std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), const conduit::Node &n_options) const + { +std::cout << "MIR getTopologyName: " << n_options["topology"].as_string() << std::endl; + return n_options["topology"].as_string(); + } + + /** + * \brief Return a new instance of the view. + * \return A new instance of the view. + */ + View view() const { return m_view; } + +private: View m_view {}; }; @@ -274,6 +329,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Copy the options. conduit::Node n_options_copy; bputils::copy(n_options_copy, n_options); + n_options_copy["topology"] = n_topo.name(); // Iterate over the materials const auto matInfo = axom::mir::views::materials(n_matset); @@ -282,6 +338,17 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { if(i == 0) { +#if 1 + // Print input mesh. + std::cout << "-------------------------------------------------------------------" << std::endl; + conduit::Node mesh; + mesh[n_topo.path()].set_external(n_topo); + mesh[n_coordset.path()].set_external(n_coordset); + mesh[n_fields.path()].set_external(n_fields); + mesh[n_matset.path()].set_external(n_matset); + printNode(mesh); + std::cout << "-------------------------------------------------------------------" << std::endl; +#endif // The first time through, we can use the supplied views. // clangformat-off iteration(m_topologyView, @@ -400,20 +467,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; - using FloatType = typename MatsetView::FloatType; - constexpr auto floatTypeID = bputils::cpp2conduit::id; std::cout << "------------------------ start of iteration " "--------------------------------\n"; - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - bputils::ConduitAllocateThroughAxom c2a; - - const std::string zoneCenteredField("__equiz__zoneMatField"); - const std::string nodeCenteredField("__equiz__nodeMatField"); const std::string colorField("__equiz__colors"); - const auto nzones = topoView.numberOfZones(); - // Make a node to zone relation. conduit::Node relation; { @@ -431,112 +489,82 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm tmpFields[n_fields[i].name()].set_external(n_fields[i]); } - // Make a zonal field for the current material's volume fractions. - conduit::Node &zoneMatField = tmpFields[zoneCenteredField]; - zoneMatField["topology"] = n_topo.name(); - zoneMatField["association"] = "element"; - conduit::Node &zoneValues = zoneMatField["values"]; - zoneValues.set_allocator(c2a.getConduitAllocatorID()); - zoneValues.set(conduit::DataType(floatTypeID, nzones)); - - // Populate the zonal volume fraction field from the material view. - auto zoneMatFieldView = bputils::make_array_view(zoneValues); + axom::mir::views::IndexNode_to_ArrayView_same(relation["zones"], relation["sizes"], relation["offsets"], + [&](auto zonesView, auto sizesView, auto offsetsView) { - MatsetToField m2f; - m2f.execute(matsetView, currentMat.number, zoneMatFieldView); - - std::cout << "\nzoneMatField = \n"; - printNode(zoneMatField); - std::cout.flush(); - } - - // Recenter the field to nodal using the node to zone relation. - conduit::Node &nodeMatField = tmpFields[nodeCenteredField]; - { - bputils::RecenterField recenter; - recenter.execute(zoneMatField, relation, nodeMatField); - std::cout << "\nnodeMatField = \n"; - printNode(nodeMatField); - std::cout.flush(); - } - - /** - NOTE - I am a little worried that I've not yet appreciated how the intersections are found - in the EquiZ algorithm and that I'll need to add a new template parameter to ClipField - that lets that behavior be customizable so I can override it here. - */ - - // Now, clip the topology using the nodal field. - conduit::Node options; - options["inside"] = 1; - options["outside"] = 1; - options["clipField"] = nodeCenteredField; - options["clipValue"] = 0.5; - options["colorField"] = colorField; - if(n_options.has_child("selectedZones")) - { - // Pass selectedZones along in the clip options, if present. - options["selectedZones"].set_external( - n_options.fetch_existing("selectedZones")); - } - { - axom::mir::clipping::ClipField clipper( - topoView, - coordsetView); - clipper.execute(n_topo, - n_coordset, - tmpFields, - options, - n_newTopo, - n_newCoordset, - n_newFields); - // Q: Would it be better here to just use ClipFieldFilterDevice? We don't need all that flexibility but it might be better for linking since it would have been created already for ClipFieldFilter. - } + // Wrap the node to zone relation in a view. + using IndexT = typename decltype(zonesView)::value_type; + using RelationViewType = RelationView; + RelationViewType rv; + rv.m_zonesView = zonesView; + rv.m_sizesView = sizesView; + rv.m_offsetsView = offsetsView; + + // Make a matset intersector. + using ConnectivityType = typename ITopologyView::ConnectivityType; + using IntersectorType = MatsetIntersector; + IntersectorType intersector(matsetView, rv); + + // Make clip options. + conduit::Node options; + options["inside"] = 1; + options["outside"] = 1; + options["colorField"] = colorField; + if(n_options.has_child("selectedZones")) + { + // Pass selectedZones along in the clip options, if present. + options["selectedZones"].set_external( + n_options.fetch_existing("selectedZones")); + } + // Tell the intersector which material we're currently working on. + options["currentMaterial"] = currentMat.number; + options["topology"] = n_options["topology"]; - n_newFields.remove(zoneCenteredField); - n_newFields.remove(nodeCenteredField); + // Clip the topology using the matset. + { + using ClipperType = axom::mir::clipping::ClipField; + ClipperType clipper(topoView, coordsetView, intersector); + clipper.execute(n_topo, + n_coordset, + tmpFields, + options, + n_newTopo, + n_newCoordset, + n_newFields); + } + // Make a new matset. + auto colorView = + bputils::make_array_view(n_newFields[colorField + "/values"]); + auto origElemView = + bputils::make_array_view(n_newFields["originalElements/values"]); + makeNewMatset(matsetView, + currentMat, + colorView, + origElemView, + n_matset, + n_newMatset); + + // Save clip results. + conduit::Node mesh; + mesh[n_newTopo.path()].set_external(n_newTopo); + mesh[n_newCoordset.path()].set_external(n_newCoordset); + mesh[n_newFields.path()].set_external(n_newFields); + mesh[n_newMatset.path()].set_external(n_newMatset); #if 1 - // Print clip results. - conduit::Node mesh; - mesh[n_newTopo.path()].set_external(n_newTopo); - mesh[n_newCoordset.path()].set_external(n_newCoordset); - mesh[n_newFields.path()].set_external(n_newFields); - // printNode(mesh); - - // Print old matset. - // printNode(n_matset); - - // Print old matset. - // printNode(n_newMatset); + printNode(mesh); + std::cout << "------------------------ end of iteration " + "--------------------------------\n"; + std::cout.flush(); + std::stringstream ss; + ss << "debug_equiz_" << currentMat.number; + conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); #endif - // Make a new matset. - auto colorView = - bputils::make_array_view(n_newFields[colorField + "/values"]); - auto origElemView = - bputils::make_array_view(n_newFields["originalElements/values"]); - makeNewMatset(matsetView, - currentMat, - colorView, - origElemView, - n_matset, - n_newMatset); + n_newFields.remove("originalElements"); -#if 1 - // Print/save mesh with new matset. - mesh[n_newMatset.path()].set_external(n_newMatset); - printNode(mesh); - std::cout << "------------------------ end of iteration " - "--------------------------------\n"; - std::cout.flush(); - std::stringstream ss; - ss << "debug_equiz_" << currentMat.number; - conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); -#endif - - n_newFields.remove("originalElements"); + }); } /** @@ -549,12 +577,12 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \param n_matset The input matset (pre-MIR). * \param n_newMatset The output matset. */ - template + template void makeNewMatset( // by value const IMatsetView currentMatsetView, const axom::mir::views::Material currentMat, const axom::ArrayView colorView, - const axom::ArrayView originalElementsView, + const axom::ArrayView originalElementsView, // by reference const conduit::Node &n_matset, conduit::Node &n_newMatset) @@ -593,7 +621,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Count how many slots are needed for the new material. Make sizes. RAJA::ReduceSum sum(0); auto sizesView = bputils::make_array_view(n_sizes); - std::cout << "makeNewMatset: nzones=" << nzones << std::endl; + std::cout << "makeNewMatset: nzones=" << nzones << ", currentMatNumber=" << currentMat.number << std::endl; const int currentMatNumber = currentMat.number; axom::for_all( nzones, @@ -612,10 +640,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // If the original zone was mixed and it contains the current material, // subtract 1 since we will not include the current material. - // if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) - // { - // nmatsThisZone--; - // } + if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) + { + nmatsThisZone--; + } } #if !defined(AXOM_DEVICE_CODE) std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] @@ -661,7 +689,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { #if !defined(AXOM_DEVICE_CODE) std::cout << "\tzone " << zoneIndex << ": offset=" << offset - << ", id=" << currentMatNumber << ", vf=1\n"; + << ", id=" << currentMatNumber << ", vf=1" << std::endl; #endif matidsView[offset] = currentMatNumber; vfView[offset] = 1; @@ -674,27 +702,36 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm typename IMatsetView::VFList vfs {}; currentMatsetView.zoneMaterials(origIndex, ids, vfs); - // Total up the materials, excluding the current material. - FloatType vfTotal {}; - for(axom::IndexType i = 0; i < ids.size(); i++) + if(ids.size() > 1) { - vfTotal += vfs[i]; //(ids[i] != currentMatNumber) ? vfs[i] : 0; - } + // The original zone was mixed. Filter out currentMatNumber - // Fill in the new materials. - for(axom::IndexType i = 0; i < ids.size(); i++) - { - // if(ids[i] != currentMatNumber) + FloatType vfTotal {}; + for(axom::IndexType i = 0; i < ids.size(); i++) + { + vfTotal += (ids[i] != currentMatNumber) ? vfs[i] : 0; + } + + // Fill in the new materials. + for(axom::IndexType i = 0; i < ids.size(); i++) { - matidsView[offset] = ids[i]; - vfView[offset] = vfs[i] / vfTotal; + if(ids[i] != currentMatNumber) + { + matidsView[offset] = ids[i]; + vfView[offset] = vfs[i] / vfTotal; #if !defined(AXOM_DEVICE_CODE) -//std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << "\n"; +std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << std::endl; #endif - offset++; + offset++; + } } } + else + { + matidsView[offset] = ids[0]; + vfView[offset] = 1; + } } }); } diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 39c3a45656..fa5a4b4333 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -156,6 +156,32 @@ class UnibufferMaterialView return false; } + /** + * \brief Return the dominant material ids for the zone (highest volume fraction material). + * \param zi The zone index. + * \return The material id with the highest volume fraction. + */ + AXOM_HOST_DEVICE + int dominantMaterial(ZoneIndex zi) const + { + assert(zi < numberOfZones()); + const auto sz = numberOfMaterials(zi); + const auto offset = m_offsets[zi]; + FloatType maxVF = -1; + int mat {}; + for(axom::IndexType i = 0; i < sz; i++) + { + const auto idx = m_indices[offset + i]; + const auto vf = m_volume_fractions[idx]; + if(vf > maxVF) + { + mat = m_material_ids[idx]; + maxVF = vf; + } + } + return mat; + } + private: axom::ArrayView m_material_ids; axom::ArrayView m_volume_fractions; @@ -272,6 +298,20 @@ class MultiBufferMaterialView return vf > 0.; } + /** + * \brief Return the dominant material ids for the zone (highest volume fraction material). + * \param zi The zone index. + * \return The material id with the highest volume fraction. + */ + AXOM_HOST_DEVICE + int dominantMaterial(ZoneIndex zi) const + { + assert(zi < numberOfZones()); + FloatType maxVF = -1; + int mat {}; + // TODO: write me + return mat; + } private: axom::StackArray, MAXMATERIALS> m_values {}; axom::StackArray, MAXMATERIALS> m_indices {}; @@ -374,6 +414,20 @@ class ElementDominantMaterialView return contains; } + /** + * \brief Return the dominant material ids for the zone (highest volume fraction material). + * \param zi The zone index. + * \return The material id with the highest volume fraction. + */ + AXOM_HOST_DEVICE + int dominantMaterial(ZoneIndex zi) const + { + assert(zi < numberOfZones()); + FloatType maxVF = -1; + int mat {}; + // TODO: write me + return mat; + } private: axom::StaticArray, MAXMATERIALS> m_volume_fractions {}; }; @@ -550,6 +604,20 @@ class MaterialDominantMaterialView return found; } + /** + * \brief Return the dominant material ids for the zone (highest volume fraction material). + * \param zi The zone index. + * \return The material id with the highest volume fraction. + */ + AXOM_HOST_DEVICE + int dominantMaterial(ZoneIndex zi) const + { + assert(zi < numberOfZones()); + FloatType maxVF = -1; + int mat {}; + // TODO: write me + return mat; + } private: StackArray, MAXMATERIALS> m_element_ids {}; StackArray, MAXMATERIALS> m_volume_fractions {}; From 68f9b3fed573629c5c626d3abe251fb83ead7795 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 15 Aug 2024 13:07:07 -0700 Subject: [PATCH 175/290] debugging --- src/axom/mir/EquiZAlgorithm.hpp | 153 ++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 28 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 3c0f7ab2bc..c5a196ee60 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -53,6 +53,23 @@ struct MatsetToField }; #if 1 +AXOM_HOST_DEVICE +inline bool zoneMatters(int zoneIndex) +{ + const int zones[] = {26, +33,34,35, +36,37,38,39,40,41, +49,50, +59,60, +69, +78,79}; + for(size_t i = 0; i < sizeof(zones)/sizeof(int); i++) + { + if(zones[i] == zoneIndex) + return true; + } + return false; +} template struct RelationView @@ -95,7 +112,7 @@ class MatsetIntersector axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIds) const { VFList vf1, vf2; - const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); + const auto dominantMaterial = m_matsetView.backgroundMaterial(zoneIndex, m_currentMaterial); //dominantMaterial(zoneIndex); getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIds, vf1, vf2); axom::IndexType clipcase = 0; @@ -104,7 +121,7 @@ class MatsetIntersector { clipcase |= (vf2[i] > vf1[i]) ? (1 << i) : 0; } -#if 1 +#if 0 std::cout << "clipcase: zoneIndex=" << zoneIndex << ", dominantMaterial=" << dominantMaterial << ", m_currentMaterial=" << m_currentMaterial @@ -141,7 +158,8 @@ std::cout << "}, clipcase=" << clipcase << std::endl; ConnectivityType nodeIds[2] = {id0, id1}; ConnectivityView nodeIdsView(nodeIds, 2); VFList vf1, vf2; - const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); +// const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); + const auto dominantMaterial = m_matsetView.backgroundMaterial(zoneIndex, m_currentMaterial); //dominantMaterial(zoneIndex); getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIdsView, vf1, vf2); //vf1[0] = axom::utilities::max(vf1[0], 0.f); @@ -168,7 +186,7 @@ std::cout << "}, clipcase=" << clipcase << std::endl; AXOM_HOST_DEVICE void getVolumeFractionAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const { -std::cout << "{\ngetVolumeFractionAtNodes: mat=" << materialNumber << std::endl; +//std::cout << "{\ngetVolumeFractionAtNodes: mat=" << materialNumber << std::endl; for(axom::IndexType i = 0; i < nodeIds.size(); i++) { const auto nid = nodeIds[i]; @@ -177,7 +195,7 @@ std::cout << "{\ngetVolumeFractionAtNodes: mat=" << materialNumber << std::endl; auto size = m_relationView.m_sizesView[nid]; using SizeType = decltype(size); const auto offset = m_relationView.m_offsetsView[nid]; -std::cout << "\tnid=" << nid << ", size=" << size << ", offset=" << offset << std::endl; +//std::cout << "\tnid=" << nid << ", size=" << size << ", offset=" << offset << std::endl; float vfSum{}; for(SizeType j = 0; j < size; j++) { @@ -185,13 +203,13 @@ std::cout << "\tnid=" << nid << ", size=" << size << ", offset=" << offset << st float vf{}; m_matsetView.zoneContainsMaterial(zoneIndex, materialNumber, vf); vfSum += vf; -std::cout <<"\t\tzoneIndex=" << zoneIndex << ", vf=" << vf << ", vfSum=" << vfSum << std::endl; +//std::cout <<"\t\tzoneIndex=" << zoneIndex << ", vf=" << vf << ", vfSum=" << vfSum << std::endl; } const float nodeVF = vfSum / static_cast(size); -std::cout << "\tnodeVF=" << nodeVF << std::endl; +//std::cout << "\tnodeVF=" << nodeVF << std::endl; vfs.push_back(nodeVF); } -std::cout << "}" << std::endl; +//std::cout << "}" << std::endl; } AXOM_HOST_DEVICE @@ -331,9 +349,16 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::copy(n_options_copy, n_options); n_options_copy["topology"] = n_topo.name(); + // Make some nodes that will contain the inputs to subsequent iterations. + // Store them under a single node so the nodes will have names. + conduit::Node n_Input; + conduit::Node &n_InputTopo = n_Input[n_topo.path()]; + conduit::Node &n_InputCoordset = n_Input[n_coordset.path()]; + conduit::Node &n_InputFields = n_Input[n_fields.path()]; + conduit::Node &n_InputMatset = n_Input[n_matset.path()]; + // Iterate over the materials const auto matInfo = axom::mir::views::materials(n_matset); - conduit::Node n_InputTopo, n_InputCoordset, n_InputFields, n_InputMatset; for(size_t i = 0; i < matInfo.size(); i++) { if(i == 0) @@ -350,7 +375,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::cout << "-------------------------------------------------------------------" << std::endl; #endif // The first time through, we can use the supplied views. - // clangformat-off iteration(m_topologyView, m_coordsetView, m_matsetView, @@ -364,7 +388,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newCoordset, n_newFields, n_newMatset); - // clangformat-on // In later iterations, we do not want to pass selectedZones through // since they are only valid on the current input topology. Also, if they @@ -409,7 +432,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::make_array_view(n_InputMatset["indices"])); // Do the next iteration. - // clangformat-off iteration( topologyView, coordsetView, @@ -424,7 +446,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newCoordset, n_newFields, n_newMatset); - // clangformat-on }); }); // clangformat-on @@ -627,27 +648,29 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm nzones, AXOM_LAMBDA(auto zoneIndex) { axom::IndexType nmatsThisZone = 0; - if(colorView[zoneIndex] == COLOR0) - { - nmatsThisZone = 1; - } - else - { - // These were the unselected parts of the zone after clipping. - // When we make the new material, this set of zones remove the current material. - const auto origIndex = originalElementsView[zoneIndex]; - nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); - // If the original zone was mixed and it contains the current material, - // subtract 1 since we will not include the current material. - if(nmatsThisZone > 1 && currentMatsetView.zoneContainsMaterial(origIndex, currentMatNumber)) + const auto origIndex = originalElementsView[zoneIndex]; + nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); + //int dominantMatNumber = currentMatsetView.dominantMaterial(origIndex); + const auto dominantMatNumber = m_matsetView.backgroundMaterial(origIndex, currentMatNumber); + + if(dominantMatNumber != currentMatNumber && nmatsThisZone > 1) + { + int matToRemove = (colorView[zoneIndex] == 0) ? currentMatNumber : dominantMatNumber; + if(currentMatsetView.zoneContainsMaterial(origIndex, matToRemove)) { nmatsThisZone--; } } #if !defined(AXOM_DEVICE_CODE) - std::cout << "\tzone " << zoneIndex << ": color=" << colorView[zoneIndex] - << ", nmats=" << nmatsThisZone << std::endl; + if(zoneMatters(origIndex)) + { + std::cout << "\tzone " << zoneIndex + << ": origElem=" << origIndex + << ", color=" << colorView[zoneIndex] + << ", dominant=" << dominantMatNumber + << ", nmats=" << nmatsThisZone << std::endl; + } #endif sizesView[zoneIndex] = nmatsThisZone; @@ -685,6 +708,79 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm nzones, AXOM_LAMBDA(auto zoneIndex) { auto offset = offsetsView[zoneIndex]; + + const auto origIndex = originalElementsView[zoneIndex]; + //int dominantMatNumber = currentMatsetView.dominantMaterial(origIndex); + const auto dominantMatNumber = m_matsetView.backgroundMaterial(origIndex, currentMatNumber); + + // Get the material ids and volume fractions from the original material's zone. + typename IMatsetView::IDList ids {}; + typename IMatsetView::VFList vfs {}; + currentMatsetView.zoneMaterials(origIndex, ids, vfs); + + if(dominantMatNumber != currentMatNumber && ids.size() > 1) + { + int matToRemove = (colorView[zoneIndex] == 0) ? currentMatNumber : dominantMatNumber; + // Copy it through without colorToRemove. + FloatType vfTotal {}; + for(axom::IndexType i = 0; i < ids.size(); i++) + { + vfTotal += (ids[i] != matToRemove) ? vfs[i] : 0; + } + for(axom::IndexType i = 0; i < ids.size(); i++) + { + if(ids[i] != matToRemove) + { + matidsView[offset] = ids[i]; + vfView[offset] = vfs[i] / vfTotal; + +#if !defined(AXOM_DEVICE_CODE) + if(zoneMatters(origIndex)) + { +std::cout << "\tzone " << zoneIndex + << ": origElem=" << origIndex + << ", color=" << colorView[zoneIndex] + << ", current=" << currentMatNumber + << ", dominant=" << dominantMatNumber + << ", ids.size=" << ids.size() + << ", offset=" << offset + << ", id=" << ids[i] + << ", vf=" << vfView[offset] + << ", matToRemove=" << matToRemove + << std::endl; + } +#endif + offset++; + } + } + } + else + { + // Copy it through. + for(axom::IndexType i = 0; i < ids.size(); i++) + { + matidsView[offset] = ids[i]; + vfView[offset] = vfs[i]; + +#if !defined(AXOM_DEVICE_CODE) + if(zoneMatters(origIndex)) + { +std::cout << "\tzone " << zoneIndex + << ": origElem=" << origIndex + << ", color=" << colorView[zoneIndex] + << ", dominant=" << dominantMatNumber + << ", offset=" << offset + << ", id=" << ids[i] + << ", vf=" << vfView[offset] + << " (pass through)" + << std::endl; + } +#endif + offset++; + } + } + +/**** if(colorView[zoneIndex] == COLOR0) { #if !defined(AXOM_DEVICE_CODE) @@ -733,6 +829,7 @@ std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" vfView[offset] = 1; } } +****/ }); } From dfff7dc87926cb0763dca62223ee753da3aefe68 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 16 Aug 2024 12:13:00 -0700 Subject: [PATCH 176/290] MIR checkpoint - switching machines --- src/axom/mir/EquiZAlgorithm.hpp | 755 ++++++++---------- src/axom/mir/views/ExplicitCoordsetView.hpp | 18 +- src/axom/mir/views/MaterialView.hpp | 35 + .../mir/views/RectilinearCoordsetView.hpp | 5 + .../views/dispatch_unstructured_topology.hpp | 197 +++-- src/axom/mir/views/view_traits.hpp | 27 + 6 files changed, 521 insertions(+), 516 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index c5a196ee60..5335fafd38 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -28,10 +28,19 @@ namespace axom { namespace mir { +using MaterialIDArray = axom::Array; +using MaterialIDView = axom::ArrayView; +using MaterialVF = float; +using MaterialVFArray = axom::Array; +using MaterialVFView = axom::ArrayView; + +constexpr static int NULL_MATERIAL = -1; +constexpr static MaterialVF NULL_MATERIAL_VF = 0.f; + /** * \brief Populate values for a new field based on a material's volume fraction. */ -template +template struct MatsetToField { using MaterialIndex = typename MatsetView::MaterialIndex; @@ -39,15 +48,31 @@ struct MatsetToField void execute(const MatsetView &matsetView, MaterialIndex mat, - axom::ArrayView &vfValues) + MaterialVFView vfView) { - const auto nzones = vfValues.size(); + const auto nzones = vfView.size(); axom::for_all( nzones, AXOM_LAMBDA(auto zoneIndex) { FloatType vf {}; matsetView.zoneContainsMaterial(zoneIndex, mat, vf); - vfValues[zoneIndex] = vf; + vfView[zoneIndex] = static_cast(vf); + }); + } + + void execute(const MatsetView &matsetView, + MaterialIndex mat, + OriginalElementsView origElementsView, + MaterialVFView vfView) + { + const auto nzones = vfView.size(); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + FloatType vf {}; + const auto origIndex = origElementsView[zoneIndex]; + matsetView.zoneContainsMaterial(origIndex, mat, vf); + vfView[zoneIndex] = static_cast(vf); }); } }; @@ -80,18 +105,21 @@ struct RelationView }; /** + * \brief This class is an intersection policy compatible with ClipField. It + * helps determine clip cases and weights using material-aware logic. + * * \tparam MAXELEMENTS the max number of nodes in any zone we'll encounter. */ -template -class MatsetIntersector +template +class MaterialIntersector { public: using ConnectivityType = ConnectivityT; using ConnectivityView = axom::ArrayView; - using MatsetView = MatsetViewType; using RelationView = RelationViewType; - MatsetIntersector(const MatsetView &mv, const RelationView &rv) : m_view(mv, rv) + MaterialIntersector(const RelationView &rv, const MaterialIDView &zoneMatIds, const MaterialVFView &zoneVFs, const MaterialVFView &nodalCurrentVF) : + m_view(rv, zoneMatIds, zoneVFs, nodalCurrentVF) { } /** @@ -99,49 +127,39 @@ class MatsetIntersector */ struct View { - using FloatType = typename MatsetView::FloatType; - using VFList = StaticArray; -// constexpr static ConnectivityType NULL_MAT = -1; + using VFList = StaticArray; + /// Constructor View() = default; - View(const MatsetView &mv, const RelationView &rv) : m_matsetView(mv), m_relationView(rv) + /// Constructor + View(const RelationView &rv, const MaterialIDView &zoneMatIds, const MaterialVFView &zoneVFs, const MaterialVFView &nodalCurrentVF) : + m_relationView(rv), m_zoneMaterialIDView(zoneMatIds), m_zoneMaterialVFView(zoneVFs), + m_nodalCurrentMaterialVFView(nodalCurrentVF) { } + /** + * \brief Determine the clipping case, taking into account the zone's material + * and the current material being added. + * + * \param zoneIndex The zone index in the zoneMaterialView field. + * \param nodeIds A view containing node ids for the current zone. + * + * \return The clip case number for the zone. + */ AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIds) const + axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIdsView) const { VFList vf1, vf2; - const auto dominantMaterial = m_matsetView.backgroundMaterial(zoneIndex, m_currentMaterial); //dominantMaterial(zoneIndex); - getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIds, vf1, vf2); + getVolumeFractionsAtNodes(m_zoneMaterialIDView[zoneIndex], nodeIdsView, vf1); + getVolumeFractionsCurrent(nodeIdsView, vf2); axom::IndexType clipcase = 0; - const auto n = nodeIds.size(); + const auto n = nodeIdsView.size(); for(IndexType i = 0; i < n; i++) { clipcase |= (vf2[i] > vf1[i]) ? (1 << i) : 0; } -#if 0 -std::cout << "clipcase: zoneIndex=" << zoneIndex - << ", dominantMaterial=" << dominantMaterial - << ", m_currentMaterial=" << m_currentMaterial - << ", nodeIds={"; - for(IndexType i = 0; i < n; i++) - { - std::cout << nodeIds[i] << ", "; - } -std::cout << "}, vf1={"; - for(IndexType i = 0; i < vf1.size(); i++) - { - std::cout << vf1[i] << ", "; - } -std::cout << "}, vf2={"; - for(IndexType i = 0; i < vf2.size(); i++) - { - std::cout << vf2[i] << ", "; - } -std::cout << "}, clipcase=" << clipcase << std::endl; -#endif return clipcase; } @@ -158,14 +176,8 @@ std::cout << "}, clipcase=" << clipcase << std::endl; ConnectivityType nodeIds[2] = {id0, id1}; ConnectivityView nodeIdsView(nodeIds, 2); VFList vf1, vf2; -// const auto dominantMaterial = m_matsetView.dominantMaterial(zoneIndex); - const auto dominantMaterial = m_matsetView.backgroundMaterial(zoneIndex, m_currentMaterial); //dominantMaterial(zoneIndex); - getVolumeFractionsAtNodes(dominantMaterial, m_currentMaterial, nodeIdsView, vf1, vf2); - - //vf1[0] = axom::utilities::max(vf1[0], 0.f); - //vf1[1] = axom::utilities::max(vf1[1], 0.f); - //vf2[0] = axom::utilities::max(vf2[0], 0.f); - //vf2[1] = axom::utilities::max(vf2[1], 0.f); + getVolumeFractionsAtNodes(m_zoneMaterialIDView[zoneIndex], nodeIdsView, vf1); + getVolumeFractionsCurrent(nodeIdsView, vf2); float numerator = vf2[0] - vf1[0]; float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; @@ -182,11 +194,14 @@ std::cout << "}, clipcase=" << clipcase << std::endl; /** * \brief Return node-averaged volume fractions for the specified material at the given node ids. + * + * \param materialNumber The material number for which we want nodal volume fractions. + * \param nodeIds A view containing the node ids where we want nodal volume fractions. + * \param[out] vfs A list of output volume fractions. */ AXOM_HOST_DEVICE - void getVolumeFractionAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const + void getVolumeFractionsAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const { -//std::cout << "{\ngetVolumeFractionAtNodes: mat=" << materialNumber << std::endl; for(axom::IndexType i = 0; i < nodeIds.size(); i++) { const auto nid = nodeIds[i]; @@ -195,55 +210,35 @@ std::cout << "}, clipcase=" << clipcase << std::endl; auto size = m_relationView.m_sizesView[nid]; using SizeType = decltype(size); const auto offset = m_relationView.m_offsetsView[nid]; -//std::cout << "\tnid=" << nid << ", size=" << size << ", offset=" << offset << std::endl; - float vfSum{}; + + MaterialVF vfSum{}; for(SizeType j = 0; j < size; j++) { const auto zoneIndex = m_relationView.m_zonesView[offset + j]; - float vf{}; - m_matsetView.zoneContainsMaterial(zoneIndex, materialNumber, vf); - vfSum += vf; -//std::cout <<"\t\tzoneIndex=" << zoneIndex << ", vf=" << vf << ", vfSum=" << vfSum << std::endl; + vfSum += (m_zoneMaterialIDView[zoneIndex] == materialNumber) ? m_zoneMaterialVFView[zoneIndex] : 0.f; } - const float nodeVF = vfSum / static_cast(size); -//std::cout << "\tnodeVF=" << nodeVF << std::endl; + + const MaterialVF nodeVF = vfSum / static_cast(size); vfs.push_back(nodeVF); } -//std::cout << "}" << std::endl; } AXOM_HOST_DEVICE - void getVolumeFractionsAtNodes(int mat1, int mat2, const ConnectivityView &nodeIds, VFList &vf1, VFList &vf2) const + void getVolumeFractionsCurrent(const ConnectivityView &nodeIds, VFList &vfs) const { -#if 1 - getVolumeFractionAtNodes(mat1, nodeIds, vf1); - getVolumeFractionAtNodes(mat2, nodeIds, vf2); -#else - if(mat1 == NULL_MAT) // Q: do we need this? - { - for(axom::IndexType i = 0; i < nodeIds.size(); i++) - vf1.push_back(-1); - } - else - { - getVolumeFractionAtNodes(mat1, nodeIds, vf1); - } - - if(mat2 == NULL_MAT) - { - for(axom::IndexType i = 0; i < nodeIds.size(); i++) - vf1.push_back(-1); - } - else + for(axom::IndexType i = 0; i < nodeIds.size(); i++) { - getVolumeFractionAtNodes(mat2, nodeIds, vf2); + vfs.push_back(m_nodalCurrentMaterialVFView[nodeIds[i]]); } -#endif } - MatsetView m_matsetView {}; RelationView m_relationView {}; - int m_currentMaterial {0}; + + MaterialIDView m_zoneMaterialIDView {}; //!< Zonal field of material ids added so far. + MaterialVFView m_zoneMaterialVFView {}; //!< Zonal vf for the zone inherited from the material + + MaterialVFView m_nodalCurrentMaterialVFView {}; //!< Nodal field with current material VFs + int m_currentMaterial {0}; //!< Id of the current material }; /** @@ -266,7 +261,6 @@ std::cout << "}, clipcase=" << clipcase << std::endl; */ std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), const conduit::Node &n_options) const { -std::cout << "MIR getTopologyName: " << n_options["topology"].as_string() << std::endl; return n_options["topology"].as_string(); } @@ -357,6 +351,19 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_InputFields = n_Input[n_fields.path()]; conduit::Node &n_InputMatset = n_Input[n_matset.path()]; + // Make "empty" material data, kind of unofficial fields on the input mesh. + const int allocatorID = axom::execution_space::allocatorID(); + const auto nzones = m_topologyView.numberOfZones(); + axom::Array zoneMaterialID(nzones, nzones, allocatorID); + axom::Array zoneMaterialVF(nzones, nzones, allocatorID); + auto zoneMaterialIDView = zoneMaterialID.view(); + auto zoneMaterialVFView = zoneMaterialVF.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + zoneMaterialIDView[zoneIndex] = NULL_MATERIAL; + zoneMaterialVFView[zoneIndex] = NULL_MATERIAL_VF; + }); + // Iterate over the materials const auto matInfo = axom::mir::views::materials(n_matset); for(size_t i = 0; i < matInfo.size(); i++) @@ -364,30 +371,33 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm if(i == 0) { #if 1 - // Print input mesh. - std::cout << "-------------------------------------------------------------------" << std::endl; - conduit::Node mesh; - mesh[n_topo.path()].set_external(n_topo); - mesh[n_coordset.path()].set_external(n_coordset); - mesh[n_fields.path()].set_external(n_fields); - mesh[n_matset.path()].set_external(n_matset); - printNode(mesh); - std::cout << "-------------------------------------------------------------------" << std::endl; + // Print input mesh. + std::cout << "-------------------------------------------------------------------" << std::endl; + conduit::Node mesh; + mesh[n_topo.path()].set_external(n_topo); + mesh[n_coordset.path()].set_external(n_coordset); + mesh[n_fields.path()].set_external(n_fields); + mesh[n_matset.path()].set_external(n_matset); + printNode(mesh); + std::cout << "-------------------------------------------------------------------" << std::endl; #endif // The first time through, we can use the supplied views. - iteration(m_topologyView, - m_coordsetView, - m_matsetView, - matInfo[i], - n_topo, - n_coordset, - n_fields, - n_matset, - n_options_copy, - n_newTopo, - n_newCoordset, - n_newFields, - n_newMatset); + iteration(m_topologyView, + m_coordsetView, + + zoneMaterialID, + zoneMaterialVF, + matInfo[i], + + n_topo, + n_coordset, + n_fields, + + n_options_copy, + + n_newTopo, + n_newCoordset, + n_newFields); // In later iterations, we do not want to pass selectedZones through // since they are only valid on the current input topology. Also, if they @@ -405,59 +415,42 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputFields.move(n_newFields); n_InputMatset.move(n_newMatset); - const auto shape = - n_InputTopo.fetch_existing("elements/shape").as_string(); - if(shape == "mixed") - { - // The data are now an unstructured view, probably a mixed shape view. - // Dispatch to an appropriate topo view - // clangformat-off - views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { - using ICoordsetView = decltype(coordsetView); - views::dispatch_unstructured_mixed_topology( - n_InputTopo, - [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { - using ITopologyView = decltype(topologyView); - - // The output of the first iteration was a unibuffer matset. - using IMatsetView = - axom::mir::views::UnibufferMaterialView; - IMatsetView matsetView; - matsetView.set( - bputils::make_array_view(n_InputMatset["material_ids"]), - bputils::make_array_view( - n_InputMatset["volume_fractions"]), - bputils::make_array_view(n_InputMatset["sizes"]), - bputils::make_array_view(n_InputMatset["offsets"]), - bputils::make_array_view(n_InputMatset["indices"])); - - // Do the next iteration. - iteration( - topologyView, - coordsetView, - matsetView, - matInfo[i], - n_InputTopo, - n_InputCoordset, - n_InputFields, - n_InputMatset, - n_options_copy, - n_newTopo, - n_newCoordset, - n_newFields, - n_newMatset); - }); - }); - // clangformat-on - } - else - { - // NOTE: what if the topo output was all clean after the 1st round? - } + // The data are now an unstructured view, probably a mixed shape view. + // Dispatch to an appropriate topo view + // clangformat-off + views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { + using ICoordsetView = decltype(coordsetView); + using ConnectivityType = typename TopologyView::ConnectivityType; + views::typed_dispatch_unstructured_topology::selected_shapes()>( + n_InputTopo, + [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { + using ITopologyView = decltype(topologyView); + + // Do the next iteration. + iteration( + topologyView, + coordsetView, + + zoneMaterialID, + zoneMaterialVF, + matInfo[i], + + n_InputTopo, + n_InputCoordset, + n_InputFields, + + n_options_copy, + n_newTopo, + n_newCoordset, + n_newFields); + }); + }); + // clangformat-on } - - // TODO: we need to build the new matset. } + + // TODO: we need to build the new matset. + } /** @@ -465,26 +458,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * * \tparam ITopologyView The topology view type for the intermediate topology. * \tparam ICoordsetView The topology view type for the intermediate coordset. - * \tparam IMatsetView The topology view type for the intermediate matset. */ - template + template void iteration(const ITopologyView &topoView, const ICoordsetView &coordsetView, - const IMatsetView &matsetView, + + MaterialIDArray &zoneMaterialID, + MaterialVFArray &zoneMaterialVF, const axom::mir::views::Material ¤tMat, const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, - const conduit::Node &n_matset, const conduit::Node &n_options, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, - conduit::Node &n_newFields, - conduit::Node &n_newMatset) + conduit::Node &n_newFields) { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; @@ -492,24 +484,103 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm "--------------------------------\n"; const std::string colorField("__equiz__colors"); + const std::string nodalFieldName("__equiz__nodal_current_material"); + const std::string zonalFieldName("__equiz__zonal_current_material"); + + const int allocatorID = axom::execution_space::allocatorID(); + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; // Make a node to zone relation. conduit::Node relation; { bputils::NodeToZoneRelationBuilder rb; rb.execute(n_topo, n_coordset, relation); - std::cout << "\nnodeToZoneRelation = \n"; printNode(relation); std::cout.flush(); } - // Make a shallow copy of the fields that we can modify. - conduit::Node tmpFields; + const auto nzones = topoView.numberOfZones(); + const auto nnodes = coordsetView.numberOfNodes(); + + //-------------------------------------------------------------------------- + // + // Make input fields we can modify + // + //-------------------------------------------------------------------------- + conduit::Node n_inputFields; for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { - tmpFields[n_fields[i].name()].set_external(n_fields[i]); + n_inputFields[n_fields[i].name()].set_external(n_fields[i]); + } + + //-------------------------------------------------------------------------- + // + // Make a zonal field on the input mesh for the current material, pulling + // out its VFs from the original matsetView. + // + // NOTE: The fields are free-floating because we just are making array views + // using that data. + //-------------------------------------------------------------------------- + conduit::Node &n_zonalField = n_inputFields[zonalFieldName]; + n_zonalField["topology"] = n_topo.name(); + n_zonalField["association"] = "element"; + n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_zonalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + auto currentZonalMaterialVFView = bputils::make_array_view(n_zonalField["values"]); + + using ConnectivityType = typename ITopologyView::ConnectivityType; + using OriginalElementsView = axom::ArrayView; + + { + MatsetToField m2f; + if(n_fields.has_path("originalElements")) + { + auto originalElementsView = bputils::make_array_view(n_fields["originalElements/values"]); + m2f.execute(m_matsetView, currentMat.number, originalElementsView, currentZonalMaterialVFView); + } + else + { + m2f.execute(m_matsetView, currentMat.number, currentZonalMaterialVFView); + } + } + + // Make a nodal field for the current material by recentering. + conduit::Node &n_nodalField = n_inputFields[nodalFieldName]; + { + n_nodalField["topology"] = n_topo.name(); + n_nodalField["association"] = "vertex"; + n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_nodalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); + bputils::RecenterField z2n; + z2n.execute(n_zonalField, relation, n_nodalField); } + auto currentNodalMaterialVFView = bputils::make_array_view(n_nodalField["values"]); +#if 1 + //-------------------------------------------------------------------------- + // + // Save the iteration inputs. + // + //-------------------------------------------------------------------------- + conduit::Node n_mesh_input; + n_mesh_input[n_topo.path()].set_external(n_topo); + n_mesh_input[n_coordset.path()].set_external(n_coordset); + n_mesh_input[n_fields.path()].set_external(n_inputFields); + + // save + std::stringstream ss1; + ss1 << "debug_equiz_input." << currentMat.number; + conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); +#endif + + //-------------------------------------------------------------------------- + // + // Invoke clipping with a material intersector. + // + //-------------------------------------------------------------------------- + const auto zoneMaterialIDView = zoneMaterialID.view(); + const auto zoneMaterialVFView = zoneMaterialVF.view(); axom::mir::views::IndexNode_to_ArrayView_same(relation["zones"], relation["sizes"], relation["offsets"], [&](auto zonesView, auto sizesView, auto offsetsView) { @@ -521,10 +592,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm rv.m_sizesView = sizesView; rv.m_offsetsView = offsetsView; - // Make a matset intersector. - using ConnectivityType = typename ITopologyView::ConnectivityType; - using IntersectorType = MatsetIntersector; - IntersectorType intersector(matsetView, rv); + // Make a material intersector. + using IntersectorType = MaterialIntersector; + IntersectorType intersector(rv, zoneMaterialIDView, zoneMaterialVFView, currentNodalMaterialVFView); // Make clip options. conduit::Node options; @@ -547,296 +617,99 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm ClipperType clipper(topoView, coordsetView, intersector); clipper.execute(n_topo, n_coordset, - tmpFields, + n_inputFields, options, n_newTopo, n_newCoordset, n_newFields); } - - // Make a new matset. - auto colorView = - bputils::make_array_view(n_newFields[colorField + "/values"]); - auto origElemView = - bputils::make_array_view(n_newFields["originalElements/values"]); - makeNewMatset(matsetView, - currentMat, - colorView, - origElemView, - n_matset, - n_newMatset); - - // Save clip results. - conduit::Node mesh; - mesh[n_newTopo.path()].set_external(n_newTopo); - mesh[n_newCoordset.path()].set_external(n_newCoordset); - mesh[n_newFields.path()].set_external(n_newFields); - mesh[n_newMatset.path()].set_external(n_newMatset); -#if 1 - printNode(mesh); - std::cout << "------------------------ end of iteration " - "--------------------------------\n"; - std::cout.flush(); - - std::stringstream ss; - ss << "debug_equiz_" << currentMat.number; - conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); -#endif - - n_newFields.remove("originalElements"); - }); - } - - /** - * \brief Make a new matset for the output dataset. - * - * \param curentMatsetView The view for the current matset. - * \param currentMat The current material information. - * \param colorView A view for the color field, which says how the dataset was clipped. - * \param originalElementsView A view for the field that contains the original element number. - * \param n_matset The input matset (pre-MIR). - * \param n_newMatset The output matset. - */ - template - void makeNewMatset( // by value - const IMatsetView currentMatsetView, - const axom::mir::views::Material currentMat, - const axom::ArrayView colorView, - const axom::ArrayView originalElementsView, - // by reference - const conduit::Node &n_matset, - conduit::Node &n_newMatset) - { - namespace bputils = axom::mir::utilities::blueprint; - using reduce_policy = - typename axom::execution_space::reduce_policy; - using IntType = typename IMatsetView::IndexType; - using FloatType = typename IMatsetView::FloatType; - const int intTypeID = bputils::cpp2conduit::id; - const int floatTypeID = bputils::cpp2conduit::id; - - // Copy the material map to the new matset. - if(n_matset.has_child("material_map")) - { - bputils::copy(n_newMatset["material_map"], - n_matset.fetch_existing("material_map")); - } - if(n_matset.has_child("topology")) - { - n_newMatset["topology"].set(n_matset.fetch_existing("topology")); - } - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; - - // Make sizes, offsets for the new matset. - const auto nzones = colorView.size(); - conduit::Node &n_sizes = n_newMatset["sizes"]; - conduit::Node &n_offsets = n_newMatset["offsets"]; - n_sizes.set_allocator(c2a.getConduitAllocatorID()); - n_offsets.set_allocator(c2a.getConduitAllocatorID()); - n_sizes.set(conduit::DataType(intTypeID, nzones)); - n_offsets.set(conduit::DataType(intTypeID, nzones)); - - // Count how many slots are needed for the new material. Make sizes. - RAJA::ReduceSum sum(0); - auto sizesView = bputils::make_array_view(n_sizes); - std::cout << "makeNewMatset: nzones=" << nzones << ", currentMatNumber=" << currentMat.number << std::endl; + //-------------------------------------------------------------------------- + // + // Updated zoneMaterialID and zoneMaterialVF based on color. + // + //-------------------------------------------------------------------------- + const auto originalElementsView = bputils::make_array_view(n_newFields.fetch_existing("originalElements/values")); + const auto colorView = bputils::make_array_view(n_newFields.fetch_existing(colorField + "/values")); + const auto nzonesNew = originalElementsView.size(); + + // Allocate new material ID/VF arrays. + MaterialIDArray newZoneMaterialID(nzonesNew, nzonesNew, allocatorID); + MaterialVFArray newZoneMaterialVF(nzonesNew, nzonesNew, allocatorID); + auto newZoneMaterialIDView = newZoneMaterialID.view(); + auto newZoneMaterialVFView = newZoneMaterialVF.view(); const int currentMatNumber = currentMat.number; - axom::for_all( - nzones, - AXOM_LAMBDA(auto zoneIndex) { - axom::IndexType nmatsThisZone = 0; - - const auto origIndex = originalElementsView[zoneIndex]; - nmatsThisZone = currentMatsetView.numberOfMaterials(origIndex); - //int dominantMatNumber = currentMatsetView.dominantMaterial(origIndex); - const auto dominantMatNumber = m_matsetView.backgroundMaterial(origIndex, currentMatNumber); + axom::for_all(nzonesNew, AXOM_LAMBDA(auto zoneIndex) + { + const auto origIndex = originalElementsView[zoneIndex]; + if(colorView[zoneIndex] == 1) + { + // Color 1 - This zone should be the current material. We can grab its + // volume fraction from the original matset. + newZoneMaterialIDView[zoneIndex] = currentMatNumber; + typename MatsetView::FloatType vf; + m_matsetView.zoneContainsMaterial(origIndex, currentMatNumber, vf); + newZoneMaterialVFView[zoneIndex] = static_cast(vf); + } + else + { + // Color 0 - This zone was not the current material. Pass through what was there before. + newZoneMaterialIDView[zoneIndex] = zoneMaterialIDView[origIndex]; + newZoneMaterialVFView[zoneIndex] = zoneMaterialVFView[origIndex]; + } + }); + // Pass the new data arrays out. + zoneMaterialID.swap(newZoneMaterialID); + zoneMaterialVF.swap(newZoneMaterialVF); - if(dominantMatNumber != currentMatNumber && nmatsThisZone > 1) - { - int matToRemove = (colorView[zoneIndex] == 0) ? currentMatNumber : dominantMatNumber; - if(currentMatsetView.zoneContainsMaterial(origIndex, matToRemove)) - { - nmatsThisZone--; - } - } -#if !defined(AXOM_DEVICE_CODE) - if(zoneMatters(origIndex)) - { - std::cout << "\tzone " << zoneIndex - << ": origElem=" << origIndex - << ", color=" << colorView[zoneIndex] - << ", dominant=" << dominantMatNumber - << ", nmats=" << nmatsThisZone << std::endl; - } +#if 1 + //-------------------------------------------------------------------------- + // + // Save the clip results. + // + //-------------------------------------------------------------------------- + +#define SAVE_ZONE_MATERIALS +#ifdef SAVE_ZONE_MATERIALS + // Attach the updated arrays to the new fields so we can look at them. + n_newFields["zoneMaterialID/association"] = "element"; + n_newFields["zoneMaterialID/topology"] = n_topo.name(); + n_newFields["zoneMaterialID/values"].set_external(zoneMaterialID.data(), zoneMaterialID.size()); + + n_newFields["zoneMaterialVF/association"] = "element"; + n_newFields["zoneMaterialVF/topology"] = n_topo.name(); + n_newFields["zoneMaterialVF/values"].set_external(zoneMaterialVF.data(), zoneMaterialVF.size()); #endif - - sizesView[zoneIndex] = nmatsThisZone; - sum += nmatsThisZone; - }); - - // Make offsets. - auto offsetsView = bputils::make_array_view(n_offsets); - axom::exclusive_scan(sizesView, offsetsView); - - // Make new matset data. - const auto arraySize = sum.get(); - std::cout << "arraySize=" << arraySize << std::endl; - conduit::Node &n_material_ids = n_newMatset["material_ids"]; - conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; - conduit::Node &n_indices = n_newMatset["indices"]; - n_material_ids.set_allocator(c2a.getConduitAllocatorID()); - n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); - n_indices.set_allocator(c2a.getConduitAllocatorID()); - n_material_ids.set(conduit::DataType(intTypeID, arraySize)); - n_volume_fractions.set(conduit::DataType(floatTypeID, arraySize)); - n_indices.set(conduit::DataType(intTypeID, arraySize)); - - auto matidsView = bputils::make_array_view(n_material_ids); - auto vfView = bputils::make_array_view(n_volume_fractions); - auto indicesView = bputils::make_array_view(n_indices); - - // Fill in the indices used. - axom::for_all( - arraySize, - AXOM_LAMBDA(auto index) { indicesView[index] = index; }); - - // Fill in the material data. - axom::for_all( - nzones, - AXOM_LAMBDA(auto zoneIndex) { - auto offset = offsetsView[zoneIndex]; - - const auto origIndex = originalElementsView[zoneIndex]; - //int dominantMatNumber = currentMatsetView.dominantMaterial(origIndex); - const auto dominantMatNumber = m_matsetView.backgroundMaterial(origIndex, currentMatNumber); - - // Get the material ids and volume fractions from the original material's zone. - typename IMatsetView::IDList ids {}; - typename IMatsetView::VFList vfs {}; - currentMatsetView.zoneMaterials(origIndex, ids, vfs); - - if(dominantMatNumber != currentMatNumber && ids.size() > 1) - { - int matToRemove = (colorView[zoneIndex] == 0) ? currentMatNumber : dominantMatNumber; - // Copy it through without colorToRemove. - FloatType vfTotal {}; - for(axom::IndexType i = 0; i < ids.size(); i++) - { - vfTotal += (ids[i] != matToRemove) ? vfs[i] : 0; - } - for(axom::IndexType i = 0; i < ids.size(); i++) - { - if(ids[i] != matToRemove) - { - matidsView[offset] = ids[i]; - vfView[offset] = vfs[i] / vfTotal; - -#if !defined(AXOM_DEVICE_CODE) - if(zoneMatters(origIndex)) - { -std::cout << "\tzone " << zoneIndex - << ": origElem=" << origIndex - << ", color=" << colorView[zoneIndex] - << ", current=" << currentMatNumber - << ", dominant=" << dominantMatNumber - << ", ids.size=" << ids.size() - << ", offset=" << offset - << ", id=" << ids[i] - << ", vf=" << vfView[offset] - << ", matToRemove=" << matToRemove - << std::endl; - } + conduit::Node mesh; + mesh[n_newTopo.path()].set_external(n_newTopo); + mesh[n_newCoordset.path()].set_external(n_newCoordset); + mesh[n_newFields.path()].set_external(n_newFields); + + // print + printNode(mesh); + std::cout.flush(); + + // save + std::stringstream ss; + ss << "debug_equiz_output." << currentMat.number; + conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); + +#ifdef SAVE_ZONE_MATERIALS + n_newFields.remove("zoneMaterialID"); + n_newFields.remove("zoneMaterialVF"); #endif - offset++; - } - } - } - else - { - // Copy it through. - for(axom::IndexType i = 0; i < ids.size(); i++) - { - matidsView[offset] = ids[i]; - vfView[offset] = vfs[i]; - -#if !defined(AXOM_DEVICE_CODE) - if(zoneMatters(origIndex)) - { -std::cout << "\tzone " << zoneIndex - << ": origElem=" << origIndex - << ", color=" << colorView[zoneIndex] - << ", dominant=" << dominantMatNumber - << ", offset=" << offset - << ", id=" << ids[i] - << ", vf=" << vfView[offset] - << " (pass through)" - << std::endl; - } #endif - offset++; - } - } -/**** - if(colorView[zoneIndex] == COLOR0) - { -#if !defined(AXOM_DEVICE_CODE) - std::cout << "\tzone " << zoneIndex << ": offset=" << offset - << ", id=" << currentMatNumber << ", vf=1" << std::endl; -#endif - matidsView[offset] = currentMatNumber; - vfView[offset] = 1; - } - else - { - // Get the material ids and volume fractions from the original material's zone. - const auto origIndex = originalElementsView[zoneIndex]; - typename IMatsetView::IDList ids {}; - typename IMatsetView::VFList vfs {}; - currentMatsetView.zoneMaterials(origIndex, ids, vfs); - - if(ids.size() > 1) - { - // The original zone was mixed. Filter out currentMatNumber - - FloatType vfTotal {}; - for(axom::IndexType i = 0; i < ids.size(); i++) - { - vfTotal += (ids[i] != currentMatNumber) ? vfs[i] : 0; - } - - // Fill in the new materials. - for(axom::IndexType i = 0; i < ids.size(); i++) - { - if(ids[i] != currentMatNumber) - { - matidsView[offset] = ids[i]; - vfView[offset] = vfs[i] / vfTotal; - -#if !defined(AXOM_DEVICE_CODE) -std::cout << "\tzone " << zoneIndex << ": origElem=" << origIndex << ", offset=" << offset << ", id=" << ids[i] << ", vf=" << vfView[offset] << std::endl; -#endif - offset++; - } - } - } - else - { - matidsView[offset] = ids[0]; - vfView[offset] = 1; - } - } -****/ - }); + std::cout << "------------------------ end of iteration " + "--------------------------------\n"; + } private: TopologyView m_topologyView; CoordsetView m_coordsetView; - MatsetView m_matsetView; + MatsetView m_matsetView; }; } // end namespace mir diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 7a8e310bc2..ded8cc8fa0 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -52,13 +52,18 @@ class ExplicitCoordsetView } /** - * \brief Return the number of points in the coordset. + * \brief Return the number of nodes in the coordset. * - * \return The number of points in the coordset. + * \return The number of nodes in the coordset. */ + /// @{ AXOM_HOST_DEVICE IndexType size() const { return m_coordinates[0].size(); } + AXOM_HOST_DEVICE + IndexType numberOfNodes() const { return m_coordinates[0].size(); } + /// @} + /** * \brief Return the requested point from the coordset. * @@ -122,13 +127,18 @@ class ExplicitCoordsetView } /** - * \brief Return the number of points in the coordset. + * \brief Return the number of nodes in the coordset. * - * \return The number of points in the coordset. + * \return The number of nodes in the coordset. */ + /// @{ AXOM_HOST_DEVICE IndexType size() const { return m_coordinates[0].size(); } + AXOM_HOST_DEVICE + IndexType numberOfNodes() const { return m_coordinates[0].size(); } + /// @} + /** * \brief Return the requested point from the coordset. * diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index fa5a4b4333..9b0f3c9241 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -182,6 +182,41 @@ class UnibufferMaterialView return mat; } + /** + * \brief Return a background material with high VF that is not the supplied material. + * \param zi The zone index. + * \return The material id with the highest volume fraction. + */ + AXOM_HOST_DEVICE + MaterialIndex backgroundMaterial(ZoneIndex zi, MaterialIndex currentMat) const + { + assert(zi < numberOfZones()); + const auto sz = numberOfMaterials(zi); + const auto offset = m_offsets[zi]; + MaterialIndex mat{}; + if(sz == 1) + { + // There is only one material so return that. + const auto idx = m_indices[offset]; + mat = m_material_ids[idx]; + } + else + { + // There are multiple materials. Return the largest VF that is not owned by currentMat. + FloatType maxVF = -1; + for(axom::IndexType i = 0; i < sz; i++) + { + const auto idx = m_indices[offset + i]; + const auto vf = m_volume_fractions[idx]; + if(m_material_ids[idx] != currentMat && vf > maxVF) + { + mat = m_material_ids[idx]; + maxVF = vf; + } + } + } + return mat; + } private: axom::ArrayView m_material_ids; axom::ArrayView m_volume_fractions; diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index 019bf31b59..2a730d6d30 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -54,9 +54,14 @@ class RectilinearCoordsetView2 * * \return The number of points in the coordset. */ + /// @{ AXOM_HOST_DEVICE IndexType size() const { return m_indexing.size(); } + AXOM_HOST_DEVICE + IndexType numberOfNodes() const { return m_indexing.size(); } + /// @} + /** * \brief Return the requested point from the coordset. * diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index d3dd0f09d2..942217ae03 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -12,6 +12,7 @@ #include "axom/mir/views/UnstructuredTopologyMixedShapeView.hpp" #include "axom/mir/views/NodeArrayView.hpp" #include "axom/mir/views/Shapes.hpp" +#include "axom/mir/blueprint_utilities.hpp" #include @@ -64,6 +65,31 @@ void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, } } +template +void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, + FuncType &&func) +{ + namespace bputils = axom::mir::utilities::blueprint; + const std::string shape = topo["elements/shape"].as_string(); + if(shape == "polyhedral") + { + auto seConnView = bputils::make_array_view(topo["subelements/connectivity"]); + auto seSizesView = bputils::make_array_view(topo["subelements/sizes"]); + auto seOffsetsView = bputils::make_array_view(topo["subelements/offsets"]); + auto connView = bputils::make_array_view(topo["elements/connectivity"]); + auto sizesView = bputils::make_array_view(topo["elements/sizes"]); + auto offsetsView = bputils::make_array_view(topo["elements/offsets"]); + + UnstructuredTopologyPolyhedralView ugView(seConnView, + seSizesView, + seOffsetsView, + connView, + sizesView, + offsetsView); + func(shape, ugView); + } +} + /** * \brief This function dispatches a Conduit mixed unstructured topology. * @@ -101,6 +127,40 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, } } +template +void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, + FuncType &&func) +{ + namespace bputils = axom::mir::utilities::blueprint; + const std::string shape = topo["elements/shape"].as_string(); + if(shape == "mixed") + { + auto connView = bputils::make_array_view(topo["elements/connectivity"]); + auto shapesView = bputils::make_array_view(topo["elements/shapes"]); + auto sizesView = bputils::make_array_view(topo["elements/sizes"]); + auto offsetsView = bputils::make_array_view(topo["elements/offsets"]); + + UnstructuredTopologyMixedShapeView ugView(topo, + connView, + shapesView, + sizesView, + offsetsView); + func(shape, ugView); + } +} + +template +constexpr int encode_shapes(Args... args) +{ + return (... | args); +} + +template +constexpr int select_shapes(Args... args) +{ + return encode_types((1 << args)...); +} + /** * \brief This function dispatches a Conduit topology to the right view type * and passes that view to the supplied function/lambda. @@ -111,13 +171,15 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, * \param topo The node that contains the topology. * \param func The function/lambda to call with the topology view. */ -template -void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) +template +void typed_dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { + namespace bputils = axom::mir::utilities::blueprint; const std::string type = topo["type"].as_string(); if(type == "unstructured") { const std::string shape = topo["elements/shape"].as_string(); + const auto connView = bputils::make_array_view(topo["elements/connectivity"]); bool eligible = true; // Conditionally add polyhedron support. @@ -125,102 +187,95 @@ void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { if(shape == "polyhedral") { - dispatch_unstructured_polyhedral_topology(topo, func); + typed_dispatch_unstructured_polyhedral_topology(topo, func); eligible = false; } } -#if 0 -// TODO: Can't use polygon with single shape view because its sizes are not known at compile time. - if constexpr (axom::utilities::bitIsSet(ShapeTypes, Polygon_ShapeID)) - { - if(eligible && shape == "polygon") - { - IndexNode_to_ArrayView_same(topo["elements/connectivity"], - topo["elements/sizes"], - topo["elements/offsets"], [&](auto connView, auto sizesView, auto offsetsView) - { - using IndexType = typename decltype(connView)::value_type; - UnstructuredTopologySingleShapeView > ugView(connView, sizesView, offsetsView); - func(shape, ugView); - }); - eligible = false; - } - } -#endif + // TODO: add polygon + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Mixed_ShapeID)) { if(eligible && shape == "mixed") { - dispatch_unstructured_mixed_topology(topo, func); + typed_dispatch_unstructured_mixed_topology(topo, func); eligible = false; } } - IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) { - using ConnType = typename decltype(connView)::value_type; - // TODO: points, lines - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) + // TODO: points, lines + + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) + { + if(eligible && shape == "tri") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) + { + if(eligible && shape == "quad") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView( - connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) + { + if(eligible && shape == "tet") { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) + { + if(eligible && shape == "pyramid") { - if(eligible && shape == "pyramid") - { - UnstructuredTopologySingleShapeView> ugView( - connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) + { + if(eligible && shape == "wedge") { - if(eligible && shape == "wedge") - { - UnstructuredTopologySingleShapeView> ugView( - connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView( + connView); + func(shape, ugView); + eligible = false; } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) + } + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) + { + if(eligible && shape == "hex") { - if(eligible && shape == "hex") - { - UnstructuredTopologySingleShapeView> ugView(connView); - func(shape, ugView); - eligible = false; - } + UnstructuredTopologySingleShapeView> ugView(connView); + func(shape, ugView); + eligible = false; } - }); + } } } +/// Dispatch in a way that does not care about the connectivity type. +template +void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) +{ + IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) { + using ConnType = typename decltype(connView)::value_type; + typed_dispatch_unstructured_topology(topo, func); + }); +} + + } // end namespace views } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index 50ae53c396..1ecfb12539 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_VIEW_TRAITS_HPP_ #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/Shapes.hpp" namespace axom { @@ -14,11 +15,34 @@ namespace mir { namespace views { +/// Return which shapes to expect for a given dimension. +static constexpr int shapes_for_dimension(int dimension) +{ + int shapes = 0; + switch(dimension) + { + case 2: + axom::utilities::setBitOn(shapes, Tri_ShapeID); + axom::utilities::setBitOn(shapes, Quad_ShapeID); + axom::utilities::setBitOn(shapes, Mixed_ShapeID); + break; + case 3: + axom::utilities::setBitOn(shapes, Tet_ShapeID); + axom::utilities::setBitOn(shapes, Pyramid_ShapeID); + axom::utilities::setBitOn(shapes, Wedge_ShapeID); + axom::utilities::setBitOn(shapes, Hex_ShapeID); + axom::utilities::setBitOn(shapes, Mixed_ShapeID); + break; + } + return shapes; +} + /// General traits for topology views. template struct view_traits { static constexpr bool supports_strided_structured() { return false; } + static constexpr int selected_shapes() { return shapes_for_dimension(TopologyView::dimension()); } }; /// If StructuredTopologyView was instantiated with StridedStructuredIndexing @@ -27,18 +51,21 @@ template struct view_traits>> { static constexpr bool supports_strided_structured() { return true; } + static constexpr int selected_shapes() { return shapes_for_dimension(3); } }; template struct view_traits>> { static constexpr bool supports_strided_structured() { return true; } + static constexpr int selected_shapes() { return shapes_for_dimension(2); } }; template struct view_traits>> { static constexpr bool supports_strided_structured() { return true; } + static constexpr int selected_shapes() { return shapes_for_dimension(1); } }; } // end namespace views From b0c9a5b33c885d4f4ddeeeebba36a37a344b74dd Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 19 Aug 2024 11:39:19 -0700 Subject: [PATCH 177/290] progress but not quite right --- src/axom/mir/EquiZAlgorithm.hpp | 600 ++++++++++++++-------------- src/axom/mir/tests/mir_equiz.cpp | 8 +- src/axom/mir/views/MaterialView.hpp | 2 + 3 files changed, 309 insertions(+), 301 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 5335fafd38..292254f1f8 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -28,8 +28,9 @@ namespace axom { namespace mir { -using MaterialIDArray = axom::Array; -using MaterialIDView = axom::ArrayView; +using MaterialID = int; +using MaterialIDArray = axom::Array; +using MaterialIDView = axom::ArrayView; using MaterialVF = float; using MaterialVFArray = axom::Array; using MaterialVFView = axom::ArrayView; @@ -37,46 +38,6 @@ using MaterialVFView = axom::ArrayView; constexpr static int NULL_MATERIAL = -1; constexpr static MaterialVF NULL_MATERIAL_VF = 0.f; -/** - * \brief Populate values for a new field based on a material's volume fraction. - */ -template -struct MatsetToField -{ - using MaterialIndex = typename MatsetView::MaterialIndex; - using FloatType = typename MatsetView::FloatType; - - void execute(const MatsetView &matsetView, - MaterialIndex mat, - MaterialVFView vfView) - { - const auto nzones = vfView.size(); - axom::for_all( - nzones, - AXOM_LAMBDA(auto zoneIndex) { - FloatType vf {}; - matsetView.zoneContainsMaterial(zoneIndex, mat, vf); - vfView[zoneIndex] = static_cast(vf); - }); - } - - void execute(const MatsetView &matsetView, - MaterialIndex mat, - OriginalElementsView origElementsView, - MaterialVFView vfView) - { - const auto nzones = vfView.size(); - axom::for_all( - nzones, - AXOM_LAMBDA(auto zoneIndex) { - FloatType vf {}; - const auto origIndex = origElementsView[zoneIndex]; - matsetView.zoneContainsMaterial(origIndex, mat, vf); - vfView[zoneIndex] = static_cast(vf); - }); - } -}; - #if 1 AXOM_HOST_DEVICE inline bool zoneMatters(int zoneIndex) @@ -107,19 +68,16 @@ struct RelationView /** * \brief This class is an intersection policy compatible with ClipField. It * helps determine clip cases and weights using material-aware logic. - * - * \tparam MAXELEMENTS the max number of nodes in any zone we'll encounter. */ -template +template class MaterialIntersector { public: using ConnectivityType = ConnectivityT; using ConnectivityView = axom::ArrayView; - using RelationView = RelationViewType; - MaterialIntersector(const RelationView &rv, const MaterialIDView &zoneMatIds, const MaterialVFView &zoneVFs, const MaterialVFView &nodalCurrentVF) : - m_view(rv, zoneMatIds, zoneVFs, nodalCurrentVF) + MaterialIntersector(const MaterialVFView &accumVFView, const MaterialVFView ¤tVFView) : + m_view(accumVFView, currentVFView) { } /** @@ -127,15 +85,11 @@ class MaterialIntersector */ struct View { - using VFList = StaticArray; - /// Constructor View() = default; /// Constructor - View(const RelationView &rv, const MaterialIDView &zoneMatIds, const MaterialVFView &zoneVFs, const MaterialVFView &nodalCurrentVF) : - m_relationView(rv), m_zoneMaterialIDView(zoneMatIds), m_zoneMaterialVFView(zoneVFs), - m_nodalCurrentMaterialVFView(nodalCurrentVF) + View(const MaterialVFView &accumVFView, const MaterialVFView ¤tVFView) : m_accumVFView(accumVFView), m_currentVFView(currentVFView) { } /** @@ -148,17 +102,17 @@ class MaterialIntersector * \return The clip case number for the zone. */ AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIdsView) const + axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIdsView) const { - VFList vf1, vf2; - getVolumeFractionsAtNodes(m_zoneMaterialIDView[zoneIndex], nodeIdsView, vf1); - getVolumeFractionsCurrent(nodeIdsView, vf2); - axom::IndexType clipcase = 0; const auto n = nodeIdsView.size(); for(IndexType i = 0; i < n; i++) { - clipcase |= (vf2[i] > vf1[i]) ? (1 << i) : 0; + const auto nid = nodeIdsView[i]; + MaterialVF vf1 = m_accumVFView[nid]; + MaterialVF vf2 = m_currentVFView[nid]; + + clipcase |= (vf2 > vf1) ? (1 << i) : 0; } return clipcase; } @@ -170,14 +124,14 @@ class MaterialIntersector * \param id1 The mesh node at the end of the edge. */ AXOM_HOST_DEVICE - float computeWeight(axom::IndexType zoneIndex, ConnectivityType id0, ConnectivityType id1) const + float computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const { // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. - ConnectivityType nodeIds[2] = {id0, id1}; - ConnectivityView nodeIdsView(nodeIds, 2); - VFList vf1, vf2; - getVolumeFractionsAtNodes(m_zoneMaterialIDView[zoneIndex], nodeIdsView, vf1); - getVolumeFractionsCurrent(nodeIdsView, vf2); + MaterialVF vf1[2], vf2[2]; + vf1[0] = m_accumVFView[id0]; + vf1[1] = m_accumVFView[id1]; + vf2[0] = m_currentVFView[id0]; + vf2[1] = m_currentVFView[id1]; float numerator = vf2[0] - vf1[0]; float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; @@ -192,53 +146,8 @@ class MaterialIntersector return t; } - /** - * \brief Return node-averaged volume fractions for the specified material at the given node ids. - * - * \param materialNumber The material number for which we want nodal volume fractions. - * \param nodeIds A view containing the node ids where we want nodal volume fractions. - * \param[out] vfs A list of output volume fractions. - */ - AXOM_HOST_DEVICE - void getVolumeFractionsAtNodes(int materialNumber, const ConnectivityView &nodeIds, VFList &vfs) const - { - for(axom::IndexType i = 0; i < nodeIds.size(); i++) - { - const auto nid = nodeIds[i]; - - // Use the relation to average VFs for this material to the node. - auto size = m_relationView.m_sizesView[nid]; - using SizeType = decltype(size); - const auto offset = m_relationView.m_offsetsView[nid]; - - MaterialVF vfSum{}; - for(SizeType j = 0; j < size; j++) - { - const auto zoneIndex = m_relationView.m_zonesView[offset + j]; - vfSum += (m_zoneMaterialIDView[zoneIndex] == materialNumber) ? m_zoneMaterialVFView[zoneIndex] : 0.f; - } - - const MaterialVF nodeVF = vfSum / static_cast(size); - vfs.push_back(nodeVF); - } - } - - AXOM_HOST_DEVICE - void getVolumeFractionsCurrent(const ConnectivityView &nodeIds, VFList &vfs) const - { - for(axom::IndexType i = 0; i < nodeIds.size(); i++) - { - vfs.push_back(m_nodalCurrentMaterialVFView[nodeIds[i]]); - } - } - - RelationView m_relationView {}; - - MaterialIDView m_zoneMaterialIDView {}; //!< Zonal field of material ids added so far. - MaterialVFView m_zoneMaterialVFView {}; //!< Zonal vf for the zone inherited from the material - - MaterialVFView m_nodalCurrentMaterialVFView {}; //!< Nodal field with current material VFs - int m_currentMaterial {0}; //!< Id of the current material + MaterialVFView m_accumVFView {}; + MaterialVFView m_currentVFView {}; }; /** @@ -246,11 +155,8 @@ class MaterialIntersector * \param n_options The node that contains the options. * \param n_fields The node that contains fields. */ - void initialize(const conduit::Node &n_options, const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) + void initialize(const conduit::Node &AXOM_UNUSED_PARAM(n_options), const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) { - // We pass in the current material from the MIR algorithm via the options. - if(n_options.has_path("currentMaterial")) - m_view.m_currentMaterial = n_options["currentMaterial"].to_int(); } /** @@ -303,7 +209,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm virtual ~EquiZAlgorithm() = default; protected: - void printNode(const conduit::Node &n) + void printNode(const conduit::Node &n) const { conduit::Node options; options["num_children_threshold"] = 10000; @@ -349,26 +255,28 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_InputTopo = n_Input[n_topo.path()]; conduit::Node &n_InputCoordset = n_Input[n_coordset.path()]; conduit::Node &n_InputFields = n_Input[n_fields.path()]; - conduit::Node &n_InputMatset = n_Input[n_matset.path()]; - // Make "empty" material data, kind of unofficial fields on the input mesh. - const int allocatorID = axom::execution_space::allocatorID(); - const auto nzones = m_topologyView.numberOfZones(); - axom::Array zoneMaterialID(nzones, nzones, allocatorID); - axom::Array zoneMaterialVF(nzones, nzones, allocatorID); - auto zoneMaterialIDView = zoneMaterialID.view(); - auto zoneMaterialVFView = zoneMaterialVF.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + // Get the materials from the matset and determine which of them are clean/mixed. + axom::mir::views::MaterialInformation cleanMats, mixedMats; + classifyMaterials(n_matset, cleanMats, mixedMats); + + //-------------------------------------------------------------------------- + // + // Make node-centered VF fields and add various working fields. + // + //-------------------------------------------------------------------------- + n_InputFields.reset(); + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { - zoneMaterialIDView[zoneIndex] = NULL_MATERIAL; - zoneMaterialVFView[zoneIndex] = NULL_MATERIAL_VF; - }); + n_InputFields[n_fields[i].name()].set_external(n_fields[i]); + } + makeNodeCenteredVFs(n_topo, n_coordset, n_InputFields, mixedMats); + makeWorkingFields(n_topo, n_InputFields, cleanMats, mixedMats); - // Iterate over the materials - const auto matInfo = axom::mir::views::materials(n_matset); - for(size_t i = 0; i < matInfo.size(); i++) + // Iterate over mixed materials. Note the first material was already accounted for so start at 1. + for(size_t i = 1; i < mixedMats.size(); i++) { - if(i == 0) + if(i == 1) { #if 1 // Print input mesh. @@ -376,22 +284,21 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node mesh; mesh[n_topo.path()].set_external(n_topo); mesh[n_coordset.path()].set_external(n_coordset); - mesh[n_fields.path()].set_external(n_fields); + mesh[n_InputFields.path()].set_external(n_InputFields); mesh[n_matset.path()].set_external(n_matset); printNode(mesh); std::cout << "-------------------------------------------------------------------" << std::endl; #endif + // The first time through, we can use the supplied views. iteration(m_topologyView, m_coordsetView, - zoneMaterialID, - zoneMaterialVF, - matInfo[i], + mixedMats[i], n_topo, n_coordset, - n_fields, + n_InputFields, n_options_copy, @@ -409,11 +316,15 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } else { - // Move the outputs of the last iteration to the inputs of this iteration. + // Clear the inputs from the last iteration. + n_InputTopo.reset(); + n_InputCoordset.reset(); + n_InputFields.reset(); + + // Move the outputs of the last iteration to the inputs of this iteration. n_InputTopo.move(n_newTopo); n_InputCoordset.move(n_newCoordset); n_InputFields.move(n_newFields); - n_InputMatset.move(n_newMatset); // The data are now an unstructured view, probably a mixed shape view. // Dispatch to an appropriate topo view @@ -431,15 +342,14 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm topologyView, coordsetView, - zoneMaterialID, - zoneMaterialVF, - matInfo[i], + mixedMats[i], n_InputTopo, n_InputCoordset, n_InputFields, n_options_copy, + n_newTopo, n_newCoordset, n_newFields); @@ -453,44 +363,49 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } - /** - * \brief Perform one round of material clipping. - * - * \tparam ITopologyView The topology view type for the intermediate topology. - * \tparam ICoordsetView The topology view type for the intermediate coordset. - */ - template - void iteration(const ITopologyView &topoView, - const ICoordsetView &coordsetView, - - MaterialIDArray &zoneMaterialID, - MaterialVFArray &zoneMaterialVF, + void classifyMaterials(const conduit::Node &n_matset, + axom::mir::views::MaterialInformation &cleanMats, + axom::mir::views::MaterialInformation &mixedMats) const + { + cleanMats.clear(); + mixedMats.clear(); + const auto matInfo = axom::mir::views::materials(n_matset); - const axom::mir::views::Material ¤tMat, + // TODO: actually determine which materials are clean/mixed. It's probably + // best to ask the matsetView since it takes some work to determine + // this. - const conduit::Node &n_topo, - const conduit::Node &n_coordset, - const conduit::Node &n_fields, + mixedMats = matInfo; + } - const conduit::Node &n_options, + std::string zonalFieldName(int matId) const + { + std::stringstream ss; + ss << "__equiz_zonal_volume_fraction_" << matId; + return ss.str(); + } - conduit::Node &n_newTopo, - conduit::Node &n_newCoordset, - conduit::Node &n_newFields) + std::string nodalFieldName(int matId) const { - namespace bputils = axom::mir::utilities::blueprint; - namespace bpmeshutils = conduit::blueprint::mesh::utils; - std::cout << "------------------------ start of iteration " - "--------------------------------\n"; + std::stringstream ss; + ss << "__equiz_nodal_volume_fraction_" << matId; + return ss.str(); + } - const std::string colorField("__equiz__colors"); - const std::string nodalFieldName("__equiz__nodal_current_material"); - const std::string zonalFieldName("__equiz__zonal_current_material"); + std::string accumulatedVFFieldName() const { return "__equiz_accumulated_VF"; } - const int allocatorID = axom::execution_space::allocatorID(); - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; + axom::mir::views::MaterialInformation mixedMaterials(const axom::mir::views::MaterialInformation &matInfo) const + { + // TODO: figure out which mats are mixed and which are clean. + return matInfo; + } + void makeNodeCenteredVFs(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + conduit::Node &n_fields, + const axom::mir::views::MaterialInformation &mixedMats) const + { + namespace bputils = axom::mir::utilities::blueprint; // Make a node to zone relation. conduit::Node relation; { @@ -500,62 +415,170 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::cout.flush(); } - const auto nzones = topoView.numberOfZones(); - const auto nnodes = coordsetView.numberOfNodes(); + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + bputils::ConduitAllocateThroughAxom c2a; - //-------------------------------------------------------------------------- - // - // Make input fields we can modify - // - //-------------------------------------------------------------------------- - conduit::Node n_inputFields; - for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + // Make nodal VFs for each mixed material. + const auto nzones = m_topologyView.numberOfZones(); + const auto nnodes = m_coordsetView.numberOfNodes(); + for(const auto &mat : mixedMats) { - n_inputFields[n_fields[i].name()].set_external(n_fields[i]); + const int matNumber = mat.number; + const std::string zonalName = zonalFieldName(matNumber); + conduit::Node &n_zonalField = n_fields[zonalName]; + n_zonalField["topology"] = n_topo.name(); + n_zonalField["association"] = "element"; + n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_zonalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + auto zonalFieldView = bputils::make_array_view(n_zonalField["values"]); + + // Fill the zonal field from the matset. + MatsetView deviceMatsetView(m_matsetView); + axom::for_all( + m_topologyView.numberOfZones(), + AXOM_LAMBDA(auto zoneIndex) { + typename MatsetView::FloatType vf {}; + deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf); + zonalFieldView[zoneIndex] = static_cast(vf); + }); + + // Make a nodal field for the current material by recentering. + const std::string nodalName = nodalFieldName(matNumber); + conduit::Node &n_nodalField = n_fields[nodalName]; + n_nodalField["topology"] = n_topo.name(); + n_nodalField["association"] = "vertex"; + n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_nodalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); + bputils::RecenterField z2n; + z2n.execute(n_zonalField, relation, n_nodalField); + +#ifndef DEBUG_EQUIZ + n_fields.remove(zonalName); +#endif } - - //-------------------------------------------------------------------------- - // - // Make a zonal field on the input mesh for the current material, pulling - // out its VFs from the original matsetView. - // - // NOTE: The fields are free-floating because we just are making array views - // using that data. - //-------------------------------------------------------------------------- - conduit::Node &n_zonalField = n_inputFields[zonalFieldName]; - n_zonalField["topology"] = n_topo.name(); - n_zonalField["association"] = "element"; - n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_zonalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - auto currentZonalMaterialVFView = bputils::make_array_view(n_zonalField["values"]); + } - using ConnectivityType = typename ITopologyView::ConnectivityType; - using OriginalElementsView = axom::ArrayView; + void makeWorkingFields(const conduit::Node &n_topo, + conduit::Node &n_fields, + const axom::mir::views::MaterialInformation &cleanMats, + const axom::mir::views::MaterialInformation &mixedMats) const + { + namespace bputils = axom::mir::utilities::blueprint; + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + bputils::ConduitAllocateThroughAxom c2a; + + const auto nzones = m_topologyView.numberOfZones(); + const auto nnodes = m_coordsetView.numberOfNodes(); + + // Make the accumulation field. + conduit::Node &n_accumField = n_fields[accumulatedVFFieldName()]; + n_accumField["topology"] = n_topo.name(); + n_accumField["association"] = "vertex"; + n_accumField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_accumField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); + auto accumFieldView = bputils::make_array_view(n_accumField["values"]); + if(!mixedMats.empty()) { - MatsetToField m2f; - if(n_fields.has_path("originalElements")) - { - auto originalElementsView = bputils::make_array_view(n_fields["originalElements/values"]); - m2f.execute(m_matsetView, currentMat.number, originalElementsView, currentZonalMaterialVFView); - } - else + // Copy the first material's data into the accumulated vfs. + conduit::Node &n_firstMat = n_fields.fetch_existing(nodalFieldName(mixedMats[0].number) + "/values"); + axom::copy(accumFieldView.data(), n_firstMat.data_ptr(), sizeof(MaterialVF) * nnodes); + } + else + { + axom::for_all(nnodes, AXOM_LAMBDA(auto nodeIndex) { - m2f.execute(m_matsetView, currentMat.number, currentZonalMaterialVFView); - } + accumFieldView[nodeIndex] = 0; + }); } - // Make a nodal field for the current material by recentering. - conduit::Node &n_nodalField = n_inputFields[nodalFieldName]; + // Make the zonal id and vf fields too. + conduit::Node &n_zonalIDField = n_fields["__equiz_zonalMaterialID"]; + n_zonalIDField["topology"] = n_topo.name(); + n_zonalIDField["association"] = "element"; + n_zonalIDField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_zonalIDField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + + conduit::Node &n_zonalVFField = n_fields["__equiz_zonalMaterialVF"]; + n_zonalVFField["topology"] = n_topo.name(); + n_zonalVFField["association"] = "element"; + n_zonalVFField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_zonalVFField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + + auto zonalIDFieldView = bputils::make_array_view(n_zonalIDField["values"]); + auto zonalVFFieldView = bputils::make_array_view(n_zonalVFField["values"]); + // Fill all zones with empty. + axom::for_all(nzones, AXOM_LAMBDA(auto nodeIndex) { - n_nodalField["topology"] = n_topo.name(); - n_nodalField["association"] = "vertex"; - n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_nodalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); - bputils::RecenterField z2n; - z2n.execute(n_zonalField, relation, n_nodalField); + zonalIDFieldView[nodeIndex] = NULL_MATERIAL; + zonalVFFieldView[nodeIndex] = NULL_MATERIAL_VF; + }); + + // Fill in the clean zones. + using FloatType = typename MatsetView::FloatType; + MatsetView deviceMatsetView(m_matsetView); + for(const auto &mat : cleanMats) + { + const int matNumber = mat.number; + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + FloatType vf {}; + if(deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf)) + { + zonalIDFieldView[zoneIndex] = matNumber; + zonalVFFieldView[zoneIndex] = static_cast(vf); + } + }); + } + // Fill in the mixed zones for the first mixed material. + if(!mixedMats.empty()) + { + const int matNumber = mixedMats[0].number; +std::cout << "************ START\n"; + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + FloatType vf {}; + if(deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf)) + { + zonalIDFieldView[zoneIndex] = matNumber; + zonalVFFieldView[zoneIndex] = static_cast(vf); +std::cout << " storing VF " << zonalVFFieldView[zoneIndex] << "\n"; + } + }); +std::cout << "************ END\n"; } - auto currentNodalMaterialVFView = bputils::make_array_view(n_nodalField["values"]); + } + + /** + * \brief Perform one round of material clipping. + * + * \tparam ITopologyView The topology view type for the intermediate topology. + * \tparam ICoordsetView The topology view type for the intermediate coordset. + */ + template + void iteration(const ITopologyView &topoView, + const ICoordsetView &coordsetView, + + const axom::mir::views::Material ¤tMat, + + const conduit::Node &n_topo, + const conduit::Node &n_coordset, + conduit::Node &n_fields, + + const conduit::Node &n_options, + + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) + { + namespace bputils = axom::mir::utilities::blueprint; + namespace bpmeshutils = conduit::blueprint::mesh::utils; + std::cout << "------------------------ start of iteration " + "--------------------------------\n"; + + const std::string colorField("__equiz__colors"); #if 1 //-------------------------------------------------------------------------- @@ -566,7 +589,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node n_mesh_input; n_mesh_input[n_topo.path()].set_external(n_topo); n_mesh_input[n_coordset.path()].set_external(n_coordset); - n_mesh_input[n_fields.path()].set_external(n_inputFields); + n_mesh_input[n_fields.path()].set_external(n_fields); // save std::stringstream ss1; @@ -579,50 +602,50 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Invoke clipping with a material intersector. // //-------------------------------------------------------------------------- - const auto zoneMaterialIDView = zoneMaterialID.view(); - const auto zoneMaterialVFView = zoneMaterialVF.view(); - axom::mir::views::IndexNode_to_ArrayView_same(relation["zones"], relation["sizes"], relation["offsets"], - [&](auto zonesView, auto sizesView, auto offsetsView) + const std::string matFieldName = nodalFieldName(currentMat.number); + + // Get the accumulated VFs and the VFs for the current material. + auto accumVFView = bputils::make_array_view(n_fields.fetch_existing(accumulatedVFFieldName() + "/values")); + auto currentMatVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); + + // Make a material intersector. + using ConnectivityType = typename ITopologyView::ConnectivityType; + using IntersectorType = MaterialIntersector; + IntersectorType intersector(accumVFView, currentMatVFView); + + // Make clip options. + conduit::Node options; + options["inside"] = 1; + options["outside"] = 1; + options["colorField"] = colorField; + if(n_options.has_child("selectedZones")) { - // Wrap the node to zone relation in a view. - using IndexT = typename decltype(zonesView)::value_type; - using RelationViewType = RelationView; - RelationViewType rv; - rv.m_zonesView = zonesView; - rv.m_sizesView = sizesView; - rv.m_offsetsView = offsetsView; - - // Make a material intersector. - using IntersectorType = MaterialIntersector; - IntersectorType intersector(rv, zoneMaterialIDView, zoneMaterialVFView, currentNodalMaterialVFView); - - // Make clip options. - conduit::Node options; - options["inside"] = 1; - options["outside"] = 1; - options["colorField"] = colorField; - if(n_options.has_child("selectedZones")) - { - // Pass selectedZones along in the clip options, if present. - options["selectedZones"].set_external( - n_options.fetch_existing("selectedZones")); - } - // Tell the intersector which material we're currently working on. - options["currentMaterial"] = currentMat.number; - options["topology"] = n_options["topology"]; + // Pass selectedZones along in the clip options, if present. + options["selectedZones"].set_external( + n_options.fetch_existing("selectedZones")); + } + options["topology"] = n_options["topology"]; - // Clip the topology using the matset. - { - using ClipperType = axom::mir::clipping::ClipField; - ClipperType clipper(topoView, coordsetView, intersector); - clipper.execute(n_topo, - n_coordset, - n_inputFields, - options, - n_newTopo, - n_newCoordset, - n_newFields); - } + // Clip the topology using the matset. + { + using ClipperType = axom::mir::clipping::ClipField; + ClipperType clipper(topoView, coordsetView, intersector); + clipper.execute(n_topo, + n_coordset, + n_fields, + options, + n_newTopo, + n_newCoordset, + n_newFields); + } + + // In the output fields, add the current material VFs to the accumulated fields. + // This raises the floor for the next iteration. + accumVFView = bputils::make_array_view(n_newFields.fetch_existing(accumulatedVFFieldName() + "/values")); + currentMatVFView = bputils::make_array_view(n_newFields.fetch_existing(matFieldName + "/values")); + axom::for_all(accumVFView.size(), AXOM_LAMBDA(auto nodeIndex) + { + accumVFView[nodeIndex] += currentMatVFView[nodeIndex]; }); //-------------------------------------------------------------------------- @@ -634,34 +657,28 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm const auto colorView = bputils::make_array_view(n_newFields.fetch_existing(colorField + "/values")); const auto nzonesNew = originalElementsView.size(); + conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing("__equiz_zonalMaterialID/values"); + conduit::Node &n_zonalMaterialVF = n_newFields.fetch_existing("__equiz_zonalMaterialVF/values"); + auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); + auto zonalMaterialVF = bputils::make_array_view(n_zonalMaterialVF); + // Allocate new material ID/VF arrays. - MaterialIDArray newZoneMaterialID(nzonesNew, nzonesNew, allocatorID); - MaterialVFArray newZoneMaterialVF(nzonesNew, nzonesNew, allocatorID); - auto newZoneMaterialIDView = newZoneMaterialID.view(); - auto newZoneMaterialVFView = newZoneMaterialVF.view(); const int currentMatNumber = currentMat.number; axom::for_all(nzonesNew, AXOM_LAMBDA(auto zoneIndex) { - const auto origIndex = originalElementsView[zoneIndex]; + // Color the part we want with the current material. if(colorView[zoneIndex] == 1) { - // Color 1 - This zone should be the current material. We can grab its - // volume fraction from the original matset. - newZoneMaterialIDView[zoneIndex] = currentMatNumber; + zonalMaterialID[zoneIndex] = currentMatNumber; + + // NOTE: I'm not 100% sure whether I need to track the VF. + + const auto origIndex = originalElementsView[zoneIndex]; typename MatsetView::FloatType vf; m_matsetView.zoneContainsMaterial(origIndex, currentMatNumber, vf); - newZoneMaterialVFView[zoneIndex] = static_cast(vf); - } - else - { - // Color 0 - This zone was not the current material. Pass through what was there before. - newZoneMaterialIDView[zoneIndex] = zoneMaterialIDView[origIndex]; - newZoneMaterialVFView[zoneIndex] = zoneMaterialVFView[origIndex]; + zonalMaterialVF[zoneIndex] = static_cast(vf); } }); - // Pass the new data arrays out. - zoneMaterialID.swap(newZoneMaterialID); - zoneMaterialVF.swap(newZoneMaterialVF); #if 1 //-------------------------------------------------------------------------- @@ -669,18 +686,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Save the clip results. // //-------------------------------------------------------------------------- - -#define SAVE_ZONE_MATERIALS -#ifdef SAVE_ZONE_MATERIALS - // Attach the updated arrays to the new fields so we can look at them. - n_newFields["zoneMaterialID/association"] = "element"; - n_newFields["zoneMaterialID/topology"] = n_topo.name(); - n_newFields["zoneMaterialID/values"].set_external(zoneMaterialID.data(), zoneMaterialID.size()); - - n_newFields["zoneMaterialVF/association"] = "element"; - n_newFields["zoneMaterialVF/topology"] = n_topo.name(); - n_newFields["zoneMaterialVF/values"].set_external(zoneMaterialVF.data(), zoneMaterialVF.size()); -#endif conduit::Node mesh; mesh[n_newTopo.path()].set_external(n_newTopo); mesh[n_newCoordset.path()].set_external(n_newCoordset); @@ -694,16 +699,17 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::stringstream ss; ss << "debug_equiz_output." << currentMat.number; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); - -#ifdef SAVE_ZONE_MATERIALS - n_newFields.remove("zoneMaterialID"); - n_newFields.remove("zoneMaterialVF"); -#endif #endif + // We can remove the current material's nodal from the new fields + if(n_newFields.has_child(matFieldName)) + { + n_newFields.remove(matFieldName); + } + n_newFields.remove(colorField); + std::cout << "------------------------ end of iteration " "--------------------------------\n"; - } private: diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 533aa14e6a..ac95174244 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -18,7 +18,7 @@ //#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -//#define AXOM_TESTING_SAVE_VISUALIZATION +#define AXOM_TESTING_SAVE_VISUALIZATION #include "axom/mir/tests/mir_testing_helpers.hpp" @@ -128,9 +128,9 @@ TEST(mir_equiz, equiz_uniform_unibuffer) { braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); -#if defined(AXOM_USE_OPENMP) - braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); -#endif +//#if defined(AXOM_USE_OPENMP) +// braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +//#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 9b0f3c9241..6d18802e85 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -149,10 +149,12 @@ class UnibufferMaterialView if(m_material_ids[idx] == mat) { vf = m_volume_fractions[idx]; +std::cout << "zoneContainsMaterial: zi=" << zi << ", mat=" << mat << ", vf=" << vf << " -> true\n"; return true; } } vf = 0; +std::cout << "zoneContainsMaterial: zi=" << zi << ", mat=" << mat << ", vf=" << vf << " -> false\n"; return false; } From 389cb1b8653e397caa65e3d197b5d0c9bd19c03f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 19 Aug 2024 16:18:36 -0700 Subject: [PATCH 178/290] Better result --- src/axom/mir/EquiZAlgorithm.hpp | 274 ++++++++++++++++++++++++++++++-- 1 file changed, 261 insertions(+), 13 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 292254f1f8..710889974f 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -65,6 +65,192 @@ struct RelationView axom::ArrayView m_offsetsView {}; }; +#if 1 +/** + * \brief This class is an intersection policy compatible with ClipField. It + * helps determine clip cases and weights using material-aware logic. + */ +template +class MaterialIntersector +{ +public: + using ConnectivityType = ConnectivityT; + using ConnectivityView = axom::ArrayView; + + /** + * \brief This is a view class for MatsetIntersector that can be used in device code. + */ + struct View + { + static constexpr int INVALID_INDEX = -1; + + /** + * \brief Determine the clipping case, taking into account the zone's material + * and the current material being added. + * + * \param zoneIndex The zone index in the zoneMaterialView field. + * \param nodeIds A view containing node ids for the current zone. + * + * \return The clip case number for the zone. + */ + AXOM_HOST_DEVICE + axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIdsView) const + { + // Determine the matvf view index for the material that owns the zone. + int backgroundIndex = INVALID_INDEX; + int zoneMatID = m_zoneMatNumberView[zoneIndex]; + if(zoneMatID != NULL_MATERIAL) + backgroundIndex = matNumberToIndex(zoneMatID); + // Determine the matvf view index for the current material. + int currentIndex = matNumberToIndex(m_currentMaterial); + + axom::IndexType clipcase = 0; + const auto n = nodeIdsView.size(); + for(IndexType i = 0; i < n; i++) + { + const auto nid = nodeIdsView[i]; + MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : 0.; + MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0.; + + clipcase |= (vf2 > vf1) ? (1 << i) : 0; + } + return clipcase; + } + + /** + * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. + * + * \param id0 The mesh node at the start of the edge. + * \param id1 The mesh node at the end of the edge. + */ + AXOM_HOST_DEVICE + float computeWeight(axom::IndexType zoneIndex, ConnectivityType id0, ConnectivityType id1) const + { + // Determine the matvf view index for the material that owns the zone. + int backgroundIndex = INVALID_INDEX; + int zoneMatID = m_zoneMatNumberView[zoneIndex]; + if(zoneMatID != NULL_MATERIAL) + backgroundIndex = matNumberToIndex(zoneMatID); + // Determine the matvf view index for the current material. + int currentIndex = matNumberToIndex(m_currentMaterial); + + // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. + MaterialVF vf1[2], vf2[2]; + vf1[0] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id0] : 0.; + vf1[1] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id1] : 0.; + vf2[0] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0.; + vf2[1] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0.; + + float numerator = vf2[0] - vf1[0]; + float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; + + float t = 0.f; + if(denominator != 0.f) + { + t = numerator / denominator; + } + t = axom::utilities::clampVal(t, 0.f, 1.f); + + return t; + } + + AXOM_HOST_DEVICE + int matNumberToIndex(int matNumber) const + { + auto index = axom::mir::utilities::bsearch(matNumber, m_matNumbersView); + return (index != -1) ? m_matIndicesView[index] : INVALID_INDEX; + } + + /// Helper initialization methods for the host. + + void addMaterial(const MaterialVFView &matvf) + { + m_matvfViews.push_back(matvf); + } + + void setMaterialNumbers(const axom::ArrayView &matNumbersView) + { + m_matNumbersView = matNumbersView; + } + + void setMaterialIndices(const axom::ArrayView &matIndicesView) + { + m_matIndicesView = matIndicesView; + } + + void setZoneMaterialID(const axom::ArrayView &zoneMatsView) + { + m_zoneMatNumberView = zoneMatsView; + } + + void setCurrentMaterial(int matNumber) + { + m_currentMaterial = matNumber; + } + + axom::StaticArray m_matvfViews {}; + axom::ArrayView m_matNumbersView {}; + axom::ArrayView m_matIndicesView {}; + axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. + int m_currentMaterial {}; //!< The current material. + }; + + /** + * \brief Initialize the object from options. + * \param n_options The node that contains the options. + * \param n_fields The node that contains fields. + */ + void initialize(const conduit::Node &AXOM_UNUSED_PARAM(n_options), const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) + { + } + + /** + * \brief Determine the name of the topology on which to operate. + * \param n_input The input mesh node. + * \param n_options The clipping options. + * \return The name of the toplogy on which to operate. + */ + std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), const conduit::Node &n_options) const + { + return n_options["topology"].as_string(); + } + + void addMaterial(const MaterialVFView &matvf) + { + m_view.addMaterial(matvf); + } + + void setMaterialNumbers(const axom::ArrayView &matNumbers) + { + m_view.setMaterialNumbers(matNumbers); + } + + void setMaterialIndices(const axom::ArrayView &matIndices) + { + m_view.setMaterialIndices(matIndices); + } + + void setZoneMaterialID(const axom::ArrayView &zoneMatsView) + { + m_view.setZoneMaterialID(zoneMatsView); + } + + void setCurrentMaterial(int matNumber) + { + m_view.setCurrentMaterial(matNumber); + } + + /** + * \brief Return a new instance of the view. + * \return A new instance of the view. + * \note Call this after all values are set. + */ + View view() const { return m_view; } + +private: + View m_view {}; +}; +#else /** * \brief This class is an intersection policy compatible with ClipField. It * helps determine clip cases and weights using material-aware logic. @@ -179,7 +365,7 @@ class MaterialIntersector private: View m_view {}; }; - +#endif #endif /** @@ -257,8 +443,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_InputFields = n_Input[n_fields.path()]; // Get the materials from the matset and determine which of them are clean/mixed. - axom::mir::views::MaterialInformation cleanMats, mixedMats; - classifyMaterials(n_matset, cleanMats, mixedMats); + axom::mir::views::MaterialInformation allMats, cleanMats, mixedMats; + classifyMaterials(n_matset, allMats, cleanMats, mixedMats); //-------------------------------------------------------------------------- // @@ -294,6 +480,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm iteration(m_topologyView, m_coordsetView, + allMats, mixedMats[i], n_topo, @@ -342,6 +529,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm topologyView, coordsetView, + allMats, mixedMats[i], n_InputTopo, @@ -364,18 +552,19 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } void classifyMaterials(const conduit::Node &n_matset, + axom::mir::views::MaterialInformation &allMats, axom::mir::views::MaterialInformation &cleanMats, axom::mir::views::MaterialInformation &mixedMats) const { cleanMats.clear(); mixedMats.clear(); - const auto matInfo = axom::mir::views::materials(n_matset); + allMats = axom::mir::views::materials(n_matset); // TODO: actually determine which materials are clean/mixed. It's probably // best to ask the matsetView since it takes some work to determine // this. - mixedMats = matInfo; + mixedMats = allMats; } std::string zonalFieldName(int matId) const @@ -536,6 +725,29 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { const int matNumber = mixedMats[0].number; std::cout << "************ START\n"; +#if 1 + const std::string matFieldName = nodalFieldName(matNumber); + auto matVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); + +// NOTE - I think this will cause too many zones to get subdivided. + + // Fill in any zone that has nodes where the nodal matVF is greater than zero. + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + constexpr MaterialVF NO_MATERIAL = 1.e-6; + const axom::IndexType n = zone.getIds().size(); + MaterialVF matvfSum {}; + for(axom::IndexType i = 0; i < n; i++) + { + matvfSum += matVFView[zone.getId(i)]; + } + if(matvfSum > NO_MATERIAL) + { + zonalIDFieldView[zoneIndex] = matNumber; + zonalVFFieldView[zoneIndex] = matvfSum / static_cast(n); + } + }); +#else axom::for_all( nzones, AXOM_LAMBDA(auto zoneIndex) { @@ -547,6 +759,7 @@ std::cout << "************ START\n"; std::cout << " storing VF " << zonalVFFieldView[zoneIndex] << "\n"; } }); +#endif std::cout << "************ END\n"; } } @@ -561,6 +774,7 @@ std::cout << "************ END\n"; void iteration(const ITopologyView &topoView, const ICoordsetView &coordsetView, + const axom::mir::views::MaterialInformation &allMats, const axom::mir::views::Material ¤tMat, const conduit::Node &n_topo, @@ -610,8 +824,42 @@ std::cout << "************ END\n"; // Make a material intersector. using ConnectivityType = typename ITopologyView::ConnectivityType; - using IntersectorType = MaterialIntersector; + using IntersectorType = MaterialIntersector; +#if 1 + IntersectorType intersector; + // Populate intersector, including making a number:index map + axom::Array matNumber, matIndex; + const int nmats = static_cast(allMats.size()); + for(int index = 0; index < nmats; index++) + { + // Add a matvf view to the intersector. + const std::string matFieldName = nodalFieldName(allMats[index].number); + auto matVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); + intersector.addMaterial(matVFView); + + matNumber.push_back(allMats[index].number); + matIndex.push_back(index); + } + // Sort indices by matNumber. + std::sort(matIndex.begin(), matIndex.end(), [&](auto idx1, auto idx2) + { + return matNumber[idx1] < matNumber[idx2]; + }); + std::sort(matNumber.begin(), matNumber.end()); + + // Store the number:index map into the intersector. + int allocatorID = axom::execution_space::allocatorID(); + axom::Array matNumberDevice(nmats, nmats, allocatorID), matIndexDevice(nmats, nmats, allocatorID); + axom::copy(matNumberDevice.data(), matNumber.data(), sizeof(int) * nmats); + axom::copy(matIndexDevice.data(), matIndex.data(), sizeof(int) * nmats); + intersector.setMaterialNumbers(matNumberDevice.view()); + intersector.setMaterialIndices(matIndexDevice.view()); + + intersector.setZoneMaterialID(bputils::make_array_view(n_fields.fetch_existing("__equiz_zonalMaterialID/values"))); + intersector.setCurrentMaterial(currentMat.number); +#else IntersectorType intersector(accumVFView, currentMatVFView); +#endif // Make clip options. conduit::Node options; @@ -657,13 +905,12 @@ std::cout << "************ END\n"; const auto colorView = bputils::make_array_view(n_newFields.fetch_existing(colorField + "/values")); const auto nzonesNew = originalElementsView.size(); + // Allocate new material ID/VF arrays. + const int currentMatNumber = currentMat.number; conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing("__equiz_zonalMaterialID/values"); conduit::Node &n_zonalMaterialVF = n_newFields.fetch_existing("__equiz_zonalMaterialVF/values"); auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); auto zonalMaterialVF = bputils::make_array_view(n_zonalMaterialVF); - - // Allocate new material ID/VF arrays. - const int currentMatNumber = currentMat.number; axom::for_all(nzonesNew, AXOM_LAMBDA(auto zoneIndex) { // Color the part we want with the current material. @@ -702,10 +949,11 @@ std::cout << "************ END\n"; #endif // We can remove the current material's nodal from the new fields - if(n_newFields.has_child(matFieldName)) - { - n_newFields.remove(matFieldName); - } +// if(n_newFields.has_child(matFieldName)) +// { +// n_newFields.remove(matFieldName); +// } + // We do not want the color field to survive into the next iteration. n_newFields.remove(colorField); std::cout << "------------------------ end of iteration " From c9c5ee8fa57ea685780412b4b8609b631dc3009f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 19 Aug 2024 18:11:26 -0700 Subject: [PATCH 179/290] code cleanup --- src/axom/mir/ClipField.hpp | 2 - src/axom/mir/EquiZAlgorithm.hpp | 505 +++++++++--------- .../mir/tests/mir_testing_data_helpers.hpp | 16 +- src/axom/mir/views/MaterialView.hpp | 105 ---- 4 files changed, 262 insertions(+), 366 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 7a229c7c89..be0a224163 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -513,13 +513,11 @@ class ClipField std::map fieldsToProcess; if(!opts.fields(fieldsToProcess)) { -std::cout << " fields to process:\n"; // Fields were not present in the options. Select all fields that have the same topology as n_topo. for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { if(n_fields[i].fetch_existing("topology").as_string() == n_topo.name()) { -std::cout << " " << n_fields[i].name() << std::endl; fieldsToProcess[n_fields[i].name()] = n_fields[i].name(); } } diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 710889974f..22ca1b1fca 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -24,6 +24,8 @@ #include #endif +#define AXOM_DEBUG_EQUIZ + namespace axom { namespace mir @@ -38,7 +40,7 @@ using MaterialVFView = axom::ArrayView; constexpr static int NULL_MATERIAL = -1; constexpr static MaterialVF NULL_MATERIAL_VF = 0.f; -#if 1 +#if 0 AXOM_HOST_DEVICE inline bool zoneMatters(int zoneIndex) { @@ -56,19 +58,19 @@ inline bool zoneMatters(int zoneIndex) } return false; } +#endif -template -struct RelationView +namespace detail { - axom::ArrayView m_zonesView {}; - axom::ArrayView m_sizesView {}; - axom::ArrayView m_offsetsView {}; -}; -#if 1 /** * \brief This class is an intersection policy compatible with ClipField. It * helps determine clip cases and weights using material-aware logic. + * + * \tparam ConnectivityT The type of index we'd see in the associated mesh's + * connectivity. We template on it so we can pass the + * array views of connectivity (node lists) to methods here. + * \tparam MAXMATERIALS The max number of materials to handle. */ template class MaterialIntersector @@ -154,6 +156,14 @@ class MaterialIntersector return t; } + /** + * \brief Return the volume fraction array index in m_matIndicesView for the + * given material number \a matNumber. + * + * \param matNumber A material number that occurs in the matset material ids. + * + * \return The m_matNumbersView index on success; INVALID_INDEX on failure. + */ AXOM_HOST_DEVICE int matNumberToIndex(int matNumber) const { @@ -188,9 +198,9 @@ class MaterialIntersector m_currentMaterial = matNumber; } - axom::StaticArray m_matvfViews {}; - axom::ArrayView m_matNumbersView {}; - axom::ArrayView m_matIndicesView {}; + axom::StaticArray m_matvfViews {}; //!< Array of volume fraction views + axom::ArrayView m_matNumbersView {}; //!< Sorted array of material numbers. + axom::ArrayView m_matIndicesView {}; //!< Array of indices into m_matvfViews for the material numbers. axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. int m_currentMaterial {}; //!< The current material. }; @@ -215,6 +225,8 @@ class MaterialIntersector return n_options["topology"].as_string(); } + /// Set various attributes. + void addMaterial(const MaterialVFView &matvf) { m_view.addMaterial(matvf); @@ -250,123 +262,8 @@ class MaterialIntersector private: View m_view {}; }; -#else -/** - * \brief This class is an intersection policy compatible with ClipField. It - * helps determine clip cases and weights using material-aware logic. - */ -template -class MaterialIntersector -{ -public: - using ConnectivityType = ConnectivityT; - using ConnectivityView = axom::ArrayView; - - MaterialIntersector(const MaterialVFView &accumVFView, const MaterialVFView ¤tVFView) : - m_view(accumVFView, currentVFView) - { } - - /** - * \brief This is a view class for MatsetIntersector that can be used in device code. - */ - struct View - { - /// Constructor - View() = default; - /// Constructor - View(const MaterialVFView &accumVFView, const MaterialVFView ¤tVFView) : m_accumVFView(accumVFView), m_currentVFView(currentVFView) - { } - - /** - * \brief Determine the clipping case, taking into account the zone's material - * and the current material being added. - * - * \param zoneIndex The zone index in the zoneMaterialView field. - * \param nodeIds A view containing node ids for the current zone. - * - * \return The clip case number for the zone. - */ - AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ConnectivityView &nodeIdsView) const - { - axom::IndexType clipcase = 0; - const auto n = nodeIdsView.size(); - for(IndexType i = 0; i < n; i++) - { - const auto nid = nodeIdsView[i]; - MaterialVF vf1 = m_accumVFView[nid]; - MaterialVF vf2 = m_currentVFView[nid]; - - clipcase |= (vf2 > vf1) ? (1 << i) : 0; - } - return clipcase; - } - - /** - * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. - * - * \param id0 The mesh node at the start of the edge. - * \param id1 The mesh node at the end of the edge. - */ - AXOM_HOST_DEVICE - float computeWeight(axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), ConnectivityType id0, ConnectivityType id1) const - { - // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. - MaterialVF vf1[2], vf2[2]; - vf1[0] = m_accumVFView[id0]; - vf1[1] = m_accumVFView[id1]; - vf2[0] = m_currentVFView[id0]; - vf2[1] = m_currentVFView[id1]; - - float numerator = vf2[0] - vf1[0]; - float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; - - float t = 0.f; - if(denominator != 0.f) - { - t = numerator / denominator; - } - t = axom::utilities::clampVal(t, 0.f, 1.f); - - return t; - } - - MaterialVFView m_accumVFView {}; - MaterialVFView m_currentVFView {}; - }; - - /** - * \brief Initialize the object from options. - * \param n_options The node that contains the options. - * \param n_fields The node that contains fields. - */ - void initialize(const conduit::Node &AXOM_UNUSED_PARAM(n_options), const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) - { - } - - /** - * \brief Determine the name of the topology on which to operate. - * \param n_input The input mesh node. - * \param n_options The clipping options. - * \return The name of the toplogy on which to operate. - */ - std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), const conduit::Node &n_options) const - { - return n_options["topology"].as_string(); - } - - /** - * \brief Return a new instance of the view. - * \return A new instance of the view. - */ - View view() const { return m_view; } - -private: - View m_view {}; -}; -#endif -#endif +} // end namespace detail /** * \accelerated @@ -395,6 +292,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm virtual ~EquiZAlgorithm() = default; protected: +#if defined(AXOM_DEBUG_EQUIZ) void printNode(const conduit::Node &n) const { conduit::Node options; @@ -402,6 +300,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm options["num_elements_threshold"] = 10000; n.to_summary_string_stream(std::cout, options); } +#endif /** * \brief Perform material interface reconstruction on a single domain. @@ -435,6 +334,20 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::copy(n_options_copy, n_options); n_options_copy["topology"] = n_topo.name(); +#if defined(AXOM_DEBUG_EQUIZ) + //-------------------------------------------------------------------------- + // + // Save the MIR input. + // + //-------------------------------------------------------------------------- + conduit::Node n_tmpInput; + n_tmpInput[n_topo.path()].set_external(n_topo); + n_tmpInput[n_coordset.path()].set_external(n_coordset); + n_tmpInput[n_fields.path()].set_external(n_fields); + n_tmpInput[n_matset.path()].set_external(n_matset); + conduit::relay::io::blueprint::save_mesh(n_tmpInput, "debug_equiz_input", "hdf5"); +#endif + // Make some nodes that will contain the inputs to subsequent iterations. // Store them under a single node so the nodes will have names. conduit::Node n_Input; @@ -459,19 +372,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm makeNodeCenteredVFs(n_topo, n_coordset, n_InputFields, mixedMats); makeWorkingFields(n_topo, n_InputFields, cleanMats, mixedMats); - // Iterate over mixed materials. Note the first material was already accounted for so start at 1. + //-------------------------------------------------------------------------- + // + // Iterate over mixed materials. Note the first material was already accounted + // for in the working fields so start at 1. + // + //-------------------------------------------------------------------------- for(size_t i = 1; i < mixedMats.size(); i++) { if(i == 1) { -#if 1 +#if defined(AXOM_DEBUG_EQUIZ) // Print input mesh. - std::cout << "-------------------------------------------------------------------" << std::endl; conduit::Node mesh; mesh[n_topo.path()].set_external(n_topo); mesh[n_coordset.path()].set_external(n_coordset); mesh[n_InputFields.path()].set_external(n_InputFields); mesh[n_matset.path()].set_external(n_matset); + + std::cout << "-------------------------------------------------------------------" << std::endl; printNode(mesh); std::cout << "-------------------------------------------------------------------" << std::endl; #endif @@ -514,11 +433,13 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputFields.move(n_newFields); // The data are now an unstructured view, probably a mixed shape view. - // Dispatch to an appropriate topo view + // Dispatch to an appropriate topo view. // clangformat-off views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { using ICoordsetView = decltype(coordsetView); using ConnectivityType = typename TopologyView::ConnectivityType; + // Dispatch to an appropriate topo view, taking into account the connectivity + // type and the possible shapes that would be supported for the input topology. views::typed_dispatch_unstructured_topology::selected_shapes()>( n_InputTopo, [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { @@ -547,10 +468,51 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } } - // TODO: we need to build the new matset. + // Build the new matset. + buildNewMatset(n_matset, n_newFields, n_newMatset); + // Cleanup. + for(const auto &mat : allMats) + { + const std::string nodalMatName(nodalFieldName(mat.number)); + if(n_newFields.has_child(nodalMatName)) + { + n_newFields.remove(nodalMatName); + } +#if defined(AXOM_DEBUG_EQUIZ) + const std::string zonalMatName(zonalFieldName(mat.number)); + if(n_newFields.has_child(zonalMatName)) + { + n_newFields.remove(zonalMatName); + } +#endif + } + n_newFields.remove(zonalMaterialIDName()); + +#if defined(AXOM_DEBUG_EQUIZ) + //-------------------------------------------------------------------------- + // + // Save the MIR output. + // + //-------------------------------------------------------------------------- + conduit::Node n_output; + n_output[n_newTopo.path()].set_external(n_newTopo); + n_output[n_newCoordset.path()].set_external(n_newCoordset); + n_output[n_newFields.path()].set_external(n_newFields); + n_output[n_newMatset.path()].set_external(n_newMatset); + conduit::relay::io::blueprint::save_mesh(n_output, "debug_equiz_output", "hdf5"); + printNode(n_output); +#endif } + /** + * \brief Examine the materials and determine which are clean/mixed. + * + * \param n_matset A Conduit node containing the matset. + * \param[out] allMats A vector of all of the materials. + * \param[out] cleanMats A vector of the clean materials. + * \param[out] mixedMats A vector of the mixed materials. + */ void classifyMaterials(const conduit::Node &n_matset, axom::mir::views::MaterialInformation &allMats, axom::mir::views::MaterialInformation &cleanMats, @@ -567,6 +529,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mixedMats = allMats; } + /** + * \brief Return the name of the zonal material field for a given matId. + * \return The name of the zonal material field. + */ std::string zonalFieldName(int matId) const { std::stringstream ss; @@ -574,6 +540,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm return ss.str(); } + /** + * \brief Return the name of the nodal material field for a given matId. + * \return The name of the nodal material field. + */ std::string nodalFieldName(int matId) const { std::stringstream ss; @@ -581,21 +551,28 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm return ss.str(); } - std::string accumulatedVFFieldName() const { return "__equiz_accumulated_VF"; } - - axom::mir::views::MaterialInformation mixedMaterials(const axom::mir::views::MaterialInformation &matInfo) const - { - // TODO: figure out which mats are mixed and which are clean. - return matInfo; - } + /** + * \brief Return the name of the zonal material id field. + * \return The name of the zonal material id field. + */ + std::string zonalMaterialIDName() const { return "__equiz_zonalMaterialID"; } + /** + * \brief Makes node-cenetered volume fractions for the materials in the matset + * and attaches them as fields. + * + * \param n_topo A Conduit node containing the input topology. + * \param n_coordset A Conduit node containin the input coordset. + * \param[inout] A Conduit node where the new fields will be added. + * \param mixedMats A vector of mixed materials. + */ void makeNodeCenteredVFs(const conduit::Node &n_topo, const conduit::Node &n_coordset, conduit::Node &n_fields, const axom::mir::views::MaterialInformation &mixedMats) const { namespace bputils = axom::mir::utilities::blueprint; - // Make a node to zone relation. + // Make a node to zone relation so we know for each node, which zones it touches. conduit::Node relation; { bputils::NodeToZoneRelationBuilder rb; @@ -641,12 +618,22 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::RecenterField z2n; z2n.execute(n_zonalField, relation, n_nodalField); -#ifndef DEBUG_EQUIZ +#if !defined(AXOM_DEBUG_EQUIZ) + // Remove the zonal field that we don't normally need (unless we're debugging). n_fields.remove(zonalName); #endif } } + /** + * \brief Set up the "working fields", mainly a zonalMaterialID that includes + * the contributions from the clean materials and the first mixed material. + * + * \param n_topo A Conduit node containing the input topology pre-MIR. + * \param n_fields A Conduit node containing the fields pre-MIR. + * \param cleanMats A vector of clean materials. + * \param mixedMats A vector of mixed materials. + */ void makeWorkingFields(const conduit::Node &n_topo, conduit::Node &n_fields, const axom::mir::views::MaterialInformation &cleanMats, @@ -658,49 +645,19 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::ConduitAllocateThroughAxom c2a; const auto nzones = m_topologyView.numberOfZones(); - const auto nnodes = m_coordsetView.numberOfNodes(); - // Make the accumulation field. - conduit::Node &n_accumField = n_fields[accumulatedVFFieldName()]; - n_accumField["topology"] = n_topo.name(); - n_accumField["association"] = "vertex"; - n_accumField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_accumField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); - auto accumFieldView = bputils::make_array_view(n_accumField["values"]); - if(!mixedMats.empty()) - { - // Copy the first material's data into the accumulated vfs. - conduit::Node &n_firstMat = n_fields.fetch_existing(nodalFieldName(mixedMats[0].number) + "/values"); - axom::copy(accumFieldView.data(), n_firstMat.data_ptr(), sizeof(MaterialVF) * nnodes); - } - else - { - axom::for_all(nnodes, AXOM_LAMBDA(auto nodeIndex) - { - accumFieldView[nodeIndex] = 0; - }); - } - - // Make the zonal id and vf fields too. - conduit::Node &n_zonalIDField = n_fields["__equiz_zonalMaterialID"]; + // Make the zonal id field. + conduit::Node &n_zonalIDField = n_fields[zonalMaterialIDName()]; n_zonalIDField["topology"] = n_topo.name(); n_zonalIDField["association"] = "element"; n_zonalIDField["values"].set_allocator(c2a.getConduitAllocatorID()); n_zonalIDField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - - conduit::Node &n_zonalVFField = n_fields["__equiz_zonalMaterialVF"]; - n_zonalVFField["topology"] = n_topo.name(); - n_zonalVFField["association"] = "element"; - n_zonalVFField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_zonalVFField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - auto zonalIDFieldView = bputils::make_array_view(n_zonalIDField["values"]); - auto zonalVFFieldView = bputils::make_array_view(n_zonalVFField["values"]); - // Fill all zones with empty. + + // Fill all zones with NULL_MATERIAL. axom::for_all(nzones, AXOM_LAMBDA(auto nodeIndex) { zonalIDFieldView[nodeIndex] = NULL_MATERIAL; - zonalVFFieldView[nodeIndex] = NULL_MATERIAL_VF; }); // Fill in the clean zones. @@ -716,59 +673,57 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm if(deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf)) { zonalIDFieldView[zoneIndex] = matNumber; - zonalVFFieldView[zoneIndex] = static_cast(vf); } }); } + // Fill in the mixed zones for the first mixed material. if(!mixedMats.empty()) { const int matNumber = mixedMats[0].number; -std::cout << "************ START\n"; -#if 1 const std::string matFieldName = nodalFieldName(matNumber); auto matVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); -// NOTE - I think this will cause too many zones to get subdivided. - // Fill in any zone that has nodes where the nodal matVF is greater than zero. + // This fuzzes it out to more zones so we get better blending with the next + // material we try to overlay. m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - constexpr MaterialVF NO_MATERIAL = 1.e-6; - const axom::IndexType n = zone.getIds().size(); + constexpr MaterialVF VOLUME_FRACTION_CUTOFF = 1.e-6; MaterialVF matvfSum {}; - for(axom::IndexType i = 0; i < n; i++) + for(const auto nid : zone.getIds()) { - matvfSum += matVFView[zone.getId(i)]; + matvfSum += matVFView[nid]; } - if(matvfSum > NO_MATERIAL) + // Overwrite the existing material. + if(matvfSum > VOLUME_FRACTION_CUTOFF) { zonalIDFieldView[zoneIndex] = matNumber; - zonalVFFieldView[zoneIndex] = matvfSum / static_cast(n); } }); -#else - axom::for_all( - nzones, - AXOM_LAMBDA(auto zoneIndex) { - FloatType vf {}; - if(deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf)) - { - zonalIDFieldView[zoneIndex] = matNumber; - zonalVFFieldView[zoneIndex] = static_cast(vf); -std::cout << " storing VF " << zonalVFFieldView[zoneIndex] << "\n"; - } - }); -#endif -std::cout << "************ END\n"; } } /** - * \brief Perform one round of material clipping. + * \brief Perform one iteration of material clipping. * * \tparam ITopologyView The topology view type for the intermediate topology. * \tparam ICoordsetView The topology view type for the intermediate coordset. + * + * \param topoView The topology view for the intermediate input topology. + * \param coordsetView The coordset view for the intermediate input coordset. + * \param allMats A vector of Material information (all materials). + * \param currentMat A Material object for the current material. + * \param n_topo A Conduit node containing the intermediate input topology. + * \param n_fields A Conduit node containing the intermediate input fields. + * \param n_options MIR options. + * \param n_newTopo[out] A Conduit node to contain the new topology. + * \param n_newCoordset[out] A Conduit node to contain the new coordset. + * \param n_newFields[out] A Conduit node to contain the new fields. + * + * \note This algorithm uses a ClipField with a MaterialIntersector that gives + * it the ability to access nodal volume fraction fields and make intersection + * decisions with that data. */ template void iteration(const ITopologyView &topoView, @@ -789,12 +744,12 @@ std::cout << "************ END\n"; { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; - std::cout << "------------------------ start of iteration " - "--------------------------------\n"; const std::string colorField("__equiz__colors"); -#if 1 +#if defined(AXOM_DEBUG_EQUIZ) + std::cout << "------------------------ iteration start" + "--------------------------------\n"; //-------------------------------------------------------------------------- // // Save the iteration inputs. @@ -807,25 +762,21 @@ std::cout << "************ END\n"; // save std::stringstream ss1; - ss1 << "debug_equiz_input." << currentMat.number; + ss1 << "debug_equiz_input_iter." << currentMat.number; conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); #endif //-------------------------------------------------------------------------- // - // Invoke clipping with a material intersector. + // Make material intersector. // //-------------------------------------------------------------------------- const std::string matFieldName = nodalFieldName(currentMat.number); - // Get the accumulated VFs and the VFs for the current material. - auto accumVFView = bputils::make_array_view(n_fields.fetch_existing(accumulatedVFFieldName() + "/values")); - auto currentMatVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); - // Make a material intersector. using ConnectivityType = typename ITopologyView::ConnectivityType; - using IntersectorType = MaterialIntersector; -#if 1 + using IntersectorType = detail::MaterialIntersector; + IntersectorType intersector; // Populate intersector, including making a number:index map axom::Array matNumber, matIndex; @@ -847,7 +798,9 @@ std::cout << "************ END\n"; }); std::sort(matNumber.begin(), matNumber.end()); - // Store the number:index map into the intersector. + // Store the number:index map into the intersector. The number:index map lets us + // ask for the field index for a material number, allowing scattered material + // numbers to be used in the matset. int allocatorID = axom::execution_space::allocatorID(); axom::Array matNumberDevice(nmats, nmats, allocatorID), matIndexDevice(nmats, nmats, allocatorID); axom::copy(matNumberDevice.data(), matNumber.data(), sizeof(int) * nmats); @@ -855,13 +808,15 @@ std::cout << "************ END\n"; intersector.setMaterialNumbers(matNumberDevice.view()); intersector.setMaterialIndices(matIndexDevice.view()); - intersector.setZoneMaterialID(bputils::make_array_view(n_fields.fetch_existing("__equiz_zonalMaterialID/values"))); + // Store the current zone material ids and current material number into the intersector. + intersector.setZoneMaterialID(bputils::make_array_view(n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); intersector.setCurrentMaterial(currentMat.number); -#else - IntersectorType intersector(accumVFView, currentMatVFView); -#endif - // Make clip options. + //-------------------------------------------------------------------------- + // + // Make clip options + // + //-------------------------------------------------------------------------- conduit::Node options; options["inside"] = 1; options["outside"] = 1; @@ -874,7 +829,11 @@ std::cout << "************ END\n"; } options["topology"] = n_options["topology"]; - // Clip the topology using the matset. + //-------------------------------------------------------------------------- + // + // Clip the topology using the material intersector. + // + //-------------------------------------------------------------------------- { using ClipperType = axom::mir::clipping::ClipField; ClipperType clipper(topoView, coordsetView, intersector); @@ -887,47 +846,28 @@ std::cout << "************ END\n"; n_newFields); } - // In the output fields, add the current material VFs to the accumulated fields. - // This raises the floor for the next iteration. - accumVFView = bputils::make_array_view(n_newFields.fetch_existing(accumulatedVFFieldName() + "/values")); - currentMatVFView = bputils::make_array_view(n_newFields.fetch_existing(matFieldName + "/values")); - axom::for_all(accumVFView.size(), AXOM_LAMBDA(auto nodeIndex) - { - accumVFView[nodeIndex] += currentMatVFView[nodeIndex]; - }); - //-------------------------------------------------------------------------- // - // Updated zoneMaterialID and zoneMaterialVF based on color. + // Update zoneMaterialID based on color field. // //-------------------------------------------------------------------------- - const auto originalElementsView = bputils::make_array_view(n_newFields.fetch_existing("originalElements/values")); const auto colorView = bputils::make_array_view(n_newFields.fetch_existing(colorField + "/values")); - const auto nzonesNew = originalElementsView.size(); + const auto nzonesNew = colorView.size(); - // Allocate new material ID/VF arrays. - const int currentMatNumber = currentMat.number; - conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing("__equiz_zonalMaterialID/values"); - conduit::Node &n_zonalMaterialVF = n_newFields.fetch_existing("__equiz_zonalMaterialVF/values"); + // Get zonalMAterialID field so we can make adjustments. + conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); - auto zonalMaterialVF = bputils::make_array_view(n_zonalMaterialVF); + const int currentMatNumber = currentMat.number; axom::for_all(nzonesNew, AXOM_LAMBDA(auto zoneIndex) { // Color the part we want with the current material. if(colorView[zoneIndex] == 1) { zonalMaterialID[zoneIndex] = currentMatNumber; - - // NOTE: I'm not 100% sure whether I need to track the VF. - - const auto origIndex = originalElementsView[zoneIndex]; - typename MatsetView::FloatType vf; - m_matsetView.zoneContainsMaterial(origIndex, currentMatNumber, vf); - zonalMaterialVF[zoneIndex] = static_cast(vf); } }); -#if 1 +#if defined(AXOM_DEBUG_EQUIZ) //-------------------------------------------------------------------------- // // Save the clip results. @@ -944,20 +884,81 @@ std::cout << "************ END\n"; // save std::stringstream ss; - ss << "debug_equiz_output." << currentMat.number; + ss << "debug_equiz_output_iter." << currentMat.number; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); + + std::cout << "------------------------ iteration end" + "--------------------------------\n"; #endif - // We can remove the current material's nodal from the new fields -// if(n_newFields.has_child(matFieldName)) -// { -// n_newFields.remove(matFieldName); -// } // We do not want the color field to survive into the next iteration. n_newFields.remove(colorField); + } - std::cout << "------------------------ end of iteration " - "--------------------------------\n"; + /** + * \brief Build a new matset with only clean zones, representing the MIR output. + * + * \param n_matset n_matset The Conduit node that contains the input matset. + * \param[inout] n_newFields The Conduit node that contains the fields for the MIR output. + * \param[out] n_newMatset The node that contains the new matset. + */ + void buildNewMatset(const conduit::Node &n_matset, conduit::Node &n_newFields, conduit::Node &n_newMatset) const + { + namespace bputils = axom::mir::utilities::blueprint; + + // Get the zonalMaterialID field that has our new material ids. + conduit::Node &n_zonalMaterialID = n_newFields[zonalMaterialIDName() + "/values"]; + auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); + const auto nzones = n_zonalMaterialID.dtype().number_of_elements(); + + // Copy some information from the old matset to the new one. + if(n_matset.has_child("topology")) + { + n_newMatset["topology"].set(n_matset.fetch_existing("topology")); + } + if(n_matset.has_child("material_map")) + { + n_newMatset["material_map"].set(n_matset.fetch_existing("material_map")); + } + + // Make new nodes in the matset. + conduit::Node &n_material_ids = n_newMatset["material_ids"]; + conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; + conduit::Node &n_sizes = n_newMatset["sizes"]; + conduit::Node &n_offsets = n_newMatset["offsets"]; + conduit::Node &n_indices = n_newMatset["indices"]; + + bputils::ConduitAllocateThroughAxom c2a; + n_material_ids.set_allocator(c2a.getConduitAllocatorID()); + n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); + n_sizes.set_allocator(c2a.getConduitAllocatorID()); + n_offsets.set_allocator(c2a.getConduitAllocatorID()); + n_indices.set_allocator(c2a.getConduitAllocatorID()); + + // We'll store the output matset in the same types as the input matset. + using MIntType = typename MatsetView::IndexType; + using MFloatType = typename MatsetView::FloatType; + n_material_ids.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_volume_fractions.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_sizes.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_offsets.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_indices.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + + auto material_ids_view = bputils::make_array_view(n_material_ids); + auto volume_fractions_view = bputils::make_array_view(n_volume_fractions); + auto sizes_view = bputils::make_array_view(n_sizes); + auto offsets_view = bputils::make_array_view(n_offsets); + auto indices_view = bputils::make_array_view(n_indices); + + // Fill in the new matset data arrays. + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + material_ids_view[zoneIndex] = static_cast(zonalMaterialID[zoneIndex]); + volume_fractions_view[zoneIndex] = 1; + sizes_view[zoneIndex] = 1; + offsets_view[zoneIndex] = static_cast(zoneIndex); + indices_view[zoneIndex] = static_cast(zoneIndex); + }); } private: diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index 1e26240559..2b85243b0b 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -55,6 +55,7 @@ void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) void make_unibuffer(const std::vector &vfA, const std::vector &vfB, const std::vector &vfC, + const std::vector &matnos, conduit::Node &matset) { std::vector material_ids; @@ -69,21 +70,21 @@ void make_unibuffer(const std::vector &vfA, if(vfA[i] > 0.) { indices.push_back(static_cast(indices.size())); - material_ids.push_back(0); + material_ids.push_back(matnos[0]); volume_fractions.push_back(vfA[i]); sizes[i]++; } if(vfB[i] > 0.) { indices.push_back(static_cast(indices.size())); - material_ids.push_back(1); + material_ids.push_back(matnos[1]); volume_fractions.push_back(vfB[i]); sizes[i]++; } if(vfC[i] > 0.) { indices.push_back(static_cast(indices.size())); - material_ids.push_back(2); + material_ids.push_back(matnos[2]); volume_fractions.push_back(vfC[i]); sizes[i]++; } @@ -185,16 +186,17 @@ void make_matset(const std::string &type, mesh["fields/vfC/values"].set(vfC); #endif + const std::vector matnos{{22,66,33}}; conduit::Node &matset = mesh["matsets/mat"]; matset["topology"] = topoName; - matset["material_map/A"] = 0; - matset["material_map/B"] = 1; - matset["material_map/C"] = 2; + matset["material_map/A"] = matnos[0]; + matset["material_map/B"] = matnos[1]; + matset["material_map/C"] = matnos[2]; // produce different material types. if(type == "unibuffer") { - make_unibuffer(vfA, vfB, vfC, matset); + make_unibuffer(vfA, vfB, vfC, matnos, matset); } // TODO: write these other cases. else if(type == "multibuffer") diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 6d18802e85..39c3a45656 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -149,76 +149,13 @@ class UnibufferMaterialView if(m_material_ids[idx] == mat) { vf = m_volume_fractions[idx]; -std::cout << "zoneContainsMaterial: zi=" << zi << ", mat=" << mat << ", vf=" << vf << " -> true\n"; return true; } } vf = 0; -std::cout << "zoneContainsMaterial: zi=" << zi << ", mat=" << mat << ", vf=" << vf << " -> false\n"; return false; } - /** - * \brief Return the dominant material ids for the zone (highest volume fraction material). - * \param zi The zone index. - * \return The material id with the highest volume fraction. - */ - AXOM_HOST_DEVICE - int dominantMaterial(ZoneIndex zi) const - { - assert(zi < numberOfZones()); - const auto sz = numberOfMaterials(zi); - const auto offset = m_offsets[zi]; - FloatType maxVF = -1; - int mat {}; - for(axom::IndexType i = 0; i < sz; i++) - { - const auto idx = m_indices[offset + i]; - const auto vf = m_volume_fractions[idx]; - if(vf > maxVF) - { - mat = m_material_ids[idx]; - maxVF = vf; - } - } - return mat; - } - - /** - * \brief Return a background material with high VF that is not the supplied material. - * \param zi The zone index. - * \return The material id with the highest volume fraction. - */ - AXOM_HOST_DEVICE - MaterialIndex backgroundMaterial(ZoneIndex zi, MaterialIndex currentMat) const - { - assert(zi < numberOfZones()); - const auto sz = numberOfMaterials(zi); - const auto offset = m_offsets[zi]; - MaterialIndex mat{}; - if(sz == 1) - { - // There is only one material so return that. - const auto idx = m_indices[offset]; - mat = m_material_ids[idx]; - } - else - { - // There are multiple materials. Return the largest VF that is not owned by currentMat. - FloatType maxVF = -1; - for(axom::IndexType i = 0; i < sz; i++) - { - const auto idx = m_indices[offset + i]; - const auto vf = m_volume_fractions[idx]; - if(m_material_ids[idx] != currentMat && vf > maxVF) - { - mat = m_material_ids[idx]; - maxVF = vf; - } - } - } - return mat; - } private: axom::ArrayView m_material_ids; axom::ArrayView m_volume_fractions; @@ -335,20 +272,6 @@ class MultiBufferMaterialView return vf > 0.; } - /** - * \brief Return the dominant material ids for the zone (highest volume fraction material). - * \param zi The zone index. - * \return The material id with the highest volume fraction. - */ - AXOM_HOST_DEVICE - int dominantMaterial(ZoneIndex zi) const - { - assert(zi < numberOfZones()); - FloatType maxVF = -1; - int mat {}; - // TODO: write me - return mat; - } private: axom::StackArray, MAXMATERIALS> m_values {}; axom::StackArray, MAXMATERIALS> m_indices {}; @@ -451,20 +374,6 @@ class ElementDominantMaterialView return contains; } - /** - * \brief Return the dominant material ids for the zone (highest volume fraction material). - * \param zi The zone index. - * \return The material id with the highest volume fraction. - */ - AXOM_HOST_DEVICE - int dominantMaterial(ZoneIndex zi) const - { - assert(zi < numberOfZones()); - FloatType maxVF = -1; - int mat {}; - // TODO: write me - return mat; - } private: axom::StaticArray, MAXMATERIALS> m_volume_fractions {}; }; @@ -641,20 +550,6 @@ class MaterialDominantMaterialView return found; } - /** - * \brief Return the dominant material ids for the zone (highest volume fraction material). - * \param zi The zone index. - * \return The material id with the highest volume fraction. - */ - AXOM_HOST_DEVICE - int dominantMaterial(ZoneIndex zi) const - { - assert(zi < numberOfZones()); - FloatType maxVF = -1; - int mat {}; - // TODO: write me - return mat; - } private: StackArray, MAXMATERIALS> m_element_ids {}; StackArray, MAXMATERIALS> m_volume_fractions {}; From b0dc1a81833aaa16e144992040f4cbe387694310 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 19 Aug 2024 18:14:24 -0700 Subject: [PATCH 180/290] make style --- src/axom/mir/ClipField.hpp | 6 +- src/axom/mir/EquiZAlgorithm.hpp | 251 ++++++++++-------- src/axom/mir/tests/mir_equiz.cpp | 6 +- .../mir/tests/mir_testing_data_helpers.hpp | 2 +- .../views/dispatch_unstructured_topology.hpp | 41 +-- src/axom/mir/views/view_traits.hpp | 5 +- 6 files changed, 182 insertions(+), 129 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index be0a224163..0a99a82d30 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -248,7 +248,8 @@ class FieldIntersector * \param n_options The clipping options. * \return The name of the toplogy on which to operate. */ - std::string getTopologyName(const conduit::Node &n_input, const conduit::Node &n_options) const + std::string getTopologyName(const conduit::Node &n_input, + const conduit::Node &n_options) const { // Get the clipField's topo name. ClipOptions opts(n_options); @@ -332,7 +333,8 @@ class ClipField { // Get the topo/coordset names in the input. ClipOptions opts(n_options); - const std::string topoName = m_intersector.getTopologyName(n_input, n_options); + const std::string topoName = + m_intersector.getTopologyName(n_input, n_options); const conduit::Node &n_topo = n_input.fetch_existing("topologies/" + topoName); const std::string coordsetName = n_topo["coordset"].as_string(); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 22ca1b1fca..7dc3b4d086 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -18,6 +18,7 @@ #include #include +#include #include #if defined(AXOM_USE_RAJA) @@ -62,7 +63,6 @@ inline bool zoneMatters(int zoneIndex) namespace detail { - /** * \brief This class is an intersection policy compatible with ClipField. It * helps determine clip cases and weights using material-aware logic. @@ -96,7 +96,8 @@ class MaterialIntersector * \return The clip case number for the zone. */ AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType zoneIndex, const ConnectivityView &nodeIdsView) const + axom::IndexType determineClipCase(axom::IndexType zoneIndex, + const ConnectivityView &nodeIdsView) const { // Determine the matvf view index for the material that owns the zone. int backgroundIndex = INVALID_INDEX; @@ -111,8 +112,11 @@ class MaterialIntersector for(IndexType i = 0; i < n; i++) { const auto nid = nodeIdsView[i]; - MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : 0.; - MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0.; + MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) + ? m_matvfViews[backgroundIndex][nid] + : 0.; + MaterialVF vf2 = + (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0.; clipcase |= (vf2 > vf1) ? (1 << i) : 0; } @@ -126,7 +130,9 @@ class MaterialIntersector * \param id1 The mesh node at the end of the edge. */ AXOM_HOST_DEVICE - float computeWeight(axom::IndexType zoneIndex, ConnectivityType id0, ConnectivityType id1) const + float computeWeight(axom::IndexType zoneIndex, + ConnectivityType id0, + ConnectivityType id1) const { // Determine the matvf view index for the material that owns the zone. int backgroundIndex = INVALID_INDEX; @@ -138,10 +144,16 @@ class MaterialIntersector // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; - vf1[0] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id0] : 0.; - vf1[1] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id1] : 0.; - vf2[0] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0.; - vf2[1] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0.; + vf1[0] = (backgroundIndex != INVALID_INDEX) + ? m_matvfViews[backgroundIndex][id0] + : 0.; + vf1[1] = (backgroundIndex != INVALID_INDEX) + ? m_matvfViews[backgroundIndex][id1] + : 0.; + vf2[0] = + (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0.; + vf2[1] = + (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0.; float numerator = vf2[0] - vf1[0]; float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; @@ -193,16 +205,14 @@ class MaterialIntersector m_zoneMatNumberView = zoneMatsView; } - void setCurrentMaterial(int matNumber) - { - m_currentMaterial = matNumber; - } + void setCurrentMaterial(int matNumber) { m_currentMaterial = matNumber; } - axom::StaticArray m_matvfViews {}; //!< Array of volume fraction views - axom::ArrayView m_matNumbersView {}; //!< Sorted array of material numbers. - axom::ArrayView m_matIndicesView {}; //!< Array of indices into m_matvfViews for the material numbers. - axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. - int m_currentMaterial {}; //!< The current material. + axom::StaticArray + m_matvfViews {}; //!< Array of volume fraction views + axom::ArrayView m_matNumbersView {}; //!< Sorted array of material numbers. + axom::ArrayView m_matIndicesView {}; //!< Array of indices into m_matvfViews for the material numbers. + axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. + int m_currentMaterial {}; //!< The current material. }; /** @@ -210,9 +220,9 @@ class MaterialIntersector * \param n_options The node that contains the options. * \param n_fields The node that contains fields. */ - void initialize(const conduit::Node &AXOM_UNUSED_PARAM(n_options), const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) - { - } + void initialize(const conduit::Node &AXOM_UNUSED_PARAM(n_options), + const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) + { } /** * \brief Determine the name of the topology on which to operate. @@ -220,17 +230,15 @@ class MaterialIntersector * \param n_options The clipping options. * \return The name of the toplogy on which to operate. */ - std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), const conduit::Node &n_options) const + std::string getTopologyName(const conduit::Node &AXOM_UNUSED_PARAM(n_input), + const conduit::Node &n_options) const { return n_options["topology"].as_string(); } /// Set various attributes. - void addMaterial(const MaterialVFView &matvf) - { - m_view.addMaterial(matvf); - } + void addMaterial(const MaterialVFView &matvf) { m_view.addMaterial(matvf); } void setMaterialNumbers(const axom::ArrayView &matNumbers) { @@ -263,7 +271,7 @@ class MaterialIntersector View m_view {}; }; -} // end namespace detail +} // end namespace detail /** * \accelerated @@ -345,7 +353,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_tmpInput[n_coordset.path()].set_external(n_coordset); n_tmpInput[n_fields.path()].set_external(n_fields); n_tmpInput[n_matset.path()].set_external(n_matset); - conduit::relay::io::blueprint::save_mesh(n_tmpInput, "debug_equiz_input", "hdf5"); + conduit::relay::io::blueprint::save_mesh(n_tmpInput, + "debug_equiz_input", + "hdf5"); #endif // Make some nodes that will contain the inputs to subsequent iterations. @@ -390,9 +400,13 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mesh[n_InputFields.path()].set_external(n_InputFields); mesh[n_matset.path()].set_external(n_matset); - std::cout << "-------------------------------------------------------------------" << std::endl; + std::cout << "---------------------------------------------------------" + "----------" + << std::endl; printNode(mesh); - std::cout << "-------------------------------------------------------------------" << std::endl; + std::cout << "---------------------------------------------------------" + "----------" + << std::endl; #endif // The first time through, we can use the supplied views. @@ -427,7 +441,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputCoordset.reset(); n_InputFields.reset(); - // Move the outputs of the last iteration to the inputs of this iteration. + // Move the outputs of the last iteration to the inputs of this iteration. n_InputTopo.move(n_newTopo); n_InputCoordset.move(n_newCoordset); n_InputFields.move(n_newFields); @@ -440,28 +454,29 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm using ConnectivityType = typename TopologyView::ConnectivityType; // Dispatch to an appropriate topo view, taking into account the connectivity // type and the possible shapes that would be supported for the input topology. - views::typed_dispatch_unstructured_topology::selected_shapes()>( + views::typed_dispatch_unstructured_topology< + ConnectivityType, + views::view_traits::selected_shapes()>( n_InputTopo, [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { using ITopologyView = decltype(topologyView); // Do the next iteration. - iteration( - topologyView, - coordsetView, + iteration(topologyView, + coordsetView, - allMats, - mixedMats[i], + allMats, + mixedMats[i], - n_InputTopo, - n_InputCoordset, - n_InputFields, + n_InputTopo, + n_InputCoordset, + n_InputFields, - n_options_copy, + n_options_copy, - n_newTopo, - n_newCoordset, - n_newFields); + n_newTopo, + n_newCoordset, + n_newFields); }); }); // clangformat-on @@ -500,7 +515,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_output[n_newCoordset.path()].set_external(n_newCoordset); n_output[n_newFields.path()].set_external(n_newFields); n_output[n_newMatset.path()].set_external(n_newMatset); - conduit::relay::io::blueprint::save_mesh(n_output, "debug_equiz_output", "hdf5"); + conduit::relay::io::blueprint::save_mesh(n_output, + "debug_equiz_output", + "hdf5"); printNode(n_output); #endif } @@ -519,7 +536,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm axom::mir::views::MaterialInformation &mixedMats) const { cleanMats.clear(); - mixedMats.clear(); + mixedMats.clear(); allMats = axom::mir::views::materials(n_matset); // TODO: actually determine which materials are clean/mixed. It's probably @@ -555,7 +572,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \brief Return the name of the zonal material id field. * \return The name of the zonal material id field. */ - std::string zonalMaterialIDName() const { return "__equiz_zonalMaterialID"; } + std::string zonalMaterialIDName() const { return "__equiz_zonalMaterialID"; } /** * \brief Makes node-cenetered volume fractions for the materials in the matset @@ -595,8 +612,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_zonalField["topology"] = n_topo.name(); n_zonalField["association"] = "element"; n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_zonalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - auto zonalFieldView = bputils::make_array_view(n_zonalField["values"]); + n_zonalField["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nzones)); + auto zonalFieldView = + bputils::make_array_view(n_zonalField["values"]); // Fill the zonal field from the matset. MatsetView deviceMatsetView(m_matsetView); @@ -606,7 +625,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm typename MatsetView::FloatType vf {}; deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf); zonalFieldView[zoneIndex] = static_cast(vf); - }); + }); // Make a nodal field for the current material by recentering. const std::string nodalName = nodalFieldName(matNumber); @@ -614,7 +633,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_nodalField["topology"] = n_topo.name(); n_nodalField["association"] = "vertex"; n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_nodalField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nnodes)); + n_nodalField["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nnodes)); bputils::RecenterField z2n; z2n.execute(n_zonalField, relation, n_nodalField); @@ -651,14 +671,17 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_zonalIDField["topology"] = n_topo.name(); n_zonalIDField["association"] = "element"; n_zonalIDField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_zonalIDField["values"].set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - auto zonalIDFieldView = bputils::make_array_view(n_zonalIDField["values"]); + n_zonalIDField["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nzones)); + auto zonalIDFieldView = + bputils::make_array_view(n_zonalIDField["values"]); // Fill all zones with NULL_MATERIAL. - axom::for_all(nzones, AXOM_LAMBDA(auto nodeIndex) - { - zonalIDFieldView[nodeIndex] = NULL_MATERIAL; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto nodeIndex) { + zonalIDFieldView[nodeIndex] = NULL_MATERIAL; + }); // Fill in the clean zones. using FloatType = typename MatsetView::FloatType; @@ -682,25 +705,26 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { const int matNumber = mixedMats[0].number; const std::string matFieldName = nodalFieldName(matNumber); - auto matVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); + auto matVFView = bputils::make_array_view( + n_fields.fetch_existing(matFieldName + "/values")); // Fill in any zone that has nodes where the nodal matVF is greater than zero. // This fuzzes it out to more zones so we get better blending with the next // material we try to overlay. - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - constexpr MaterialVF VOLUME_FRACTION_CUTOFF = 1.e-6; - MaterialVF matvfSum {}; - for(const auto nid : zone.getIds()) - { - matvfSum += matVFView[nid]; - } - // Overwrite the existing material. - if(matvfSum > VOLUME_FRACTION_CUTOFF) - { - zonalIDFieldView[zoneIndex] = matNumber; - } - }); + m_topologyView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + constexpr MaterialVF VOLUME_FRACTION_CUTOFF = 1.e-6; + MaterialVF matvfSum {}; + for(const auto nid : zone.getIds()) + { + matvfSum += matVFView[nid]; + } + // Overwrite the existing material. + if(matvfSum > VOLUME_FRACTION_CUTOFF) + { + zonalIDFieldView[zoneIndex] = matNumber; + } + }); } } @@ -775,7 +799,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make a material intersector. using ConnectivityType = typename ITopologyView::ConnectivityType; - using IntersectorType = detail::MaterialIntersector; + using IntersectorType = + detail::MaterialIntersector; IntersectorType intersector; // Populate intersector, including making a number:index map @@ -785,15 +810,15 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { // Add a matvf view to the intersector. const std::string matFieldName = nodalFieldName(allMats[index].number); - auto matVFView = bputils::make_array_view(n_fields.fetch_existing(matFieldName + "/values")); + auto matVFView = bputils::make_array_view( + n_fields.fetch_existing(matFieldName + "/values")); intersector.addMaterial(matVFView); matNumber.push_back(allMats[index].number); matIndex.push_back(index); } // Sort indices by matNumber. - std::sort(matIndex.begin(), matIndex.end(), [&](auto idx1, auto idx2) - { + std::sort(matIndex.begin(), matIndex.end(), [&](auto idx1, auto idx2) { return matNumber[idx1] < matNumber[idx2]; }); std::sort(matNumber.begin(), matNumber.end()); @@ -802,14 +827,16 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // ask for the field index for a material number, allowing scattered material // numbers to be used in the matset. int allocatorID = axom::execution_space::allocatorID(); - axom::Array matNumberDevice(nmats, nmats, allocatorID), matIndexDevice(nmats, nmats, allocatorID); + axom::Array matNumberDevice(nmats, nmats, allocatorID), + matIndexDevice(nmats, nmats, allocatorID); axom::copy(matNumberDevice.data(), matNumber.data(), sizeof(int) * nmats); axom::copy(matIndexDevice.data(), matIndex.data(), sizeof(int) * nmats); intersector.setMaterialNumbers(matNumberDevice.view()); intersector.setMaterialIndices(matIndexDevice.view()); // Store the current zone material ids and current material number into the intersector. - intersector.setZoneMaterialID(bputils::make_array_view(n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); + intersector.setZoneMaterialID(bputils::make_array_view( + n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); intersector.setCurrentMaterial(currentMat.number); //-------------------------------------------------------------------------- @@ -835,7 +862,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - using ClipperType = axom::mir::clipping::ClipField; + using ClipperType = + axom::mir::clipping::ClipField; ClipperType clipper(topoView, coordsetView, intersector); clipper.execute(n_topo, n_coordset, @@ -851,21 +879,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Update zoneMaterialID based on color field. // //-------------------------------------------------------------------------- - const auto colorView = bputils::make_array_view(n_newFields.fetch_existing(colorField + "/values")); + const auto colorView = bputils::make_array_view( + n_newFields.fetch_existing(colorField + "/values")); const auto nzonesNew = colorView.size(); // Get zonalMAterialID field so we can make adjustments. - conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); - auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); + conduit::Node &n_zonalMaterialID = + n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); + auto zonalMaterialID = + bputils::make_array_view(n_zonalMaterialID); const int currentMatNumber = currentMat.number; - axom::for_all(nzonesNew, AXOM_LAMBDA(auto zoneIndex) - { - // Color the part we want with the current material. - if(colorView[zoneIndex] == 1) - { - zonalMaterialID[zoneIndex] = currentMatNumber; - } - }); + axom::for_all( + nzonesNew, + AXOM_LAMBDA(auto zoneIndex) { + // Color the part we want with the current material. + if(colorView[zoneIndex] == 1) + { + zonalMaterialID[zoneIndex] = currentMatNumber; + } + }); #if defined(AXOM_DEBUG_EQUIZ) //-------------------------------------------------------------------------- @@ -902,13 +934,17 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \param[inout] n_newFields The Conduit node that contains the fields for the MIR output. * \param[out] n_newMatset The node that contains the new matset. */ - void buildNewMatset(const conduit::Node &n_matset, conduit::Node &n_newFields, conduit::Node &n_newMatset) const + void buildNewMatset(const conduit::Node &n_matset, + conduit::Node &n_newFields, + conduit::Node &n_newMatset) const { namespace bputils = axom::mir::utilities::blueprint; // Get the zonalMaterialID field that has our new material ids. - conduit::Node &n_zonalMaterialID = n_newFields[zonalMaterialIDName() + "/values"]; - auto zonalMaterialID = bputils::make_array_view(n_zonalMaterialID); + conduit::Node &n_zonalMaterialID = + n_newFields[zonalMaterialIDName() + "/values"]; + auto zonalMaterialID = + bputils::make_array_view(n_zonalMaterialID); const auto nzones = n_zonalMaterialID.dtype().number_of_elements(); // Copy some information from the old matset to the new one. @@ -938,33 +974,38 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // We'll store the output matset in the same types as the input matset. using MIntType = typename MatsetView::IndexType; using MFloatType = typename MatsetView::FloatType; - n_material_ids.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); - n_volume_fractions.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_material_ids.set( + conduit::DataType(bputils::cpp2conduit::id, nzones)); + n_volume_fractions.set( + conduit::DataType(bputils::cpp2conduit::id, nzones)); n_sizes.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); n_offsets.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); n_indices.set(conduit::DataType(bputils::cpp2conduit::id, nzones)); auto material_ids_view = bputils::make_array_view(n_material_ids); - auto volume_fractions_view = bputils::make_array_view(n_volume_fractions); + auto volume_fractions_view = + bputils::make_array_view(n_volume_fractions); auto sizes_view = bputils::make_array_view(n_sizes); auto offsets_view = bputils::make_array_view(n_offsets); auto indices_view = bputils::make_array_view(n_indices); // Fill in the new matset data arrays. - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - material_ids_view[zoneIndex] = static_cast(zonalMaterialID[zoneIndex]); - volume_fractions_view[zoneIndex] = 1; - sizes_view[zoneIndex] = 1; - offsets_view[zoneIndex] = static_cast(zoneIndex); - indices_view[zoneIndex] = static_cast(zoneIndex); - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + material_ids_view[zoneIndex] = + static_cast(zonalMaterialID[zoneIndex]); + volume_fractions_view[zoneIndex] = 1; + sizes_view[zoneIndex] = 1; + offsets_view[zoneIndex] = static_cast(zoneIndex); + indices_view[zoneIndex] = static_cast(zoneIndex); + }); } private: TopologyView m_topologyView; CoordsetView m_coordsetView; - MatsetView m_matsetView; + MatsetView m_matsetView; }; } // end namespace mir diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index ac95174244..121edbbf8c 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -128,9 +128,9 @@ TEST(mir_equiz, equiz_uniform_unibuffer) { braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); -//#if defined(AXOM_USE_OPENMP) -// braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); -//#endif + //#if defined(AXOM_USE_OPENMP) + // braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); + //#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index 2b85243b0b..d5b089f1e0 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -186,7 +186,7 @@ void make_matset(const std::string &type, mesh["fields/vfC/values"].set(vfC); #endif - const std::vector matnos{{22,66,33}}; + const std::vector matnos {{22, 66, 33}}; conduit::Node &matset = mesh["matsets/mat"]; matset["topology"] = topoName; matset["material_map/A"] = matnos[0]; diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 942217ae03..ecb8d8cf31 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -73,12 +73,17 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, const std::string shape = topo["elements/shape"].as_string(); if(shape == "polyhedral") { - auto seConnView = bputils::make_array_view(topo["subelements/connectivity"]); - auto seSizesView = bputils::make_array_view(topo["subelements/sizes"]); - auto seOffsetsView = bputils::make_array_view(topo["subelements/offsets"]); - auto connView = bputils::make_array_view(topo["elements/connectivity"]); + auto seConnView = + bputils::make_array_view(topo["subelements/connectivity"]); + auto seSizesView = + bputils::make_array_view(topo["subelements/sizes"]); + auto seOffsetsView = + bputils::make_array_view(topo["subelements/offsets"]); + auto connView = + bputils::make_array_view(topo["elements/connectivity"]); auto sizesView = bputils::make_array_view(topo["elements/sizes"]); - auto offsetsView = bputils::make_array_view(topo["elements/offsets"]); + auto offsetsView = + bputils::make_array_view(topo["elements/offsets"]); UnstructuredTopologyPolyhedralView ugView(seConnView, seSizesView, @@ -135,10 +140,13 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, const std::string shape = topo["elements/shape"].as_string(); if(shape == "mixed") { - auto connView = bputils::make_array_view(topo["elements/connectivity"]); - auto shapesView = bputils::make_array_view(topo["elements/shapes"]); + auto connView = + bputils::make_array_view(topo["elements/connectivity"]); + auto shapesView = + bputils::make_array_view(topo["elements/shapes"]); auto sizesView = bputils::make_array_view(topo["elements/sizes"]); - auto offsetsView = bputils::make_array_view(topo["elements/offsets"]); + auto offsetsView = + bputils::make_array_view(topo["elements/offsets"]); UnstructuredTopologyMixedShapeView ugView(topo, connView, @@ -172,14 +180,16 @@ constexpr int select_shapes(Args... args) * \param func The function/lambda to call with the topology view. */ template -void typed_dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) +void typed_dispatch_unstructured_topology(const conduit::Node &topo, + FuncType &&func) { namespace bputils = axom::mir::utilities::blueprint; const std::string type = topo["type"].as_string(); if(type == "unstructured") { const std::string shape = topo["elements/shape"].as_string(); - const auto connView = bputils::make_array_view(topo["elements/connectivity"]); + const auto connView = + bputils::make_array_view(topo["elements/connectivity"]); bool eligible = true; // Conditionally add polyhedron support. @@ -218,8 +228,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, FuncType && { if(eligible && shape == "quad") { - UnstructuredTopologySingleShapeView> ugView( - connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } @@ -247,8 +256,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, FuncType && { if(eligible && shape == "wedge") { - UnstructuredTopologySingleShapeView> ugView( - connView); + UnstructuredTopologySingleShapeView> ugView(connView); func(shape, ugView); eligible = false; } @@ -270,12 +278,11 @@ template void dispatch_unstructured_topology(const conduit::Node &topo, FuncType &&func) { IndexNode_to_ArrayView(topo["elements/connectivity"], [&](auto connView) { - using ConnType = typename decltype(connView)::value_type; - typed_dispatch_unstructured_topology(topo, func); + using ConnType = typename decltype(connView)::value_type; + typed_dispatch_unstructured_topology(topo, func); }); } - } // end namespace views } // end namespace mir } // end namespace axom diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index 1ecfb12539..a07fff4e33 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -42,7 +42,10 @@ template struct view_traits { static constexpr bool supports_strided_structured() { return false; } - static constexpr int selected_shapes() { return shapes_for_dimension(TopologyView::dimension()); } + static constexpr int selected_shapes() + { + return shapes_for_dimension(TopologyView::dimension()); + } }; /// If StructuredTopologyView was instantiated with StridedStructuredIndexing From 44e75d4d21110e4ef2ce687b4948a80e4a001d70 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 20 Aug 2024 13:27:45 -0700 Subject: [PATCH 181/290] Added timers --- src/axom/mir/CMakeLists.txt | 2 +- src/axom/mir/EquiZAlgorithm.hpp | 303 +++++++++++++++++++------------- 2 files changed, 179 insertions(+), 126 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 20ecbafe94..4518c74c20 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and # other Axom Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (BSD-3-Clause) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 7dc3b4d086..4410204cfa 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -27,6 +27,11 @@ #define AXOM_DEBUG_EQUIZ +// This enables a tweak to the algorithm that tries to skip the first iteration +// by incorporating the first material's ids into the zonalMaterialID field. It +// could be faster but it might not be as robust. +// #define AXOM_EQUIZ_SKIP_FIRST_ITERATION + namespace axom { namespace mir @@ -39,7 +44,7 @@ using MaterialVFArray = axom::Array; using MaterialVFView = axom::ArrayView; constexpr static int NULL_MATERIAL = -1; -constexpr static MaterialVF NULL_MATERIAL_VF = 0.f; +constexpr static MaterialVF NULL_MATERIAL_VF = -1.f; #if 0 AXOM_HOST_DEVICE @@ -112,11 +117,10 @@ class MaterialIntersector for(IndexType i = 0; i < n; i++) { const auto nid = nodeIdsView[i]; - MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) - ? m_matvfViews[backgroundIndex][nid] - : 0.; - MaterialVF vf2 = - (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0.; + // clangformat-off + MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; + MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0; + // clangformat-on clipcase |= (vf2 > vf1) ? (1 << i) : 0; } @@ -144,16 +148,12 @@ class MaterialIntersector // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; - vf1[0] = (backgroundIndex != INVALID_INDEX) - ? m_matvfViews[backgroundIndex][id0] - : 0.; - vf1[1] = (backgroundIndex != INVALID_INDEX) - ? m_matvfViews[backgroundIndex][id1] - : 0.; - vf2[0] = - (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0.; - vf2[1] = - (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0.; + // clangformat-off + vf1[0] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id0] : NULL_MATERIAL_VF; + vf1[1] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id1] : NULL_MATERIAL_VF; + vf2[0] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0; + vf2[1] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0; + // clangformat-on float numerator = vf2[0] - vf1[0]; float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; @@ -336,6 +336,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newMatset) override { namespace bputils = axom::mir::utilities::blueprint; + axom::utilities::Timer t0(true); // Copy the options. conduit::Node n_options_copy; @@ -384,33 +385,21 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm //-------------------------------------------------------------------------- // - // Iterate over mixed materials. Note the first material was already accounted - // for in the working fields so start at 1. + // Iterate over mixed materials. // //-------------------------------------------------------------------------- - for(size_t i = 1; i < mixedMats.size(); i++) +#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) + constexpr int first = 1; +#else + constexpr int first = 0; +#endif + for(size_t i = first; i < mixedMats.size(); i++) { - if(i == 1) + if(i == first) { -#if defined(AXOM_DEBUG_EQUIZ) - // Print input mesh. - conduit::Node mesh; - mesh[n_topo.path()].set_external(n_topo); - mesh[n_coordset.path()].set_external(n_coordset); - mesh[n_InputFields.path()].set_external(n_InputFields); - mesh[n_matset.path()].set_external(n_matset); - - std::cout << "---------------------------------------------------------" - "----------" - << std::endl; - printNode(mesh); - std::cout << "---------------------------------------------------------" - "----------" - << std::endl; -#endif - // The first time through, we can use the supplied views. - iteration(m_topologyView, + iteration(i, + m_topologyView, m_coordsetView, allMats, @@ -462,7 +451,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm using ITopologyView = decltype(topologyView); // Do the next iteration. - iteration(topologyView, + iteration(i, + topologyView, coordsetView, allMats, @@ -487,6 +477,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm buildNewMatset(n_matset, n_newFields, n_newMatset); // Cleanup. + axom::utilities::Timer t1(true); for(const auto &mat : allMats) { const std::string nodalMatName(nodalFieldName(mat.number)); @@ -503,6 +494,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif } n_newFields.remove(zonalMaterialIDName()); + SLIC_INFO( + axom::fmt::format("\t Cleanup took {:.6Lf} seconds ", t1.elapsed())); #if defined(AXOM_DEBUG_EQUIZ) //-------------------------------------------------------------------------- @@ -518,8 +511,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::relay::io::blueprint::save_mesh(n_output, "debug_equiz_output", "hdf5"); - printNode(n_output); + //printNode(n_output); #endif + + SLIC_INFO( + axom::fmt::format("EquiZAlgorithm took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -535,6 +531,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm axom::mir::views::MaterialInformation &cleanMats, axom::mir::views::MaterialInformation &mixedMats) const { + axom::utilities::Timer timer(true); + cleanMats.clear(); mixedMats.clear(); allMats = axom::mir::views::materials(n_matset); @@ -544,6 +542,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // this. mixedMats = allMats; + + SLIC_INFO( + axom::fmt::format("\t Classifying materials took {:.6Lf} seconds ", timer.elapsed())); } /** @@ -588,14 +589,19 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_fields, const axom::mir::views::MaterialInformation &mixedMats) const { + axom::utilities::Timer t0(true); + namespace bputils = axom::mir::utilities::blueprint; // Make a node to zone relation so we know for each node, which zones it touches. conduit::Node relation; { + axom::utilities::Timer t1(true); bputils::NodeToZoneRelationBuilder rb; rb.execute(n_topo, n_coordset, relation); - printNode(relation); - std::cout.flush(); + //printNode(relation); + //std::cout.flush(); + SLIC_INFO( + axom::fmt::format("\t\t Making node-to-zone relation took {:.6Lf} seconds ", t1.elapsed())); } // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. @@ -643,6 +649,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_fields.remove(zonalName); #endif } + + SLIC_INFO( + axom::fmt::format("\t Making node-centered volume fractions took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -657,9 +666,15 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm void makeWorkingFields(const conduit::Node &n_topo, conduit::Node &n_fields, const axom::mir::views::MaterialInformation &cleanMats, - const axom::mir::views::MaterialInformation &mixedMats) const +#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) + const axom::mir::views::MaterialInformation &mixedMats +#else + const axom::mir::views::MaterialInformation &AXOM_UNUSED_PARAM(mixedMats) +#endif + ) const { namespace bputils = axom::mir::utilities::blueprint; + axom::utilities::Timer t0(true); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. bputils::ConduitAllocateThroughAxom c2a; @@ -699,7 +714,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } }); } - +#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) // Fill in the mixed zones for the first mixed material. if(!mixedMats.empty()) { @@ -726,6 +741,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } }); } +#endif + + SLIC_INFO( + axom::fmt::format("\t Making working fields took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -734,6 +753,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \tparam ITopologyView The topology view type for the intermediate topology. * \tparam ICoordsetView The topology view type for the intermediate coordset. * + * \param iter The iteration number. * \param topoView The topology view for the intermediate input topology. * \param coordsetView The coordset view for the intermediate input coordset. * \param allMats A vector of Material information (all materials). @@ -750,7 +770,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * decisions with that data. */ template - void iteration(const ITopologyView &topoView, + void iteration(int iter, + const ITopologyView &topoView, const ICoordsetView &coordsetView, const axom::mir::views::MaterialInformation &allMats, @@ -768,26 +789,31 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; + axom::utilities::Timer t0(true); const std::string colorField("__equiz__colors"); #if defined(AXOM_DEBUG_EQUIZ) - std::cout << "------------------------ iteration start" - "--------------------------------\n"; //-------------------------------------------------------------------------- // // Save the iteration inputs. // //-------------------------------------------------------------------------- - conduit::Node n_mesh_input; - n_mesh_input[n_topo.path()].set_external(n_topo); - n_mesh_input[n_coordset.path()].set_external(n_coordset); - n_mesh_input[n_fields.path()].set_external(n_fields); - - // save - std::stringstream ss1; - ss1 << "debug_equiz_input_iter." << currentMat.number; - conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); + { + axom::utilities::Timer t1(true); + conduit::Node n_mesh_input; + n_mesh_input[n_topo.path()].set_external(n_topo); + n_mesh_input[n_coordset.path()].set_external(n_coordset); + n_mesh_input[n_fields.path()].set_external(n_fields); + + // save + std::stringstream ss1; + ss1 << "debug_equiz_input_iter." << iter; + conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); + + SLIC_INFO( + axom::fmt::format("\t\t Saving output {} took {:.6Lf} seconds ", iter, t1.elapsed())); + } #endif //-------------------------------------------------------------------------- @@ -795,49 +821,54 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make material intersector. // //-------------------------------------------------------------------------- - const std::string matFieldName = nodalFieldName(currentMat.number); - - // Make a material intersector. using ConnectivityType = typename ITopologyView::ConnectivityType; using IntersectorType = detail::MaterialIntersector; - IntersectorType intersector; - // Populate intersector, including making a number:index map - axom::Array matNumber, matIndex; - const int nmats = static_cast(allMats.size()); - for(int index = 0; index < nmats; index++) - { - // Add a matvf view to the intersector. - const std::string matFieldName = nodalFieldName(allMats[index].number); - auto matVFView = bputils::make_array_view( - n_fields.fetch_existing(matFieldName + "/values")); - intersector.addMaterial(matVFView); +// const std::string matFieldName = nodalFieldName(currentMat.number); - matNumber.push_back(allMats[index].number); - matIndex.push_back(index); - } - // Sort indices by matNumber. - std::sort(matIndex.begin(), matIndex.end(), [&](auto idx1, auto idx2) { - return matNumber[idx1] < matNumber[idx2]; - }); - std::sort(matNumber.begin(), matNumber.end()); - - // Store the number:index map into the intersector. The number:index map lets us - // ask for the field index for a material number, allowing scattered material - // numbers to be used in the matset. + IntersectorType intersector; int allocatorID = axom::execution_space::allocatorID(); + const int nmats = static_cast(allMats.size()); axom::Array matNumberDevice(nmats, nmats, allocatorID), matIndexDevice(nmats, nmats, allocatorID); - axom::copy(matNumberDevice.data(), matNumber.data(), sizeof(int) * nmats); - axom::copy(matIndexDevice.data(), matIndex.data(), sizeof(int) * nmats); - intersector.setMaterialNumbers(matNumberDevice.view()); - intersector.setMaterialIndices(matIndexDevice.view()); - - // Store the current zone material ids and current material number into the intersector. - intersector.setZoneMaterialID(bputils::make_array_view( - n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); - intersector.setCurrentMaterial(currentMat.number); + { + axom::utilities::Timer t2(true); + // Populate intersector, including making a number:index map + axom::Array matNumber, matIndex; + for(int index = 0; index < nmats; index++) + { + // Add a matvf view to the intersector. + const std::string matFieldName = nodalFieldName(allMats[index].number); + auto matVFView = bputils::make_array_view( + n_fields.fetch_existing(matFieldName + "/values")); + intersector.addMaterial(matVFView); + + matNumber.push_back(allMats[index].number); + matIndex.push_back(index); + } + // Sort indices by matNumber. + std::sort(matIndex.begin(), matIndex.end(), [&](auto idx1, auto idx2) { + return matNumber[idx1] < matNumber[idx2]; + }); + std::sort(matNumber.begin(), matNumber.end()); + + // Store the number:index map into the intersector. The number:index map lets us + // ask for the field index for a material number, allowing scattered material + // numbers to be used in the matset. + axom::copy(matNumberDevice.data(), matNumber.data(), sizeof(int) * nmats); + axom::copy(matIndexDevice.data(), matIndex.data(), sizeof(int) * nmats); + intersector.setMaterialNumbers(matNumberDevice.view()); + intersector.setMaterialIndices(matIndexDevice.view()); + + // Store the current zone material ids and current material number into the intersector. + intersector.setZoneMaterialID(bputils::make_array_view( + n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); + intersector.setCurrentMaterial(currentMat.number); + + SLIC_INFO( + axom::fmt::format("\t\t Intersector setup took {:.6Lf} seconds ", t2.elapsed())); + } //-------------------------------------------------------------------------- // @@ -862,6 +893,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { + axom::utilities::Timer t3(true); + using ClipperType = axom::mir::clipping::ClipField; ClipperType clipper(topoView, coordsetView, intersector); @@ -872,6 +905,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newTopo, n_newCoordset, n_newFields); + + SLIC_INFO( + axom::fmt::format("\t\t Clipper took {:.6Lf} seconds ", t3.elapsed())); } //-------------------------------------------------------------------------- @@ -879,25 +915,32 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Update zoneMaterialID based on color field. // //-------------------------------------------------------------------------- - const auto colorView = bputils::make_array_view( - n_newFields.fetch_existing(colorField + "/values")); - const auto nzonesNew = colorView.size(); + { + axom::utilities::Timer t4(true); + + const auto colorView = bputils::make_array_view( + n_newFields.fetch_existing(colorField + "/values")); + const auto nzonesNew = colorView.size(); + + // Get zonalMAterialID field so we can make adjustments. + conduit::Node &n_zonalMaterialID = + n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); + auto zonalMaterialID = + bputils::make_array_view(n_zonalMaterialID); + const int currentMatNumber = currentMat.number; + axom::for_all( + nzonesNew, + AXOM_LAMBDA(auto zoneIndex) { + // Color the part we want with the current material. + if(colorView[zoneIndex] == 1) + { + zonalMaterialID[zoneIndex] = currentMatNumber; + } + }); - // Get zonalMAterialID field so we can make adjustments. - conduit::Node &n_zonalMaterialID = - n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); - auto zonalMaterialID = - bputils::make_array_view(n_zonalMaterialID); - const int currentMatNumber = currentMat.number; - axom::for_all( - nzonesNew, - AXOM_LAMBDA(auto zoneIndex) { - // Color the part we want with the current material. - if(colorView[zoneIndex] == 1) - { - zonalMaterialID[zoneIndex] = currentMatNumber; - } - }); + SLIC_INFO( + axom::fmt::format("\t\t Updating zonalMaterialID took {:.6Lf} seconds ", t4.elapsed())); + } #if defined(AXOM_DEBUG_EQUIZ) //-------------------------------------------------------------------------- @@ -905,26 +948,32 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Save the clip results. // //-------------------------------------------------------------------------- - conduit::Node mesh; - mesh[n_newTopo.path()].set_external(n_newTopo); - mesh[n_newCoordset.path()].set_external(n_newCoordset); - mesh[n_newFields.path()].set_external(n_newFields); - - // print - printNode(mesh); - std::cout.flush(); - - // save - std::stringstream ss; - ss << "debug_equiz_output_iter." << currentMat.number; - conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); - - std::cout << "------------------------ iteration end" - "--------------------------------\n"; + { + axom::utilities::Timer t5(true); + conduit::Node mesh; + mesh[n_newTopo.path()].set_external(n_newTopo); + mesh[n_newCoordset.path()].set_external(n_newCoordset); + mesh[n_newFields.path()].set_external(n_newFields); + + // print + //printNode(mesh); + //std::cout.flush(); + + // save + std::stringstream ss; + ss << "debug_equiz_output_iter." << iter; + conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); + + SLIC_INFO( + axom::fmt::format("\t\t Saving output {} took {:.6Lf} seconds ", iter, t5.elapsed())); + } #endif // We do not want the color field to survive into the next iteration. n_newFields.remove(colorField); + + SLIC_INFO( + axom::fmt::format("\t Iteration {} took {:.6Lf} seconds ", iter, t0.elapsed())); } /** @@ -939,6 +988,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newMatset) const { namespace bputils = axom::mir::utilities::blueprint; + axom::utilities::Timer t0(true); // Get the zonalMaterialID field that has our new material ids. conduit::Node &n_zonalMaterialID = @@ -1000,6 +1050,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm offsets_view[zoneIndex] = static_cast(zoneIndex); indices_view[zoneIndex] = static_cast(zoneIndex); }); + + SLIC_INFO( + axom::fmt::format("\t Making new matset took {:.6Lf} seconds ", t0.elapsed())); } private: From d645a4f6e6863c8f523ee3b0ce8e70fa2f37224e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 20 Aug 2024 16:09:34 -0700 Subject: [PATCH 182/290] Replaced timer with annotation so it will instead use Caliper. --- src/axom/mir/EquiZAlgorithm.hpp | 96 +++++++--------------- src/axom/mir/tests/mir_equiz.cpp | 55 ++++++++++--- src/axom/mir/tests/mir_testing_helpers.hpp | 44 +++++++--- 3 files changed, 107 insertions(+), 88 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 4410204cfa..4264314261 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -16,7 +16,6 @@ #include "axom/mir/NodeToZoneRelationBuilder.hpp" #include -#include #include #include @@ -25,13 +24,18 @@ #include #endif -#define AXOM_DEBUG_EQUIZ +// Uncomment to save inputs and outputs. +// #define AXOM_DEBUG_EQUIZ // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It // could be faster but it might not be as robust. // #define AXOM_EQUIZ_SKIP_FIRST_ITERATION +#if defined(AXOM_DEBUG_EQUIZ) + #include +#endif + namespace axom { namespace mir @@ -336,7 +340,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newMatset) override { namespace bputils = axom::mir::utilities::blueprint; - axom::utilities::Timer t0(true); + AXOM_ANNOTATE_SCOPE("EquizAlgorithm"); // Copy the options. conduit::Node n_options_copy; @@ -477,25 +481,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm buildNewMatset(n_matset, n_newFields, n_newMatset); // Cleanup. - axom::utilities::Timer t1(true); - for(const auto &mat : allMats) { - const std::string nodalMatName(nodalFieldName(mat.number)); - if(n_newFields.has_child(nodalMatName)) + AXOM_ANNOTATE_SCOPE("cleanup"); + for(const auto &mat : allMats) { - n_newFields.remove(nodalMatName); - } + const std::string nodalMatName(nodalFieldName(mat.number)); + if(n_newFields.has_child(nodalMatName)) + { + n_newFields.remove(nodalMatName); + } #if defined(AXOM_DEBUG_EQUIZ) - const std::string zonalMatName(zonalFieldName(mat.number)); - if(n_newFields.has_child(zonalMatName)) - { - n_newFields.remove(zonalMatName); - } + const std::string zonalMatName(zonalFieldName(mat.number)); + if(n_newFields.has_child(zonalMatName)) + { + n_newFields.remove(zonalMatName); + } #endif + } + n_newFields.remove(zonalMaterialIDName()); } - n_newFields.remove(zonalMaterialIDName()); - SLIC_INFO( - axom::fmt::format("\t Cleanup took {:.6Lf} seconds ", t1.elapsed())); #if defined(AXOM_DEBUG_EQUIZ) //-------------------------------------------------------------------------- @@ -513,9 +517,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm "hdf5"); //printNode(n_output); #endif - - SLIC_INFO( - axom::fmt::format("EquiZAlgorithm took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -531,7 +532,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm axom::mir::views::MaterialInformation &cleanMats, axom::mir::views::MaterialInformation &mixedMats) const { - axom::utilities::Timer timer(true); + AXOM_ANNOTATE_SCOPE("classifyMaterials"); cleanMats.clear(); mixedMats.clear(); @@ -542,9 +543,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // this. mixedMats = allMats; - - SLIC_INFO( - axom::fmt::format("\t Classifying materials took {:.6Lf} seconds ", timer.elapsed())); } /** @@ -589,19 +587,16 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_fields, const axom::mir::views::MaterialInformation &mixedMats) const { - axom::utilities::Timer t0(true); + AXOM_ANNOTATE_SCOPE("makeNodeCenteredVFs"); namespace bputils = axom::mir::utilities::blueprint; // Make a node to zone relation so we know for each node, which zones it touches. conduit::Node relation; { - axom::utilities::Timer t1(true); bputils::NodeToZoneRelationBuilder rb; rb.execute(n_topo, n_coordset, relation); //printNode(relation); //std::cout.flush(); - SLIC_INFO( - axom::fmt::format("\t\t Making node-to-zone relation took {:.6Lf} seconds ", t1.elapsed())); } // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. @@ -649,9 +644,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_fields.remove(zonalName); #endif } - - SLIC_INFO( - axom::fmt::format("\t Making node-centered volume fractions took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -674,7 +666,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm ) const { namespace bputils = axom::mir::utilities::blueprint; - axom::utilities::Timer t0(true); + AXOM_ANNOTATE_SCOPE("makeWorkingFields"); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. bputils::ConduitAllocateThroughAxom c2a; @@ -742,9 +734,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm }); } #endif - - SLIC_INFO( - axom::fmt::format("\t Making working fields took {:.6Lf} seconds ", t0.elapsed())); } /** @@ -789,7 +778,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; - axom::utilities::Timer t0(true); + AXOM_ANNOTATE_SCOPE(axom::fmt::format("iteration {}", iter)); const std::string colorField("__equiz__colors"); @@ -800,7 +789,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - axom::utilities::Timer t1(true); + AXOM_ANNOTATE_SCOPE("Saving input"); conduit::Node n_mesh_input; n_mesh_input[n_topo.path()].set_external(n_topo); n_mesh_input[n_coordset.path()].set_external(n_coordset); @@ -810,9 +799,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::stringstream ss1; ss1 << "debug_equiz_input_iter." << iter; conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); - - SLIC_INFO( - axom::fmt::format("\t\t Saving output {} took {:.6Lf} seconds ", iter, t1.elapsed())); } #endif @@ -825,15 +811,13 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm using IntersectorType = detail::MaterialIntersector; -// const std::string matFieldName = nodalFieldName(currentMat.number); - IntersectorType intersector; int allocatorID = axom::execution_space::allocatorID(); const int nmats = static_cast(allMats.size()); axom::Array matNumberDevice(nmats, nmats, allocatorID), matIndexDevice(nmats, nmats, allocatorID); { - axom::utilities::Timer t2(true); + AXOM_ANNOTATE_SCOPE("Intersector setup"); // Populate intersector, including making a number:index map axom::Array matNumber, matIndex; for(int index = 0; index < nmats; index++) @@ -865,9 +849,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm intersector.setZoneMaterialID(bputils::make_array_view( n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); intersector.setCurrentMaterial(currentMat.number); - - SLIC_INFO( - axom::fmt::format("\t\t Intersector setup took {:.6Lf} seconds ", t2.elapsed())); } //-------------------------------------------------------------------------- @@ -893,7 +874,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - axom::utilities::Timer t3(true); + AXOM_ANNOTATE_SCOPE("Clipping"); using ClipperType = axom::mir::clipping::ClipField; @@ -905,9 +886,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newTopo, n_newCoordset, n_newFields); - - SLIC_INFO( - axom::fmt::format("\t\t Clipper took {:.6Lf} seconds ", t3.elapsed())); } //-------------------------------------------------------------------------- @@ -916,7 +894,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - axom::utilities::Timer t4(true); + AXOM_ANNOTATE_SCOPE("Update zonalMaterialID"); const auto colorView = bputils::make_array_view( n_newFields.fetch_existing(colorField + "/values")); @@ -937,9 +915,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm zonalMaterialID[zoneIndex] = currentMatNumber; } }); - - SLIC_INFO( - axom::fmt::format("\t\t Updating zonalMaterialID took {:.6Lf} seconds ", t4.elapsed())); } #if defined(AXOM_DEBUG_EQUIZ) @@ -949,7 +924,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - axom::utilities::Timer t5(true); + AXOM_ANNOTATE_SCOPE("Saving output"); conduit::Node mesh; mesh[n_newTopo.path()].set_external(n_newTopo); mesh[n_newCoordset.path()].set_external(n_newCoordset); @@ -963,17 +938,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm std::stringstream ss; ss << "debug_equiz_output_iter." << iter; conduit::relay::io::blueprint::save_mesh(mesh, ss.str(), "hdf5"); - - SLIC_INFO( - axom::fmt::format("\t\t Saving output {} took {:.6Lf} seconds ", iter, t5.elapsed())); } #endif // We do not want the color field to survive into the next iteration. n_newFields.remove(colorField); - - SLIC_INFO( - axom::fmt::format("\t Iteration {} took {:.6Lf} seconds ", iter, t0.elapsed())); } /** @@ -988,7 +957,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newMatset) const { namespace bputils = axom::mir::utilities::blueprint; - axom::utilities::Timer t0(true); + AXOM_ANNOTATE_SCOPE("buildNewMatset"); // Get the zonalMaterialID field that has our new material ids. conduit::Node &n_zonalMaterialID = @@ -1050,9 +1019,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm offsets_view[zoneIndex] = static_cast(zoneIndex); indices_view[zoneIndex] = static_cast(zoneIndex); }); - - SLIC_INFO( - axom::fmt::format("\t Making new matset took {:.6Lf} seconds ", t0.elapsed())); } private: diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 121edbbf8c..241c92aa16 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -15,7 +15,7 @@ #define DEBUGGING_TEST_CASES // Uncomment to generate baselines -//#define AXOM_TESTING_GENERATE_BASELINES +#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) #define AXOM_TESTING_SAVE_VISUALIZATION @@ -83,7 +83,6 @@ void braid2d_mat_test(const std::string &type, using TopologyView = decltype(topologyView); conduit::Node deviceMIRMesh; - if(mattype == "unibuffer") { // clang-format off @@ -99,7 +98,7 @@ void braid2d_mat_test(const std::string &type, using MIR = axom::mir::EquiZAlgorithm; MIR m(topologyView, coordsetView, matsetView); - conduit::Node options, deviceMIRMesh; + conduit::Node options; options["matset"] = "mat"; m.execute(deviceMesh, options, deviceMIRMesh); } @@ -109,7 +108,6 @@ void braid2d_mat_test(const std::string &type, axom::mir::utilities::blueprint::copy(hostMIRMesh, deviceMIRMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) - printNode(hostMIRMesh); conduit::relay::io::blueprint::save_mesh(hostMIRMesh, name, "hdf5"); #endif // Handle baseline comparison. @@ -124,22 +122,36 @@ void braid2d_mat_test(const std::string &type, } } -TEST(mir_equiz, equiz_uniform_unibuffer) +//------------------------------------------------------------------------------ +TEST(mir_equiz, equiz_uniform_unibuffer_seq) { + AXOM_ANNOTATE_SCOPE("equiz_uniform_unibuffer_seq"); braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +} - //#if defined(AXOM_USE_OPENMP) - // braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); - //#endif +#if defined(AXOM_USE_OPENMP) +TEST(mir_equiz, equiz_uniform_unibuffer_omp) +{ + AXOM_ANNOTATE_SCOPE("equiz_uniform_unibuffer_omp"); + braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +} +#endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_equiz, equiz_uniform_unibuffer_cuda) +{ + AXOM_ANNOTATE_SCOPE("equiz_uniform_unibuffer_cuda"); braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); +} #endif #if defined(AXOM_USE_HIP) +TEST(mir_equiz, equiz_uniform_unibuffer_hip) +{ + AXOM_ANNOTATE_SCOPE("equiz_uniform_unibuffer_hip"); braid2d_mat_test("uniform", "unibuffer", "equiz_uniform_unibuffer"); -#endif } +#endif //------------------------------------------------------------------------------ #if defined(DEBUGGING_TEST_CASES) @@ -158,9 +170,32 @@ int main(int argc, char *argv[]) int result = 0; ::testing::InitGoogleTest(&argc, argv); + // Define command line options. + bool handler = true; + axom::CLI::App app; + app.add_option("--handler", handler)->description("Install a custom error handler that loops forever."); +#if defined(AXOM_USE_CALIPER) + std::string annotationMode("report"); + app.add_option("--caliper", annotationMode) + ->description( + "caliper annotation mode. Valid options include 'none' and 'report'. " + "Use 'help' to see full list.") + ->capture_default_str() + ->check(axom::utilities::ValidCaliperMode); +#endif + // Parse command line options. + app.parse(argc, argv); + +#if defined(AXOM_USE_CALIPER) + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper(annotationMode); +#endif + axom::slic::SimpleLogger logger; // create & initialize test logger, #if defined(DEBUGGING_TEST_CASES) - conduit::utils::set_error_handler(conduit_debug_err_handler); + if(handler) + { + conduit::utils::set_error_handler(conduit_debug_err_handler); + } #endif result = RUN_ALL_TESTS(); return result; diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 2b72781b3b..06a33aeaf8 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -111,6 +111,14 @@ std::string yamlRoot(const std::string &filepath) return retval; } +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); +} + bool compareConduit(const conduit::Node &n1, const conduit::Node &n2, double tolerance, @@ -150,13 +158,31 @@ bool compareConduit(const conduit::Node &n1, void saveBaseline(const std::string &filename, const conduit::Node &n) { std::string file_with_ext(filename + ".yaml"); - SLIC_INFO(axom::fmt::format("Save baseline {}", file_with_ext)); - conduit::relay::io::save(n, file_with_ext, "yaml"); + try + { + SLIC_INFO(axom::fmt::format("Save baseline {}", file_with_ext)); + conduit::relay::io::save(n, file_with_ext, "yaml"); + #if defined(AXOM_TESTING_SAVE_VISUALIZATION) - SLIC_INFO(axom::fmt::format("Save visualization files...")); - conduit::relay::io::blueprint::save_mesh(n, filename + "_hdf5", "hdf5"); - axom::mir::utilities::blueprint::save_vtk(n, filename + "_vtk.vtk"); + SLIC_INFO(axom::fmt::format("Save visualization files...")); + conduit::relay::io::blueprint::save_mesh(n, filename + "_hdf5", "hdf5"); + axom::mir::utilities::blueprint::save_vtk(n, filename + "_vtk.vtk"); #endif + } + catch(...) + { + SLIC_INFO( + axom::fmt::format("Could not save baseline to {}!", file_with_ext)); + + printNode(n); + + // Check the data for errors. + conduit::Node info; + if(!conduit::blueprint::mesh::verify(n, info)) + { + printNode(info); + } + } } void saveBaseline(const std::vector &baselinePaths, @@ -241,12 +267,4 @@ bool compareBaseline(const std::vector &baselinePaths, return success; } -void printNode(const conduit::Node &n) -{ - conduit::Node options; - options["num_children_threshold"] = 10000; - options["num_elements_threshold"] = 10000; - n.to_summary_string_stream(std::cout, options); -} - #endif From 61e0ec0f6d2a6e53a74be683435cc06229dfeec0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 20 Aug 2024 16:14:26 -0700 Subject: [PATCH 183/290] make style --- src/axom/mir/EquiZAlgorithm.hpp | 19 ++++++++++--------- src/axom/mir/tests/mir_equiz.cpp | 16 +++++++++------- src/axom/mir/tests/mir_testing_helpers.hpp | 3 +-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 4264314261..5893875d62 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -121,10 +121,10 @@ class MaterialIntersector for(IndexType i = 0; i < n; i++) { const auto nid = nodeIdsView[i]; - // clangformat-off + // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0; - // clangformat-on + // clang-format on clipcase |= (vf2 > vf1) ? (1 << i) : 0; } @@ -152,12 +152,12 @@ class MaterialIntersector // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; - // clangformat-off + // clang-format off vf1[0] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id0] : NULL_MATERIAL_VF; vf1[1] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id1] : NULL_MATERIAL_VF; vf2[0] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0; vf2[1] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0; - // clangformat-on + // clang-format on float numerator = vf2[0] - vf1[0]; float denominator = -vf1[0] + vf1[1] + vf2[0] - vf2[1]; @@ -441,7 +441,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // The data are now an unstructured view, probably a mixed shape view. // Dispatch to an appropriate topo view. - // clangformat-off + // clang-format off views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { using ICoordsetView = decltype(coordsetView); using ConnectivityType = typename TopologyView::ConnectivityType; @@ -473,7 +473,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newFields); }); }); - // clangformat-on + // clang-format on } } @@ -515,7 +515,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::relay::io::blueprint::save_mesh(n_output, "debug_equiz_output", "hdf5"); - //printNode(n_output); + //printNode(n_output); #endif } @@ -661,9 +661,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) const axom::mir::views::MaterialInformation &mixedMats #else - const axom::mir::views::MaterialInformation &AXOM_UNUSED_PARAM(mixedMats) + const axom::mir::views::MaterialInformation + &AXOM_UNUSED_PARAM(mixedMats) #endif - ) const + ) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("makeWorkingFields"); diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 241c92aa16..1592274b4e 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -173,21 +173,23 @@ int main(int argc, char *argv[]) // Define command line options. bool handler = true; axom::CLI::App app; - app.add_option("--handler", handler)->description("Install a custom error handler that loops forever."); + app.add_option("--handler", handler) + ->description("Install a custom error handler that loops forever."); #if defined(AXOM_USE_CALIPER) std::string annotationMode("report"); app.add_option("--caliper", annotationMode) - ->description( - "caliper annotation mode. Valid options include 'none' and 'report'. " - "Use 'help' to see full list.") - ->capture_default_str() - ->check(axom::utilities::ValidCaliperMode); + ->description( + "caliper annotation mode. Valid options include 'none' and 'report'. " + "Use 'help' to see full list.") + ->capture_default_str() + ->check(axom::utilities::ValidCaliperMode); #endif // Parse command line options. app.parse(argc, argv); #if defined(AXOM_USE_CALIPER) - axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper(annotationMode); + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper( + annotationMode); #endif axom::slic::SimpleLogger logger; // create & initialize test logger, diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 06a33aeaf8..417cf63a2e 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -171,8 +171,7 @@ void saveBaseline(const std::string &filename, const conduit::Node &n) } catch(...) { - SLIC_INFO( - axom::fmt::format("Could not save baseline to {}!", file_with_ext)); + SLIC_INFO(axom::fmt::format("Could not save baseline to {}!", file_with_ext)); printNode(n); From 393fe77b76fa49c160e88d46341c01c85725de99 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 21 Aug 2024 18:12:57 -0700 Subject: [PATCH 184/290] Minor fixes --- src/axom/mir/CMakeLists.txt | 2 ++ src/axom/mir/ClipField.hpp | 9 +++++ src/axom/mir/blueprint_utilities.hpp | 6 ++-- src/axom/mir/views/ExplicitCoordsetView.hpp | 33 +++++++++++-------- .../mir/views/RectilinearCoordsetView.hpp | 16 ++++----- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 4518c74c20..43f56ca42f 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -32,6 +32,7 @@ set(mir_headers ClipFieldFilterDevice.hpp ClipFieldFilter.hpp + BlendGroupBuilder.hpp CoordsetBlender.hpp EquiZAlgorithm.hpp FieldBlender.hpp @@ -64,6 +65,7 @@ set(mir_headers views/UnstructuredTopologyPolyhedralView.hpp views/UnstructuredTopologyMixedShapeView.hpp views/UnstructuredTopologySingleShapeView.hpp + views/view_traits.hpp clipping/ClipCases.h clipping/ClipTableManager.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 0a99a82d30..9f9c00c1b7 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -374,6 +374,7 @@ class ClipField { namespace bputils = axom::mir::utilities::blueprint; const auto allocatorID = axom::execution_space::allocatorID(); + AXOM_ANNOTATE_SCOPE("ClipField"); // Make the selected zones and get the size. ClipOptions opts(n_options); @@ -622,6 +623,7 @@ class ClipField const ClipOptions &opts, const SelectedZones &selectedZones) const { + AXOM_ANNOTATE_SCOPE("computeSizes"); const auto selection = getSelection(opts); auto blendGroupsView = builder.state().m_blendGroupsView; @@ -742,6 +744,7 @@ class ClipField void computeFragmentSizes(FragmentData &fragmentData, const SelectedZones &selectedZones) const { + AXOM_ANNOTATE_SCOPE("computeFragmentSizes"); const auto nzones = selectedZones.view().size(); // Sum the number of fragments. @@ -770,6 +773,7 @@ class ClipField */ void computeFragmentOffsets(FragmentData &fragmentData) const { + AXOM_ANNOTATE_SCOPE("computeFragmentOffsets"); axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); axom::exclusive_scan(fragmentData.m_fragmentsSizeView, @@ -792,6 +796,7 @@ class ClipField const ClipOptions &opts, const SelectedZones &selectedZones) const { + AXOM_ANNOTATE_SCOPE("makeBlendGroups"); const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); @@ -916,6 +921,7 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { + AXOM_ANNOTATE_SCOPE("makeConnectivity"); namespace bputils = axom::mir::utilities::blueprint; constexpr auto connTypeID = bputils::cpp2conduit::id; const auto selection = getSelection(opts); @@ -1084,6 +1090,7 @@ class ClipField const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { + AXOM_ANNOTATE_SCOPE("makeCoordset"); axom::mir::utilities::blueprint::CoordsetBlender< ExecSpace, CoordsetView, @@ -1110,6 +1117,7 @@ class ClipField const conduit::Node &n_fields, conduit::Node &n_out_fields) const { + AXOM_ANNOTATE_SCOPE("makeFields"); for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { const conduit::Node &n_field = n_fields.fetch_existing(it->first); @@ -1221,6 +1229,7 @@ class ClipField conduit::Node &n_newTopo, conduit::Node &n_newFields) const { + AXOM_ANNOTATE_SCOPE("makeOriginalElements"); namespace bputils = axom::mir::utilities::blueprint; constexpr auto connTypeID = bputils::cpp2conduit::id; diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 420eb4f5a8..b8a32b6521 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -349,9 +349,9 @@ void to_unstructured(const conduit::Node &topo, AXOM_LAMBDA(auto zoneIndex, const auto &zone) { const auto start = zoneIndex * ptsPerZone; for(int i = 0; i < ptsPerZone; i++) - connView[start + i] = - static_cast(zone.getIds()[i]); - + { + connView[start + i] = static_cast(zone.getIds()[i]); + } sizesView[zoneIndex] = ptsPerZone; offsetsView[zoneIndex] = start; }); diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index ded8cc8fa0..9da0547632 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -7,10 +7,9 @@ #define AXOM_MIR_EXPLICIT_COORDSET_VIEW_HPP_ #include "axom/core/ArrayView.hpp" +#include "axom/slic.hpp" #include "axom/primal/geometry/Point.hpp" -#include - namespace axom { namespace mir @@ -48,7 +47,7 @@ class ExplicitCoordsetView const axom::ArrayView &y) : m_coordinates {x, y} { - SLIC_ASSERT(x.size() == y.size()); + SLIC_ASSERT_MSG(x.size() == y.size(), "Coordinate size mismatch."); } /** @@ -74,10 +73,14 @@ class ExplicitCoordsetView AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - SLIC_ASSERT(vertex_index < size()); - return PointType( - std::initializer_list {m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index]}); +#if defined(AXOM_DEVICE_CODE) + assert(vertex_index < size()); +#else + SLIC_ASSERT_MSG(vertex_index < size(), axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); +#endif + const DataType X[3] = {m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index]}; + return PointType(X); } /** @@ -123,7 +126,7 @@ class ExplicitCoordsetView const axom::ArrayView &z) : m_coordinates {x, y, z} { - SLIC_ASSERT(x.size() == y.size() && x.size() == z.size()); + SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), "Coordinate size mismatch."); } /** @@ -149,11 +152,15 @@ class ExplicitCoordsetView AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { - SLIC_ASSERT(vertex_index < size()); - return PointType( - std::initializer_list {m_coordinates[0][vertex_index], - m_coordinates[1][vertex_index], - m_coordinates[2][vertex_index]}); +#if defined(AXOM_DEVICE_CODE) + assert(vertex_index < size()); +#else + SLIC_ASSERT_MSG(vertex_index < size(), axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); +#endif + const DataType X[3] = {m_coordinates[0][vertex_index], + m_coordinates[1][vertex_index], + m_coordinates[2][vertex_index]}; + return PointType(X); } /** diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index 2a730d6d30..a759e306e9 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -11,8 +11,6 @@ #include "axom/primal/geometry/Point.hpp" #include "axom/mir/views/StructuredIndexing.hpp" -#include - namespace axom { namespace mir @@ -72,9 +70,9 @@ class RectilinearCoordsetView2 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType( - std::initializer_list {m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]]}); + const DataType X[2] = {m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]]}; + return PointType(X); } /** @@ -174,10 +172,10 @@ class RectilinearCoordsetView3 AXOM_HOST_DEVICE PointType getPoint(LogicalIndex vertex_index) const { - return PointType( - std::initializer_list {m_coordinates[0][vertex_index[0]], - m_coordinates[1][vertex_index[1]], - m_coordinates[2][vertex_index[2]]}); + const DataType X[3] = {m_coordinates[0][vertex_index[0]], + m_coordinates[1][vertex_index[1]], + m_coordinates[2][vertex_index[2]]}; + return PointType(X); } /** From 189b3c3169842e6bb3e92efa8d70d98b5759471e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 22 Aug 2024 15:18:14 -0700 Subject: [PATCH 185/290] code cleanup --- src/axom/mir/ClipField.hpp | 13 ++++---- src/axom/mir/EquiZAlgorithm.hpp | 21 +++++++++++-- src/axom/mir/TODO.txt | 22 +++++++------ src/axom/mir/blueprint_utilities.hpp | 47 +++++++++++++++++++++++++--- 4 files changed, 81 insertions(+), 22 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9f9c00c1b7..97c960c6e3 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -218,7 +218,7 @@ class FieldIntersector const conduit::Node &n_clip_field = n_fields.fetch_existing(opts.clipField()); const conduit::Node &n_clip_field_values = n_clip_field["values"]; SLIC_ASSERT(n_clip_field["association"].as_string() == "vertex"); - if(n_clip_field_values.dtype().is_float32()) + if(n_clip_field_values.dtype().id() == bputils::cpp2conduit::id) { // Make a view. m_view.m_clipFieldView = @@ -265,7 +265,7 @@ class FieldIntersector View view() const { return m_view; } private: - axom::Array m_clipFieldData {}; + axom::Array m_clipFieldData {}; View m_view {}; }; @@ -484,7 +484,7 @@ class ClipField bputils::BlendData blend = builder.makeBlendData(); // Make the clipped mesh - makeConnectivity(clipTableViews, + makeTopology(clipTableViews, builder, zoneData, fragmentData, @@ -797,7 +797,6 @@ class ClipField const SelectedZones &selectedZones) const { AXOM_ANNOTATE_SCOPE("makeBlendGroups"); - const auto clipValue = static_cast(opts.clipValue()); const auto selection = getSelection(opts); const auto deviceIntersector = m_intersector.view(); @@ -897,7 +896,7 @@ class ClipField } /** - * \brief Make connectivity for the clipped mesh. + * \brief Make the clipped mesh topology. * * \param[in] clipTableViews An object that holds views of the clipping table data. * \param[in] builder This object holds views to blend group data and helps with building/access. @@ -911,7 +910,7 @@ class ClipField * * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ - void makeConnectivity(ClipTableViews clipTableViews, + void makeTopology(ClipTableViews clipTableViews, BlendGroupBuilderType builder, ZoneData zoneData, FragmentData fragmentData, @@ -921,7 +920,7 @@ class ClipField conduit::Node &n_newCoordset, conduit::Node &n_newFields) const { - AXOM_ANNOTATE_SCOPE("makeConnectivity"); + AXOM_ANNOTATE_SCOPE("makeTopology"); namespace bputils = axom::mir::utilities::blueprint; constexpr auto connTypeID = bputils::cpp2conduit::id; const auto selection = getSelection(opts); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 5893875d62..0e3aef440f 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -8,6 +8,7 @@ #include "axom/config.hpp" #include "axom/core.hpp" #include "axom/mir.hpp" +#include "axom/slic.hpp" // Include these directly for now. #include "axom/mir/views/MaterialView.hpp" @@ -121,6 +122,11 @@ class MaterialIntersector for(IndexType i = 0; i < n; i++) { const auto nid = nodeIdsView[i]; +#if defined(AXOM_DEVICE_CODE) + assert(nid >= 0 && nid < m_matvfViews[0].size()); +#else + SLIC_ASSERT_MSG(nid >= 0 && nid < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", nid, m_matvfViews[0].size())); +#endif // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0; @@ -148,7 +154,15 @@ class MaterialIntersector if(zoneMatID != NULL_MATERIAL) backgroundIndex = matNumberToIndex(zoneMatID); // Determine the matvf view index for the current material. + int currentIndex = matNumberToIndex(m_currentMaterial); +#if defined(AXOM_DEVICE_CODE) + assert(id0 >= 0 && id0 < m_matvfViews[0].size()); + assert(id1 >= 0 && id1 < m_matvfViews[0].size()); +#else + SLIC_ASSERT_MSG(id0 >= 0 && id0 < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", id0, m_matvfViews[0].size())); + SLIC_ASSERT_MSG(id1 >= 0 && id1 < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", id1, m_matvfViews[0].size())); +#endif // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; @@ -867,6 +881,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm options["selectedZones"].set_external( n_options.fetch_existing("selectedZones")); } + if(n_options.has_child("fields")) + { + // Pass along fields, if present. + options["fields"].set_external(n_options.fetch_existing("fields")); + } options["topology"] = n_options["topology"]; //-------------------------------------------------------------------------- @@ -875,8 +894,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // //-------------------------------------------------------------------------- { - AXOM_ANNOTATE_SCOPE("Clipping"); - using ClipperType = axom::mir::clipping::ClipField; ClipperType clipper(topoView, coordsetView, intersector); diff --git a/src/axom/mir/TODO.txt b/src/axom/mir/TODO.txt index 00cde61bcd..94baaef713 100644 --- a/src/axom/mir/TODO.txt +++ b/src/axom/mir/TODO.txt @@ -27,14 +27,14 @@ Partial TODO list - Test storing results in same Node, use names/fields map to change names (done)5. Create UnstructuredTopologyMixedShapeView and dispatch (done)6. Make sure ClipFilter can use the mixed topology view - it probably needs some changes -7. Code cleanup pass in ClipFilter - - Make sure array view data types are consistent, change to IndexType as needed. - - Make sure views have "View" in the name. - - Consolidate some arrays in structs to make passing data to methods easier - - Split the stages into methods -8. Write a multi-domain, dispatch-based ClipFilter that follows what the original EquiZAlgorithm was doing. -8. Make routine for making a volfrac field from a matset. -9. Create EquiZ implementation +(done)7. Code cleanup pass in ClipFilter +(done) - Make sure array view data types are consistent, change to IndexType as needed. +(done) - Make sure views have "View" in the name. +(done) - Consolidate some arrays in structs to make passing data to methods easier +(done) - Split the stages into methods +(done)8. Write a multi-domain, dispatch-based ClipFilter that follows what the original EquiZAlgorithm was doing. +(done)8. Make routine for making a volfrac field from a matset. +(done)9. Create EquiZ implementation 10. Write EquiZ tests 11. Create ELVIRA MIR 12. Create routines that take 2 meshes, do MIR on mesh1, and then intersect mesh1 MIR'd geometry with the mesh2 and compute VF fields @@ -45,8 +45,12 @@ Partial TODO list 17. Revisit NodeArrayView code with interleaved data - make the right views. 18. [lower priority] Revisit NodeArrayView and constness/references. 19. [lower priority] Can ClipField be generalized a little into something like TopologyExtractor? Then use it to make other filters? -20. [lower priority] ConduitAllocateThroughAxom uses statics. I'd prefer to template it on ExecSpace and make the allocator a member but Conduit's API does not let us pass a void* to the allocation callbacks. +(done)20. [lower priority] ConduitAllocateThroughAxom uses statics. I'd prefer to template it on ExecSpace and make the allocator a member but Conduit's API does not let us pass a void* to the allocation callbacks. (done)21. Check the edge definitions in pyr, wedge Shape types. 22. Compile for all compute back-ends and make sure tests run/pass. - Right now, OMP causes the compiler to crash when linking 23. Make some timing tests so we can see how Clipfield/MIR do at various mesh sizes. +24. Make example showing how to use MIR +25. Make utility that can load data from MFEM/Blueprint/Silo into blueprint, do MIR, and save out results. +26. Make sure OMP/HIP performance are good +27. Make sure it builds using CUDA diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index b8a32b6521..48aa0f4806 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -40,7 +40,7 @@ namespace blueprint template struct cpp2conduit { - static constexpr conduit::index_t type = conduit::DataType::EMPTY_ID; + static constexpr conduit::index_t id = conduit::DataType::EMPTY_ID; }; template <> @@ -117,7 +117,14 @@ struct cpp2conduit /** * \brief Make an axom::ArrayView from a Conduit node. + * + * \tparam T The type for the array view elements. + * + * \param n The conduit node for which we want an array view. + * + * \return An axom::ArrayView that wraps the data in the Conduit node. */ +/// @{ template inline axom::ArrayView make_array_view(conduit::Node &n) { @@ -131,18 +138,25 @@ inline axom::ArrayView make_array_view(const conduit::Node &n) return axom::ArrayView(static_cast(const_cast(n.data_ptr())), n.dtype().number_of_elements()); } +/// @} //------------------------------------------------------------------------------ /** * \brief This class registers a Conduit allocator that can make Conduit allocate - * through Axom's allocate/deallocate functions using a specific allocator. This - * permits Conduit to allocate through Axom's UMPIRE logic. + * through Axom's allocate/deallocate functions using a specific allocator. + * This permits Conduit to allocate through Axom's UMPIRE logic. * + * \tparam ExecSpace The execution space. */ template class ConduitAllocateThroughAxom { public: + /** + * \brief Get the Conduit allocator ID for this ExecSpace. + * + * \return The Conduit allocator ID for this ExecSpace. + */ static conduit::index_t getConduitAllocatorID() { static conduit::index_t conduitAllocatorID = -1; @@ -155,6 +169,14 @@ class ConduitAllocateThroughAxom } private: + /** + * \brief A function we register with Conduit to allocate memory. + * + * \param items The number of items to allocate. + * \param item_size The size of each item in bytes. + * + * \brief A block of newly allocated memory large enough for the requested items. + */ static void *internal_allocate(size_t items, size_t item_size) { const auto axomAllocatorID = axom::execution_space::allocatorID(); @@ -164,6 +186,9 @@ class ConduitAllocateThroughAxom return ptr; } + /** + * \brief A deallocation function we register with Conduit. + */ static void internal_free(void *ptr) { //std::cout << axom::execution_space::name() << ": Dellocating for Conduit via axom: ptr=" << ptr << std::endl; @@ -172,6 +197,15 @@ class ConduitAllocateThroughAxom }; //------------------------------------------------------------------------------ +/** + * \brief Fill an array with int values from a Conduit node. + * + * \tparam ArrayType The array type being filled. + * + * \param n The node that contains the data. + * \param key The name of the node that contains the data in \a n. + * \param[out] arr The array being filled. + */ template bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr) { @@ -363,6 +397,11 @@ void to_unstructured(const conduit::Node &topo, * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, * making sure to allocate array data in the appropriate memory space for * the execution space. + * + * \tparam The destination execution space (e.g. axom::SEQ_EXEC). + * + * \param dest The conduit node that will receive the copied data. + * \param src The source data to be copied. */ template void copy(conduit::Node &dest, const conduit::Node &src) @@ -391,7 +430,7 @@ void copy(conduit::Node &dest, const conduit::Node &src) axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); else { - // NOTE: this assumes that src is on the host. Why would we have strided data on device? + // NOTE: This assumes that src is on the host. conduit::Node tmp; src.compact_to(tmp); axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); From aa51d5a9d15d25695233afe5755f5ae3014db376 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 22 Aug 2024 18:48:45 -0700 Subject: [PATCH 186/290] I renumbered clipping table symbol values to reduce the size of a stack array in a kernel, allowing it to run. --- src/axom/mir/ClipField.hpp | 105 ++++++++++++++++++++++++++---- src/axom/mir/clipping/ClipCases.h | 33 +++++----- 2 files changed, 109 insertions(+), 29 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 97c960c6e3..2b8aadad4b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -19,6 +19,7 @@ #include "axom/mir/BlendGroupBuilder.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/SelectedZones.hpp" +#include "axom/slic.hpp" #include #include @@ -43,9 +44,9 @@ namespace details * \return The ShapeID value that matches the st_index, or 0 if there is no match. */ template -inline AXOM_HOST_DEVICE int ST_Index_to_ShapeID(IntegerType st_index) +inline AXOM_HOST_DEVICE IntegerType ST_Index_to_ShapeID(IntegerType st_index) { - int shapeID = 0; + IntegerType shapeID = 0; switch(st_index) { case ST_LIN: @@ -132,6 +133,24 @@ inline bool shapeIsSelected(unsigned char color, int selection) (color1Selected(selection) && color == COLOR1); } +#if defined(AXOM_DEBUG_CLIP_FIELD) +template +void printHost(const std::string &name, const ViewType &deviceView) +{ + using value_type = typename ViewType::value_type; + int nn = deviceView.size(); + value_type *host = new value_type[nn]; + axom::copy(host, deviceView.data(), sizeof(value_type) * nn); + std::cout << name << "[" << nn<< "] = {"; + for(int ii = 0; ii < nn; ii++) + { + std::cout << ", " << host[ii]; + } + std::cout << "}" << std::endl; + delete [] host; +} +#endif + } // end namespace details //------------------------------------------------------------------------------ @@ -734,6 +753,17 @@ class ClipField blendGroupsView[szIndex] = thisBlendGroups; blendGroupsLenView[szIndex] = thisBlendGroupLen; }); + +#if defined(AXOM_DEBUG_CLIP_FIELD) + std::cout << "------------------------ computeSizes ------------------------" << std::endl; + detail::printHost("fragmentData.m_fragmentsView", fragmentData.m_fragmentsView); + detail::printHost("fragmentData.m_fragmentsSizeView", fragmentData.m_fragmentsSizeView); + detail::printHost("blendGroupsView", blendGroupsView); + detail::printHost("blendGroupsLenView", blendGroupsLenView); + detail::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + std::cout << "--------------------------------------------------------------" << std::endl; +#endif } /** @@ -778,6 +808,13 @@ class ClipField fragmentData.m_fragmentOffsetsView); axom::exclusive_scan(fragmentData.m_fragmentsSizeView, fragmentData.m_fragmentSizeOffsetsView); + +#if defined(AXOM_DEBUG_CLIP_FIELD) + std::cout << "------------------------ computeFragmentOffsets ------------------------" << std::endl; + detail::printHost("fragmentData.m_fragmentOffsetsView", fragmentData.m_fragmentOffsetsView); + detail::printHost("fragmentData.m_fragmentSizeOffsetsView", fragmentData.m_fragmentSizeOffsetsView); + std::cout << "------------------------------------------------------------------------" << std::endl; +#endif } /** @@ -966,9 +1003,29 @@ class ClipField n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); auto colorView = bputils::make_array_view(n_color_values); +#if defined(AXOM_DEBUG_CLIP_FIELD) + // Initialize the values beforehand. For debugging. + axom::for_all(connView.size(), AXOM_LAMBDA(auto index) + { + connView[index] = -1; + }); + axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) + { + shapesView[index] = -2; + sizesView[index] = -3; + offsetsView[index] = -4; + colorView[index] = -5; + }); +#endif + // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. - RAJA::ReduceBitOr shapesUsed_reduce(0); + // + // NOTE: During development, I ran into problems with this kernel not executing + // due to point_2_new being too large. The solution was to reduce the values + // for EA-EL, N0-N3 to shrink the array to the point where it can fit in + // memory available to the thread. + // m_topologyView.template for_selected_zones( selectedZones.view(), AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { @@ -984,7 +1041,7 @@ class ClipField // in the final points. const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; ConnectivityType point_2_new[N3 + 1]; - for(unsigned char pid = N0; pid <= N3; pid++) + for(int pid = N0; pid <= N3; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -992,7 +1049,7 @@ class ClipField groups++; } } - for(unsigned char pid = P0; pid <= P7; pid++) + for(int pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -1000,7 +1057,7 @@ class ClipField groups++; } } - for(unsigned char pid = EA; pid <= EL; pid++) + for(int pid = EA; pid <= EL; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -1041,16 +1098,33 @@ class ClipField shapesView[sizeIndex] = shapeID; colorView[sizeIndex] = fragment[1] - COLOR0; sizeIndex++; - - // Record which shape type was used. Use a bit for each shape. - axom::utilities::setBitOn(shapesUsed, shapeID); } } } - - shapesUsed_reduce |= shapesUsed; }); + // Reduce outside of the for_selected_zones loop. + RAJA::ReduceBitOr shapesUsed_reduce(0); + axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) + { + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); + +#if defined(AXOM_DEBUG_CLIP_FIELD) + std::cout << "------------------------ makeTopology ------------------------" << std::endl; + detail::printHost("selectedZones", selectedZones.view()); + detail::printHost("m_fragmentsView", fragmentData.m_fragmentsView); + detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + detail::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + detail::printHost("conn", connView); + detail::printHost("sizes", sizesView); + detail::printHost("shapes", shapesView); + detail::printHost("color", colorView); + std::cout << "--------------------------------------------------------------" << std::endl; +#endif + // If inside and outside are not selected, remove the color field since we should not need it. if(!(opts.inside() && opts.outside())) { @@ -1059,10 +1133,16 @@ class ClipField // Make offsets axom::exclusive_scan(sizesView, offsetsView); +#if defined(AXOM_DEBUG_CLIP_FIELD) + detail::printHost("offsets", offsetsView); + std::cout << "--------------------------------------------------------------" << std::endl; +#endif // Add shape information to the connectivity. - const auto shapesUsed = shapesUsed_reduce.get(); + const BitSet shapesUsed = shapesUsed_reduce.get(); + SLIC_ASSERT_MSG(shapesUsed != 0, "No shapes were produced!"); const auto shapeMap = shapeMap_FromFlags(shapesUsed); + SLIC_ASSERT_MSG(shapeMap.empty() == false, "The shape map is empty!"); if(axom::utilities::countBits(shapesUsed) > 1) { n_newTopo["elements/shape"] = "mixed"; @@ -1072,7 +1152,6 @@ class ClipField } else { - n_shapes.reset(); n_newTopo["elements"].remove("shapes"); n_newTopo["elements/shape"] = shapeMap.begin()->first; } diff --git a/src/axom/mir/clipping/ClipCases.h b/src/axom/mir/clipping/ClipCases.h index 6b7f7c4210..8c1ca266f3 100644 --- a/src/axom/mir/clipping/ClipCases.h +++ b/src/axom/mir/clipping/ClipCases.h @@ -6,6 +6,7 @@ #define AXOM_VISIT_CLIP_CASES_H //--------------------------------------------------------------------------- // Axom modifications +// NOTE: The values for EA-EL and N0-N3 were reduced. // clang-format off //#include #define VISIT_VTK_LIGHT_API @@ -50,25 +51,25 @@ namespace visit { // Edges of original cell (up to 12, for the hex) // Note: we assume these values are contiguous and monotonic. -#define EA 20 -#define EB 21 -#define EC 22 -#define ED 23 -#define EE 24 -#define EF 25 -#define EG 26 -#define EH 27 -#define EI 28 -#define EJ 29 -#define EK 30 -#define EL 31 +#define EA 8 +#define EB 9 +#define EC 10 +#define ED 11 +#define EE 12 +#define EF 13 +#define EG 14 +#define EH 15 +#define EI 16 +#define EJ 17 +#define EK 18 +#define EL 19 // New interpolated points (ST_PNT outputs) // Note: we assume these values are contiguous and monotonic. -#define N0 40 -#define N1 41 -#define N2 42 -#define N3 43 +#define N0 20 +#define N1 21 +#define N2 22 +#define N3 23 // Shapes #define ST_TET 100 From ac4aea5d7a24e1a0fbbe9b547272ff767c913ddf Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 22 Aug 2024 18:53:46 -0700 Subject: [PATCH 187/290] make style --- src/axom/mir/ClipField.hpp | 111 ++++++++++++-------- src/axom/mir/EquiZAlgorithm.hpp | 15 ++- src/axom/mir/blueprint_utilities.hpp | 3 +- src/axom/mir/views/ExplicitCoordsetView.hpp | 11 +- 4 files changed, 87 insertions(+), 53 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 2b8aadad4b..29c3149d02 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -141,13 +141,13 @@ void printHost(const std::string &name, const ViewType &deviceView) int nn = deviceView.size(); value_type *host = new value_type[nn]; axom::copy(host, deviceView.data(), sizeof(value_type) * nn); - std::cout << name << "[" << nn<< "] = {"; + std::cout << name << "[" << nn << "] = {"; for(int ii = 0; ii < nn; ii++) { std::cout << ", " << host[ii]; } std::cout << "}" << std::endl; - delete [] host; + delete[] host; } #endif @@ -504,14 +504,14 @@ class ClipField // Make the clipped mesh makeTopology(clipTableViews, - builder, - zoneData, - fragmentData, - opts, - selectedZones, - n_newTopo, - n_newCoordset, - n_newFields); + builder, + zoneData, + fragmentData, + opts, + selectedZones, + n_newTopo, + n_newCoordset, + n_newFields); makeCoordset(blend, n_coordset, n_newCoordset); bputils::SliceData slice; @@ -755,14 +755,20 @@ class ClipField }); #if defined(AXOM_DEBUG_CLIP_FIELD) - std::cout << "------------------------ computeSizes ------------------------" << std::endl; - detail::printHost("fragmentData.m_fragmentsView", fragmentData.m_fragmentsView); - detail::printHost("fragmentData.m_fragmentsSizeView", fragmentData.m_fragmentsSizeView); + std::cout + << "------------------------ computeSizes ------------------------" + << std::endl; + detail::printHost("fragmentData.m_fragmentsView", + fragmentData.m_fragmentsView); + detail::printHost("fragmentData.m_fragmentsSizeView", + fragmentData.m_fragmentsSizeView); detail::printHost("blendGroupsView", blendGroupsView); detail::printHost("blendGroupsLenView", blendGroupsLenView); detail::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); - std::cout << "--------------------------------------------------------------" << std::endl; + std::cout + << "--------------------------------------------------------------" + << std::endl; #endif } @@ -810,10 +816,16 @@ class ClipField fragmentData.m_fragmentSizeOffsetsView); #if defined(AXOM_DEBUG_CLIP_FIELD) - std::cout << "------------------------ computeFragmentOffsets ------------------------" << std::endl; - detail::printHost("fragmentData.m_fragmentOffsetsView", fragmentData.m_fragmentOffsetsView); - detail::printHost("fragmentData.m_fragmentSizeOffsetsView", fragmentData.m_fragmentSizeOffsetsView); - std::cout << "------------------------------------------------------------------------" << std::endl; + std::cout << "------------------------ computeFragmentOffsets " + "------------------------" + << std::endl; + detail::printHost("fragmentData.m_fragmentOffsetsView", + fragmentData.m_fragmentOffsetsView); + detail::printHost("fragmentData.m_fragmentSizeOffsetsView", + fragmentData.m_fragmentSizeOffsetsView); + std::cout << "-------------------------------------------------------------" + "-----------" + << std::endl; #endif } @@ -948,14 +960,14 @@ class ClipField * \note Objects that we need to capture into kernels are passed by value (they only contain views anyway). Data can be modified through the views. */ void makeTopology(ClipTableViews clipTableViews, - BlendGroupBuilderType builder, - ZoneData zoneData, - FragmentData fragmentData, - const ClipOptions &opts, - const SelectedZones &selectedZones, - conduit::Node &n_newTopo, - conduit::Node &n_newCoordset, - conduit::Node &n_newFields) const + BlendGroupBuilderType builder, + ZoneData zoneData, + FragmentData fragmentData, + const ClipOptions &opts, + const SelectedZones &selectedZones, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields) const { AXOM_ANNOTATE_SCOPE("makeTopology"); namespace bputils = axom::mir::utilities::blueprint; @@ -1005,17 +1017,17 @@ class ClipField #if defined(AXOM_DEBUG_CLIP_FIELD) // Initialize the values beforehand. For debugging. - axom::for_all(connView.size(), AXOM_LAMBDA(auto index) - { - connView[index] = -1; - }); - axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) - { - shapesView[index] = -2; - sizesView[index] = -3; - offsetsView[index] = -4; - colorView[index] = -5; - }); + axom::for_all( + connView.size(), + AXOM_LAMBDA(auto index) { connView[index] = -1; }); + axom::for_all( + shapesView.size(), + AXOM_LAMBDA(auto index) { + shapesView[index] = -2; + sizesView[index] = -3; + offsetsView[index] = -4; + colorView[index] = -5; + }); #endif // Here we fill in the new connectivity, sizes, shapes. @@ -1105,15 +1117,18 @@ class ClipField // Reduce outside of the for_selected_zones loop. RAJA::ReduceBitOr shapesUsed_reduce(0); - axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) - { - BitSet shapeBit {}; - axom::utilities::setBitOn(shapeBit, shapesView[index]); - shapesUsed_reduce |= shapeBit; - }); + axom::for_all( + shapesView.size(), + AXOM_LAMBDA(auto index) { + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); #if defined(AXOM_DEBUG_CLIP_FIELD) - std::cout << "------------------------ makeTopology ------------------------" << std::endl; + std::cout + << "------------------------ makeTopology ------------------------" + << std::endl; detail::printHost("selectedZones", selectedZones.view()); detail::printHost("m_fragmentsView", fragmentData.m_fragmentsView); detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); @@ -1122,7 +1137,9 @@ class ClipField detail::printHost("sizes", sizesView); detail::printHost("shapes", shapesView); detail::printHost("color", colorView); - std::cout << "--------------------------------------------------------------" << std::endl; + std::cout + << "--------------------------------------------------------------" + << std::endl; #endif // If inside and outside are not selected, remove the color field since we should not need it. @@ -1135,7 +1152,9 @@ class ClipField axom::exclusive_scan(sizesView, offsetsView); #if defined(AXOM_DEBUG_CLIP_FIELD) detail::printHost("offsets", offsetsView); - std::cout << "--------------------------------------------------------------" << std::endl; + std::cout + << "--------------------------------------------------------------" + << std::endl; #endif // Add shape information to the connectivity. diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 0e3aef440f..40c0e79177 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -125,7 +125,10 @@ class MaterialIntersector #if defined(AXOM_DEVICE_CODE) assert(nid >= 0 && nid < m_matvfViews[0].size()); #else - SLIC_ASSERT_MSG(nid >= 0 && nid < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", nid, m_matvfViews[0].size())); + SLIC_ASSERT_MSG(nid >= 0 && nid < m_matvfViews[0].size(), + axom::fmt::format("Node id {} is not in range [0, {}).", + nid, + m_matvfViews[0].size())); #endif // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; @@ -160,8 +163,14 @@ class MaterialIntersector assert(id0 >= 0 && id0 < m_matvfViews[0].size()); assert(id1 >= 0 && id1 < m_matvfViews[0].size()); #else - SLIC_ASSERT_MSG(id0 >= 0 && id0 < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", id0, m_matvfViews[0].size())); - SLIC_ASSERT_MSG(id1 >= 0 && id1 < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", id1, m_matvfViews[0].size())); + SLIC_ASSERT_MSG(id0 >= 0 && id0 < m_matvfViews[0].size(), + axom::fmt::format("Node id {} is not in range [0, {}).", + id0, + m_matvfViews[0].size())); + SLIC_ASSERT_MSG(id1 >= 0 && id1 < m_matvfViews[0].size(), + axom::fmt::format("Node id {} is not in range [0, {}).", + id1, + m_matvfViews[0].size())); #endif // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 48aa0f4806..eb162cb4cc 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -384,7 +384,8 @@ void to_unstructured(const conduit::Node &topo, const auto start = zoneIndex * ptsPerZone; for(int i = 0; i < ptsPerZone; i++) { - connView[start + i] = static_cast(zone.getIds()[i]); + connView[start + i] = + static_cast(zone.getIds()[i]); } sizesView[zoneIndex] = ptsPerZone; offsetsView[zoneIndex] = start; diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 9da0547632..4612d5f533 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -76,7 +76,9 @@ class ExplicitCoordsetView #if defined(AXOM_DEVICE_CODE) assert(vertex_index < size()); #else - SLIC_ASSERT_MSG(vertex_index < size(), axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); + SLIC_ASSERT_MSG( + vertex_index < size(), + axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index]}; @@ -126,7 +128,8 @@ class ExplicitCoordsetView const axom::ArrayView &z) : m_coordinates {x, y, z} { - SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), "Coordinate size mismatch."); + SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), + "Coordinate size mismatch."); } /** @@ -155,7 +158,9 @@ class ExplicitCoordsetView #if defined(AXOM_DEVICE_CODE) assert(vertex_index < size()); #else - SLIC_ASSERT_MSG(vertex_index < size(), axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); + SLIC_ASSERT_MSG( + vertex_index < size(), + axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index], From e82a13e4f10270f09bb05bba0218ab2d4759a1d5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 23 Aug 2024 11:32:12 -0700 Subject: [PATCH 188/290] Added more timers, some code renaming --- src/axom/mir/BlendGroupBuilder.hpp | 2 + src/axom/mir/ClipField.hpp | 82 +++++++++++++++++++----------- src/axom/mir/CoordsetBlender.hpp | 1 + src/axom/mir/FieldBlender.hpp | 10 ++-- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index e3faed2e28..4faa3b2992 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -81,6 +81,7 @@ class BlendGroupBuilder */ void computeBlendGroupSizes(IndexType &bgSum, IndexType &bgLenSum) { + AXOM_ANNOTATE_SCOPE("computeBlendGroupSizes"); using reduce_policy = typename axom::execution_space::reduce_policy; RAJA::ReduceSum blendGroups_sum(0); @@ -115,6 +116,7 @@ class BlendGroupBuilder */ void computeBlendGroupOffsets() { + AXOM_ANNOTATE_SCOPE("computeBlendGroupOffsets"); axom::exclusive_scan(m_state.m_blendGroupsLenView, m_state.m_blendOffsetView); axom::exclusive_scan(m_state.m_blendGroupsView, diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 29c3149d02..5f465683ff 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -313,7 +313,7 @@ class ClipField using ClipTableViews = axom::StackArray; using Intersector = IntersectPolicy; - using BitSet = std::uint64_t; + using BitSet = std::uint32_t; using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -403,7 +403,10 @@ class ClipField const auto nzones = selectedZones.view().size(); // Give the intersector a chance to further initialize. - m_intersector.initialize(n_options, n_fields); + { + AXOM_ANNOTATE_SCOPE("Initialize intersector"); + m_intersector.initialize(n_options, n_fields); + } // Load clip table data and make views. m_clipTables.load(m_topologyView.dimension()); @@ -411,6 +414,7 @@ class ClipField createClipTableViews(clipTableViews, m_topologyView.dimension()); // Allocate some memory and store views in ZoneData, FragmentData. + AXOM_ANNOTATE_BEGIN("allocation"); axom::Array clipCases(nzones, nzones, allocatorID); // The clip case for a zone. @@ -455,6 +459,7 @@ class ClipField nzones, nzones, allocatorID); // Start of zone's blend group offsets in definitions. + AXOM_ANNOTATE_END("allocation"); // Make an object to help manage building the blend groups. BlendGroupBuilderType builder; @@ -471,6 +476,7 @@ class ClipField builder.computeBlendGroupOffsets(); // Allocate memory for blend groups. + AXOM_ANNOTATE_BEGIN("allocation2"); axom::Array blendNames(blendGroupsSize, blendGroupsSize, allocatorID); axom::Array blendGroupSizes(blendGroupsSize, blendGroupsSize, @@ -491,15 +497,19 @@ class ClipField blendGroupStart.view(), blendIds.view(), blendCoeff.view()); + AXOM_ANNOTATE_END("allocation2"); makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones); // Make the blend groups unique axom::Array uNames; axom::Array uIndices; - axom::mir::utilities::unique(builder.blendNames(), - uNames, - uIndices); - builder.setUniqueNames(uNames.view(), uIndices.view()); + { + AXOM_ANNOTATE_SCOPE("unique"); + axom::mir::utilities::unique(builder.blendNames(), + uNames, + uIndices); + builder.setUniqueNames(uNames.view(), uIndices.view()); + } bputils::BlendData blend = builder.makeBlendData(); // Make the clipped mesh @@ -514,25 +524,9 @@ class ClipField n_newFields); makeCoordset(blend, n_coordset, n_newCoordset); - bputils::SliceData slice; - axom::Array sliceIndices(fragmentData.m_finalNumZones, - fragmentData.m_finalNumZones, - allocatorID); - auto sliceIndicesView = sliceIndices.view(); - - // Fill in sliceIndicesView. - const auto selectedZonesView = selectedZones.view(); - axom::for_all( - nzones, - AXOM_LAMBDA(auto index) { - const auto zoneIndex = selectedZonesView[index]; - const auto start = fragmentData.m_fragmentOffsetsView[index]; - for(int i = 0; i < fragmentData.m_fragmentsView[index]; i++) - sliceIndicesView[start + i] = zoneIndex; - }); - // Get the fields that we want to process. std::map fieldsToProcess; + int numElementFields = 0; if(!opts.fields(fieldsToProcess)) { // Fields were not present in the options. Select all fields that have the same topology as n_topo. @@ -540,12 +534,37 @@ class ClipField { if(n_fields[i].fetch_existing("topology").as_string() == n_topo.name()) { + numElementFields += (n_fields[i].fetch_existing("association").as_string() == "element") ? 1 : 0; + fieldsToProcess[n_fields[i].name()] = n_fields[i].name(); } } } - slice.m_indicesView = sliceIndicesView; + // Make slice indices if we have element fields. + bputils::SliceData slice; + axom::Array sliceIndices; + if(numElementFields > 0) + { + AXOM_ANNOTATE_SCOPE("sliceIndices"); + sliceIndices = axom::Array(fragmentData.m_finalNumZones, + fragmentData.m_finalNumZones, + allocatorID); + auto sliceIndicesView = sliceIndices.view(); + + // Fill in sliceIndicesView. + const auto selectedZonesView = selectedZones.view(); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + const auto zoneIndex = selectedZonesView[index]; + const auto start = fragmentData.m_fragmentOffsetsView[index]; + for(int i = 0; i < fragmentData.m_fragmentsView[index]; i++) + sliceIndicesView[start + i] = zoneIndex; + }); + slice.m_indicesView = sliceIndicesView; + } + makeFields(blend, slice, opts.topologyName(n_topo.name()), @@ -603,6 +622,7 @@ class ClipField */ void createClipTableViews(ClipTableViews &views, int dimension) { + AXOM_ANNOTATE_SCOPE("createClipTableViews"); if(dimension == -1 || dimension == 2) { views[details::getClipTableIndex(views::Tri_ShapeID)] = @@ -1040,7 +1060,7 @@ class ClipField // m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto /*zoneIndex*/, const auto &zone) { + AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; @@ -1053,7 +1073,7 @@ class ClipField // in the final points. const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; ConnectivityType point_2_new[N3 + 1]; - for(int pid = N0; pid <= N3; pid++) + for(BitSet pid = N0; pid <= N3; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -1061,7 +1081,7 @@ class ClipField groups++; } } - for(int pid = P0; pid <= P7; pid++) + for(BitSet pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -1069,7 +1089,7 @@ class ClipField groups++; } } - for(int pid = EA; pid <= EL; pid++) + for(BitSet pid = EA; pid <= EL; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { @@ -1191,7 +1211,7 @@ class ClipField axom::mir::utilities::blueprint::CoordsetBlender< ExecSpace, CoordsetView, - axom::mir::utilities::blueprint::SelectThroughArrayView> + axom::mir::utilities::blueprint::SelectSubsetPolicy> cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); @@ -1284,7 +1304,7 @@ class ClipField // Blend the field. axom::mir::utilities::blueprint::FieldBlender< ExecSpace, - axom::mir::utilities::blueprint::SelectThroughArrayView, + axom::mir::utilities::blueprint::SelectSubsetPolicy, IndexingPolicy> b(indexing); b.execute(blend, n_field, n_out_fields[it->second]); @@ -1297,7 +1317,7 @@ class ClipField // Blend the field. axom::mir::utilities::blueprint::FieldBlender< ExecSpace, - axom::mir::utilities::blueprint::SelectThroughArrayView> + axom::mir::utilities::blueprint::SelectSubsetPolicy> b; b.execute(blend, n_field, n_out_fields[it->second]); } diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 0218212d2e..9fca6dce2d 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -10,6 +10,7 @@ #include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" +#include "axom/slic.hpp" #include #include diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 26a6b3a6bc..9534120399 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -40,13 +40,13 @@ struct BlendData struct SelectAllPolicy { AXOM_HOST_DEVICE - static IndexType size(const BlendData &blend) + static inline IndexType size(const BlendData &blend) { return blend.m_blendGroupSizesView.size(); } AXOM_HOST_DEVICE - static IndexType selectedIndex(const BlendData & /*blend*/, IndexType index) + static inline IndexType selectedIndex(const BlendData & /*blend*/, IndexType index) { return index; } @@ -55,16 +55,16 @@ struct SelectAllPolicy /** * \brief This policy can be used with FieldBlender to select a subset of blend groups, according to m_selectedIndicesView. */ -struct SelectThroughArrayView +struct SelectSubsetPolicy { AXOM_HOST_DEVICE - static IndexType size(const BlendData &blend) + static inline IndexType size(const BlendData &blend) { return blend.m_selectedIndicesView.size(); } AXOM_HOST_DEVICE - static IndexType selectedIndex(const BlendData &blend, IndexType index) + static inline IndexType selectedIndex(const BlendData &blend, IndexType index) { return blend.m_selectedIndicesView[index]; } From 4b863becb5e14b42a0159651ff7e8c6f59900298 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 23 Aug 2024 19:09:07 -0700 Subject: [PATCH 189/290] porting MeshTester. --- src/axom/mir/ClipField.hpp | 1 - src/axom/mir/EquiZAlgorithm.hpp | 2 +- src/axom/mir/MIRAlgorithm.cpp | 6 +- src/axom/mir/MeshTester.cpp | 692 ++++++++++++++++-- src/axom/mir/MeshTester.hpp | 42 +- .../mir/examples/mir_concentric_circles.cpp | 69 +- 6 files changed, 729 insertions(+), 83 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 5f465683ff..26fd3d9a37 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1102,7 +1102,6 @@ class ClipField int outputIndex = fragmentData.m_fragmentSizeOffsetsView[szIndex]; // This is where the output fragment sizes/shapes start for this zone. int sizeIndex = fragmentData.m_fragmentOffsetsView[szIndex]; - BitSet shapesUsed = 0; // Iterate over the selected fragments and emit connectivity for them. const auto clipcase = zoneData.m_clipCasesView[szIndex]; diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 40c0e79177..bf7849c90c 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -26,7 +26,7 @@ #endif // Uncomment to save inputs and outputs. -// #define AXOM_DEBUG_EQUIZ +#define AXOM_DEBUG_EQUIZ // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It diff --git a/src/axom/mir/MIRAlgorithm.cpp b/src/axom/mir/MIRAlgorithm.cpp index dccbbc49ec..f85a5a2673 100644 --- a/src/axom/mir/MIRAlgorithm.cpp +++ b/src/axom/mir/MIRAlgorithm.cpp @@ -89,7 +89,11 @@ void MIRAlgorithm::executeSetup(const conduit::Node &n_domain, } else { - conduit::Node n_fields, newFields; + // There are no input fields, but make sure n_fields has a name. + conduit::Node tmp; + conduit::Node &n_fields = tmp["fields"]; + // MIR is likely to output some created fields. + conduit::Node &newFields = n_newDomain["fields"]; executeDomain(*n_topo, *n_coordset, n_fields, diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index c8de116006..7fb25bef3a 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -4,14 +4,86 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "MeshTester.hpp" +#include namespace numerics = axom::numerics; namespace slam = axom::slam; +namespace bputils = axom::mir::utilities::blueprint; namespace axom { namespace mir { + +//-------------------------------------------------------------------------------- + /** + * \brief Calculates the percent overlap between the given circle and quad. + * + * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * /return The percent value overlap of the circle and the quad between [0, 1]. + */ + +template +static axom::float64 calculatePercentOverlapMonteCarlo( + int gridSize, + const PointType& circleCenter, + axom::float64 circleRadius, + const PointType& quadP0, + const PointType& quadP1, + const PointType& quadP2, + const PointType& quadP3) +{ + // Check if any of the quad's corners are within the circle + auto d0Sq = primal::squared_distance(quadP0, circleCenter); + auto d1Sq = primal::squared_distance(quadP1, circleCenter); + auto d2Sq = primal::squared_distance(quadP2, circleCenter); + auto d3Sq = primal::squared_distance(quadP3, circleCenter); + auto dRSq = circleRadius * circleRadius; + + int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) + ((d1Sq < dRSq) ? 1 << 1 : 0) + + ((d2Sq < dRSq) ? 1 << 2 : 0) + ((d3Sq < dRSq) ? 1 << 3 : 0); + const int allFlags = 15; + const int noFlags = 0; + + if(inFlags == allFlags) + { + // The entire quad overlaps the circle + return 1.; + } + else if(inFlags == noFlags) + { + return 0.; + } + else + { + // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much + axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / + static_cast(gridSize - 1); + axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / + static_cast(gridSize - 1); + int countOverlap = 0; + for(int y = 0; y < gridSize; ++y) + { + for(int x = 0; x < gridSize; ++x) + { + PointType samplePoint = + PointType::make_point(delta_x * x + quadP1[0], + delta_y * y + quadP1[1]); + if(primal::squared_distance(samplePoint, circleCenter) < dRSq) + ++countOverlap; + } + } + return countOverlap / static_cast(gridSize * gridSize); + } +} + //-------------------------------------------------------------------------------- MIRMesh MeshTester::initTestCaseOne() @@ -105,6 +177,77 @@ MIRMesh MeshTester::initTestCaseOne() return testMesh; } +//-------------------------------------------------------------------------------- +void MeshTester::mesh3x3(conduit::Node &mesh) +{ + // clang-format off + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(std::vector{{ + 0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3. + }}); + mesh["coordsets/coords/values/y"].set(std::vector{{ + 0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3. + }}); + + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "quad"; + mesh["topologies/mesh/elements/connectivity"].set(std::vector{{ + 0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10, 8,9,13,12, 9,10,14,13, 10,11,15,14 + }}); + mesh["topologies/mesh/elements/sizes"].set(std::vector{{ + 4,4,4,4, 4,4,4,4, 4,4,4,4, 4,4,4,4 + }}); + mesh["topologies/mesh/elements/offsets"].set(std::vector{{ + 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60 + }}); + // clang-format on +} + +//-------------------------------------------------------------------------------- +void MeshTester::initTestCaseOne(conduit::Node &mesh) +{ + mesh3x3(mesh); + + // clang-format off + mesh["matsets/mat/topology"] = "mesh"; + mesh["matsets/mat/material_map/green"] = 0; + mesh["matsets/mat/material_map/blue"] = 1; + mesh["matsets/mat/material_ids"].set(std::vector{{ + 0, + 0, + 0, + 0, + 0, 1, + 0, 1, + 0, 1, + 1, + 1 + }}); + mesh["matsets/mat/volume_fractions"].set(std::vector{{ + 1., + 1., + 1., + 1., + 0.5, 0.5, + 0.2, 0.8, + 0.2, 0.8, + 1., + 1. + }}); + mesh["matsets/mat/sizes"].set(std::vector{{ + 1, 1, 1, 1, 2, 2, 2, 1, 1 + }}); + mesh["matsets/mat/offsets"].set(std::vector{{ + //0, 1, 2, 3, 5, 7, 9, 10, 11 + 0, 1, 2, 3, 4, 6, 8, 10, 11 + }}); + mesh["matsets/mat/indices"].set(std::vector{{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + }}); + // clang-format on +} + //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseTwo() @@ -199,6 +342,53 @@ mir::MIRMesh MeshTester::initTestCaseTwo() return testMesh; } +//-------------------------------------------------------------------------------- +void MeshTester::initTestCaseTwo(conduit::Node &mesh) +{ + mesh3x3(mesh); + + // clang-format off + constexpr int BLUE = 0; + constexpr int RED = 1; + constexpr int ORANGE = 2; + mesh["matsets/mat/topology"] = "mesh"; + mesh["matsets/mat/material_map/blue"] = BLUE; + mesh["matsets/mat/material_map/red"] = RED; + mesh["matsets/mat/material_map/orange"] = ORANGE; + mesh["matsets/mat/material_ids"].set(std::vector{{ + BLUE, + BLUE, + BLUE, + BLUE, + BLUE, RED, ORANGE, + BLUE, RED, + BLUE, ORANGE, + RED, ORANGE, + RED + }}); + mesh["matsets/mat/volume_fractions"].set(std::vector{{ + 1., + 1., + 1., + 1., + 0.5, 0.3, 0.2, + 0.2, 0.8, + 0.2, 0.8, + 0.3, 0.7, + 1. + }}); + mesh["matsets/mat/sizes"].set(std::vector{{ + 1, 1, 1, 1, 3, 2, 2, 2, 1 + }}); + mesh["matsets/mat/offsets"].set(std::vector{{ + 0, 1, 2, 3, 4, 7, 9, 11, 13 + }}); + mesh["matsets/mat/indices"].set(std::vector{{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 + }}); + // clang-format on +} + //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseThree() @@ -277,6 +467,55 @@ mir::MIRMesh MeshTester::initTestCaseThree() return testMesh; } +void MeshTester::initTestCaseThree(conduit::Node &mesh) +{ + // clang-format off + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(std::vector{{ + 1., 0.5, 1.5, 0., 1., 2., + }}); + mesh["coordsets/coords/values/y"].set(std::vector{{ + 2., 1., 1., 0., 0., 0. + }}); + + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "tri"; + mesh["topologies/mesh/elements/connectivity"].set(std::vector{{ + 0,1,2, 1,3,4, 1,4,2, 2,4,5 + }}); + mesh["topologies/mesh/elements/sizes"].set(std::vector{{3,3,3,3}}); + mesh["topologies/mesh/elements/offsets"].set(std::vector{{0,3,6,9}}); + + constexpr int BLUE = 0; + constexpr int RED = 1; + mesh["matsets/mat/topology"] = "mesh"; + mesh["matsets/mat/material_map/blue"] = BLUE; + mesh["matsets/mat/material_map/red"] = RED; + mesh["matsets/mat/material_ids"].set(std::vector{{ + RED, + BLUE, RED, + BLUE, RED, + BLUE, RED + }}); + mesh["matsets/mat/volume_fractions"].set(std::vector{{ + 1., + 0.5, 0.5, + 0.8, 0.2, + 0.5, 0.5 + }}); + mesh["matsets/mat/sizes"].set(std::vector{{ + 1, 2, 2, 2 + }}); + mesh["matsets/mat/offsets"].set(std::vector{{ + 0, 1, 3, 5 + }}); + mesh["matsets/mat/indices"].set(std::vector{{ + 0, 1, 2, 3, 4, 5, 6 + }}); + // clang-format on +} + //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseFour() @@ -396,6 +635,92 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- +template +static void addCircleMaterial(const TopoView &topoView, + const CoordsetView &coordsetView, conduit::Node &mesh, const mir::Point2 &circleCenter, axom::float64 circleRadius, int numSamples) +{ + constexpr int GREEN = 0; + constexpr int BLUE = 1; + + int numElements = topoView.numberOfZones(); + std::vector volFracs[2]; + volFracs[GREEN].resize(numElements); + volFracs[BLUE].resize(numElements); + + // Generate the element volume fractions for the circle + axom::ArrayView greenView(volFracs[GREEN].data(), numElements); + axom::ArrayView blueView(volFracs[BLUE].data(), numElements); + typename CoordsetView::PointType center; + center[0] = circleCenter[0]; + center[1] = circleCenter[1]; + topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + auto vf = calculatePercentOverlapMonteCarlo(numSamples, + center, + circleRadius, + coordsetView[zone.getId(0)], + coordsetView[zone.getId(1)], + coordsetView[zone.getId(2)], + coordsetView[zone.getId(3)]); + greenView[zoneIndex] = vf; + blueView[zoneIndex] = 1.0 - vf; + }); + + // Figure out the material buffers from the volume fractions. + std::vector material_ids, sizes, offsets, indices; + std::vector volume_fractions; + for(int i = 0; i < numElements; ++i) + { + int nmats = 0; + offsets.push_back(indices.size()); + if(volFracs[GREEN][i] > 0.) + { + material_ids.push_back(GREEN); + volume_fractions.push_back(volFracs[GREEN][i]); + indices.push_back(indices.size()); + nmats++; + } + if(volFracs[BLUE][i] > 0.) + { + material_ids.push_back(BLUE); + volume_fractions.push_back(volFracs[BLUE][i]); + indices.push_back(indices.size()); + nmats++; + } + sizes.push_back(nmats); + } + + mesh["matsets/mat/topology"] = "mesh"; + mesh["matsets/mat/material_map/green"] = GREEN; + mesh["matsets/mat/material_map/blue"] = BLUE; + mesh["matsets/mat/material_ids"].set(material_ids); + mesh["matsets/mat/volume_fractions"].set(volume_fractions); + mesh["matsets/mat/sizes"].set(sizes); + mesh["matsets/mat/offsets"].set(offsets); + mesh["matsets/mat/indices"].set(indices); +} + +//-------------------------------------------------------------------------------- +void MeshTester::initTestCaseFour(conduit::Node &mesh) +{ + mesh3x3(mesh); + + // Make views + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + + // Add material + const auto circleCenter = mir::Point2::make_point(1.5, 1.5); + const axom::float64 circleRadius = 1.25; + const int numSamples = 100; + addCircleMaterial(topoView, coordsetView, mesh, circleCenter, circleRadius, numSamples); +} + +//-------------------------------------------------------------------------------- + mir::MIRMesh MeshTester::createUniformGridTestCaseMesh( int gridSize, const mir::Point2& circleCenter, @@ -461,58 +786,24 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh( } //-------------------------------------------------------------------------------- - -axom::float64 MeshTester::calculatePercentOverlapMonteCarlo( +void MeshTester::createUniformGridTestCaseMesh( int gridSize, const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3) + axom::float64 circleRadius, conduit::Node &mesh) { - // Check if any of the quad's corners are within the circle - auto d0Sq = primal::squared_distance(quadP0, circleCenter); - auto d1Sq = primal::squared_distance(quadP1, circleCenter); - auto d2Sq = primal::squared_distance(quadP2, circleCenter); - auto d3Sq = primal::squared_distance(quadP3, circleCenter); - auto dRSq = circleRadius * circleRadius; - - int inFlags = ((d0Sq < dRSq) ? 1 << 0 : 0) + ((d1Sq < dRSq) ? 1 << 1 : 0) + - ((d2Sq < dRSq) ? 1 << 2 : 0) + ((d3Sq < dRSq) ? 1 << 3 : 0); - const int allFlags = 15; - const int noFlags = 0; - - if(inFlags == allFlags) - { - // The entire quad overlaps the circle - return 1.; - } - else if(inFlags == noFlags) - { - return 0.; - } - else - { - // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much - axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / - static_cast(gridSize - 1); - axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / - static_cast(gridSize - 1); - int countOverlap = 0; - for(int y = 0; y < gridSize; ++y) - { - for(int x = 0; x < gridSize; ++x) - { - mir::Point2 samplePoint = - mir::Point2::make_point(delta_x * x + quadP1[0], - delta_y * y + quadP1[1]); - if(primal::squared_distance(samplePoint, circleCenter) < dRSq) - ++countOverlap; - } - } - return countOverlap / static_cast(gridSize * gridSize); - } + // Generate the mesh + generateGrid(gridSize, mesh); + + // Make views + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + + // Add material + int numSamples = 100; + addCircleMaterial(topoView, coordsetView, mesh, circleCenter, circleRadius, numSamples); } //-------------------------------------------------------------------------------- @@ -630,6 +921,113 @@ mir::CellData MeshTester::generateGrid(int gridSize) return data; } +void MeshTester::generateGrid(int gridSize, conduit::Node &mesh) +{ + int nx = gridSize + 1; + int ny = gridSize + 1; + int nzones = gridSize * gridSize; + int nnodes = nx * ny; + + std::vector xc, yc; + xc.reserve(nnodes); + yc.reserve(nnodes); + for(int j = 0; j < ny; j++) + { + for(int i = 0; i < nx; i++) + { + xc.push_back(i); + yc.push_back(j); + } + } + + std::vector conn, sizes, offsets; + conn.reserve(nzones * 4); + sizes.reserve(nzones); + offsets.reserve(nzones); + for(int j = 0; j < gridSize; j++) + { + for(int i = 0; i < gridSize; i++) + { + offsets.push_back(offsets.size()); + sizes.push_back(4); + conn.push_back(j * nx + i); + conn.push_back(j * nx + i + 1); + conn.push_back((j + 1) * nx + i + 1); + conn.push_back((j + 1) * nx + i); + } + } + + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(xc); + mesh["coordsets/coords/values/y"].set(yc); + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "quad"; + mesh["topologies/mesh/elements/connectivity"].set(conn); + mesh["topologies/mesh/elements/sizes"].set(sizes); + mesh["topologies/mesh/elements/offsets"].set(offsets); +} + +void MeshTester::generateGrid3D(int gridSize, conduit::Node &mesh) +{ + int nx = gridSize + 1; + int ny = gridSize + 1; + int nz = gridSize + 1; + int nzones = gridSize * gridSize * gridSize; + int nnodes = nx * ny * nz; + + std::vector xc, yc, zc; + xc.reserve(nnodes); + yc.reserve(nnodes); + zc.reserve(nnodes); + for(int k = 0; k < nz; k++) + { + for(int j = 0; j < ny; j++) + { + for(int i = 0; i < nx; i++) + { + xc.push_back(i); + yc.push_back(j); + zc.push_back(k); + } + } + } + + std::vector conn, sizes, offsets; + conn.reserve(nzones * 8); + sizes.reserve(nzones); + offsets.reserve(nzones); + for(int k = 0; k < gridSize; k++) + { + for(int j = 0; j < gridSize; j++) + { + for(int i = 0; i < gridSize; i++) + { + offsets.push_back(offsets.size()); + sizes.push_back(8); + conn.push_back((k * nx * ny) + (j * nx) + i); + conn.push_back((k * nx * ny) + (j * nx) + i + 1); + conn.push_back((k * nx * ny) + ((j + 1) * nx) + i + 1); + conn.push_back((k * nx * ny) + ((j + 1) * nx) + i); + conn.push_back(((k + 1) * nx * ny) + (j * nx) + i); + conn.push_back(((k + 1) * nx * ny) + (j * nx) + i + 1); + conn.push_back(((k + 1) * nx * ny) + ((j + 1) * nx) + i + 1); + conn.push_back(((k + 1) * nx * ny) + ((j + 1) * nx) + i); + } + } + } + + mesh["coordsets/coords/type"] = "explicit"; + mesh["coordsets/coords/values/x"].set(xc); + mesh["coordsets/coords/values/y"].set(yc); + mesh["coordsets/coords/values/z"].set(zc); + mesh["topologies/mesh/type"] = "unstructured"; + mesh["topologies/mesh/coordset"] = "coords"; + mesh["topologies/mesh/elements/shape"] = "hex"; + mesh["topologies/mesh/elements/connectivity"].set(conn); + mesh["topologies/mesh/elements/sizes"].set(sizes); + mesh["topologies/mesh/elements/offsets"].set(offsets); +} //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) @@ -774,6 +1172,159 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) return testMesh; } +template +void addConcentricCircleMaterial(const TopoView &topoView, const CoordsetView &coordsetView, + const mir::Point2 &circleCenter, std::vector &circleRadii, int numSamples, conduit::Node &mesh) +{ + + // Generate the element volume fractions with concentric circles + int numMaterials = circleRadii.size() + 1; + int defaultMaterialID = + numMaterials - 1; // default material is always the last index + + // Initialize all material volume fractions to 0 + std::vector> materialVolumeFractionsData(numMaterials); + constexpr int MAXMATERIALS = 100; + axom::StackArray, MAXMATERIALS> matvfViews; + for(int i = 0; i < numMaterials; ++i) + { + const auto len = topoView.numberOfZones(); + materialVolumeFractionsData[i].resize(len, 0.); + matvfViews[i] = axom::ArrayView(materialVolumeFractionsData[i].data(), len); + } + auto circleRadiiView = axom::ArrayView(circleRadii.data(), circleRadii.size()); + const int numCircles = circleRadii.size(); + + // Use the uniform sampling method to generate volume fractions for each material + // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation + topoView. template for_all_zones(AXOM_LAMBDA(auto eID, const auto &zone) + { + auto v0 = coordsetView[zone.getId(0)]; + auto v1 = coordsetView[zone.getId(1)]; + auto v2 = coordsetView[zone.getId(2)]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; + for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; + + axom::float64 delta_x = + axom::utilities::abs(v1[0] - v0[0]) / (axom::float64)(numSamples - 1); + axom::float64 delta_y = + axom::utilities::abs(v2[1] - v1[1]) / (axom::float64)(numSamples - 1); + + for(int y = 0; y < numSamples; ++y) + { + for(int x = 0; x < numSamples; ++x) + { + mir::Point2 samplePoint = + mir::Point2::make_point(delta_x * x + v0[0], delta_y * y + v0[1]); + bool isPointSampled = false; + for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) + { + const auto r = circleRadiiView[cID]; + if(primal::squared_distance(samplePoint, circleCenter) < r * r) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if(!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; + } + } + } + + // Assign the element volume fractions based on the count of the samples in each circle + for(int matID = 0; matID < numMaterials; ++matID) + { + matvfViews[matID][eID] = + materialCount[matID] / (axom::float64)(numSamples * numSamples); + } + }); + + // Figure out the material buffers from the volume fractions. + std::vector material_ids, sizes, offsets, indices; + std::vector volume_fractions; + const axom::IndexType numElements = topoView.numberOfZones(); + std::vector sums(numMaterials, 0.); + for(axom::IndexType i = 0; i < numElements; ++i) + { + int nmats = 0; + offsets.push_back(indices.size()); + for(int mat = 0; mat < numMaterials; mat++) + { + if(materialVolumeFractionsData[mat][i] > 0.) + { + material_ids.push_back(mat); + volume_fractions.push_back(materialVolumeFractionsData[mat][i]); + indices.push_back(indices.size()); + nmats++; + + // Keep a total of the VFs for each material. + sums[mat] += materialVolumeFractionsData[mat][i]; + } + } + sizes.push_back(nmats); + } + + // Add the material + mesh["matsets/mat/topology"] = "mesh"; + for(int mat = 0; mat < numMaterials; mat++) + { + if(sums[mat] > 0.) + { + std::stringstream ss; + ss << "matsets/mat/material_map/mat" << mat; + mesh[ss.str()] = mat; + } + } + mesh["matsets/mat/material_ids"].set(material_ids); + mesh["matsets/mat/volume_fractions"].set(volume_fractions); + mesh["matsets/mat/sizes"].set(sizes); + mesh["matsets/mat/offsets"].set(offsets); + mesh["matsets/mat/indices"].set(indices); +} + +void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node &mesh) +{ + // Generate the mesh topology + generateGrid(gridSize, mesh); + + mir::Point2 circleCenter = mir::Point2::make_point( + gridSize / 2.0, + gridSize / 2.0); // all circles are centered around the same point + + // Initialize the radii of the circles + std::vector circleRadii; + axom::float64 maxRadius = + gridSize / 2.4; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = + gridSize / 8; // Note: The choice of divisor is arbitrary + + axom::float64 radiusDelta; + if(numCircles <= 1) + radiusDelta = (maxRadius - minRadius); + else + radiusDelta = (maxRadius - minRadius) / (double)(numCircles - 1); + + for(int i = 0; i < numCircles; ++i) + { + circleRadii.push_back(minRadius + (i * radiusDelta)); + } + + // Make views + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + + // Add the material + addConcentricCircleMaterial(topoView, coordsetView, circleCenter, circleRadii, 100, mesh); +} + //-------------------------------------------------------------------------------- int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, @@ -846,6 +1397,49 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() return testMesh; } +void MeshTester::initQuadClippingTestMesh(conduit::Node &mesh) +{ + // Generate the mesh topology + constexpr int gridSize = 3; + generateGrid(gridSize, mesh); + + // clang-format off + mesh["matsets/mat/topology"] = "mesh"; + mesh["matsets/mat/material_map/green"] = 0; + mesh["matsets/mat/material_map/blue"] = 1; + mesh["matsets/mat/material_ids"].set(std::vector{{ + 0, + 0, + 0, + 0, 1, + 0, 1, + 0, 1, + 1, + 1, + 1 + }}); + mesh["matsets/mat/volume_fractions"].set(std::vector{{ + 1., + 1., + 1., + 0.5, 0.5, + 0.5, 0.5, + 0.5, 0.5, + 1., + 1., + 1. + }}); + mesh["matsets/mat/sizes"].set(std::vector{{ + 1, 1, 1, 2, 2, 2, 1, 1, 1 + }}); + mesh["matsets/mat/offsets"].set(std::vector{{ + 0, 1, 2, 3, 5, 7, 9, 10, 11 + }}); + mesh["matsets/mat/indices"].set(std::vector{{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 + }}); + // clang-format on +} //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 1d111b410b..ca590ef872 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -22,6 +22,8 @@ #include #include +#include + namespace axom { namespace mir @@ -63,6 +65,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseOne(); + void initTestCaseOne(conduit::Node &mesh); /** * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. @@ -72,6 +75,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseTwo(); + void initTestCaseTwo(conduit::Node &mesh); /** * \brief Initializes an MIRMesh used for testing triangle clipping cases. @@ -81,6 +85,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseThree(); + void initTestCaseThree(conduit::Node &mesh); /** * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. @@ -91,6 +96,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseFour(); + void initTestCaseFour(conduit::Node &mesh); /** * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform 2D grid. @@ -103,6 +109,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); + void initTestCaseFive(int gridSize, int numCircles, conduit::Node &mesh); /** * \brief Initializes a mesh to be used for testing a set of concentric spheres centered in a uniform 3D grid. @@ -128,7 +135,10 @@ class MeshTester mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, const mir::Point2& circleCenter, axom::float64 circleRadius); - + void createUniformGridTestCaseMesh(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + conduit::Node &mesh); /** * \brief Initializes a mesh to be used for validating the results of quad clipping. * @@ -138,14 +148,22 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initQuadClippingTestMesh(); + void initQuadClippingTestMesh(conduit::Node &mesh); private: + /** + * \brief make a 3x3 mesh of quads. + * \param mesh A conduit node that will contain the new mesh. + */ + void mesh3x3(conduit::Node &mesh); + /** * \brief Generates a 2D uniform grid of n x n elements. * * \param gridSize The number of elements in the width and height of the uniform grid. */ mir::CellData generateGrid(int gridSize); + void generateGrid(int gridSize, conduit::Node &mesh); /** * \brief Generates a 3D uniform grid of n x n x n elements. @@ -153,27 +171,7 @@ class MeshTester * \param gridSize The number of elements in the width, height, and depth of the uniform grid. */ mir::CellData generateGrid3D(int gridSize); - - /** - * \brief Calculates the percent overlap between the given circle and quad. - * - * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. - * \param circleCenter The center point of the circle. - * \param circleRadius The radius of the circle. - * \param quadP0 The upper left vertex of the quad. - * \param quadP1 The lower left vertex of the quad. - * \param quadP2 The lower right vertex of the quad. - * \param quadP3 The upper right vertex of the quad. - * - * /return The percent value overlap of the circle and the quad between [0, 1]. - */ - axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3); + void generateGrid3D(int gridSize, conduit::Node &mesh); /** * \brief Calculates the number of corners of the quad that are within the circle. diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 6a93311cba..fbaafa01fd 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -7,10 +7,14 @@ #include "axom/slic.hpp" #include "axom/mir.hpp" // for Mir classes & functions +#include +#include + #include // namespace aliases namespace mir = axom::mir; +namespace bputils = axom::mir::utilities::blueprint; //-------------------------------------------------------------------------------- @@ -19,17 +23,34 @@ std::string usageString() return "Args are "; } +//-------------------------------------------------------------------------------- + +void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) +{ +// SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); + // This is on purpose. + while(1) + ; +} + +//-------------------------------------------------------------------------------- + int main(int argc, char **argv) { axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel(axom::slic::message::Info); + conduit::utils::set_error_handler(conduit_debug_err_handler); +#if defined(AXOM_USE_CALIPER) + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper("report"); +#endif if(argc != 4) { SLIC_WARNING("Incorrect number of args. " << usageString()); - return 1; + return -1; } + int retval = 0; try { // Parse the command line arguments @@ -40,32 +61,62 @@ int main(int argc, char **argv) // Initialize a mesh for testing MIR auto timer = axom::utilities::Timer(true); mir::MeshTester tester; - mir::MIRMesh testMesh = tester.initTestCaseFive(gridSize, numCircles); + conduit::Node mesh; + tester.initTestCaseFive(gridSize, numCircles, mesh); + mesh.print(); timer.stop(); SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + // Output initial mesh. + conduit::relay::io::blueprint::save_mesh(mesh, "concentric_circles", "hdf5"); + + // Make views (we know beforehand which types to make) + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + constexpr int MAXMATERIALS = 20; + auto materialInfo = axom::mir::views::materials(mesh["matsets/mat"]); + if(materialInfo.size() >= MAXMATERIALS) + { + SLIC_WARNING(axom::fmt::format("To use more than {} materials, recompile with larger MAXMATERIALS value.", MAXMATERIALS)); + return -4; + } + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(mesh["matsets/mat/material_ids"]), + bputils::make_array_view(mesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(mesh["matsets/mat/sizes"]), + bputils::make_array_view(mesh["matsets/mat/offsets"]), + bputils::make_array_view(mesh["matsets/mat/indices"])); + // Begin material interface reconstruction + using MIR = axom::mir::EquiZAlgorithm; timer.start(); - mir::InterfaceReconstructor reconstructor; - mir::MIRMesh processedMesh; - reconstructor.computeReconstructedInterface(testMesh, processedMesh); + conduit::Node options, processedMesh; + MIR m(topoView, coordsetView, matsetView); + options["matset"] = "mat"; + m.execute(mesh, options, processedMesh); timer.stop(); SLIC_INFO("Material interface reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results - processedMesh.writeMeshToFile(outputFilePath, "outputConcentricCircles.vtk"); + conduit::relay::io::blueprint::save_mesh(processedMesh, outputFilePath, "hdf5"); - return 0; + retval = 0; } catch(std::invalid_argument const &e) { SLIC_WARNING("Bad input. " << usageString()); - return 1; + retval = -2; } catch(std::out_of_range const &e) { SLIC_WARNING("Integer overflow. " << usageString()); - return 1; + retval = -3; } + return retval; } From f29c26fa383f05c8d3e5b24379250794bf8f5843 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 26 Aug 2024 18:18:01 -0700 Subject: [PATCH 190/290] Emit edge points using endpoint ids in blend groups if close to start/end points. Turn degenerate quads to triangles. Fix element fields. Add test cases for point merge. --- src/axom/mir/ClipField.hpp | 104 +++++++++++--- src/axom/mir/tests/mir_clipfield.cpp | 194 ++++++++++++++++++++------- 2 files changed, 235 insertions(+), 63 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 26fd3d9a37..dd0465d26b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -527,16 +527,29 @@ class ClipField // Get the fields that we want to process. std::map fieldsToProcess; int numElementFields = 0; - if(!opts.fields(fieldsToProcess)) + if(opts.fields(fieldsToProcess)) + { + // Fields were present in the options. Count the element fields. + for(auto it = fieldsToProcess.begin(); it != fieldsToProcess.end(); it++) + { + const conduit::Node &n_field = n_fields.fetch_existing(it->first); + if(n_field.fetch_existing("topology").as_string() == n_topo.name()) + { + numElementFields += (n_field.fetch_existing("association").as_string() == "element") ? 1 : 0; + } + } + } + else { // Fields were not present in the options. Select all fields that have the same topology as n_topo. for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { - if(n_fields[i].fetch_existing("topology").as_string() == n_topo.name()) + const conduit::Node &n_field = n_fields[i]; + if(n_field.fetch_existing("topology").as_string() == n_topo.name()) { - numElementFields += (n_fields[i].fetch_existing("association").as_string() == "element") ? 1 : 0; + numElementFields += (n_field.fetch_existing("association").as_string() == "element") ? 1 : 0; - fieldsToProcess[n_fields[i].name()] = n_fields[i].name(); + fieldsToProcess[n_field.name()] = n_field.name(); } } } @@ -955,9 +968,24 @@ class ClipField // Figure out the blend for edge. const auto t = deviceIntersector.computeWeight(zoneIndex, id0, id1); + // Close to the endpoints, just count the edge blend group + // as an endpoint to ensure better blend group matching later. + constexpr decltype(t) LOWER = 1.e-4; + constexpr decltype(t) UPPER = 1. - LOWER; groups.beginGroup(); - groups.add(id0, 1.f - t); - groups.add(id1, t); + if(t < LOWER) + { + groups.add(id0, 1.f); + } + else if(t > UPPER) + { + groups.add(id1, 1.f); + } + else + { + groups.add(id0, 1.f - t); + groups.add(id1, t); + } groups.endGroup(); } } @@ -1135,14 +1163,18 @@ class ClipField }); // Reduce outside of the for_selected_zones loop. - RAJA::ReduceBitOr shapesUsed_reduce(0); - axom::for_all( - shapesView.size(), - AXOM_LAMBDA(auto index) { - BitSet shapeBit {}; - axom::utilities::setBitOn(shapeBit, shapesView[index]); - shapesUsed_reduce |= shapeBit; - }); + BitSet shapesUsed {}; + { + RAJA::ReduceBitOr shapesUsed_reduce(0); + axom::for_all( + shapesView.size(), + AXOM_LAMBDA(auto index) { + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); + shapesUsed = shapesUsed_reduce.get(); + } #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -1176,8 +1208,50 @@ class ClipField << std::endl; #endif +#if 1 + // Handle some quad->tri degeneracies + if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) + { + const axom::IndexType numOutputZones = shapesView.size(); + RAJA::ReduceBitOr shapesUsed_reduce(0); + axom::for_all( + numOutputZones, + AXOM_LAMBDA(auto index) { + if(shapesView[index] == views::Quad_ShapeID) + { + const auto offset = offsetsView[index]; + ConnectivityType pts[4]; + int npts = 0; + for(int current = 0; current < 4; current++) + { + int next = (current + 1) % 4; + ConnectivityType curNode = connView[offset + current]; + ConnectivityType nextNode = connView[offset + next]; + if(curNode != nextNode) + pts[npts++] = curNode; + } + + if(npts == 3) + { + shapesView[index] = views::Tri_ShapeID; + sizesView[index] = 3; + connView[offset] = pts[0]; + connView[offset + 1] = pts[1]; + connView[offset + 2] = pts[2]; + connView[offset + 3] = pts[2]; // Repeat the last point (it won't be used though). + } + } + + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); + // We redid shapesUsed reduction in case triangles appeared. + shapesUsed = shapesUsed_reduce.get(); + } +#endif + // Add shape information to the connectivity. - const BitSet shapesUsed = shapesUsed_reduce.get(); SLIC_ASSERT_MSG(shapesUsed != 0, "No shapes were produced!"); const auto shapeMap = shapeMap_FromFlags(shapesUsed); SLIC_ASSERT_MSG(shapeMap.empty() == false, "The shape map is empty!"); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 78b471d2bb..eb438714f0 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -13,8 +13,13 @@ #include #include +namespace bputils = axom::mir::utilities::blueprint; + //------------------------------------------------------------------------------ +// Uncomment to make Conduit errors hang. +//#define DEBUGGING_TEST_CASES + // Uncomment to generate baselines //#define AXOM_TESTING_GENERATE_BASELINES @@ -485,6 +490,11 @@ void braid2d_clip_test(const std::string &type, const std::string &name) #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif +#if defined(DEBUGGING_TEST_CASES) + std::cout << "---------------------------------- input mesh -------------------------------------" << std::endl; + printNode(hostMesh); + std::cout << "-----------------------------------------------------------------------------------" << std::endl; +#endif // Create views axom::StackArray origin {0., 0.}, spacing {1., 1.}; @@ -502,17 +512,6 @@ void braid2d_clip_test(const std::string &type, const std::string &name) options["fields/braid"] = "new_braid"; options["fields/radial"] = "new_radial"; -#if 0 - // Select the left half of zones - std::vector sel; - for(int i = 0; i < topoView.numberOfZones(); i++) - { - if(i % zoneDims[0] < 5) - sel.push_back(i); - } - options["selectedZones"].set(sel); -#endif - // Clip the data conduit::Node deviceClipMesh; axom::mir::clipping::ClipField clipper( @@ -524,6 +523,12 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::Node hostClipMesh; axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); +#if defined(DEBUGGING_TEST_CASES) + std::cout << "---------------------------------- clipped uniform ----------------------------------" << std::endl; + printNode(hostClipMesh); + std::cout << "-------------------------------------------------------------------------------------" << std::endl; +#endif + // Handle baseline comparison. { std::string baselineName(yamlRoot(name)); @@ -536,47 +541,15 @@ void braid2d_clip_test(const std::string &type, const std::string &name) } // Now, take the clipped mesh and clip it again using a mixed topology view. - using MixedTopoView = - axom::mir::views::UnstructuredTopologyMixedShapeView; using ExpCoordsetView = axom::mir::views::ExplicitCoordsetView; - conduit::Node &n_x = - deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x"); - conduit::Node &n_y = - deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y"); - axom::ArrayView xView(static_cast(n_x.data_ptr()), - n_x.dtype().number_of_elements()); - axom::ArrayView yView(static_cast(n_y.data_ptr()), - n_y.dtype().number_of_elements()); - ExpCoordsetView mixedCoordsetView(xView, yView); + const auto xView = bputils::make_array_view(deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x")); + const auto yView = bputils::make_array_view(deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y")); + ExpCoordsetView expCoordsetView(xView, yView); conduit::Node &n_device_topo = deviceClipMesh.fetch_existing("topologies/" + clipTopoName); - conduit::Node &n_conn = n_device_topo.fetch_existing("elements/connectivity"); - conduit::Node &n_shapes = n_device_topo.fetch_existing("elements/shapes"); - conduit::Node &n_sizes = n_device_topo.fetch_existing("elements/sizes"); - conduit::Node &n_offsets = n_device_topo.fetch_existing("elements/offsets"); - axom::ArrayView connView( - static_cast(n_conn.data_ptr()), - n_conn.dtype().number_of_elements()); - axom::ArrayView shapesView( - static_cast(n_shapes.data_ptr()), - n_shapes.dtype().number_of_elements()); - axom::ArrayView sizesView( - static_cast(n_sizes.data_ptr()), - n_sizes.dtype().number_of_elements()); - axom::ArrayView offsetsView( - static_cast(n_offsets.data_ptr()), - n_offsets.dtype().number_of_elements()); - MixedTopoView mixedTopoView(n_device_topo, - connView, - shapesView, - sizesView, - offsetsView); + const auto connView = bputils::make_array_view(n_device_topo.fetch_existing("elements/connectivity")); - // Clip the data - conduit::Node deviceClipMixedMesh; - axom::mir::clipping::ClipField - mixedClipper(mixedTopoView, mixedCoordsetView); options["clipField"] = "new_braid"; options["clipValue"] = 1.; options["fields"].reset(); @@ -584,12 +557,48 @@ void braid2d_clip_test(const std::string &type, const std::string &name) options["fields/color"] = "new_color"; options["fields/new_radial"] = "new_radial2"; - mixedClipper.execute(deviceClipMesh, options, deviceClipMixedMesh); + conduit::Node deviceClipMixedMesh; + if(n_device_topo.fetch_existing("elements/shape").as_string() == "mixed") + { + auto shapesView = bputils::make_array_view(n_device_topo.fetch_existing("elements/shapes")); + const auto sizesView = bputils::make_array_view(n_device_topo.fetch_existing("elements/sizes")); + const auto offsetsView = bputils::make_array_view(n_device_topo.fetch_existing("elements/offsets")); + + using MixedTopoView = + axom::mir::views::UnstructuredTopologyMixedShapeView; + MixedTopoView mixedTopoView(n_device_topo, + connView, + shapesView, + sizesView, + offsetsView); + + // Clip the data + axom::mir::clipping::ClipField + mixedClipper(mixedTopoView, expCoordsetView); + mixedClipper.execute(deviceClipMesh, options, deviceClipMixedMesh); + } + else + { + // Depending on optimizations, we might get a mesh with just quads. + using QuadTopoView = + axom::mir::views::UnstructuredTopologySingleShapeView>; + QuadTopoView quadTopoView(connView); + + // Clip the data + axom::mir::clipping::ClipField + quadClipper(quadTopoView, expCoordsetView); + quadClipper.execute(deviceClipMesh, options, deviceClipMixedMesh); + } // Copy device->host conduit::Node hostClipMixedMesh; axom::mir::utilities::blueprint::copy(hostClipMixedMesh, deviceClipMixedMesh); +#if defined(DEBUGGING_TEST_CASES) + std::cout << "---------------------------------- clipped mixed ----------------------------------" << std::endl; + printNode(hostClipMixedMesh); + std::cout << "-----------------------------------------------------------------------------------" << std::endl; +#endif // Handle baseline comparison. { @@ -988,6 +997,95 @@ TEST(mir_clipfield, mixed) #endif } +//------------------------------------------------------------------------------ + +template +void compare_values(const Container1 &c1, const Container2 &c2) +{ + EXPECT_EQ(c1.size(), c2.number_of_elements()); + for(size_t i = 0; i < c1.size(); i++) + { + EXPECT_EQ(c1[i], c2[i]); + } +} + +template +void point_merge_test() +{ + conduit::Node hostMesh; + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 1., 2., 0., 1., 2., 0., 1., 2.}}); + hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1., 1., 1., 2., 2., 2.}}); + hostMesh["topologies/mesh/type"] = "unstructured"; + hostMesh["topologies/mesh/coordset"] = "coords"; + hostMesh["topologies/mesh/elements/shape"] = "quad"; + hostMesh["topologies/mesh/elements/connectivity"].set(std::vector{{0,1,4,3, 1,2,5,4, 3,4,7,6, 4,5,8,7}}); + hostMesh["topologies/mesh/elements/sizes"].set(std::vector{{4,4,4,4}}); + hostMesh["topologies/mesh/elements/offsets"].set(std::vector{{0,4,8,12}}); + hostMesh["fields/clip/topology"] = "mesh"; + hostMesh["fields/clip/association"] = "vertex"; + hostMesh["fields/clip/values"].set(std::vector{{1., 1., 0.5, 1., 1., 0., 0.5, 0., 0.5}}); + + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + + // Set up views for the mesh. + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView(bputils::make_array_view(deviceMesh.fetch_existing("coordsets/coords/values/x")), + bputils::make_array_view(deviceMesh.fetch_existing("coordsets/coords/values/y"))); + + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topologyView(bputils::make_array_view(deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"))); + + // Clip + conduit::Node options, deviceClipMesh; + options["clipField"] = "clip"; + options["clipValue"] = 0.5; + using Clip = axom::mir::clipping::ClipField; + Clip clip(topologyView, coordsetView); + clip.execute(deviceMesh, options, deviceClipMesh); + + conduit::Node hostClipMesh; + axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + //printNode(hostClipMesh); + + // Check that the points were merged when making the new mesh. + std::vector x{{2.0, 1.0, 2.0, 0.0, 1.5, 2.0, 1.0}}; + std::vector y{{1.0, 1.5, 2.0, 2.0, 1.0, 0.0, 2.0}}; + EXPECT_EQ(x.size(), 7); + EXPECT_EQ(y.size(), 7); + for(size_t i = 0; i < x.size(); i++) + { + EXPECT_FLOAT_EQ(hostClipMesh["coordsets/coords/values/x"].as_float_accessor()[i], x[i]); + EXPECT_FLOAT_EQ(hostClipMesh["coordsets/coords/values/y"].as_float_accessor()[i], y[i]); + } + + // Check that the degenerate quads were turned into triangles. + std::vector shapes{{2, 2, 3, 2}}; + std::vector sizes{{3, 3, 4, 3}}; + std::vector offsets{{0, 4, 8, 12}}; + compare_values(shapes, hostClipMesh["topologies/mesh/elements/shapes"].as_int_accessor()); + compare_values(sizes, hostClipMesh["topologies/mesh/elements/sizes"].as_int_accessor()); + compare_values(offsets, hostClipMesh["topologies/mesh/elements/offsets"].as_int_accessor()); +} + +TEST(mir_clipfield, pointmerging) +{ + point_merge_test(); + +#if defined(AXOM_USE_OPENMP) + point_merge_test(); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + point_merge_test(); +#endif + +#if defined(AXOM_USE_HIP) + point_merge_test(); +#endif +} + //------------------------------------------------------------------------------ #if defined(DEBUGGING_TEST_CASES) void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) From e2ab64eb64b98aab3a6f6f97a5430c2b288f8cae Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 26 Aug 2024 18:20:30 -0700 Subject: [PATCH 191/290] make style --- src/axom/mir/ClipField.hpp | 22 +- src/axom/mir/FieldBlender.hpp | 3 +- src/axom/mir/MeshTester.cpp | 231 ++++++++++-------- src/axom/mir/MeshTester.hpp | 14 +- .../mir/examples/mir_concentric_circles.cpp | 41 ++-- src/axom/mir/tests/mir_clipfield.cpp | 112 ++++++--- 6 files changed, 254 insertions(+), 169 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index dd0465d26b..1eec68244a 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -535,7 +535,9 @@ class ClipField const conduit::Node &n_field = n_fields.fetch_existing(it->first); if(n_field.fetch_existing("topology").as_string() == n_topo.name()) { - numElementFields += (n_field.fetch_existing("association").as_string() == "element") ? 1 : 0; + numElementFields += + (n_field.fetch_existing("association").as_string() == "element") ? 1 + : 0; } } } @@ -547,7 +549,9 @@ class ClipField const conduit::Node &n_field = n_fields[i]; if(n_field.fetch_existing("topology").as_string() == n_topo.name()) { - numElementFields += (n_field.fetch_existing("association").as_string() == "element") ? 1 : 0; + numElementFields += + (n_field.fetch_existing("association").as_string() == "element") ? 1 + : 0; fieldsToProcess[n_field.name()] = n_field.name(); } @@ -1227,8 +1231,7 @@ class ClipField int next = (current + 1) % 4; ConnectivityType curNode = connView[offset + current]; ConnectivityType nextNode = connView[offset + next]; - if(curNode != nextNode) - pts[npts++] = curNode; + if(curNode != nextNode) pts[npts++] = curNode; } if(npts == 3) @@ -1238,7 +1241,8 @@ class ClipField connView[offset] = pts[0]; connView[offset + 1] = pts[1]; connView[offset + 2] = pts[2]; - connView[offset + 3] = pts[2]; // Repeat the last point (it won't be used though). + connView[offset + 3] = + pts[2]; // Repeat the last point (it won't be used though). } } @@ -1281,11 +1285,9 @@ class ClipField conduit::Node &n_newCoordset) const { AXOM_ANNOTATE_SCOPE("makeCoordset"); - axom::mir::utilities::blueprint::CoordsetBlender< - ExecSpace, - CoordsetView, - axom::mir::utilities::blueprint::SelectSubsetPolicy> - cb; + axom::mir::utilities::blueprint:: + CoordsetBlender + cb; n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 9534120399..b1302caf8c 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -46,7 +46,8 @@ struct SelectAllPolicy } AXOM_HOST_DEVICE - static inline IndexType selectedIndex(const BlendData & /*blend*/, IndexType index) + static inline IndexType selectedIndex(const BlendData & /*blend*/, + IndexType index) { return index; } diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 7fb25bef3a..3eae705d4f 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -14,9 +14,8 @@ namespace axom { namespace mir { - //-------------------------------------------------------------------------------- - /** +/** * \brief Calculates the percent overlap between the given circle and quad. * * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. @@ -31,14 +30,13 @@ namespace mir */ template -static axom::float64 calculatePercentOverlapMonteCarlo( - int gridSize, - const PointType& circleCenter, - axom::float64 circleRadius, - const PointType& quadP0, - const PointType& quadP1, - const PointType& quadP2, - const PointType& quadP3) +static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, + const PointType& circleCenter, + axom::float64 circleRadius, + const PointType& quadP0, + const PointType& quadP1, + const PointType& quadP2, + const PointType& quadP3) { // Check if any of the quad's corners are within the circle auto d0Sq = primal::squared_distance(quadP0, circleCenter); @@ -73,9 +71,8 @@ static axom::float64 calculatePercentOverlapMonteCarlo( { for(int x = 0; x < gridSize; ++x) { - PointType samplePoint = - PointType::make_point(delta_x * x + quadP1[0], - delta_y * y + quadP1[1]); + PointType samplePoint = PointType::make_point(delta_x * x + quadP1[0], + delta_y * y + quadP1[1]); if(primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } @@ -178,7 +175,7 @@ MIRMesh MeshTester::initTestCaseOne() } //-------------------------------------------------------------------------------- -void MeshTester::mesh3x3(conduit::Node &mesh) +void MeshTester::mesh3x3(conduit::Node& mesh) { // clang-format off mesh["coordsets/coords/type"] = "explicit"; @@ -205,7 +202,7 @@ void MeshTester::mesh3x3(conduit::Node &mesh) } //-------------------------------------------------------------------------------- -void MeshTester::initTestCaseOne(conduit::Node &mesh) +void MeshTester::initTestCaseOne(conduit::Node& mesh) { mesh3x3(mesh); @@ -343,7 +340,7 @@ mir::MIRMesh MeshTester::initTestCaseTwo() } //-------------------------------------------------------------------------------- -void MeshTester::initTestCaseTwo(conduit::Node &mesh) +void MeshTester::initTestCaseTwo(conduit::Node& mesh) { mesh3x3(mesh); @@ -467,7 +464,7 @@ mir::MIRMesh MeshTester::initTestCaseThree() return testMesh; } -void MeshTester::initTestCaseThree(conduit::Node &mesh) +void MeshTester::initTestCaseThree(conduit::Node& mesh) { // clang-format off mesh["coordsets/coords/type"] = "explicit"; @@ -636,8 +633,12 @@ mir::MIRMesh MeshTester::initTestCaseFour() //-------------------------------------------------------------------------------- template -static void addCircleMaterial(const TopoView &topoView, - const CoordsetView &coordsetView, conduit::Node &mesh, const mir::Point2 &circleCenter, axom::float64 circleRadius, int numSamples) +static void addCircleMaterial(const TopoView& topoView, + const CoordsetView& coordsetView, + conduit::Node& mesh, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + int numSamples) { constexpr int GREEN = 0; constexpr int BLUE = 1; @@ -653,18 +654,18 @@ static void addCircleMaterial(const TopoView &topoView, typename CoordsetView::PointType center; center[0] = circleCenter[0]; center[1] = circleCenter[1]; - topoView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - auto vf = calculatePercentOverlapMonteCarlo(numSamples, - center, - circleRadius, - coordsetView[zone.getId(0)], - coordsetView[zone.getId(1)], - coordsetView[zone.getId(2)], - coordsetView[zone.getId(3)]); - greenView[zoneIndex] = vf; - blueView[zoneIndex] = 1.0 - vf; - }); + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto& zone) { + auto vf = calculatePercentOverlapMonteCarlo(numSamples, + center, + circleRadius, + coordsetView[zone.getId(0)], + coordsetView[zone.getId(1)], + coordsetView[zone.getId(2)], + coordsetView[zone.getId(3)]); + greenView[zoneIndex] = vf; + blueView[zoneIndex] = 1.0 - vf; + }); // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; @@ -701,22 +702,30 @@ static void addCircleMaterial(const TopoView &topoView, } //-------------------------------------------------------------------------------- -void MeshTester::initTestCaseFour(conduit::Node &mesh) +void MeshTester::initTestCaseFour(conduit::Node& mesh) { mesh3x3(mesh); // Make views using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), - bputils::make_array_view(mesh["coordsets/coords/values/y"])); - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + CoordsetView coordsetView( + bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopoView topoView(bputils::make_array_view( + mesh["topologies/mesh/elements/connectivity"])); // Add material const auto circleCenter = mir::Point2::make_point(1.5, 1.5); const axom::float64 circleRadius = 1.25; const int numSamples = 100; - addCircleMaterial(topoView, coordsetView, mesh, circleCenter, circleRadius, numSamples); + addCircleMaterial(topoView, + coordsetView, + mesh, + circleCenter, + circleRadius, + numSamples); } //-------------------------------------------------------------------------------- @@ -786,24 +795,32 @@ mir::MIRMesh MeshTester::createUniformGridTestCaseMesh( } //-------------------------------------------------------------------------------- -void MeshTester::createUniformGridTestCaseMesh( - int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius, conduit::Node &mesh) +void MeshTester::createUniformGridTestCaseMesh(int gridSize, + const mir::Point2& circleCenter, + axom::float64 circleRadius, + conduit::Node& mesh) { // Generate the mesh generateGrid(gridSize, mesh); // Make views using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), - bputils::make_array_view(mesh["coordsets/coords/values/y"])); - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + CoordsetView coordsetView( + bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopoView topoView(bputils::make_array_view( + mesh["topologies/mesh/elements/connectivity"])); // Add material int numSamples = 100; - addCircleMaterial(topoView, coordsetView, mesh, circleCenter, circleRadius, numSamples); + addCircleMaterial(topoView, + coordsetView, + mesh, + circleCenter, + circleRadius, + numSamples); } //-------------------------------------------------------------------------------- @@ -921,7 +938,7 @@ mir::CellData MeshTester::generateGrid(int gridSize) return data; } -void MeshTester::generateGrid(int gridSize, conduit::Node &mesh) +void MeshTester::generateGrid(int gridSize, conduit::Node& mesh) { int nx = gridSize + 1; int ny = gridSize + 1; @@ -968,7 +985,7 @@ void MeshTester::generateGrid(int gridSize, conduit::Node &mesh) mesh["topologies/mesh/elements/offsets"].set(offsets); } -void MeshTester::generateGrid3D(int gridSize, conduit::Node &mesh) +void MeshTester::generateGrid3D(int gridSize, conduit::Node& mesh) { int nx = gridSize + 1; int ny = gridSize + 1; @@ -1173,76 +1190,82 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) } template -void addConcentricCircleMaterial(const TopoView &topoView, const CoordsetView &coordsetView, - const mir::Point2 &circleCenter, std::vector &circleRadii, int numSamples, conduit::Node &mesh) +void addConcentricCircleMaterial(const TopoView& topoView, + const CoordsetView& coordsetView, + const mir::Point2& circleCenter, + std::vector& circleRadii, + int numSamples, + conduit::Node& mesh) { - // Generate the element volume fractions with concentric circles int numMaterials = circleRadii.size() + 1; int defaultMaterialID = numMaterials - 1; // default material is always the last index // Initialize all material volume fractions to 0 - std::vector> materialVolumeFractionsData(numMaterials); + std::vector> materialVolumeFractionsData( + numMaterials); constexpr int MAXMATERIALS = 100; axom::StackArray, MAXMATERIALS> matvfViews; for(int i = 0; i < numMaterials; ++i) { const auto len = topoView.numberOfZones(); materialVolumeFractionsData[i].resize(len, 0.); - matvfViews[i] = axom::ArrayView(materialVolumeFractionsData[i].data(), len); + matvfViews[i] = + axom::ArrayView(materialVolumeFractionsData[i].data(), len); } - auto circleRadiiView = axom::ArrayView(circleRadii.data(), circleRadii.size()); + auto circleRadiiView = + axom::ArrayView(circleRadii.data(), circleRadii.size()); const int numCircles = circleRadii.size(); // Use the uniform sampling method to generate volume fractions for each material // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation - topoView. template for_all_zones(AXOM_LAMBDA(auto eID, const auto &zone) - { - auto v0 = coordsetView[zone.getId(0)]; - auto v1 = coordsetView[zone.getId(1)]; - auto v2 = coordsetView[zone.getId(2)]; - - // Run the uniform sampling to determine how much of the current cell is composed of each material - int materialCount[numMaterials]; - for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - - axom::float64 delta_x = - axom::utilities::abs(v1[0] - v0[0]) / (axom::float64)(numSamples - 1); - axom::float64 delta_y = - axom::utilities::abs(v2[1] - v1[1]) / (axom::float64)(numSamples - 1); - - for(int y = 0; y < numSamples; ++y) - { - for(int x = 0; x < numSamples; ++x) + topoView.template for_all_zones( + AXOM_LAMBDA(auto eID, const auto& zone) { + auto v0 = coordsetView[zone.getId(0)]; + auto v1 = coordsetView[zone.getId(1)]; + auto v2 = coordsetView[zone.getId(2)]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; + for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; + + axom::float64 delta_x = + axom::utilities::abs(v1[0] - v0[0]) / (axom::float64)(numSamples - 1); + axom::float64 delta_y = + axom::utilities::abs(v2[1] - v1[1]) / (axom::float64)(numSamples - 1); + + for(int y = 0; y < numSamples; ++y) { - mir::Point2 samplePoint = - mir::Point2::make_point(delta_x * x + v0[0], delta_y * y + v0[1]); - bool isPointSampled = false; - for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) + for(int x = 0; x < numSamples; ++x) { - const auto r = circleRadiiView[cID]; - if(primal::squared_distance(samplePoint, circleCenter) < r * r) + mir::Point2 samplePoint = + mir::Point2::make_point(delta_x * x + v0[0], delta_y * y + v0[1]); + bool isPointSampled = false; + for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) { - materialCount[cID]++; - isPointSampled = true; + const auto r = circleRadiiView[cID]; + if(primal::squared_distance(samplePoint, circleCenter) < r * r) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if(!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; } - } - if(!isPointSampled) - { - // The point was not within any of the circles, so increment the count for the default material - materialCount[defaultMaterialID]++; } } - } - // Assign the element volume fractions based on the count of the samples in each circle - for(int matID = 0; matID < numMaterials; ++matID) - { - matvfViews[matID][eID] = - materialCount[matID] / (axom::float64)(numSamples * numSamples); - } - }); + // Assign the element volume fractions based on the count of the samples in each circle + for(int matID = 0; matID < numMaterials; ++matID) + { + matvfViews[matID][eID] = + materialCount[matID] / (axom::float64)(numSamples * numSamples); + } + }); // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; @@ -1287,7 +1310,7 @@ void addConcentricCircleMaterial(const TopoView &topoView, const CoordsetView &c mesh["matsets/mat/indices"].set(indices); } -void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node &mesh) +void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& mesh) { // Generate the mesh topology generateGrid(gridSize, mesh); @@ -1316,13 +1339,21 @@ void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node &m // Make views using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), - bputils::make_array_view(mesh["coordsets/coords/values/y"])); - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + CoordsetView coordsetView( + bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopoView topoView(bputils::make_array_view( + mesh["topologies/mesh/elements/connectivity"])); // Add the material - addConcentricCircleMaterial(topoView, coordsetView, circleCenter, circleRadii, 100, mesh); + addConcentricCircleMaterial(topoView, + coordsetView, + circleCenter, + circleRadii, + 100, + mesh); } //-------------------------------------------------------------------------------- @@ -1397,7 +1428,7 @@ mir::MIRMesh MeshTester::initQuadClippingTestMesh() return testMesh; } -void MeshTester::initQuadClippingTestMesh(conduit::Node &mesh) +void MeshTester::initQuadClippingTestMesh(conduit::Node& mesh) { // Generate the mesh topology constexpr int gridSize = 3; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index ca590ef872..54cc746118 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -133,10 +133,10 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, - const mir::Point2& circleCenter, + const mir::Point2 &circleCenter, axom::float64 circleRadius); void createUniformGridTestCaseMesh(int gridSize, - const mir::Point2& circleCenter, + const mir::Point2 &circleCenter, axom::float64 circleRadius, conduit::Node &mesh); /** @@ -185,12 +185,12 @@ class MeshTester * * \return The number of corners of the quad that are within the circle. */ - int circleQuadCornersOverlaps(const mir::Point2& circleCenter, + int circleQuadCornersOverlaps(const mir::Point2 &circleCenter, axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3); + const mir::Point2 &quadP0, + const mir::Point2 &quadP1, + const mir::Point2 &quadP2, + const mir::Point2 &quadP3); }; } // namespace mir } // namespace axom diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index fbaafa01fd..db0537022f 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -27,7 +27,7 @@ std::string usageString() void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { -// SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); + // SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); // This is on purpose. while(1) ; @@ -71,29 +71,38 @@ int main(int argc, char **argv) // Make views (we know beforehand which types to make) using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView(bputils::make_array_view(mesh["coordsets/coords/values/x"]), - bputils::make_array_view(mesh["coordsets/coords/values/y"])); + CoordsetView coordsetView( + bputils::make_array_view(mesh["coordsets/coords/values/x"]), + bputils::make_array_view(mesh["coordsets/coords/values/y"])); - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopoView topoView(bputils::make_array_view( + mesh["topologies/mesh/elements/connectivity"])); constexpr int MAXMATERIALS = 20; auto materialInfo = axom::mir::views::materials(mesh["matsets/mat"]); if(materialInfo.size() >= MAXMATERIALS) { - SLIC_WARNING(axom::fmt::format("To use more than {} materials, recompile with larger MAXMATERIALS value.", MAXMATERIALS)); + SLIC_WARNING( + axom::fmt::format("To use more than {} materials, recompile with " + "larger MAXMATERIALS value.", + MAXMATERIALS)); return -4; } - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = + axom::mir::views::UnibufferMaterialView; MatsetView matsetView; - matsetView.set(bputils::make_array_view(mesh["matsets/mat/material_ids"]), - bputils::make_array_view(mesh["matsets/mat/volume_fractions"]), - bputils::make_array_view(mesh["matsets/mat/sizes"]), - bputils::make_array_view(mesh["matsets/mat/offsets"]), - bputils::make_array_view(mesh["matsets/mat/indices"])); - + matsetView.set( + bputils::make_array_view(mesh["matsets/mat/material_ids"]), + bputils::make_array_view(mesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(mesh["matsets/mat/sizes"]), + bputils::make_array_view(mesh["matsets/mat/offsets"]), + bputils::make_array_view(mesh["matsets/mat/indices"])); + // Begin material interface reconstruction - using MIR = axom::mir::EquiZAlgorithm; + using MIR = + axom::mir::EquiZAlgorithm; timer.start(); conduit::Node options, processedMesh; MIR m(topoView, coordsetView, matsetView); @@ -104,7 +113,9 @@ int main(int argc, char **argv) << timer.elapsedTimeInMilliSec() << " ms."); // Output results - conduit::relay::io::blueprint::save_mesh(processedMesh, outputFilePath, "hdf5"); + conduit::relay::io::blueprint::save_mesh(processedMesh, + outputFilePath, + "hdf5"); retval = 0; } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index eb438714f0..027a5968ca 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -491,9 +491,13 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif #if defined(DEBUGGING_TEST_CASES) - std::cout << "---------------------------------- input mesh -------------------------------------" << std::endl; + std::cout << "---------------------------------- input mesh " + "-------------------------------------" + << std::endl; printNode(hostMesh); - std::cout << "-----------------------------------------------------------------------------------" << std::endl; + std::cout << "---------------------------------------------------------------" + "--------------------" + << std::endl; #endif // Create views @@ -524,9 +528,13 @@ void braid2d_clip_test(const std::string &type, const std::string &name) axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); #if defined(DEBUGGING_TEST_CASES) - std::cout << "---------------------------------- clipped uniform ----------------------------------" << std::endl; + std::cout << "---------------------------------- clipped uniform " + "----------------------------------" + << std::endl; printNode(hostClipMesh); - std::cout << "-------------------------------------------------------------------------------------" << std::endl; + std::cout << "---------------------------------------------------------------" + "----------------------" + << std::endl; #endif // Handle baseline comparison. @@ -542,13 +550,16 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Now, take the clipped mesh and clip it again using a mixed topology view. using ExpCoordsetView = axom::mir::views::ExplicitCoordsetView; - const auto xView = bputils::make_array_view(deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x")); - const auto yView = bputils::make_array_view(deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y")); + const auto xView = bputils::make_array_view( + deviceClipMesh.fetch_existing("coordsets/clipcoords/values/x")); + const auto yView = bputils::make_array_view( + deviceClipMesh.fetch_existing("coordsets/clipcoords/values/y")); ExpCoordsetView expCoordsetView(xView, yView); conduit::Node &n_device_topo = deviceClipMesh.fetch_existing("topologies/" + clipTopoName); - const auto connView = bputils::make_array_view(n_device_topo.fetch_existing("elements/connectivity")); + const auto connView = bputils::make_array_view( + n_device_topo.fetch_existing("elements/connectivity")); options["clipField"] = "new_braid"; options["clipValue"] = 1.; @@ -560,9 +571,12 @@ void braid2d_clip_test(const std::string &type, const std::string &name) conduit::Node deviceClipMixedMesh; if(n_device_topo.fetch_existing("elements/shape").as_string() == "mixed") { - auto shapesView = bputils::make_array_view(n_device_topo.fetch_existing("elements/shapes")); - const auto sizesView = bputils::make_array_view(n_device_topo.fetch_existing("elements/sizes")); - const auto offsetsView = bputils::make_array_view(n_device_topo.fetch_existing("elements/offsets")); + auto shapesView = bputils::make_array_view( + n_device_topo.fetch_existing("elements/shapes")); + const auto sizesView = bputils::make_array_view( + n_device_topo.fetch_existing("elements/sizes")); + const auto offsetsView = bputils::make_array_view( + n_device_topo.fetch_existing("elements/offsets")); using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; @@ -580,8 +594,8 @@ void braid2d_clip_test(const std::string &type, const std::string &name) else { // Depending on optimizations, we might get a mesh with just quads. - using QuadTopoView = - axom::mir::views::UnstructuredTopologySingleShapeView>; + using QuadTopoView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; QuadTopoView quadTopoView(connView); // Clip the data @@ -595,9 +609,13 @@ void braid2d_clip_test(const std::string &type, const std::string &name) axom::mir::utilities::blueprint::copy(hostClipMixedMesh, deviceClipMixedMesh); #if defined(DEBUGGING_TEST_CASES) - std::cout << "---------------------------------- clipped mixed ----------------------------------" << std::endl; + std::cout << "---------------------------------- clipped mixed " + "----------------------------------" + << std::endl; printNode(hostClipMixedMesh); - std::cout << "-----------------------------------------------------------------------------------" << std::endl; + std::cout << "---------------------------------------------------------------" + "--------------------" + << std::endl; #endif // Handle baseline comparison. @@ -1014,59 +1032,81 @@ void point_merge_test() { conduit::Node hostMesh; hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set(std::vector{{0., 1., 2., 0., 1., 2., 0., 1., 2.}}); - hostMesh["coordsets/coords/values/y"].set(std::vector{{0., 0., 0., 1., 1., 1., 2., 2., 2.}}); + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 1., 2., 0., 1., 2., 0., 1., 2.}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 1., 1., 1., 2., 2., 2.}}); hostMesh["topologies/mesh/type"] = "unstructured"; hostMesh["topologies/mesh/coordset"] = "coords"; hostMesh["topologies/mesh/elements/shape"] = "quad"; - hostMesh["topologies/mesh/elements/connectivity"].set(std::vector{{0,1,4,3, 1,2,5,4, 3,4,7,6, 4,5,8,7}}); - hostMesh["topologies/mesh/elements/sizes"].set(std::vector{{4,4,4,4}}); - hostMesh["topologies/mesh/elements/offsets"].set(std::vector{{0,4,8,12}}); + hostMesh["topologies/mesh/elements/connectivity"].set( + std::vector {{0, 1, 4, 3, 1, 2, 5, 4, 3, 4, 7, 6, 4, 5, 8, 7}}); + hostMesh["topologies/mesh/elements/sizes"].set(std::vector {{4, 4, 4, 4}}); + hostMesh["topologies/mesh/elements/offsets"].set( + std::vector {{0, 4, 8, 12}}); hostMesh["fields/clip/topology"] = "mesh"; hostMesh["fields/clip/association"] = "vertex"; - hostMesh["fields/clip/values"].set(std::vector{{1., 1., 0.5, 1., 1., 0., 0.5, 0., 0.5}}); + hostMesh["fields/clip/values"].set( + std::vector {{1., 1., 0.5, 1., 1., 0., 0.5, 0., 0.5}}); conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); // Set up views for the mesh. using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView(bputils::make_array_view(deviceMesh.fetch_existing("coordsets/coords/values/x")), - bputils::make_array_view(deviceMesh.fetch_existing("coordsets/coords/values/y"))); + CoordsetView coordsetView( + bputils::make_array_view( + deviceMesh.fetch_existing("coordsets/coords/values/x")), + bputils::make_array_view( + deviceMesh.fetch_existing("coordsets/coords/values/y"))); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopologyView topologyView(bputils::make_array_view(deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"))); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopologyView topologyView(bputils::make_array_view( + deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"))); // Clip conduit::Node options, deviceClipMesh; options["clipField"] = "clip"; options["clipValue"] = 0.5; - using Clip = axom::mir::clipping::ClipField; + using Clip = + axom::mir::clipping::ClipField; Clip clip(topologyView, coordsetView); clip.execute(deviceMesh, options, deviceClipMesh); conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + axom::mir::utilities::blueprint::copy(hostClipMesh, + deviceClipMesh); //printNode(hostClipMesh); // Check that the points were merged when making the new mesh. - std::vector x{{2.0, 1.0, 2.0, 0.0, 1.5, 2.0, 1.0}}; - std::vector y{{1.0, 1.5, 2.0, 2.0, 1.0, 0.0, 2.0}}; + std::vector x {{2.0, 1.0, 2.0, 0.0, 1.5, 2.0, 1.0}}; + std::vector y {{1.0, 1.5, 2.0, 2.0, 1.0, 0.0, 2.0}}; EXPECT_EQ(x.size(), 7); EXPECT_EQ(y.size(), 7); for(size_t i = 0; i < x.size(); i++) { - EXPECT_FLOAT_EQ(hostClipMesh["coordsets/coords/values/x"].as_float_accessor()[i], x[i]); - EXPECT_FLOAT_EQ(hostClipMesh["coordsets/coords/values/y"].as_float_accessor()[i], y[i]); + EXPECT_FLOAT_EQ( + hostClipMesh["coordsets/coords/values/x"].as_float_accessor()[i], + x[i]); + EXPECT_FLOAT_EQ( + hostClipMesh["coordsets/coords/values/y"].as_float_accessor()[i], + y[i]); } // Check that the degenerate quads were turned into triangles. - std::vector shapes{{2, 2, 3, 2}}; - std::vector sizes{{3, 3, 4, 3}}; - std::vector offsets{{0, 4, 8, 12}}; - compare_values(shapes, hostClipMesh["topologies/mesh/elements/shapes"].as_int_accessor()); - compare_values(sizes, hostClipMesh["topologies/mesh/elements/sizes"].as_int_accessor()); - compare_values(offsets, hostClipMesh["topologies/mesh/elements/offsets"].as_int_accessor()); + std::vector shapes {{2, 2, 3, 2}}; + std::vector sizes {{3, 3, 4, 3}}; + std::vector offsets {{0, 4, 8, 12}}; + compare_values( + shapes, + hostClipMesh["topologies/mesh/elements/shapes"].as_int_accessor()); + compare_values( + sizes, + hostClipMesh["topologies/mesh/elements/sizes"].as_int_accessor()); + compare_values( + offsets, + hostClipMesh["topologies/mesh/elements/offsets"].as_int_accessor()); } TEST(mir_clipfield, pointmerging) From 0249309437a0a167d5d0c8ce253c853176452a1a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 28 Aug 2024 17:50:35 -0700 Subject: [PATCH 192/290] Add degenerate zone filtering. Enable multiple policies in concentric circles example. Enable old MIR too. --- src/axom/mir/ClipField.hpp | 336 +++++++++++++----- src/axom/mir/EquiZAlgorithm.hpp | 60 ++-- src/axom/mir/MeshTester.cpp | 4 +- .../mir/examples/mir_concentric_circles.cpp | 299 ++++++++++++---- .../UnstructuredTopologySingleShapeView.hpp | 36 +- .../views/dispatch_unstructured_topology.hpp | 19 +- 6 files changed, 539 insertions(+), 215 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 1eec68244a..370f94105c 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -28,6 +28,9 @@ #include #include +//#define AXOM_DEBUG_CLIP_FIELD +#define AXOM_CLIP_FILTER_DEGENERATES + namespace axom { namespace mir @@ -133,6 +136,33 @@ inline bool shapeIsSelected(unsigned char color, int selection) (color1Selected(selection) && color == COLOR1); } +template +inline AXOM_HOST_DEVICE int unique_count(const IdType *values, int n) +{ + IdType v[MAXSIZE]; + // Start off with one unique element + int nv = 1; + v[0] = values[0]; + // Scan the rest + for(int j = 1; j < n; j++) + { + int fi = -1; + for(int i = 0; i < nv; i++) + { + if(values[j] == v[i]) + { + fi = i; + break; + } + } + if(fi == -1) + { + v[nv++] = values[j]; + } + } + return nv; +} + #if defined(AXOM_DEBUG_CLIP_FIELD) template void printHost(const std::string &name, const ViewType &deviceView) @@ -795,14 +825,14 @@ class ClipField std::cout << "------------------------ computeSizes ------------------------" << std::endl; - detail::printHost("fragmentData.m_fragmentsView", + details::printHost("fragmentData.m_fragmentsView", fragmentData.m_fragmentsView); - detail::printHost("fragmentData.m_fragmentsSizeView", + details::printHost("fragmentData.m_fragmentsSizeView", fragmentData.m_fragmentsSizeView); - detail::printHost("blendGroupsView", blendGroupsView); - detail::printHost("blendGroupsLenView", blendGroupsLenView); - detail::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); - detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + details::printHost("blendGroupsView", blendGroupsView); + details::printHost("blendGroupsLenView", blendGroupsLenView); + details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); std::cout << "--------------------------------------------------------------" << std::endl; @@ -856,9 +886,9 @@ class ClipField std::cout << "------------------------ computeFragmentOffsets " "------------------------" << std::endl; - detail::printHost("fragmentData.m_fragmentOffsetsView", + details::printHost("fragmentData.m_fragmentOffsetsView", fragmentData.m_fragmentOffsetsView); - detail::printHost("fragmentData.m_fragmentSizeOffsetsView", + details::printHost("fragmentData.m_fragmentSizeOffsetsView", fragmentData.m_fragmentSizeOffsetsView); std::cout << "-------------------------------------------------------------" "-----------" @@ -1026,6 +1056,7 @@ class ClipField constexpr auto connTypeID = bputils::cpp2conduit::id; const auto selection = getSelection(opts); + AXOM_ANNOTATE_BEGIN("allocation"); n_newTopo.reset(); n_newTopo["type"] = "unstructured"; n_newTopo["coordset"] = n_newCoordset.name(); @@ -1067,11 +1098,13 @@ class ClipField n_color_values.set(conduit::DataType::int32(fragmentData.m_finalNumZones)); auto colorView = bputils::make_array_view(n_color_values); -#if defined(AXOM_DEBUG_CLIP_FIELD) - // Initialize the values beforehand. For debugging. + // Fill in connectivity values in case we leave empty slots later. axom::for_all( connView.size(), - AXOM_LAMBDA(auto index) { connView[index] = -1; }); + AXOM_LAMBDA(auto index) { connView[index] = 0; }); + +#if defined(AXOM_DEBUG_CLIP_FIELD) + // Initialize the values beforehand. For debugging. axom::for_all( shapesView.size(), AXOM_LAMBDA(auto index) { @@ -1081,6 +1114,7 @@ class ClipField colorView[index] = -5; }); #endif + AXOM_ANNOTATE_END("allocation"); // Here we fill in the new connectivity, sizes, shapes. // We get the node ids from the unique blend names, de-duplicating points when making the new connectivity. @@ -1090,85 +1124,210 @@ class ClipField // for EA-EL, N0-N3 to shrink the array to the point where it can fit in // memory available to the thread. // - m_topologyView.template for_selected_zones( - selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { - // If there are no fragments, return from lambda. - if(fragmentData.m_fragmentsView[szIndex] == 0) return; - - // Seek to the start of the blend groups for this zone. - auto groups = builder.blendGroupsForZone(szIndex); - - // Go through the points in the order they would have been added as blend - // groups, get their blendName, and then overall index of that blendName - // in uNames, the unique list of new dof names. That will be their index - // in the final points. - const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; - ConnectivityType point_2_new[N3 + 1]; - for(BitSet pid = N0; pid <= N3; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + RAJA::ReduceBitOr degenerates_reduce(0); +#endif + { + AXOM_ANNOTATE_SCOPE("build"); + m_topologyView.template for_selected_zones( + selectedZones.view(), + AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { + // If there are no fragments, return from lambda. + if(fragmentData.m_fragmentsView[szIndex] == 0) return; + + // Seek to the start of the blend groups for this zone. + auto groups = builder.blendGroupsForZone(szIndex); + + // Go through the points in the order they would have been added as blend + // groups, get their blendName, and then overall index of that blendName + // in uNames, the unique list of new dof names. That will be their index + // in the final points. + const BitSet ptused = zoneData.m_pointsUsedView[szIndex]; + ConnectivityType point_2_new[N3 + 1]; + for(BitSet pid = N0; pid <= N3; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - for(BitSet pid = P0; pid <= P7; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + for(BitSet pid = P0; pid <= P7; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - for(BitSet pid = EA; pid <= EL; pid++) - { - if(axom::utilities::bitIsSet(ptused, pid)) + for(BitSet pid = EA; pid <= EL; pid++) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); - groups++; + if(axom::utilities::bitIsSet(ptused, pid)) + { + point_2_new[pid] = groups.uniqueBlendGroupIndex(); + groups++; + } } - } - // This is where the output fragment connectivity start for this zone - int outputIndex = fragmentData.m_fragmentSizeOffsetsView[szIndex]; - // This is where the output fragment sizes/shapes start for this zone. - int sizeIndex = fragmentData.m_fragmentOffsetsView[szIndex]; - - // Iterate over the selected fragments and emit connectivity for them. - const auto clipcase = zoneData.m_clipCasesView[szIndex]; - const auto clipTableIndex = details::getClipTableIndex(zone.id()); - const auto ctView = clipTableViews[clipTableIndex]; - auto it = ctView.begin(clipcase); - const auto end = ctView.end(clipcase); - for(; it != end; it++) - { - // Get the current shape in the clip case. - const auto fragment = *it; - const auto fragmentShape = static_cast(fragment[0]); - - if(fragmentShape != ST_PNT) + // This is where the output fragment connectivity start for this zone + int outputIndex = fragmentData.m_fragmentSizeOffsetsView[szIndex]; + // This is where the output fragment sizes/shapes start for this zone. + int sizeIndex = fragmentData.m_fragmentOffsetsView[szIndex]; +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + bool degenerates = false; +#endif + // Iterate over the selected fragments and emit connectivity for them. + const auto clipcase = zoneData.m_clipCasesView[szIndex]; + const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto ctView = clipTableViews[clipTableIndex]; + auto it = ctView.begin(clipcase); + const auto end = ctView.end(clipcase); + for(; it != end; it++) { - if(details::shapeIsSelected(fragment[1], selection)) + // Get the current shape in the clip case. + const auto fragment = *it; + const auto fragmentShape = fragment[0]; + + if(fragmentShape != ST_PNT) { - // Output the nodes used in this zone. - for(int i = 2; i < fragment.size(); i++) - connView[outputIndex++] = point_2_new[fragment[i]]; - - const auto nIdsInFragment = fragment.size() - 2; - const auto shapeID = details::ST_Index_to_ShapeID(fragmentShape); - sizesView[sizeIndex] = nIdsInFragment; - shapesView[sizeIndex] = shapeID; - colorView[sizeIndex] = fragment[1] - COLOR0; - sizeIndex++; + if(details::shapeIsSelected(fragment[1], selection)) + { + // Output the nodes used in this zone. + const int fragmentSize = fragment.size(); +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + int connStart = outputIndex; +#endif + offsetsView[sizeIndex] = outputIndex; + for(int i = 2; i < fragmentSize; i++) + connView[outputIndex++] = point_2_new[fragment[i]]; + + const auto nIdsThisFragment = fragmentSize - 2; +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + // Check for degenerate + int nUniqueIds = details::unique_count(connView.data() + connStart, nIdsThisFragment); + bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); + degenerates |= thisFragmentDegenerate; + + // Rewind the outputIndex so we don't emit it in the connectivity. + if(thisFragmentDegenerate) + { + //std::cout << "degenerate " << szIndex << " {"; + //for(int i = 0; i < nIdsThisFragment; i++) + //{ + // std::cout << connView[connStart + i] << ", "; + //} + //std::cout << std::endl; + + outputIndex = connStart; + + // There is one less fragment than we're expecting in the output. + fragmentData.m_fragmentsView[szIndex] -= 1; + } + // Mark empty size. + sizesView[sizeIndex] = thisFragmentDegenerate ? 0 : nIdsThisFragment; +#else + sizesView[sizeIndex] = nIdsThisFragment; +#endif + shapesView[sizeIndex] = details::ST_Index_to_ShapeID(fragmentShape); + colorView[sizeIndex] = fragment[1] - COLOR0; + sizeIndex++; + } } } - } + +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + // Reduce overall whether there are degenerates. + degenerates_reduce |= degenerates; +#endif }); - // Reduce outside of the for_selected_zones loop. +#if defined(AXOM_DEBUG_CLIP_FIELD) + std::cout + << "------------------------ makeTopology ------------------------" + << std::endl; + std::cout << "degenerates_reduce=" << degenerates_reduce.get() << std::endl; +// details::printHost("selectedZones", selectedZones.view()); + details::printHost("m_fragmentsView", fragmentData.m_fragmentsView); +// details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); +// details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + details::printHost("conn", connView); + details::printHost("sizes", sizesView); + details::printHost("offsets", offsetsView); + details::printHost("shapes", shapesView); + details::printHost("color", colorView); + std::cout + << "--------------------------------------------------------------" + << std::endl; +#endif + } + +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + // We get into this block when degenerate zones were detected where + // all of their nodes are the same. We need to filter those out. + if(degenerates_reduce.get()) + { + AXOM_ANNOTATE_SCOPE("degenerates"); + + // There were degenerates so the expected number of fragments per zone (m_fragmentsView) + // was adjusted down. That means redoing the offsets. These need to be up + // to date to handle zonal fields later. + axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); + + // Use sizesView to make a mask that has 1's where size > 0. + axom::IndexType nz = fragmentData.m_finalNumZones; + axom::Array mask(nz, nz, axom::execution_space::allocatorID()); + axom::Array maskOffsets(nz, nz, axom::execution_space::allocatorID()); + auto maskView = mask.view(); + auto maskOffsetsView = maskOffsets.view(); + RAJA::ReduceSum mask_reduce(0); + axom::for_all(nz, AXOM_LAMBDA(auto index) + { + const int ival = (sizesView[index] > 0) ? 1 : 0; + maskView[index] = ival; + mask_reduce += ival; + }); + const axom::IndexType filteredZoneCount = mask_reduce.get(); + + // Make offsets + axom::exclusive_scan(maskView, maskOffsetsView); + + // Replace data in the input Conduit node with a denser version using the mask. + auto filter = [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) + { + using value_type = typename decltype(srcView)::value_type; + conduit::Node n_values; + n_values.set_allocator(conduitAllocatorID); + n_values.set(conduit::DataType(bputils::cpp2conduit::id, newSize)); + auto valuesView = bputils::make_array_view(n_values); + const auto nValues = maskView.size(); + axom::for_all(nValues, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + const auto destIndex = maskOffsetsView[index]; + valuesView[destIndex] = srcView[index]; + } + }); + + n_src.swap(n_values); + return bputils::make_array_view(n_src); + }; + + // Filter sizes, shapes, color using the mask + sizesView = filter(n_sizes, sizesView, filteredZoneCount); + offsetsView = filter(n_offsets, offsetsView, filteredZoneCount); + shapesView = filter(n_shapes, shapesView, filteredZoneCount); + colorView = filter(n_color_values, colorView, filteredZoneCount); + + // Record the filtered size. + fragmentData.m_finalNumZones = filteredZoneCount; + } +#endif + + // Figure out which shapes were used. BitSet shapesUsed {}; { + AXOM_ANNOTATE_SCOPE("shapesUsed"); RAJA::ReduceBitOr shapesUsed_reduce(0); axom::for_all( shapesView.size(), @@ -1184,14 +1343,15 @@ class ClipField std::cout << "------------------------ makeTopology ------------------------" << std::endl; - detail::printHost("selectedZones", selectedZones.view()); - detail::printHost("m_fragmentsView", fragmentData.m_fragmentsView); - detail::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); - detail::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); - detail::printHost("conn", connView); - detail::printHost("sizes", sizesView); - detail::printHost("shapes", shapesView); - detail::printHost("color", colorView); + details::printHost("selectedZones", selectedZones.view()); + details::printHost("m_fragmentsView", fragmentData.m_fragmentsView); + details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + details::printHost("conn", connView); + details::printHost("sizes", sizesView); + details::printHost("offsets", offsetsView); + details::printHost("shapes", shapesView); + details::printHost("color", colorView); std::cout << "--------------------------------------------------------------" << std::endl; @@ -1203,19 +1363,11 @@ class ClipField n_newFields.remove(opts.colorField()); } - // Make offsets - axom::exclusive_scan(sizesView, offsetsView); -#if defined(AXOM_DEBUG_CLIP_FIELD) - detail::printHost("offsets", offsetsView); - std::cout - << "--------------------------------------------------------------" - << std::endl; -#endif - #if 1 // Handle some quad->tri degeneracies if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) { + AXOM_ANNOTATE_SCOPE("quadtri"); const axom::IndexType numOutputZones = shapesView.size(); RAJA::ReduceBitOr shapesUsed_reduce(0); axom::for_all( diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index bf7849c90c..55f019c3a1 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -26,7 +26,7 @@ #endif // Uncomment to save inputs and outputs. -#define AXOM_DEBUG_EQUIZ +// #define AXOM_DEBUG_EQUIZ // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It @@ -51,26 +51,6 @@ using MaterialVFView = axom::ArrayView; constexpr static int NULL_MATERIAL = -1; constexpr static MaterialVF NULL_MATERIAL_VF = -1.f; -#if 0 -AXOM_HOST_DEVICE -inline bool zoneMatters(int zoneIndex) -{ - const int zones[] = {26, -33,34,35, -36,37,38,39,40,41, -49,50, -59,60, -69, -78,79}; - for(size_t i = 0; i < sizeof(zones)/sizeof(int); i++) - { - if(zones[i] == zoneIndex) - return true; - } - return false; -} -#endif - namespace detail { /** @@ -114,8 +94,6 @@ class MaterialIntersector int zoneMatID = m_zoneMatNumberView[zoneIndex]; if(zoneMatID != NULL_MATERIAL) backgroundIndex = matNumberToIndex(zoneMatID); - // Determine the matvf view index for the current material. - int currentIndex = matNumberToIndex(m_currentMaterial); axom::IndexType clipcase = 0; const auto n = nodeIdsView.size(); @@ -132,7 +110,7 @@ class MaterialIntersector #endif // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; - MaterialVF vf2 = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][nid] : 0; + MaterialVF vf2 = (m_currentMaterialIndex != INVALID_INDEX) ? m_matvfViews[m_currentMaterialIndex][nid] : 0; // clang-format on clipcase |= (vf2 > vf1) ? (1 << i) : 0; @@ -158,7 +136,6 @@ class MaterialIntersector backgroundIndex = matNumberToIndex(zoneMatID); // Determine the matvf view index for the current material. - int currentIndex = matNumberToIndex(m_currentMaterial); #if defined(AXOM_DEVICE_CODE) assert(id0 >= 0 && id0 < m_matvfViews[0].size()); assert(id1 >= 0 && id1 < m_matvfViews[0].size()); @@ -178,8 +155,8 @@ class MaterialIntersector // clang-format off vf1[0] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id0] : NULL_MATERIAL_VF; vf1[1] = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][id1] : NULL_MATERIAL_VF; - vf2[0] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id0] : 0; - vf2[1] = (currentIndex != INVALID_INDEX) ? m_matvfViews[currentIndex][id1] : 0; + vf2[0] = (m_currentMaterialIndex != INVALID_INDEX) ? m_matvfViews[m_currentMaterialIndex][id0] : 0; + vf2[1] = (m_currentMaterialIndex != INVALID_INDEX) ? m_matvfViews[m_currentMaterialIndex][id1] : 0; // clang-format on float numerator = vf2[0] - vf1[0]; @@ -204,7 +181,7 @@ class MaterialIntersector * \return The m_matNumbersView index on success; INVALID_INDEX on failure. */ AXOM_HOST_DEVICE - int matNumberToIndex(int matNumber) const + inline int matNumberToIndex(int matNumber) const { auto index = axom::mir::utilities::bsearch(matNumber, m_matNumbersView); return (index != -1) ? m_matIndicesView[index] : INVALID_INDEX; @@ -232,7 +209,11 @@ class MaterialIntersector m_zoneMatNumberView = zoneMatsView; } - void setCurrentMaterial(int matNumber) { m_currentMaterial = matNumber; } + void setCurrentMaterial(int matNumber, int matNumberIndex) + { + m_currentMaterial = matNumber; + m_currentMaterialIndex = matNumberIndex; + } axom::StaticArray m_matvfViews {}; //!< Array of volume fraction views @@ -240,6 +221,7 @@ class MaterialIntersector axom::ArrayView m_matIndicesView {}; //!< Array of indices into m_matvfViews for the material numbers. axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. int m_currentMaterial {}; //!< The current material. + int m_currentMaterialIndex {}; //!< The current material's index in the m_matvfViews. }; /** @@ -282,9 +264,9 @@ class MaterialIntersector m_view.setZoneMaterialID(zoneMatsView); } - void setCurrentMaterial(int matNumber) + void setCurrentMaterial(int matNumber, int matNumberIndex) { - m_view.setCurrentMaterial(matNumber); + m_view.setCurrentMaterial(matNumber, matNumberIndex); } /** @@ -860,6 +842,16 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm return matNumber[idx1] < matNumber[idx2]; }); std::sort(matNumber.begin(), matNumber.end()); + // Get the current material's index in the number:index map. + int currentMatIndex = 0; + for(axom::IndexType i = 0; i < matNumber.size(); i++) + { + if(matNumber[i] == currentMat.number) + { + currentMatIndex = matIndex[i]; + break; + } + } // Store the number:index map into the intersector. The number:index map lets us // ask for the field index for a material number, allowing scattered material @@ -868,11 +860,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm axom::copy(matIndexDevice.data(), matIndex.data(), sizeof(int) * nmats); intersector.setMaterialNumbers(matNumberDevice.view()); intersector.setMaterialIndices(matIndexDevice.view()); + intersector.setCurrentMaterial(currentMat.number, currentMatIndex); // Store the current zone material ids and current material number into the intersector. intersector.setZoneMaterialID(bputils::make_array_view( n_fields.fetch_existing(zonalMaterialIDName() + "/values"))); - intersector.setCurrentMaterial(currentMat.number); } //-------------------------------------------------------------------------- @@ -923,11 +915,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { AXOM_ANNOTATE_SCOPE("Update zonalMaterialID"); - const auto colorView = bputils::make_array_view( + const auto colorView = bputils::make_array_view( n_newFields.fetch_existing(colorField + "/values")); const auto nzonesNew = colorView.size(); - // Get zonalMAterialID field so we can make adjustments. + // Get zonalMaterialID field so we can make adjustments. conduit::Node &n_zonalMaterialID = n_newFields.fetch_existing(zonalMaterialIDName() + "/values"); auto zonalMaterialID = diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 3eae705d4f..22161b7258 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -965,7 +965,7 @@ void MeshTester::generateGrid(int gridSize, conduit::Node& mesh) { for(int i = 0; i < gridSize; i++) { - offsets.push_back(offsets.size()); + offsets.push_back(offsets.size() * 4); sizes.push_back(4); conn.push_back(j * nx + i); conn.push_back(j * nx + i + 1); @@ -1020,7 +1020,7 @@ void MeshTester::generateGrid3D(int gridSize, conduit::Node& mesh) { for(int i = 0; i < gridSize; i++) { - offsets.push_back(offsets.size()); + offsets.push_back(offsets.size() * 8); sizes.push_back(8); conn.push_back((k * nx * ny) + (j * nx) + i); conn.push_back((k * nx * ny) + (j * nx) + i + 1); diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index db0537022f..505fae8a44 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -16,117 +16,276 @@ namespace mir = axom::mir; namespace bputils = axom::mir::utilities::blueprint; -//-------------------------------------------------------------------------------- - -std::string usageString() -{ - return "Args are "; -} +using RuntimePolicy = axom::runtime_policy::Policy; //-------------------------------------------------------------------------------- - void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { - // SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); + SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); // This is on purpose. - while(1) - ; + //while(1) + // ; } //-------------------------------------------------------------------------------- +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); +} -int main(int argc, char **argv) +//-------------------------------------------------------------------------------- +template +int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit::Node &hostResult) { - axom::slic::SimpleLogger logger; // create & initialize test logger - axom::slic::setLoggingMsgLevel(axom::slic::message::Info); - conduit::utils::set_error_handler(conduit_debug_err_handler); -#if defined(AXOM_USE_CALIPER) - axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper("report"); -#endif + using namespace axom::mir::views; + AXOM_ANNOTATE_SCOPE("runMIR"); + SLIC_INFO(axom::fmt::format("Using policy {}", axom::execution_space::name())); - if(argc != 4) + // Check materials. + constexpr int MAXMATERIALS = 20; + auto materialInfo = materials(hostMesh["matsets/mat"]); + if(materialInfo.size() >= MAXMATERIALS) { - SLIC_WARNING("Incorrect number of args. " << usageString()); - return -1; + SLIC_WARNING( + axom::fmt::format("To use more than {} materials, recompile with " + "larger MAXMATERIALS value.", + MAXMATERIALS)); + return -4; } - int retval = 0; - try - { - // Parse the command line arguments - int gridSize = std::stoi(argv[1]); - int numCircles = std::stoi(argv[2]); - std::string outputFilePath = std::string(argv[3]); + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Make views (we know beforehand which types to make) + using CoordsetView = ExplicitCoordsetView; + CoordsetView coordsetView( + bputils::make_array_view(deviceMesh["coordsets/coords/values/x"]), + bputils::make_array_view(deviceMesh["coordsets/coords/values/y"])); + + using TopoView = UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"])); + + using MatsetView = UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + using MIR = axom::mir::EquiZAlgorithm; + MIR m(topoView, coordsetView, matsetView); + conduit::Node deviceResult; + m.execute(deviceMesh, options, deviceResult); + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +//-------------------------------------------------------------------------------- +int runMIR(RuntimePolicy policy, int gridSize, int numCircles, const std::string &outputFilePath) +{ // Initialize a mesh for testing MIR auto timer = axom::utilities::Timer(true); mir::MeshTester tester; conduit::Node mesh; - tester.initTestCaseFive(gridSize, numCircles, mesh); - mesh.print(); + { + AXOM_ANNOTATE_SCOPE("generate"); + tester.initTestCaseFive(gridSize, numCircles, mesh); + // printNode(mesh); + } timer.stop(); SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + // Output initial mesh. - conduit::relay::io::blueprint::save_mesh(mesh, "concentric_circles", "hdf5"); - - // Make views (we know beforehand which types to make) - using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView( - bputils::make_array_view(mesh["coordsets/coords/values/x"]), - bputils::make_array_view(mesh["coordsets/coords/values/y"])); - - using TopoView = axom::mir::views::UnstructuredTopologySingleShapeView< - axom::mir::views::QuadShape>; - TopoView topoView(bputils::make_array_view( - mesh["topologies/mesh/elements/connectivity"])); - - constexpr int MAXMATERIALS = 20; - auto materialInfo = axom::mir::views::materials(mesh["matsets/mat"]); - if(materialInfo.size() >= MAXMATERIALS) { - SLIC_WARNING( - axom::fmt::format("To use more than {} materials, recompile with " - "larger MAXMATERIALS value.", - MAXMATERIALS)); - return -4; + AXOM_ANNOTATE_SCOPE("save_input"); + conduit::relay::io::blueprint::save_mesh(mesh, "concentric_circles", "hdf5"); } - using MatsetView = - axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set( - bputils::make_array_view(mesh["matsets/mat/material_ids"]), - bputils::make_array_view(mesh["matsets/mat/volume_fractions"]), - bputils::make_array_view(mesh["matsets/mat/sizes"]), - bputils::make_array_view(mesh["matsets/mat/offsets"]), - bputils::make_array_view(mesh["matsets/mat/indices"])); + // Begin material interface reconstruction - using MIR = - axom::mir::EquiZAlgorithm; timer.start(); - conduit::Node options, processedMesh; - MIR m(topoView, coordsetView, matsetView); + conduit::Node options, resultMesh; options["matset"] = "mat"; - m.execute(mesh, options, processedMesh); + + int retval = 0; + if(policy == RuntimePolicy::seq) + { + retval = runMIR(mesh, options, resultMesh); + } +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) +#if defined(AXOM_USE_OPENMP) + else if(policy == RuntimePolicy::omp) + { + retval = runMIR(mesh, options, resultMesh); + } +#endif +#if defined(AXOM_USE_CUDA) + else if(policy == RuntimePolicy::cuda) + { + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + retval = runMIR(mesh, options, resultMesh); + } +#endif +#if defined(AXOM_USE_HIP) + else if(policy == RuntimePolicy::hip) + { + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + retval = runMIR(mesh, options, resultMesh); + } +#endif +#endif + else + { + retval = -1; + SLIC_ERROR("Unhandled policy."); + } + timer.stop(); + SLIC_INFO("Material interface reconstruction time: " + << timer.elapsedTimeInMilliSec() << " ms."); + + // Output results + { + AXOM_ANNOTATE_SCOPE("save_output"); + conduit::relay::io::blueprint::save_mesh(resultMesh, + outputFilePath, + "hdf5"); + } + + return retval; +} + +//-------------------------------------------------------------------------------- +int runMIROld(int gridSize, int numCircles, const std::string &outputFilePath) +{ + // Initialize a mesh for testing MIR + auto timer = axom::utilities::Timer(true); + mir::MeshTester tester; + mir::MIRMesh mesh; + { + AXOM_ANNOTATE_SCOPE("generate"); + mesh = tester.initTestCaseFive(gridSize, numCircles); + } + timer.stop(); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + + // Begin material interface reconstruction + timer.start(); + mir::MIRMesh outputMesh; + { + AXOM_ANNOTATE_SCOPE("runMIR"); + mir::InterfaceReconstructor m; + m.computeReconstructedInterface(mesh, outputMesh); + } timer.stop(); SLIC_INFO("Material interface reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); // Output results - conduit::relay::io::blueprint::save_mesh(processedMesh, - outputFilePath, - "hdf5"); + { + AXOM_ANNOTATE_SCOPE("save_output"); + outputMesh.writeMeshToFile(".", outputFilePath + ".vtk"); + } - retval = 0; + return 0; +} + +//-------------------------------------------------------------------------------- +int main(int argc, char **argv) +{ + axom::slic::SimpleLogger logger; // create & initialize test logger + axom::slic::setLoggingMsgLevel(axom::slic::message::Info); + + // Define command line options. + bool handler = true, old = false; + int gridSize = 5; + int numCircles = 2; + std::string outputFilePath("output"); + axom::CLI::App app; + app.add_flag("--handler", handler) + ->description("Install a custom error handler that loops forever.") + ->capture_default_str(); + app.add_flag("--old", old) + ->description("Use the old MIR method.") + ->capture_default_str(); + app.add_option("--gridsize", gridSize) + ->description("The number of zones along an axis."); + app.add_option("--numcircles", numCircles) + ->description("The number of circles to use for material creation."); + app.add_option("--output", outputFilePath) + ->description("The file path for output files"); + +#if defined(AXOM_USE_CALIPER) + std::string annotationMode("report"); + app.add_option("--caliper", annotationMode) + ->description( + "caliper annotation mode. Valid options include 'none' and 'report'. " + "Use 'help' to see full list.") + ->capture_default_str() + ->check(axom::utilities::ValidCaliperMode); +#endif + + RuntimePolicy policy {RuntimePolicy::seq}; + std::stringstream pol_sstr; + pol_sstr << "Set runtime policy for intersection-based sampling method."; +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) + pol_sstr << "\nSet to 'seq' or 0 to use the RAJA sequential policy."; + #ifdef AXOM_USE_OPENMP + pol_sstr << "\nSet to 'omp' or 1 to use the RAJA OpenMP policy."; + #endif + #ifdef AXOM_USE_CUDA + pol_sstr << "\nSet to 'cuda' or 2 to use the RAJA CUDA policy."; + #endif + #ifdef AXOM_USE_HIP + pol_sstr << "\nSet to 'hip' or 3 to use the RAJA HIP policy."; + #endif +#endif + app.add_option("-p, --policy", policy, pol_sstr.str()) + ->capture_default_str() + ->transform( + axom::CLI::CheckedTransformer(axom::runtime_policy::s_nameToPolicy)); + + // Parse command line options. + app.parse(argc, argv); + + if(handler) + { + conduit::utils::set_error_handler(conduit_debug_err_handler); + } +#if defined(AXOM_USE_CALIPER) + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper(annotationMode); +#endif + + int retval = 0; + try + { + if(old) + { + retval = runMIROld(gridSize, numCircles, outputFilePath); + } + else + { + retval = runMIR(policy, gridSize, numCircles, outputFilePath); + } } catch(std::invalid_argument const &e) { - SLIC_WARNING("Bad input. " << usageString()); + SLIC_WARNING("Bad input. " << e.what()); retval = -2; } catch(std::out_of_range const &e) { - SLIC_WARNING("Integer overflow. " << usageString()); + SLIC_WARNING("Integer overflow. " << e.what()); retval = -3; } return retval; diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 2ec9f6c45d..ad36a63b79 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -53,8 +53,6 @@ class UnstructuredTopologySingleShapeView , m_sizesView(sizes) , m_offsetsView(offsets) { - SLIC_ASSERT(m_sizesView.size() != 0); - SLIC_ASSERT(m_offsetsView.size() != 0); SLIC_ASSERT(m_offsetsView.size() == m_sizesView.size()); } @@ -99,23 +97,31 @@ class UnstructuredTopologySingleShapeView 0, nzones, AXOM_LAMBDA(auto zoneIndex) { - const ConnectivityView shapeDataView( + const ConnectivityView shapeIdsView( connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); - const ShapeType shape(shapeDataView); + const ShapeType shape(shapeIdsView); func(zoneIndex, shape); }); } else { + ConnectivityView sizesView(m_sizesView); + ConnectivityView offsetsView(m_offsetsView); axom::for_all( 0, nzones, AXOM_LAMBDA(auto zoneIndex) { - const ConnectivityView shapeDataView( - connectivityView.data() + ShapeType::zoneOffset(zoneIndex), - ShapeType::numberOfNodes()); - const ShapeType shape(shapeDataView); + ConnectivityView shapeIdsView {}; + if(sizesView.empty()) + { + shapeIdsView = ConnectivityView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + } + else + { + shapeIdsView = ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + } + const ShapeType shape(shapeIdsView); func(zoneIndex, shape); }); } @@ -155,14 +161,22 @@ class UnstructuredTopologySingleShapeView } else { + ConnectivityView sizesView(m_sizesView); + ConnectivityView offsetsView(m_offsetsView); axom::for_all( 0, nSelectedZones, AXOM_LAMBDA(auto selectIndex) { const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeIdsView( - connectivityView.data() + ShapeType::zoneOffset(zoneIndex), - ShapeType::numberOfNodes()); + ConnectivityView shapeIdsView {}; + if(sizesView.empty()) + { + shapeIdsView = ConnectivityView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + } + else + { + shapeIdsView = ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + } const ShapeType shape(shapeIdsView); func(selectIndex, zoneIndex, shape); }); diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index ecb8d8cf31..6cd4aaa156 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -215,11 +215,18 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, // TODO: points, lines + // Make sizes / offsets views if the values are present. + axom::ArrayView sizesView, offsetsView; + if(topo.has_path("elements/sizes")) + sizesView = bputils::make_array_view(topo.fetch_existing("elements/sizes")); + if(topo.has_path("elements/offsets")) + offsetsView = bputils::make_array_view(topo.fetch_existing("elements/offsets")); + if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) { if(eligible && shape == "tri") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } @@ -228,7 +235,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "quad") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } @@ -237,7 +244,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } @@ -247,7 +254,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, if(eligible && shape == "pyramid") { UnstructuredTopologySingleShapeView> ugView( - connView); + connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } @@ -256,7 +263,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "wedge") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } @@ -265,7 +272,7 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "hex") { - UnstructuredTopologySingleShapeView> ugView(connView); + UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); func(shape, ugView); eligible = false; } From da2d5479820f44060575248945541916dd87524a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 28 Aug 2024 17:51:50 -0700 Subject: [PATCH 193/290] make style --- src/axom/mir/ClipField.hpp | 95 +++++---- src/axom/mir/EquiZAlgorithm.hpp | 4 +- .../mir/examples/mir_concentric_circles.cpp | 199 +++++++++--------- .../UnstructuredTopologySingleShapeView.hpp | 16 +- .../views/dispatch_unstructured_topology.hpp | 35 ++- 5 files changed, 199 insertions(+), 150 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 370f94105c..00276ab1b9 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -826,9 +826,9 @@ class ClipField << "------------------------ computeSizes ------------------------" << std::endl; details::printHost("fragmentData.m_fragmentsView", - fragmentData.m_fragmentsView); + fragmentData.m_fragmentsView); details::printHost("fragmentData.m_fragmentsSizeView", - fragmentData.m_fragmentsSizeView); + fragmentData.m_fragmentsSizeView); details::printHost("blendGroupsView", blendGroupsView); details::printHost("blendGroupsLenView", blendGroupsLenView); details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); @@ -887,9 +887,9 @@ class ClipField "------------------------" << std::endl; details::printHost("fragmentData.m_fragmentOffsetsView", - fragmentData.m_fragmentOffsetsView); + fragmentData.m_fragmentOffsetsView); details::printHost("fragmentData.m_fragmentSizeOffsetsView", - fragmentData.m_fragmentSizeOffsetsView); + fragmentData.m_fragmentSizeOffsetsView); std::cout << "-------------------------------------------------------------" "-----------" << std::endl; @@ -1131,7 +1131,9 @@ class ClipField AXOM_ANNOTATE_SCOPE("build"); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { + AXOM_LAMBDA(auto szIndex, + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; @@ -1204,7 +1206,9 @@ class ClipField const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) // Check for degenerate - int nUniqueIds = details::unique_count(connView.data() + connStart, nIdsThisFragment); + int nUniqueIds = details::unique_count( + connView.data() + connStart, + nIdsThisFragment); bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); degenerates |= thisFragmentDegenerate; @@ -1224,11 +1228,13 @@ class ClipField fragmentData.m_fragmentsView[szIndex] -= 1; } // Mark empty size. - sizesView[sizeIndex] = thisFragmentDegenerate ? 0 : nIdsThisFragment; + sizesView[sizeIndex] = + thisFragmentDegenerate ? 0 : nIdsThisFragment; #else sizesView[sizeIndex] = nIdsThisFragment; #endif - shapesView[sizeIndex] = details::ST_Index_to_ShapeID(fragmentShape); + shapesView[sizeIndex] = + details::ST_Index_to_ShapeID(fragmentShape); colorView[sizeIndex] = fragment[1] - COLOR0; sizeIndex++; } @@ -1239,17 +1245,17 @@ class ClipField // Reduce overall whether there are degenerates. degenerates_reduce |= degenerates; #endif - }); + }); #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout << "------------------------ makeTopology ------------------------" << std::endl; std::cout << "degenerates_reduce=" << degenerates_reduce.get() << std::endl; -// details::printHost("selectedZones", selectedZones.view()); + // details::printHost("selectedZones", selectedZones.view()); details::printHost("m_fragmentsView", fragmentData.m_fragmentsView); -// details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); -// details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + // details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + // details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); details::printHost("conn", connView); details::printHost("sizes", sizesView); details::printHost("offsets", offsetsView); @@ -1271,47 +1277,56 @@ class ClipField // There were degenerates so the expected number of fragments per zone (m_fragmentsView) // was adjusted down. That means redoing the offsets. These need to be up // to date to handle zonal fields later. - axom::exclusive_scan(fragmentData.m_fragmentsView, fragmentData.m_fragmentOffsetsView); + axom::exclusive_scan(fragmentData.m_fragmentsView, + fragmentData.m_fragmentOffsetsView); // Use sizesView to make a mask that has 1's where size > 0. axom::IndexType nz = fragmentData.m_finalNumZones; - axom::Array mask(nz, nz, axom::execution_space::allocatorID()); - axom::Array maskOffsets(nz, nz, axom::execution_space::allocatorID()); + axom::Array mask(nz, + nz, + axom::execution_space::allocatorID()); + axom::Array maskOffsets( + nz, + nz, + axom::execution_space::allocatorID()); auto maskView = mask.view(); auto maskOffsetsView = maskOffsets.view(); RAJA::ReduceSum mask_reduce(0); - axom::for_all(nz, AXOM_LAMBDA(auto index) - { - const int ival = (sizesView[index] > 0) ? 1 : 0; - maskView[index] = ival; - mask_reduce += ival; - }); + axom::for_all( + nz, + AXOM_LAMBDA(auto index) { + const int ival = (sizesView[index] > 0) ? 1 : 0; + maskView[index] = ival; + mask_reduce += ival; + }); const axom::IndexType filteredZoneCount = mask_reduce.get(); // Make offsets axom::exclusive_scan(maskView, maskOffsetsView); // Replace data in the input Conduit node with a denser version using the mask. - auto filter = [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) - { - using value_type = typename decltype(srcView)::value_type; - conduit::Node n_values; - n_values.set_allocator(conduitAllocatorID); - n_values.set(conduit::DataType(bputils::cpp2conduit::id, newSize)); - auto valuesView = bputils::make_array_view(n_values); - const auto nValues = maskView.size(); - axom::for_all(nValues, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - const auto destIndex = maskOffsetsView[index]; - valuesView[destIndex] = srcView[index]; - } - }); + auto filter = + [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) { + using value_type = typename decltype(srcView)::value_type; + conduit::Node n_values; + n_values.set_allocator(conduitAllocatorID); + n_values.set( + conduit::DataType(bputils::cpp2conduit::id, newSize)); + auto valuesView = bputils::make_array_view(n_values); + const auto nValues = maskView.size(); + axom::for_all( + nValues, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + const auto destIndex = maskOffsetsView[index]; + valuesView[destIndex] = srcView[index]; + } + }); - n_src.swap(n_values); - return bputils::make_array_view(n_src); - }; + n_src.swap(n_values); + return bputils::make_array_view(n_src); + }; // Filter sizes, shapes, color using the mask sizesView = filter(n_sizes, sizesView, filteredZoneCount); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 55f019c3a1..57f3b6427f 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -134,7 +134,7 @@ class MaterialIntersector int zoneMatID = m_zoneMatNumberView[zoneIndex]; if(zoneMatID != NULL_MATERIAL) backgroundIndex = matNumberToIndex(zoneMatID); - // Determine the matvf view index for the current material. + // Determine the matvf view index for the current material. #if defined(AXOM_DEVICE_CODE) assert(id0 >= 0 && id0 < m_matvfViews[0].size()); @@ -221,7 +221,7 @@ class MaterialIntersector axom::ArrayView m_matIndicesView {}; //!< Array of indices into m_matvfViews for the material numbers. axom::ArrayView m_zoneMatNumberView {}; //!< Contains the current material number that owns each zone. int m_currentMaterial {}; //!< The current material. - int m_currentMaterialIndex {}; //!< The current material's index in the m_matvfViews. + int m_currentMaterialIndex {}; //!< The current material's index in the m_matvfViews. }; /** diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 505fae8a44..c07d1b99fc 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -21,7 +21,8 @@ using RuntimePolicy = axom::runtime_policy::Policy; //-------------------------------------------------------------------------------- void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { - SLIC_ERROR(axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); + SLIC_ERROR( + axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); // This is on purpose. //while(1) // ; @@ -38,11 +39,14 @@ void printNode(const conduit::Node &n) //-------------------------------------------------------------------------------- template -int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit::Node &hostResult) +int runMIR(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) { using namespace axom::mir::views; AXOM_ANNOTATE_SCOPE("runMIR"); - SLIC_INFO(axom::fmt::format("Using policy {}", axom::execution_space::name())); + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); // Check materials. constexpr int MAXMATERIALS = 20; @@ -67,7 +71,8 @@ int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit: bputils::make_array_view(deviceMesh["coordsets/coords/values/y"])); using TopoView = UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"])); + TopoView topoView(bputils::make_array_view( + deviceMesh["topologies/mesh/elements/connectivity"])); using MatsetView = UnibufferMaterialView; MatsetView matsetView; @@ -78,7 +83,8 @@ int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit: bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), bputils::make_array_view(deviceMesh["matsets/mat/indices"])); - using MIR = axom::mir::EquiZAlgorithm; + using MIR = + axom::mir::EquiZAlgorithm; MIR m(topoView, coordsetView, matsetView); conduit::Node deviceResult; m.execute(deviceMesh, options, deviceResult); @@ -90,77 +96,77 @@ int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit: } //-------------------------------------------------------------------------------- -int runMIR(RuntimePolicy policy, int gridSize, int numCircles, const std::string &outputFilePath) +int runMIR(RuntimePolicy policy, + int gridSize, + int numCircles, + const std::string &outputFilePath) { - // Initialize a mesh for testing MIR - auto timer = axom::utilities::Timer(true); - mir::MeshTester tester; - conduit::Node mesh; - { - AXOM_ANNOTATE_SCOPE("generate"); - tester.initTestCaseFive(gridSize, numCircles, mesh); - // printNode(mesh); - } - timer.stop(); - SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); - - // Output initial mesh. - { - AXOM_ANNOTATE_SCOPE("save_input"); - conduit::relay::io::blueprint::save_mesh(mesh, "concentric_circles", "hdf5"); - } + // Initialize a mesh for testing MIR + auto timer = axom::utilities::Timer(true); + mir::MeshTester tester; + conduit::Node mesh; + { + AXOM_ANNOTATE_SCOPE("generate"); + tester.initTestCaseFive(gridSize, numCircles, mesh); + // printNode(mesh); + } + timer.stop(); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + // Output initial mesh. + { + AXOM_ANNOTATE_SCOPE("save_input"); + conduit::relay::io::blueprint::save_mesh(mesh, "concentric_circles", "hdf5"); + } - // Begin material interface reconstruction - timer.start(); - conduit::Node options, resultMesh; - options["matset"] = "mat"; + // Begin material interface reconstruction + timer.start(); + conduit::Node options, resultMesh; + options["matset"] = "mat"; - int retval = 0; - if(policy == RuntimePolicy::seq) - { - retval = runMIR(mesh, options, resultMesh); - } + int retval = 0; + if(policy == RuntimePolicy::seq) + { + retval = runMIR(mesh, options, resultMesh); + } #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) -#if defined(AXOM_USE_OPENMP) - else if(policy == RuntimePolicy::omp) - { - retval = runMIR(mesh, options, resultMesh); - } -#endif -#if defined(AXOM_USE_CUDA) - else if(policy == RuntimePolicy::cuda) - { - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - retval = runMIR(mesh, options, resultMesh); - } -#endif -#if defined(AXOM_USE_HIP) - else if(policy == RuntimePolicy::hip) - { - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - retval = runMIR(mesh, options, resultMesh); - } -#endif + #if defined(AXOM_USE_OPENMP) + else if(policy == RuntimePolicy::omp) + { + retval = runMIR(mesh, options, resultMesh); + } + #endif + #if defined(AXOM_USE_CUDA) + else if(policy == RuntimePolicy::cuda) + { + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + retval = runMIR(mesh, options, resultMesh); + } + #endif + #if defined(AXOM_USE_HIP) + else if(policy == RuntimePolicy::hip) + { + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + retval = runMIR(mesh, options, resultMesh); + } + #endif #endif - else - { - retval = -1; - SLIC_ERROR("Unhandled policy."); - } - timer.stop(); - SLIC_INFO("Material interface reconstruction time: " - << timer.elapsedTimeInMilliSec() << " ms."); + else + { + retval = -1; + SLIC_ERROR("Unhandled policy."); + } + timer.stop(); + SLIC_INFO("Material interface reconstruction time: " + << timer.elapsedTimeInMilliSec() << " ms."); - // Output results - { - AXOM_ANNOTATE_SCOPE("save_output"); - conduit::relay::io::blueprint::save_mesh(resultMesh, - outputFilePath, - "hdf5"); - } + // Output results + { + AXOM_ANNOTATE_SCOPE("save_output"); + conduit::relay::io::blueprint::save_mesh(resultMesh, outputFilePath, "hdf5"); + } return retval; } @@ -168,34 +174,34 @@ int runMIR(RuntimePolicy policy, int gridSize, int numCircles, const std::string //-------------------------------------------------------------------------------- int runMIROld(int gridSize, int numCircles, const std::string &outputFilePath) { - // Initialize a mesh for testing MIR - auto timer = axom::utilities::Timer(true); - mir::MeshTester tester; - mir::MIRMesh mesh; - { - AXOM_ANNOTATE_SCOPE("generate"); - mesh = tester.initTestCaseFive(gridSize, numCircles); - } - timer.stop(); - SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); + // Initialize a mesh for testing MIR + auto timer = axom::utilities::Timer(true); + mir::MeshTester tester; + mir::MIRMesh mesh; + { + AXOM_ANNOTATE_SCOPE("generate"); + mesh = tester.initTestCaseFive(gridSize, numCircles); + } + timer.stop(); + SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); - // Begin material interface reconstruction - timer.start(); - mir::MIRMesh outputMesh; - { - AXOM_ANNOTATE_SCOPE("runMIR"); - mir::InterfaceReconstructor m; - m.computeReconstructedInterface(mesh, outputMesh); - } - timer.stop(); - SLIC_INFO("Material interface reconstruction time: " - << timer.elapsedTimeInMilliSec() << " ms."); + // Begin material interface reconstruction + timer.start(); + mir::MIRMesh outputMesh; + { + AXOM_ANNOTATE_SCOPE("runMIR"); + mir::InterfaceReconstructor m; + m.computeReconstructedInterface(mesh, outputMesh); + } + timer.stop(); + SLIC_INFO("Material interface reconstruction time: " + << timer.elapsedTimeInMilliSec() << " ms."); - // Output results - { - AXOM_ANNOTATE_SCOPE("save_output"); - outputMesh.writeMeshToFile(".", outputFilePath + ".vtk"); - } + // Output results + { + AXOM_ANNOTATE_SCOPE("save_output"); + outputMesh.writeMeshToFile(".", outputFilePath + ".vtk"); + } return 0; } @@ -263,7 +269,8 @@ int main(int argc, char **argv) conduit::utils::set_error_handler(conduit_debug_err_handler); } #if defined(AXOM_USE_CALIPER) - axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper(annotationMode); + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper( + annotationMode); #endif int retval = 0; diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index ad36a63b79..0934fd2c68 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -115,11 +115,15 @@ class UnstructuredTopologySingleShapeView ConnectivityView shapeIdsView {}; if(sizesView.empty()) { - shapeIdsView = ConnectivityView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + shapeIdsView = ConnectivityView( + connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); } else { - shapeIdsView = ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + shapeIdsView = + ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], + sizesView[zoneIndex]); } const ShapeType shape(shapeIdsView); func(zoneIndex, shape); @@ -171,11 +175,15 @@ class UnstructuredTopologySingleShapeView ConnectivityView shapeIdsView {}; if(sizesView.empty()) { - shapeIdsView = ConnectivityView(connectivityView.data() + ShapeType::zoneOffset(zoneIndex), ShapeType::numberOfNodes()); + shapeIdsView = ConnectivityView( + connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); } else { - shapeIdsView = ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); + shapeIdsView = + ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], + sizesView[zoneIndex]); } const ShapeType shape(shapeIdsView); func(selectIndex, zoneIndex, shape); diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 6cd4aaa156..53d3e7fd24 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -218,15 +218,20 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, // Make sizes / offsets views if the values are present. axom::ArrayView sizesView, offsetsView; if(topo.has_path("elements/sizes")) - sizesView = bputils::make_array_view(topo.fetch_existing("elements/sizes")); + sizesView = bputils::make_array_view( + topo.fetch_existing("elements/sizes")); if(topo.has_path("elements/offsets")) - offsetsView = bputils::make_array_view(topo.fetch_existing("elements/offsets")); + offsetsView = bputils::make_array_view( + topo.fetch_existing("elements/offsets")); if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) { if(eligible && shape == "tri") { - UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -235,7 +240,10 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "quad") { - UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -244,7 +252,10 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -254,7 +265,9 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, if(eligible && shape == "pyramid") { UnstructuredTopologySingleShapeView> ugView( - connView, sizesView, offsetsView); + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -263,7 +276,10 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "wedge") { - UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -272,7 +288,10 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, { if(eligible && shape == "hex") { - UnstructuredTopologySingleShapeView> ugView(connView, sizesView, offsetsView); + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } From f58b7bc631cbbcec009b95f3647499ee29a28fb4 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 28 Aug 2024 18:28:02 -0700 Subject: [PATCH 194/290] More timings --- src/axom/mir/EquiZAlgorithm.hpp | 80 +++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 57f3b6427f..86ce22e682 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -598,6 +598,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make a node to zone relation so we know for each node, which zones it touches. conduit::Node relation; { + AXOM_ANNOTATE_SCOPE("relation"); bputils::NodeToZoneRelationBuilder rb; rb.execute(n_topo, n_coordset, relation); //printNode(relation); @@ -610,44 +611,57 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make nodal VFs for each mixed material. const auto nzones = m_topologyView.numberOfZones(); const auto nnodes = m_coordsetView.numberOfNodes(); - for(const auto &mat : mixedMats) { - const int matNumber = mat.number; - const std::string zonalName = zonalFieldName(matNumber); - conduit::Node &n_zonalField = n_fields[zonalName]; - n_zonalField["topology"] = n_topo.name(); - n_zonalField["association"] = "element"; - n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_zonalField["values"].set( - conduit::DataType(bputils::cpp2conduit::id, nzones)); - auto zonalFieldView = - bputils::make_array_view(n_zonalField["values"]); - - // Fill the zonal field from the matset. - MatsetView deviceMatsetView(m_matsetView); - axom::for_all( - m_topologyView.numberOfZones(), - AXOM_LAMBDA(auto zoneIndex) { - typename MatsetView::FloatType vf {}; - deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf); - zonalFieldView[zoneIndex] = static_cast(vf); - }); + AXOM_ANNOTATE_SCOPE("zonal"); + for(const auto &mat : mixedMats) + { + const int matNumber = mat.number; + const std::string zonalName = zonalFieldName(matNumber); + conduit::Node &n_zonalField = n_fields[zonalName]; + n_zonalField["topology"] = n_topo.name(); + n_zonalField["association"] = "element"; + n_zonalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_zonalField["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nzones)); + auto zonalFieldView = + bputils::make_array_view(n_zonalField["values"]); + + // Fill the zonal field from the matset. + MatsetView deviceMatsetView(m_matsetView); + axom::for_all( + m_topologyView.numberOfZones(), + AXOM_LAMBDA(auto zoneIndex) { + typename MatsetView::FloatType vf {}; + deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf); + zonalFieldView[zoneIndex] = static_cast(vf); + }); + } + } - // Make a nodal field for the current material by recentering. - const std::string nodalName = nodalFieldName(matNumber); - conduit::Node &n_nodalField = n_fields[nodalName]; - n_nodalField["topology"] = n_topo.name(); - n_nodalField["association"] = "vertex"; - n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); - n_nodalField["values"].set( - conduit::DataType(bputils::cpp2conduit::id, nnodes)); - bputils::RecenterField z2n; - z2n.execute(n_zonalField, relation, n_nodalField); + { + AXOM_ANNOTATE_SCOPE("recenter"); + for(const auto &mat : mixedMats) + { + const int matNumber = mat.number; + const std::string zonalName = zonalFieldName(matNumber); + conduit::Node &n_zonalField = n_fields[zonalName]; + + // Make a nodal field for the current material by recentering. + const std::string nodalName = nodalFieldName(matNumber); + conduit::Node &n_nodalField = n_fields[nodalName]; + n_nodalField["topology"] = n_topo.name(); + n_nodalField["association"] = "vertex"; + n_nodalField["values"].set_allocator(c2a.getConduitAllocatorID()); + n_nodalField["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nnodes)); + bputils::RecenterField z2n; + z2n.execute(n_zonalField, relation, n_nodalField); #if !defined(AXOM_DEBUG_EQUIZ) - // Remove the zonal field that we don't normally need (unless we're debugging). - n_fields.remove(zonalName); + // Remove the zonal field that we don't normally need (unless we're debugging). + n_fields.remove(zonalName); #endif + } } } From 4d86bb487c577c884dcf7e853b2b64f2eea5c613 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 29 Aug 2024 15:37:40 -0700 Subject: [PATCH 195/290] Sorting --- src/axom/core/CMakeLists.txt | 1 + src/axom/core/utilities/Sorting.hpp | 211 ++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 src/axom/core/utilities/Sorting.hpp diff --git a/src/axom/core/CMakeLists.txt b/src/axom/core/CMakeLists.txt index 5d0ba6defe..f479ffd982 100644 --- a/src/axom/core/CMakeLists.txt +++ b/src/axom/core/CMakeLists.txt @@ -27,6 +27,7 @@ set(core_headers utilities/CommandLineUtilities.hpp utilities/FileUtilities.hpp utilities/RAII.hpp + utilities/Sorting.hpp utilities/StringUtilities.hpp utilities/System.hpp utilities/Timer.hpp diff --git a/src/axom/core/utilities/Sorting.hpp b/src/axom/core/utilities/Sorting.hpp new file mode 100644 index 0000000000..c74eaa5ccc --- /dev/null +++ b/src/axom/core/utilities/Sorting.hpp @@ -0,0 +1,211 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_CORE_UTILITIES_SORTING_HPP +#define AXOM_CORE_UTILITIES_SORTING_HPP +#include +#include + +namespace axom +{ +namespace utilities +{ + +/** + * \brief This is a template suitable for sorting small arrays on device. + * + * \tparam T The data type being sorted. + * \tparam N The biggest array size that will be used. + * + * \note For very short arrays, a simpler sort is faster. As the array size + * increases, the algorithm switches to qsort. Also, this is designed as + * a template class so it can be specialized. + */ +template ::max()> +class Sorting +{ +public: + /** + * \brief Sort an array of values in place. + * + * \param[inout] values The array of values to sort. + * \param n The number of values in the array. + */ + AXOM_HOST_DEVICE + static void sort(T *values, int n) + { + if(n < 11) + bubble_sort(values, n); + else + qsort(values, n); + } + +private: + /** + * \brief Computes stack size for qsort. + * \return A number of elements for an array-based stack. + */ + constexpr static int stack_size() + { + int v = N; + int i = 0; + while (v > 0) + { + i++; + v /= 2; + } + return i * 2; + } + + /** + * \brief Sort the input array using qsort. + * + * \param[inout] values The array to be sorted. + * \param n The number of values in the array. + */ + AXOM_HOST_DEVICE + static void qsort(T *values, int n) + { + if(n <= 1) + return; + int stack[stack_size()][2]; + int stack_count = 1; + stack[0][0] = 0; + stack[0][1] = n - 1; + + while(stack_count > 0) + { + stack_count--; + int low = stack[stack_count][0]; + int high = stack[stack_count][1]; + + if(low < high) + { + int pivot_index = partition(values, low, high); + + stack[stack_count][0] = low; + stack[stack_count][1] = pivot_index - 1; + stack_count++; + + stack[stack_count][0] = pivot_index + 1; + stack[stack_count][1] = high; + stack_count++; + } + } + } + + /** + * \brief Create a partition for qsort. + * + * \param[inout] values The array to be sorted. + * \param low The lower bound of the input partition. + * \param high The upper bound of the input partition. + * + * \return A new pivot. + */ + AXOM_HOST_DEVICE + static int partition(T *values, int low, int high) + { + int pivot = values[high]; + int i = low - 1; + for(int j = low; j < high; j++) + { + if(values[j] <= pivot) + { + i++; + axom::utilities::swap(values[i], values[j]); + } + } + axom::utilities::swap(values[i+1], values[high]); + return i + 1; + } + + /** + * \brief Sort the input array using bubble sort. + * + * \param[inout] values The array to be sorted. + * \param n The number of values in the array. + */ + AXOM_HOST_DEVICE + static void bubble_sort(T *values, int n) + { + for(int i = 0; i < n - 1; i++) + { + const int m = n - i - 1; + for(int j = 0; j < m; j++) + { + if(values[j] > values[j + 1]) + { + axom::utilities::swap(values[j], values[j + 1]); + } + } + } + } +}; + +/** + * \brief Swap 2 elements if b < a. + * param a The first value. + * param b The second value. + */ +template +AXOM_HOST_DEVICE +inline void ifswap(T& a, T& b) +{ + if(b < a) + { + T tmp = a; + a = b; + b = tmp; + } +} + +/** + * \brief Template specialization for sorting arrays with 3 elements. + */ +template +struct Sorting +{ + /** + * \brief Sort the input array. + * + * \param[inout] values The array to be sorted. + */ + AXOM_HOST_DEVICE + inline static void sort(T *values) + { + ifswap(values[0], values[1]); + ifswap(values[1], values[2]); + ifswap(values[0], values[1]); + } +}; + +/** + * \brief Template specialization for sorting arrays with 4 elements. + */ +template +struct Sorting +{ + /** + * \brief Sort the input array. + * + * \param[inout] values The array to be sorted. + */ + AXOM_HOST_DEVICE + inline static void sort(T* values) + { + ifswap(values[0], values[1]); + ifswap(values[2], values[3]); + ifswap(values[1], values[2]); + ifswap(values[0], values[1]); + ifswap(values[2], values[3]); + ifswap(values[1], values[2]); + } +}; + +} // end namespace utilities +} // end namespace axom + +#endif From 5b74ac2e776bc2fad29b1b8b3b37cbf265cf5a25 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 29 Aug 2024 15:54:30 -0700 Subject: [PATCH 196/290] timers --- src/axom/mir/examples/mir_concentric_circles.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index c07d1b99fc..c4ea482221 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -44,7 +44,6 @@ int runMIR(const conduit::Node &hostMesh, conduit::Node &hostResult) { using namespace axom::mir::views; - AXOM_ANNOTATE_SCOPE("runMIR"); SLIC_INFO(axom::fmt::format("Using policy {}", axom::execution_space::name())); @@ -64,6 +63,7 @@ int runMIR(const conduit::Node &hostMesh, conduit::Node deviceMesh; bputils::copy(deviceMesh, hostMesh); + AXOM_ANNOTATE_BEGIN("runMIR"); // Make views (we know beforehand which types to make) using CoordsetView = ExplicitCoordsetView; CoordsetView coordsetView( @@ -89,6 +89,8 @@ int runMIR(const conduit::Node &hostMesh, conduit::Node deviceResult; m.execute(deviceMesh, options, deviceResult); + AXOM_ANNOTATE_END("runMIR"); + // device->host bputils::copy(hostResult, deviceResult); From 3c319fcfafb591ffcdfce51c7e443b0797e068bf Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 29 Aug 2024 17:06:10 -0700 Subject: [PATCH 197/290] Change to naming policy - could be faster in 3D --- src/axom/core/utilities/Sorting.hpp | 2 +- src/axom/mir/BlendGroupBuilder.hpp | 51 ++++--- src/axom/mir/ClipField.hpp | 36 +++-- src/axom/mir/tests/mir_clipfield.cpp | 4 +- src/axom/mir/utilities.hpp | 211 +++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 33 deletions(-) diff --git a/src/axom/core/utilities/Sorting.hpp b/src/axom/core/utilities/Sorting.hpp index c74eaa5ccc..985910cbbd 100644 --- a/src/axom/core/utilities/Sorting.hpp +++ b/src/axom/core/utilities/Sorting.hpp @@ -108,7 +108,7 @@ class Sorting AXOM_HOST_DEVICE static int partition(T *values, int low, int high) { - int pivot = values[high]; + const T pivot = values[high]; int i = low - 1; for(int j = low; j < high; j++) { diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index 4faa3b2992..f272d9a989 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -18,15 +18,15 @@ namespace clipping * \brief This class encapsulates the logic for building blend groups. * * \tparam ExecSpace The execution space where the algorithm will run. - * \tparam NamingPolicy The naming policy used to create names from node ids. + * \tparam NamingPolicyView The type of a view that can access functionality in the naming policy that is used to make nodeids. * * \note The class only contains views to data so it can be copied into lambdas. */ -template +template class BlendGroupBuilder { public: - using KeyType = typename NamingPolicy::KeyType; + using KeyType = typename NamingPolicyView::KeyType; /** * \brief This struct holds the views that represent data for blend groups. @@ -34,21 +34,22 @@ class BlendGroupBuilder struct State { // clang-format off - IndexType m_nzones; - - axom::ArrayView m_blendGroupsView; // Number of blend groups in each zone. - axom::ArrayView m_blendGroupsLenView; // total size of blend group data for each zone. - axom::ArrayView m_blendOffsetView; // The offset of each zone's blend groups. - axom::ArrayView m_blendGroupOffsetsView; // Start of each zone's blend group data. - - axom::ArrayView m_blendNamesView; // Blend group names - axom::ArrayView m_blendGroupSizesView; // Size of individual blend group. - axom::ArrayView m_blendGroupStartView; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. - axom::ArrayView m_blendIdsView; // blend group ids. - axom::ArrayView m_blendCoeffView; // blend group weights. - - axom::ArrayView m_blendUniqueNamesView; // Unique names of blend groups. - axom::ArrayView m_blendUniqueIndicesView; // Indices of the unique names in blend group definitions. + IndexType m_nzones {}; + NamingPolicyView m_namingView {}; + + axom::ArrayView m_blendGroupsView {}; // Number of blend groups in each zone. + axom::ArrayView m_blendGroupsLenView {}; // total size of blend group data for each zone. + axom::ArrayView m_blendOffsetView {}; // The offset of each zone's blend groups. + axom::ArrayView m_blendGroupOffsetsView {}; // Start of each zone's blend group data. + + axom::ArrayView m_blendNamesView {}; // Blend group names + axom::ArrayView m_blendGroupSizesView {}; // Size of individual blend group. + axom::ArrayView m_blendGroupStartView {}; // Start of individual blend group's data in m_blendIdsView/m_blendCoeffView. + axom::ArrayView m_blendIdsView {}; // blend group ids. + axom::ArrayView m_blendCoeffView {}; // blend group weights. + + axom::ArrayView m_blendUniqueNamesView {}; // Unique names of blend groups. + axom::ArrayView m_blendUniqueIndicesView {}; // Indices of the unique names in blend group definitions. // clang-format on }; @@ -59,6 +60,16 @@ class BlendGroupBuilder State &state() { return m_state; } const State &state() const { return m_state; } + /** + * \brief Provide a hint to the naming policy view so it can do narrowing. + * + * \param nnodes The number of nodes in the input mesh. + */ + void setNamingPolicy(const NamingPolicyView &view) + { + m_state.m_namingView = view; + } + /** * \brief Set the number of zones. * @@ -226,9 +237,7 @@ class BlendGroupBuilder m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; // Store "name" of blend group. - KeyType blendName = - NamingPolicy::makeName(m_state->m_blendIdsView.data() + m_startOffset, - numIds); + KeyType blendName = m_state->m_namingView.makeName(m_state->m_blendIdsView.data() + m_startOffset, numIds); m_state->m_blendNamesView[m_blendGroupId] = blendName; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 00276ab1b9..abfa73f978 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -334,7 +334,7 @@ template , - typename NamingPolicy = axom::mir::utilities::HashNaming> + typename NamingPolicy = axom::mir::utilities::HashNaming > class ClipField { public: @@ -348,7 +348,7 @@ class ClipField using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; using ConnectivityType = typename TopologyView::ConnectivityType; - using BlendGroupBuilderType = BlendGroupBuilder; + using BlendGroupBuilderType = BlendGroupBuilder; using ClipFieldType = float; /** @@ -365,8 +365,19 @@ class ClipField , m_coordsetView(coordsetView) , m_intersector(intersector) , m_clipTables() + , m_naming() { } + /** + * \brief Allow the user to pass in a NamingPolicy to use when making blend group names. + * + * \param naming A new naming policy object. + */ + void setNamingPolicy(NamingPolicy &naming) + { + m_naming = naming; + } + /** * \brief Execute the clipping operation using the data stored in the specified \a clipField. * @@ -491,8 +502,12 @@ class ClipField allocatorID); // Start of zone's blend group offsets in definitions. AXOM_ANNOTATE_END("allocation"); + // Make sure the naming policy knows the number of nodes. + m_naming.setMaxId(m_coordsetView.numberOfNodes()); + // Make an object to help manage building the blend groups. BlendGroupBuilderType builder; + builder.setNamingPolicy(m_naming.view()); builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets @@ -523,10 +538,10 @@ class ClipField // Make the blend groups. builder.setBlendViews(blendNames.view(), - blendGroupSizes.view(), - blendGroupStart.view(), - blendIds.view(), - blendCoeff.view()); + blendGroupSizes.view(), + blendGroupStart.view(), + blendIds.view(), + blendCoeff.view()); AXOM_ANNOTATE_END("allocation2"); makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones); @@ -1689,10 +1704,11 @@ class ClipField } private: - TopologyView m_topologyView; - CoordsetView m_coordsetView; - Intersector m_intersector; - axom::mir::clipping::ClipTableManager m_clipTables; + TopologyView m_topologyView {}; + CoordsetView m_coordsetView {}; + Intersector m_intersector {}; + axom::mir::clipping::ClipTableManager m_clipTables {}; + NamingPolicy m_naming {}; }; } // end namespace clipping diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 027a5968ca..900f1fce2a 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -218,7 +218,9 @@ TEST(mir_clipfield, blend_group_builder) axom::Array blendUniqueNames {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}; axom::Array blendUniqueIndices {{1, 2, 9, 3, 4, 11, 0, 5, 6, 7}}; - axom::mir::clipping::BlendGroupBuilder builder; + using NamingPolicyView = typename axom::mir::utilities::HashNaming::View; + + axom::mir::clipping::BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); builder.setBlendGroupOffsets(blendOffsets.view(), blendGroupOffsets.view()); builder.setBlendViews(blendNames.view(), diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 3dcc48057a..6a5654b5a3 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -262,6 +262,7 @@ AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, } //------------------------------------------------------------------------------ +#if 0 /** * \brief This class implements a naming policy that uses some hashing functions * to produce a "name" for an array of ids. @@ -301,6 +302,216 @@ class HashNaming return name; } }; +#else +template +class HashNaming +{ +public: + using KeyType = std::uint64_t; + using IndexType = IndexT; + + // The top 2 bits are reserved for the key type. + constexpr static KeyType KeyIDSingle = 0; + constexpr static KeyType KeyIDPair = KeyType(1) << 62; + constexpr static KeyType KeyIDPack = KeyType(2) << 62; + constexpr static KeyType KeyIDHash = KeyType(3) << 62; + + // The rest of the bits can be divided in various ways. + constexpr static KeyType KeyMask = KeyType(3) << 62; + constexpr static KeyType PayloadMask = ~KeyMask; + constexpr static KeyType Max15Bit = (KeyType(1) << 15) - 1; + constexpr static KeyType Max16Bit = (KeyType(1) << 16) - 1; + constexpr static KeyType Max20Bit = (KeyType(1) << 20) - 1; + constexpr static KeyType Max31Bit = (KeyType(1) << 31) - 1; + constexpr static KeyType Max32Bit = (KeyType(1) << 32) - 1; + + class View + { + public: + using KeyType = HashNaming::KeyType; + + AXOM_HOST_DEVICE + KeyType makeName(const IndexType* p, int n) const + { + KeyType retval{}; + if (n == 1) + retval = make_name_1(p[0]); + else if (n == 2) + retval = make_name_2(p[0], p[1]); + else + retval = make_name_n(p, n); + return retval; + } + + AXOM_HOST_DEVICE + void setMaxId(IndexType m) + { + m_maxId = static_cast(m); + } + + private: + AXOM_HOST_DEVICE + inline KeyType make_name_1(IndexType p0) const + { + assert(p0 < PayloadMask); + // Store p0 in the key as a 62-bit integer + KeyType k0 = (static_cast(p0) & PayloadMask); + return KeyIDSingle | k0; + } + + AXOM_HOST_DEVICE + inline KeyType make_name_2(IndexType p0, IndexType p1) const + { + assert(p0 <= Max31Bit && p1 <= Max31Bit); + // Store p0 and p1 both in the 64-bit key as 31-bit integers + KeyType k0 = (static_cast(std::min(p0, p1)) & Max31Bit); + KeyType k1 = (static_cast(std::max(p0, p1)) & Max31Bit); + return KeyIDPair | (k0 << 31) | k1; + } + + AXOM_HOST_DEVICE + KeyType make_name_n(const IndexType *p, int n) const + { + KeyType retval {}; + if(n == 3 && m_maxId <= Max20Bit) + { + // We can pack 3 values into the id lossless + IndexType sorted[3]; + sorted[0] = p[0]; + sorted[1] = p[1]; + sorted[2] = p[2]; + axom::utilities::Sorting::sort(sorted); + + KeyType k0 = static_cast(sorted[0]) & Max20Bit; + KeyType k1 = static_cast(sorted[1]) & Max20Bit; + KeyType k2 = static_cast(sorted[2]) & Max20Bit; + constexpr KeyType len = KeyType(3 - 1) << 60; + retval = KeyIDPack | len | (k0 << 40) | (k1 << 20) | k2; + } + else if (n == 4 && m_maxId <= Max15Bit) + { + // We can pack 4 values into the id lossless + IndexType sorted[4]; + sorted[0] = p[0]; + sorted[1] = p[1]; + sorted[2] = p[2]; + sorted[3] = p[3]; + axom::utilities::Sorting::sort(sorted); + + KeyType k0 = static_cast(sorted[0]) & Max15Bit; + KeyType k1 = static_cast(sorted[1]) & Max15Bit; + KeyType k2 = static_cast(sorted[2]) & Max15Bit; + KeyType k3 = static_cast(sorted[3]) & Max15Bit; + constexpr KeyType len = KeyType(4 - 1) << 60; + retval = KeyIDPack | len | (k0 << 45) | (k1 << 30) | (k2 << 15) | k3; + } + else if(m_maxId < Max16Bit) + { + // Narrow to 16-bit, sort + std::uint16_t sorted[MAXIDS]; + for(int i = 0; i < n; i++) + sorted[i] = static_cast(p[i]); + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the narrowed ids + void *ptr = static_cast(sorted); + KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n << 1); + retval = KeyIDHash | (k0 & PayloadMask); + } + else if (m_maxId < Max32Bit) + { + // Narrow to 32-bit, sort + std::uint32_t sorted[MAXIDS]; + for (int i = 0; i < n; i++) + sorted[i] = static_cast(p[i]); + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the narrowed ids + void *ptr = static_cast(sorted); + KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n << 2); + retval = KeyIDHash | (k0 & PayloadMask); + } + else + { + IndexType sorted[MAXIDS]; + for (int i = 0; i < n; i++) + sorted[i] = p[i]; + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the ids + void *ptr = static_cast(sorted); + KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n * sizeof(IndexType)); + retval = KeyIDHash | (k0 & PayloadMask); + } + return retval; + } + + KeyType m_maxId {}; + }; + + // Host-callable methods + + KeyType makeName(const IndexType* p, int n) const + { + return m_view.makeName(p, n); + } + + void setMaxId(IndexType n) + { + m_view.setMaxId(n); + } + + View view() + { + return m_view; + } + + static std::string toString(KeyType key) + { + std::stringstream ss; + auto kt = key & KeyMask; + if(kt == KeyIDSingle) + { + auto id = key & PayloadMask; + ss << "single("<< std::hex << id << ")"; + } + else if(kt == KeyIDPair) + { + auto payload = key & PayloadMask; + auto p0 = (payload >> 31) & Max31Bit; + auto p1 = payload & Max31Bit; + ss << "pair(" << std::hex << p0 << ", " << p1 << ")"; + } + else if(kt == KeyIDHash) + { + ss << "hash(" << std::hex << key << ")"; + } + else if(kt == KeyIDPack) + { + auto npts = ((key >> 60) & 3) + 1; + if(npts == 3) + { + auto p0 = (key >> 40) & Max20Bit; + auto p1 = (key >> 20) & Max20Bit; + auto p2 = (key) & Max20Bit; + ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ")"; + } + else if(npts == 4) + { + auto p0 = (key >> 45) & Max15Bit; + auto p1 = (key >> 30) & Max15Bit; + auto p2 = (key >> 15) & Max15Bit; + auto p3 = (key) & Max15Bit; + ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ", " << p3 << ")"; + } + } + return ss.str(); + } + + View m_view {}; +}; + +#endif //------------------------------------------------------------------------------ /** From 28493facb299c49d802aeac0a988f4800dfbee5e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 29 Aug 2024 17:08:29 -0700 Subject: [PATCH 198/290] make style --- src/axom/core/utilities/Sorting.hpp | 17 +-- src/axom/mir/BlendGroupBuilder.hpp | 6 +- src/axom/mir/ClipField.hpp | 18 ++- src/axom/mir/tests/mir_clipfield.cpp | 3 +- src/axom/mir/utilities.hpp | 191 +++++++++++++-------------- 5 files changed, 114 insertions(+), 121 deletions(-) diff --git a/src/axom/core/utilities/Sorting.hpp b/src/axom/core/utilities/Sorting.hpp index 985910cbbd..5fee2d4a32 100644 --- a/src/axom/core/utilities/Sorting.hpp +++ b/src/axom/core/utilities/Sorting.hpp @@ -12,7 +12,6 @@ namespace axom { namespace utilities { - /** * \brief This is a template suitable for sorting small arrays on device. * @@ -51,7 +50,7 @@ class Sorting { int v = N; int i = 0; - while (v > 0) + while(v > 0) { i++; v /= 2; @@ -68,8 +67,7 @@ class Sorting AXOM_HOST_DEVICE static void qsort(T *values, int n) { - if(n <= 1) - return; + if(n <= 1) return; int stack[stack_size()][2]; int stack_count = 1; stack[0][0] = 0; @@ -118,7 +116,7 @@ class Sorting axom::utilities::swap(values[i], values[j]); } } - axom::utilities::swap(values[i+1], values[high]); + axom::utilities::swap(values[i + 1], values[high]); return i + 1; } @@ -151,8 +149,7 @@ class Sorting * param b The second value. */ template -AXOM_HOST_DEVICE -inline void ifswap(T& a, T& b) +AXOM_HOST_DEVICE inline void ifswap(T &a, T &b) { if(b < a) { @@ -194,7 +191,7 @@ struct Sorting * \param[inout] values The array to be sorted. */ AXOM_HOST_DEVICE - inline static void sort(T* values) + inline static void sort(T *values) { ifswap(values[0], values[1]); ifswap(values[2], values[3]); @@ -205,7 +202,7 @@ struct Sorting } }; -} // end namespace utilities -} // end namespace axom +} // end namespace utilities +} // end namespace axom #endif diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index f272d9a989..cb9661c2af 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -67,7 +67,7 @@ class BlendGroupBuilder */ void setNamingPolicy(const NamingPolicyView &view) { - m_state.m_namingView = view; + m_state.m_namingView = view; } /** @@ -237,7 +237,9 @@ class BlendGroupBuilder m_state->m_blendGroupSizesView[m_blendGroupId] = numIds; // Store "name" of blend group. - KeyType blendName = m_state->m_namingView.makeName(m_state->m_blendIdsView.data() + m_startOffset, numIds); + KeyType blendName = m_state->m_namingView.makeName( + m_state->m_blendIdsView.data() + m_startOffset, + numIds); m_state->m_blendNamesView[m_blendGroupId] = blendName; #if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index abfa73f978..77ea5b069f 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -334,7 +334,7 @@ template , - typename NamingPolicy = axom::mir::utilities::HashNaming > + typename NamingPolicy = axom::mir::utilities::HashNaming> class ClipField { public: @@ -348,7 +348,8 @@ class ClipField using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; using ConnectivityType = typename TopologyView::ConnectivityType; - using BlendGroupBuilderType = BlendGroupBuilder; + using BlendGroupBuilderType = + BlendGroupBuilder; using ClipFieldType = float; /** @@ -373,10 +374,7 @@ class ClipField * * \param naming A new naming policy object. */ - void setNamingPolicy(NamingPolicy &naming) - { - m_naming = naming; - } + void setNamingPolicy(NamingPolicy &naming) { m_naming = naming; } /** * \brief Execute the clipping operation using the data stored in the specified \a clipField. @@ -538,10 +536,10 @@ class ClipField // Make the blend groups. builder.setBlendViews(blendNames.view(), - blendGroupSizes.view(), - blendGroupStart.view(), - blendIds.view(), - blendCoeff.view()); + blendGroupSizes.view(), + blendGroupStart.view(), + blendIds.view(), + blendCoeff.view()); AXOM_ANNOTATE_END("allocation2"); makeBlendGroups(clipTableViews, builder, zoneData, opts, selectedZones); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 900f1fce2a..9f41cdb005 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -218,7 +218,8 @@ TEST(mir_clipfield, blend_group_builder) axom::Array blendUniqueNames {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}}; axom::Array blendUniqueIndices {{1, 2, 9, 3, 4, 11, 0, 5, 6, 7}}; - using NamingPolicyView = typename axom::mir::utilities::HashNaming::View; + using NamingPolicyView = + typename axom::mir::utilities::HashNaming::View; axom::mir::clipping::BlendGroupBuilder builder; builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 6a5654b5a3..e372343d52 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -331,12 +331,12 @@ class HashNaming using KeyType = HashNaming::KeyType; AXOM_HOST_DEVICE - KeyType makeName(const IndexType* p, int n) const + KeyType makeName(const IndexType *p, int n) const { - KeyType retval{}; - if (n == 1) + KeyType retval {}; + if(n == 1) retval = make_name_1(p[0]); - else if (n == 2) + else if(n == 2) retval = make_name_2(p[0], p[1]); else retval = make_name_n(p, n); @@ -344,10 +344,7 @@ class HashNaming } AXOM_HOST_DEVICE - void setMaxId(IndexType m) - { - m_maxId = static_cast(m); - } + void setMaxId(IndexType m) { m_maxId = static_cast(m); } private: AXOM_HOST_DEVICE @@ -366,9 +363,9 @@ class HashNaming // Store p0 and p1 both in the 64-bit key as 31-bit integers KeyType k0 = (static_cast(std::min(p0, p1)) & Max31Bit); KeyType k1 = (static_cast(std::max(p0, p1)) & Max31Bit); - return KeyIDPair | (k0 << 31) | k1; + return KeyIDPair | (k0 << 31) | k1; } - + AXOM_HOST_DEVICE KeyType make_name_n(const IndexType *p, int n) const { @@ -388,60 +385,63 @@ class HashNaming constexpr KeyType len = KeyType(3 - 1) << 60; retval = KeyIDPack | len | (k0 << 40) | (k1 << 20) | k2; } - else if (n == 4 && m_maxId <= Max15Bit) + else if(n == 4 && m_maxId <= Max15Bit) { - // We can pack 4 values into the id lossless - IndexType sorted[4]; - sorted[0] = p[0]; - sorted[1] = p[1]; - sorted[2] = p[2]; - sorted[3] = p[3]; - axom::utilities::Sorting::sort(sorted); - - KeyType k0 = static_cast(sorted[0]) & Max15Bit; - KeyType k1 = static_cast(sorted[1]) & Max15Bit; - KeyType k2 = static_cast(sorted[2]) & Max15Bit; - KeyType k3 = static_cast(sorted[3]) & Max15Bit; - constexpr KeyType len = KeyType(4 - 1) << 60; - retval = KeyIDPack | len | (k0 << 45) | (k1 << 30) | (k2 << 15) | k3; + // We can pack 4 values into the id lossless + IndexType sorted[4]; + sorted[0] = p[0]; + sorted[1] = p[1]; + sorted[2] = p[2]; + sorted[3] = p[3]; + axom::utilities::Sorting::sort(sorted); + + KeyType k0 = static_cast(sorted[0]) & Max15Bit; + KeyType k1 = static_cast(sorted[1]) & Max15Bit; + KeyType k2 = static_cast(sorted[2]) & Max15Bit; + KeyType k3 = static_cast(sorted[3]) & Max15Bit; + constexpr KeyType len = KeyType(4 - 1) << 60; + retval = KeyIDPack | len | (k0 << 45) | (k1 << 30) | (k2 << 15) | k3; } else if(m_maxId < Max16Bit) { - // Narrow to 16-bit, sort - std::uint16_t sorted[MAXIDS]; - for(int i = 0; i < n; i++) - sorted[i] = static_cast(p[i]); - axom::utilities::Sorting::sort(sorted, n); - - // Make a hash from the narrowed ids - void *ptr = static_cast(sorted); - KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n << 1); - retval = KeyIDHash | (k0 & PayloadMask); + // Narrow to 16-bit, sort + std::uint16_t sorted[MAXIDS]; + for(int i = 0; i < n; i++) sorted[i] = static_cast(p[i]); + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the narrowed ids + void *ptr = static_cast(sorted); + KeyType k0 = + axom::mir::utilities::hash_bytes(static_cast(ptr), + n << 1); + retval = KeyIDHash | (k0 & PayloadMask); } - else if (m_maxId < Max32Bit) + else if(m_maxId < Max32Bit) { - // Narrow to 32-bit, sort - std::uint32_t sorted[MAXIDS]; - for (int i = 0; i < n; i++) - sorted[i] = static_cast(p[i]); - axom::utilities::Sorting::sort(sorted, n); - - // Make a hash from the narrowed ids - void *ptr = static_cast(sorted); - KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n << 2); - retval = KeyIDHash | (k0 & PayloadMask); + // Narrow to 32-bit, sort + std::uint32_t sorted[MAXIDS]; + for(int i = 0; i < n; i++) sorted[i] = static_cast(p[i]); + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the narrowed ids + void *ptr = static_cast(sorted); + KeyType k0 = + axom::mir::utilities::hash_bytes(static_cast(ptr), + n << 2); + retval = KeyIDHash | (k0 & PayloadMask); } else { - IndexType sorted[MAXIDS]; - for (int i = 0; i < n; i++) - sorted[i] = p[i]; - axom::utilities::Sorting::sort(sorted, n); - - // Make a hash from the ids - void *ptr = static_cast(sorted); - KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), n * sizeof(IndexType)); - retval = KeyIDHash | (k0 & PayloadMask); + IndexType sorted[MAXIDS]; + for(int i = 0; i < n; i++) sorted[i] = p[i]; + axom::utilities::Sorting::sort(sorted, n); + + // Make a hash from the ids + void *ptr = static_cast(sorted); + KeyType k0 = + axom::mir::utilities::hash_bytes(static_cast(ptr), + n * sizeof(IndexType)); + retval = KeyIDHash | (k0 & PayloadMask); } return retval; } @@ -451,61 +451,56 @@ class HashNaming // Host-callable methods - KeyType makeName(const IndexType* p, int n) const + KeyType makeName(const IndexType *p, int n) const { return m_view.makeName(p, n); } - void setMaxId(IndexType n) - { - m_view.setMaxId(n); - } + void setMaxId(IndexType n) { m_view.setMaxId(n); } - View view() - { - return m_view; - } + View view() { return m_view; } static std::string toString(KeyType key) { - std::stringstream ss; - auto kt = key & KeyMask; - if(kt == KeyIDSingle) - { - auto id = key & PayloadMask; - ss << "single("<< std::hex << id << ")"; - } - else if(kt == KeyIDPair) - { - auto payload = key & PayloadMask; - auto p0 = (payload >> 31) & Max31Bit; - auto p1 = payload & Max31Bit; - ss << "pair(" << std::hex << p0 << ", " << p1 << ")"; - } - else if(kt == KeyIDHash) + std::stringstream ss; + auto kt = key & KeyMask; + if(kt == KeyIDSingle) + { + auto id = key & PayloadMask; + ss << "single(" << std::hex << id << ")"; + } + else if(kt == KeyIDPair) + { + auto payload = key & PayloadMask; + auto p0 = (payload >> 31) & Max31Bit; + auto p1 = payload & Max31Bit; + ss << "pair(" << std::hex << p0 << ", " << p1 << ")"; + } + else if(kt == KeyIDHash) + { + ss << "hash(" << std::hex << key << ")"; + } + else if(kt == KeyIDPack) + { + auto npts = ((key >> 60) & 3) + 1; + if(npts == 3) { - ss << "hash(" << std::hex << key << ")"; + auto p0 = (key >> 40) & Max20Bit; + auto p1 = (key >> 20) & Max20Bit; + auto p2 = (key)&Max20Bit; + ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ")"; } - else if(kt == KeyIDPack) + else if(npts == 4) { - auto npts = ((key >> 60) & 3) + 1; - if(npts == 3) - { - auto p0 = (key >> 40) & Max20Bit; - auto p1 = (key >> 20) & Max20Bit; - auto p2 = (key) & Max20Bit; - ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ")"; - } - else if(npts == 4) - { - auto p0 = (key >> 45) & Max15Bit; - auto p1 = (key >> 30) & Max15Bit; - auto p2 = (key >> 15) & Max15Bit; - auto p3 = (key) & Max15Bit; - ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ", " << p3 << ")"; - } + auto p0 = (key >> 45) & Max15Bit; + auto p1 = (key >> 30) & Max15Bit; + auto p2 = (key >> 15) & Max15Bit; + auto p3 = (key)&Max15Bit; + ss << "pack(" << std::hex << p0 << ", " << p1 << ", " << p2 << ", " + << p3 << ")"; } - return ss.str(); + } + return ss.str(); } View m_view {}; From 2fbe41829e89194bdb58a4b9395b51f3b72a3752 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 3 Sep 2024 12:19:14 -0700 Subject: [PATCH 199/290] Changes to unique and n2z relation builder. --- src/axom/mir/ClipField.hpp | 269 ++++++++-------- src/axom/mir/NodeToZoneRelationBuilder.hpp | 109 ++++--- src/axom/mir/tests/mir_clipfield.cpp | 21 +- src/axom/mir/utilities.hpp | 339 +++++++++------------ 4 files changed, 367 insertions(+), 371 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 77ea5b069f..804a57056a 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -548,9 +548,9 @@ class ClipField axom::Array uIndices; { AXOM_ANNOTATE_SCOPE("unique"); - axom::mir::utilities::unique(builder.blendNames(), - uNames, - uIndices); + axom::mir::utilities::Unique::execute(builder.blendNames(), + uNames, + uIndices); builder.setUniqueNames(uNames.view(), uIndices.view()); } bputils::BlendData blend = builder.makeBlendData(); @@ -1209,40 +1209,49 @@ class ClipField { // Output the nodes used in this zone. const int fragmentSize = fragment.size(); -#if defined(AXOM_CLIP_FILTER_DEGENERATES) - int connStart = outputIndex; -#endif +//#if defined(AXOM_CLIP_FILTER_DEGENERATES) +// int connStart = outputIndex; +//#endif offsetsView[sizeIndex] = outputIndex; for(int i = 2; i < fragmentSize; i++) connView[outputIndex++] = point_2_new[fragment[i]]; const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) - // Check for degenerate - int nUniqueIds = details::unique_count( - connView.data() + connStart, - nIdsThisFragment); - bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); - degenerates |= thisFragmentDegenerate; - - // Rewind the outputIndex so we don't emit it in the connectivity. - if(thisFragmentDegenerate) + if constexpr (TopologyView::dimension() == 2) { - //std::cout << "degenerate " << szIndex << " {"; - //for(int i = 0; i < nIdsThisFragment; i++) - //{ - // std::cout << connView[connStart + i] << ", "; - //} - //std::cout << std::endl; - - outputIndex = connStart; - - // There is one less fragment than we're expecting in the output. - fragmentData.m_fragmentsView[szIndex] -= 1; + int connStart = outputIndex - nIdsThisFragment; + + // Check for degenerate + int nUniqueIds = details::unique_count( + connView.data() + connStart, + nIdsThisFragment); + bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); + degenerates |= thisFragmentDegenerate; + + // Rewind the outputIndex so we don't emit it in the connectivity. + if(thisFragmentDegenerate) + { + //std::cout << "degenerate " << szIndex << " {"; + //for(int i = 0; i < nIdsThisFragment; i++) + //{ + // std::cout << connView[connStart + i] << ", "; + //} + //std::cout << std::endl; + + outputIndex = connStart; + + // There is one less fragment than we're expecting in the output. + fragmentData.m_fragmentsView[szIndex] -= 1; + } + // Mark empty size. + sizesView[sizeIndex] = + thisFragmentDegenerate ? 0 : nIdsThisFragment; + } + else + { + sizesView[sizeIndex] = nIdsThisFragment; } - // Mark empty size. - sizesView[sizeIndex] = - thisFragmentDegenerate ? 0 : nIdsThisFragment; #else sizesView[sizeIndex] = nIdsThisFragment; #endif @@ -1281,74 +1290,77 @@ class ClipField } #if defined(AXOM_CLIP_FILTER_DEGENERATES) - // We get into this block when degenerate zones were detected where - // all of their nodes are the same. We need to filter those out. - if(degenerates_reduce.get()) + if constexpr (TopologyView::dimension() == 2) { - AXOM_ANNOTATE_SCOPE("degenerates"); - - // There were degenerates so the expected number of fragments per zone (m_fragmentsView) - // was adjusted down. That means redoing the offsets. These need to be up - // to date to handle zonal fields later. - axom::exclusive_scan(fragmentData.m_fragmentsView, - fragmentData.m_fragmentOffsetsView); - - // Use sizesView to make a mask that has 1's where size > 0. - axom::IndexType nz = fragmentData.m_finalNumZones; - axom::Array mask(nz, - nz, - axom::execution_space::allocatorID()); - axom::Array maskOffsets( - nz, - nz, - axom::execution_space::allocatorID()); - auto maskView = mask.view(); - auto maskOffsetsView = maskOffsets.view(); - RAJA::ReduceSum mask_reduce(0); - axom::for_all( - nz, - AXOM_LAMBDA(auto index) { - const int ival = (sizesView[index] > 0) ? 1 : 0; - maskView[index] = ival; - mask_reduce += ival; - }); - const axom::IndexType filteredZoneCount = mask_reduce.get(); - - // Make offsets - axom::exclusive_scan(maskView, maskOffsetsView); - - // Replace data in the input Conduit node with a denser version using the mask. - auto filter = - [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) { - using value_type = typename decltype(srcView)::value_type; - conduit::Node n_values; - n_values.set_allocator(conduitAllocatorID); - n_values.set( - conduit::DataType(bputils::cpp2conduit::id, newSize)); - auto valuesView = bputils::make_array_view(n_values); - const auto nValues = maskView.size(); - axom::for_all( - nValues, - AXOM_LAMBDA(auto index) { - if(maskView[index] > 0) - { - const auto destIndex = maskOffsetsView[index]; - valuesView[destIndex] = srcView[index]; - } - }); + // We get into this block when degenerate zones were detected where + // all of their nodes are the same. We need to filter those out. + if(degenerates_reduce.get()) + { + AXOM_ANNOTATE_SCOPE("degenerates"); + + // There were degenerates so the expected number of fragments per zone (m_fragmentsView) + // was adjusted down. That means redoing the offsets. These need to be up + // to date to handle zonal fields later. + axom::exclusive_scan(fragmentData.m_fragmentsView, + fragmentData.m_fragmentOffsetsView); + + // Use sizesView to make a mask that has 1's where size > 0. + axom::IndexType nz = fragmentData.m_finalNumZones; + axom::Array mask(nz, + nz, + axom::execution_space::allocatorID()); + axom::Array maskOffsets( + nz, + nz, + axom::execution_space::allocatorID()); + auto maskView = mask.view(); + auto maskOffsetsView = maskOffsets.view(); + RAJA::ReduceSum mask_reduce(0); + axom::for_all( + nz, + AXOM_LAMBDA(auto index) { + const int ival = (sizesView[index] > 0) ? 1 : 0; + maskView[index] = ival; + mask_reduce += ival; + }); + const axom::IndexType filteredZoneCount = mask_reduce.get(); + + // Make offsets + axom::exclusive_scan(maskView, maskOffsetsView); + + // Replace data in the input Conduit node with a denser version using the mask. + auto filter = + [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) { + using value_type = typename decltype(srcView)::value_type; + conduit::Node n_values; + n_values.set_allocator(conduitAllocatorID); + n_values.set( + conduit::DataType(bputils::cpp2conduit::id, newSize)); + auto valuesView = bputils::make_array_view(n_values); + const auto nValues = maskView.size(); + axom::for_all( + nValues, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + const auto destIndex = maskOffsetsView[index]; + valuesView[destIndex] = srcView[index]; + } + }); - n_src.swap(n_values); - return bputils::make_array_view(n_src); - }; + n_src.swap(n_values); + return bputils::make_array_view(n_src); + }; - // Filter sizes, shapes, color using the mask - sizesView = filter(n_sizes, sizesView, filteredZoneCount); - offsetsView = filter(n_offsets, offsetsView, filteredZoneCount); - shapesView = filter(n_shapes, shapesView, filteredZoneCount); - colorView = filter(n_color_values, colorView, filteredZoneCount); + // Filter sizes, shapes, color using the mask + sizesView = filter(n_sizes, sizesView, filteredZoneCount); + offsetsView = filter(n_offsets, offsetsView, filteredZoneCount); + shapesView = filter(n_shapes, shapesView, filteredZoneCount); + colorView = filter(n_color_values, colorView, filteredZoneCount); - // Record the filtered size. - fragmentData.m_finalNumZones = filteredZoneCount; + // Record the filtered size. + fragmentData.m_finalNumZones = filteredZoneCount; + } } #endif @@ -1393,45 +1405,48 @@ class ClipField #if 1 // Handle some quad->tri degeneracies - if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) + if constexpr (TopologyView::dimension() == 2) { - AXOM_ANNOTATE_SCOPE("quadtri"); - const axom::IndexType numOutputZones = shapesView.size(); - RAJA::ReduceBitOr shapesUsed_reduce(0); - axom::for_all( - numOutputZones, - AXOM_LAMBDA(auto index) { - if(shapesView[index] == views::Quad_ShapeID) - { - const auto offset = offsetsView[index]; - ConnectivityType pts[4]; - int npts = 0; - for(int current = 0; current < 4; current++) + if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) + { + AXOM_ANNOTATE_SCOPE("quadtri"); + const axom::IndexType numOutputZones = shapesView.size(); + RAJA::ReduceBitOr shapesUsed_reduce(0); + axom::for_all( + numOutputZones, + AXOM_LAMBDA(auto index) { + if(shapesView[index] == views::Quad_ShapeID) { - int next = (current + 1) % 4; - ConnectivityType curNode = connView[offset + current]; - ConnectivityType nextNode = connView[offset + next]; - if(curNode != nextNode) pts[npts++] = curNode; - } + const auto offset = offsetsView[index]; + ConnectivityType pts[4]; + int npts = 0; + for(int current = 0; current < 4; current++) + { + int next = (current + 1) % 4; + ConnectivityType curNode = connView[offset + current]; + ConnectivityType nextNode = connView[offset + next]; + if(curNode != nextNode) pts[npts++] = curNode; + } - if(npts == 3) - { - shapesView[index] = views::Tri_ShapeID; - sizesView[index] = 3; - connView[offset] = pts[0]; - connView[offset + 1] = pts[1]; - connView[offset + 2] = pts[2]; - connView[offset + 3] = - pts[2]; // Repeat the last point (it won't be used though). + if(npts == 3) + { + shapesView[index] = views::Tri_ShapeID; + sizesView[index] = 3; + connView[offset] = pts[0]; + connView[offset + 1] = pts[1]; + connView[offset + 2] = pts[2]; + connView[offset + 3] = + pts[2]; // Repeat the last point (it won't be used though). + } } - } - BitSet shapeBit {}; - axom::utilities::setBitOn(shapeBit, shapesView[index]); - shapesUsed_reduce |= shapeBit; - }); - // We redid shapesUsed reduction in case triangles appeared. - shapesUsed = shapesUsed_reduce.get(); + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); + // We redid shapesUsed reduction in case triangles appeared. + shapesUsed = shapesUsed_reduce.get(); + } } #endif diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 7229fb5f86..1d79674f69 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -17,6 +17,9 @@ #include +#include +#include + namespace axom { namespace mir @@ -25,27 +28,8 @@ namespace utilities { namespace blueprint { -/** - * \brief Build an o2m relation that lets us look up the zones for a node. - * - * \note The zone list for each point is not sorted. - */ -template -class NodeToZoneRelationBuilder +namespace details { -public: - /** - * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. - * - * \param topo The topology for which we're building the O2M relation. - * \param coordset The topology's coordset. - * \param[out] The node that will contain the O2M relation. - */ - void execute(const conduit::Node &topo, - const conduit::Node &coordset, - conduit::Node &relation); - -private: /** * \brief Given views that contain the nodes and zones, sort the zones using the * node numbers to produce a list of zones for each node and an offsets array @@ -55,11 +39,14 @@ class NodeToZoneRelationBuilder * \param[inout[ zones_view A view (same size as \a nodes_view) that contains the zone number of each node. * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. */ - template - void buildRelation(const ViewType &nodes_view, - ViewType &zones_view, - ViewType &offsets_view) const +template +struct FillZonesAndOffsets +{ + static void execute(const ViewType &nodes_view, + ViewType &zones_view, + ViewType &offsets_view) { + AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); assert(nodes_view.size() == zones_view.size()); using loop_policy = typename axom::execution_space::loop_policy; @@ -109,10 +96,59 @@ class NodeToZoneRelationBuilder } }; +/// Partial specialization for axom::SEQ_EXEC. +template +struct FillZonesAndOffsets +{ + static void execute(const ViewType &nodes_view, + ViewType &zones_view, + ViewType &offsets_view) + { + AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); + assert(nodes_view.size() == zones_view.size()); + using value_type = typename ViewType::value_type; + + std::map> n2z; + const auto n = nodes_view.size(); + for(axom::IndexType index = 0; index < n; index++) + { + n2z[nodes_view[index]].push_back(zones_view[index]); + } + + // Flatten the map to fill zones_view and make offsets + axom::IndexType offset = 0, zi = 0, oi = 0; + for(auto it = n2z.begin(); it != n2z.end(); it++) + { + offsets_view[oi++] = offset; + const auto n = it->second.size(); + for(size_t i = 0; i < n; i++) + zones_view[zi++] = it->second[i]; + offset += n; + } + } +}; + +} // end namespace details + +/** + * \brief Build an o2m relation that lets us look up the zones for a node. + * + * \note The zone list for each point is not sorted. + */ template -void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, - const conduit::Node &coordset, - conduit::Node &relation) +class NodeToZoneRelationBuilder +{ +public: + /** + * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. + * + * \param topo The topology for which we're building the O2M relation. + * \param coordset The topology's coordset. + * \param[out] The node that will contain the O2M relation. + */ + void execute(const conduit::Node &topo, + const conduit::Node &coordset, + conduit::Node &relation) { const std::string type = topo.fetch_existing("type").as_string(); @@ -200,9 +236,9 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, - zonesView, - offsetsView); + details::FillZonesAndOffsets::execute(connectivityView, + zonesView, + offsetsView); // Compute sizes from offsets. axom::for_all( @@ -251,9 +287,9 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, - zonesView, - offsetsView); + details::FillZonesAndOffsets::execute(connectivityView, + zonesView, + offsetsView); // Compute sizes from offsets. axom::for_all( @@ -290,9 +326,9 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, }); // Make the relation, outputting into the zonesView and offsetsView. using ViewType = decltype(connectivityView); - this->template buildRelation(connectivityView, - zonesView, - offsetsView); + details::FillZonesAndOffsets::execute(connectivityView, + zonesView, + offsetsView); // Compute sizes from offsets. axom::for_all( @@ -319,6 +355,7 @@ void NodeToZoneRelationBuilder::execute(const conduit::Node &topo, execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); } } +}; } // end namespace blueprint } // end namespace utilities diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 9f41cdb005..b0cc155ad1 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -311,7 +311,7 @@ TEST(mir_clipfield, sort_values) for(int trial = 1; trial <= n; trial++) { auto values = makeUnsortedArray(n); - axom::mir::utilities::sort_values(values.data(), values.size()); + axom::utilities::Sorting::sort(values.data(), values.size()); EXPECT_TRUE(increasing(values)); } } @@ -331,7 +331,7 @@ TEST(mir_clipfield, unique) 4, 5, 9, 8, 5, 6, 10, 9, 6, 7, 11, 10}}; axom::Array uIds, uIndices; - axom::mir::utilities::unique(ids.view(), uIds, uIndices); + axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); EXPECT_EQ(uIds.size(), 12); EXPECT_EQ(uIndices.size(), 12); for(axom::IndexType i = 0; i < uIds.size(); i++) @@ -344,30 +344,21 @@ TEST(mir_clipfield, unique) //------------------------------------------------------------------------------ TEST(mir_clipfield, make_name) { + axom::mir::utilities::HashNaming naming; + for(int n = 1; n < 14; n++) { // Make a set of scrambled ids. auto values = makeRandomArray(n); - std::uint64_t name = 0, name2 = 0; // Compute the name for that list of ids. - if(n == 1) - name = axom::mir::utilities::make_name_1(values[0]); - else if(n == 2) - name = axom::mir::utilities::make_name_2(values[0], values[1]); - else - name = axom::mir::utilities::make_name_n(values.data(), values.size()); + auto name = naming.makeName(values.data(), n); for(int trial = 0; trial < 1000; trial++) { // Scramble the id list. auto values2 = permute(values); // Compute the name for that list of ids. - if(n == 1) - name2 = axom::mir::utilities::make_name_1(values2[0]); - else if(n == 2) - name2 = axom::mir::utilities::make_name_2(values2[0], values2[1]); - else - name2 = axom::mir::utilities::make_name_n(values2.data(), values2.size()); + auto name2 = naming.makeName(values2.data(), n); // The names for the 2 scrambled lists of numbers should be the same. EXPECT_EQ(name, name2); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index e372343d52..482fe30141 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -175,134 +175,10 @@ inline std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) } //------------------------------------------------------------------------------ -/** - * \brief A very basic sort for small arrays. - * \param[inout] v The values to be sorted in place. - * \param[in] n The number of values to be sorted. - */ -template -AXOM_HOST_DEVICE void sort_values(ValueType *v, IndexType n) -{ - for(IndexType i = 0; i < n - 1; i++) - { - const IndexType m = n - i - 1; - for(IndexType j = 0; j < m; j++) - { - if(v[j] > v[j + 1]) - { - axom::utilities::swap(v[j], v[j + 1]); - } - } - } -} - -//------------------------------------------------------------------------------ -/** - * \brief Make a hashed "name" for one id. - * - * \param[in] id The id we're hashing. - * \return A hashed name for the id. - */ -template -AXOM_HOST_DEVICE std::uint64_t make_name_1(ValueType id) -{ - return hash_bytes(reinterpret_cast(&id), sizeof(ValueType)); -}; - -//------------------------------------------------------------------------------ -/** - * \brief Make a hashed "name" for two ids. - * - * \param[in] id0 The first id we're hashing. - * \param[in] id1 The second id we're hashing. - * \return A hashed name for the ids. - */ -template -AXOM_HOST_DEVICE std::uint64_t make_name_2(ValueType id0, ValueType id1) -{ - ValueType data[2] = {id0, id1}; - if(id1 < id0) - { - data[0] = id1; - data[1] = id0; - } - return hash_bytes(reinterpret_cast(data), - 2 * sizeof(ValueType)); -}; - -//------------------------------------------------------------------------------ -/** - * \brief Make a hashed "name" for multiple ids. - * - * \tparam MaxValues The largest expected number of values. - * - * \param[in] values The ids that are being hashed. - * \param[in] start The starting index for ids. - * \param[in] n The number if ids being hashed. - * - * \return A hashed name for the ids. - */ -template -AXOM_HOST_DEVICE std::uint64_t make_name_n(const ValueType *values, - std::uint32_t n) -{ - assert(n <= MaxValues); - if(n == 2) return make_name_2(values[0], values[1]); - - // Make sure the values are sorted before hashing. - ValueType sorted[MaxValues]; - for(std::uint32_t i = 0; i < n; i++) - { - sorted[i] = values[i]; - } - sort_values(sorted, n); - - return hash_bytes(reinterpret_cast(sorted), - n * sizeof(ValueType)); -} - -//------------------------------------------------------------------------------ -#if 0 /** * \brief This class implements a naming policy that uses some hashing functions * to produce a "name" for an array of ids. */ -class HashNaming -{ -public: - using KeyType = std::uint64_t; - - /** - * \brief Make a name from an array of ids. - * - * \param ids The array of ids. - * \param numIds The number of ids in the array. - * - * \return The name that describes the array of ids. - * - * \note Different make_name_* functions are used because we can skip most - * sorting for 1,2 element arrays. - */ - AXOM_HOST_DEVICE - inline static KeyType makeName(const IndexType *ids, IndexType numIds) - { - KeyType name {}; - if(numIds == 1) - { - name = axom::mir::utilities::make_name_1(ids[0]); - } - else if(numIds == 2) - { - name = axom::mir::utilities::make_name_2(ids[0], ids[1]); - } - else - { - name = axom::mir::utilities::make_name_n(ids, numIds); - } - return name; - } -}; -#else template class HashNaming { @@ -325,28 +201,49 @@ class HashNaming constexpr static KeyType Max31Bit = (KeyType(1) << 31) - 1; constexpr static KeyType Max32Bit = (KeyType(1) << 32) - 1; + /** + * \brief A view for making names, suitable for use in device code. + */ class View { public: using KeyType = HashNaming::KeyType; + /** + * \brief Make a name from an array of ids. + * + * \param p The array of ids. + * \param n The number of ids in the array. + * + * \return The name that describes the array of ids. + * + * \note Different make_name_* functions are used because we can skip most + * sorting for 1,2 element arrays. Also, there is a small benefit + * to some of the other shortcuts for smaller arrays. + */ AXOM_HOST_DEVICE KeyType makeName(const IndexType *p, int n) const { - KeyType retval {}; + KeyType name {}; if(n == 1) - retval = make_name_1(p[0]); + name = make_name_1(p[0]); else if(n == 2) - retval = make_name_2(p[0], p[1]); + name = make_name_2(p[0], p[1]); else - retval = make_name_n(p, n); - return retval; + name = make_name_n(p, n); + return name; } + /// Set the max number of nodes, which can be useful for packing/narrowing. AXOM_HOST_DEVICE void setMaxId(IndexType m) { m_maxId = static_cast(m); } private: + /** + * \brief Encode a single id as a name. + * \param p0 The id to encode. + * \return A name that encodes the id. + */ AXOM_HOST_DEVICE inline KeyType make_name_1(IndexType p0) const { @@ -356,6 +253,12 @@ class HashNaming return KeyIDSingle | k0; } + /** + * \brief Encode 2 ids as a name. + * \param p0 The first id to encode. + * \param p1 The second id to encode. + * \return A name that encodes the ids. + */ AXOM_HOST_DEVICE inline KeyType make_name_2(IndexType p0, IndexType p1) const { @@ -366,6 +269,12 @@ class HashNaming return KeyIDPair | (k0 << 31) | k1; } + /** + * \brief Encode multiple ids as a name. + * \param p The ids to encode. + * \param n The number of ids. + * \return A name that encodes the ids. + */ AXOM_HOST_DEVICE KeyType make_name_n(const IndexType *p, int n) const { @@ -413,7 +322,7 @@ class HashNaming void *ptr = static_cast(sorted); KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), - n << 1); + n * sizeof(std::uint16_t)); retval = KeyIDHash | (k0 & PayloadMask); } else if(m_maxId < Max32Bit) @@ -427,7 +336,7 @@ class HashNaming void *ptr = static_cast(sorted); KeyType k0 = axom::mir::utilities::hash_bytes(static_cast(ptr), - n << 2); + n * sizeof(std::uint32_t)); retval = KeyIDHash | (k0 & PayloadMask); } else @@ -446,20 +355,31 @@ class HashNaming return retval; } - KeyType m_maxId {}; + KeyType m_maxId {std::numeric_limits::max()}; }; // Host-callable methods + /// Make a name from the array of ids. KeyType makeName(const IndexType *p, int n) const { return m_view.makeName(p, n); } + /** + * \brief Set the max number of nodes, which can help with id packing/narrowing. + * \param n The number of nodes. + */ void setMaxId(IndexType n) { m_view.setMaxId(n); } + /// Return a view that can be used on device. View view() { return m_view; } + /** + * \brief Turn name into a string. + * \param key The name. + * \return A string that represents the name. + */ static std::string toString(KeyType key) { std::stringstream ss; @@ -506,8 +426,6 @@ class HashNaming View m_view {}; }; -#endif - //------------------------------------------------------------------------------ /** * \brief This function makes a unique array of values from an input list of keys. @@ -521,70 +439,105 @@ class HashNaming * */ template -void unique(const axom::ArrayView &keys_orig_view, +struct Unique +{ + static void execute(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) + { + using loop_policy = typename axom::execution_space::loop_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a copy of the keys and make original indices. + const auto n = keys_orig_view.size(); + axom::Array keys(n, n, allocatorID); + axom::Array indices(n, n, allocatorID); + auto keys_view = keys.view(); + auto indices_view = indices.view(); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + keys_view[i] = keys_orig_view[i]; + indices_view[i] = i; + }); + + // Sort the keys, indices in place. + RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), + RAJA::make_span(indices_view.data(), n)); + + // Make a mask array for where differences occur. + axom::Array mask(n, n, allocatorID); + auto mask_view = mask.view(); + RAJA::ReduceSum mask_sum(0); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + const axom::IndexType m = + (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; + mask_view[i] = m; + mask_sum += m; + }); + + // Do a scan on the mask array to build an offset array. + axom::Array offsets(n, n, allocatorID); + auto offsets_view = offsets.view(); + RAJA::exclusive_scan(RAJA::make_span(mask_view.data(), n), + RAJA::make_span(offsets_view.data(), n), + RAJA::operators::plus {}); + + // Allocate the output arrays. + const axom::IndexType newsize = mask_sum.get(); + skeys = axom::Array(newsize, newsize, allocatorID); + sindices = axom::Array(newsize, newsize, allocatorID); + + // Iterate over the mask/offsets to store values at the right + // offset in the new array. + auto skeys_view = skeys.view(); + auto sindices_view = sindices.view(); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType i) { + if(mask_view[i]) + { + skeys_view[offsets_view[i]] = keys_view[i]; + sindices_view[offsets_view[i]] = indices_view[i]; + } + }); + } +}; + +template +struct Unique { - using loop_policy = typename axom::execution_space::loop_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; - const int allocatorID = axom::execution_space::allocatorID(); - - // Make a copy of the keys and make original indices. - const auto n = keys_orig_view.size(); - axom::Array keys(n, n, allocatorID); - axom::Array indices(n, n, allocatorID); - auto keys_view = keys.view(); - auto indices_view = indices.view(); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { - keys_view[i] = keys_orig_view[i]; - indices_view[i] = i; - }); - - // Sort the keys, indices in place. - RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), - RAJA::make_span(indices_view.data(), n)); - - // Make a mask array for where differences occur. - axom::Array mask(n, n, allocatorID); - auto mask_view = mask.view(); - RAJA::ReduceSum mask_sum(0); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { - const axom::IndexType m = - (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; - mask_view[i] = m; - mask_sum += m; - }); - - // Do a scan on the mask array to build an offset array. - axom::Array offsets(n, n, allocatorID); - auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view.data(), n), - RAJA::make_span(offsets_view.data(), n), - RAJA::operators::plus {}); - - // Allocate the output arrays. - const axom::IndexType newsize = mask_sum.get(); - skeys = axom::Array(newsize, newsize, allocatorID); - sindices = axom::Array(newsize, newsize, allocatorID); - - // Iterate over the mask/offsets to store values at the right - // offset in the new array. - auto skeys_view = skeys.view(); - auto sindices_view = sindices.view(); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType i) { - if(mask_view[i]) + static void execute(const axom::ArrayView &keys_orig_view, + axom::Array &skeys, + axom::Array &sindices) + { + std::map unique; + const axom::IndexType n = keys_orig_view.size(); + axom::IndexType index = 0; + for(; index < n; index++) + { + const auto k = keys_orig_view[index]; + //if(unique.find(k) == unique.end()) { - skeys_view[offsets_view[i]] = keys_view[i]; - sindices_view[offsets_view[i]] = indices_view[i]; + unique[k] = index; } - }); -} + } + // Allocate the output arrays. + const axom::IndexType newsize = unique.size(); + const int allocatorID = axom::execution_space::allocatorID(); + skeys = axom::Array(newsize, newsize, allocatorID); + sindices = axom::Array(newsize, newsize, allocatorID); + index = 0; + for(auto it = unique.begin(); it != unique.end(); it++, index++) + { + skeys[index] = it->first; + sindices[index] = it->second; + } + } +}; } // end namespace utilities } // end namespace mir From 45415b2f2e3c54c232bc80bd0ba8259603eb8775 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 3 Sep 2024 12:20:18 -0700 Subject: [PATCH 200/290] make style --- src/axom/mir/ClipField.hpp | 22 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 403 +++++++++++---------- src/axom/mir/utilities.hpp | 11 +- 3 files changed, 221 insertions(+), 215 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 804a57056a..4e436016ae 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -548,9 +548,10 @@ class ClipField axom::Array uIndices; { AXOM_ANNOTATE_SCOPE("unique"); - axom::mir::utilities::Unique::execute(builder.blendNames(), - uNames, - uIndices); + axom::mir::utilities::Unique::execute( + builder.blendNames(), + uNames, + uIndices); builder.setUniqueNames(uNames.view(), uIndices.view()); } bputils::BlendData blend = builder.makeBlendData(); @@ -1209,16 +1210,16 @@ class ClipField { // Output the nodes used in this zone. const int fragmentSize = fragment.size(); -//#if defined(AXOM_CLIP_FILTER_DEGENERATES) -// int connStart = outputIndex; -//#endif + //#if defined(AXOM_CLIP_FILTER_DEGENERATES) + // int connStart = outputIndex; + //#endif offsetsView[sizeIndex] = outputIndex; for(int i = 2; i < fragmentSize; i++) connView[outputIndex++] = point_2_new[fragment[i]]; const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) - if constexpr (TopologyView::dimension() == 2) + if constexpr(TopologyView::dimension() == 2) { int connStart = outputIndex - nIdsThisFragment; @@ -1226,7 +1227,8 @@ class ClipField int nUniqueIds = details::unique_count( connView.data() + connStart, nIdsThisFragment); - bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); + bool thisFragmentDegenerate = + nUniqueIds < (nIdsThisFragment - 1); degenerates |= thisFragmentDegenerate; // Rewind the outputIndex so we don't emit it in the connectivity. @@ -1290,7 +1292,7 @@ class ClipField } #if defined(AXOM_CLIP_FILTER_DEGENERATES) - if constexpr (TopologyView::dimension() == 2) + if constexpr(TopologyView::dimension() == 2) { // We get into this block when degenerate zones were detected where // all of their nodes are the same. We need to filter those out. @@ -1405,7 +1407,7 @@ class ClipField #if 1 // Handle some quad->tri degeneracies - if constexpr (TopologyView::dimension() == 2) + if constexpr(TopologyView::dimension() == 2) { if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) { diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 1d79674f69..61ffb4cfb7 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -30,7 +30,7 @@ namespace blueprint { namespace details { - /** +/** * \brief Given views that contain the nodes and zones, sort the zones using the * node numbers to produce a list of zones for each node and an offsets array * that points to the start of each list of zones. @@ -121,14 +121,13 @@ struct FillZonesAndOffsets { offsets_view[oi++] = offset; const auto n = it->second.size(); - for(size_t i = 0; i < n; i++) - zones_view[zi++] = it->second[i]; + for(size_t i = 0; i < n; i++) zones_view[zi++] = it->second[i]; offset += n; } } }; -} // end namespace details +} // end namespace details /** * \brief Build an o2m relation that lets us look up the zones for a node. @@ -149,212 +148,216 @@ class NodeToZoneRelationBuilder void execute(const conduit::Node &topo, const conduit::Node &coordset, conduit::Node &relation) -{ - const std::string type = topo.fetch_existing("type").as_string(); - - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; - const int conduitAllocatorID = c2a.getConduitAllocatorID(); - - conduit::Node &n_zones = relation["zones"]; - conduit::Node &n_sizes = relation["sizes"]; - conduit::Node &n_offsets = relation["offsets"]; - n_zones.set_allocator(conduitAllocatorID); - n_sizes.set_allocator(conduitAllocatorID); - n_offsets.set_allocator(conduitAllocatorID); - - if(type == "unstructured") { - conduit::blueprint::mesh::utils::ShapeType shape(topo); - const conduit::Node &n_connectivity = topo["elements/connectivity"]; - const std::string shapeType = topo["elements/shape"].as_string(); - const auto intTypeId = n_connectivity.dtype().id(); - const auto connSize = n_connectivity.dtype().number_of_elements(); + const std::string type = topo.fetch_existing("type").as_string(); - // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. - const auto nnodes = - conduit::blueprint::mesh::utils::coordset::length(coordset); + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + const int conduitAllocatorID = c2a.getConduitAllocatorID(); - if(shape.is_polyhedral()) - { - using reduce_policy = - typename axom::execution_space::reduce_policy; - const auto allocatorID = axom::execution_space::allocatorID(); - - views::dispatch_unstructured_polyhedral_topology( - topo, - [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { - const auto nzones = topoView.numberOfZones(); - axom::Array sizes(nzones, nzones, allocatorID); - auto sizes_view = sizes.view(); - - // Run through the topology once to do a count of each zone's unique node ids. - RAJA::ReduceSum count(0); - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); - const auto connSize = count.get(); - - // Do a scan on the size array to build an offset array. - axom::Array offsets(nzones, nzones, allocatorID); - auto offsets_view = offsets.view(); - axom::exclusive_scan(sizes_view, offsets_view); - sizes.clear(); - - // Allocate Conduit arrays on the device in a data type that matches the connectivity. - conduit::Node n_conn; - n_conn.set_allocator(conduitAllocatorID); - n_conn.set(conduit::DataType(intTypeId, connSize)); - - n_zones.set(conduit::DataType(intTypeId, connSize)); - n_sizes.set(conduit::DataType(intTypeId, nnodes)); - n_offsets.set(conduit::DataType(intTypeId, nnodes)); - - views::IndexNode_to_ArrayView_same( - n_conn, - n_zones, - n_sizes, - n_offsets, - [&](auto connectivityView, - auto zonesView, - auto sizesView, - auto offsetsView) { - // Run through the data one more time to build the nodes and zones arrays. - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } - }); - - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute(connectivityView, - zonesView, - offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); - }); - }); - } - else if(shape.is_polygonal() || shapeType == "mixed") + conduit::Node &n_zones = relation["zones"]; + conduit::Node &n_sizes = relation["sizes"]; + conduit::Node &n_offsets = relation["offsets"]; + n_zones.set_allocator(conduitAllocatorID); + n_sizes.set_allocator(conduitAllocatorID); + n_offsets.set_allocator(conduitAllocatorID); + + if(type == "unstructured") { - const conduit::Node &n_topo_sizes = topo["elements/sizes"]; - const conduit::Node &n_topo_offsets = topo["elements/offsets"]; - - const auto nzones = n_topo_sizes.dtype().number_of_elements(); - - // Allocate Conduit arrays on the device in a data type that matches the connectivity. - n_zones.set(conduit::DataType(intTypeId, connSize)); - n_sizes.set(conduit::DataType(intTypeId, nnodes)); - n_offsets.set(conduit::DataType(intTypeId, nnodes)); - - // Make zones for each node - views::IndexNode_to_ArrayView_same( - n_zones, - n_topo_sizes, - n_topo_offsets, - [&](auto zonesView, auto sizesView, auto offsetsView) { - using DataType = typename decltype(zonesView)::value_type; - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - for(DataType i = 0; i < sizesView[zoneIndex]; i++) - zonesView[offsetsView[zoneIndex] + i] = zoneIndex; - }); - }); - - views::IndexNode_to_ArrayView_same( - n_connectivity, - n_zones, - n_sizes, - n_offsets, - [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute(connectivityView, - zonesView, - offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); - }); + conduit::blueprint::mesh::utils::ShapeType shape(topo); + const conduit::Node &n_connectivity = topo["elements/connectivity"]; + const std::string shapeType = topo["elements/shape"].as_string(); + const auto intTypeId = n_connectivity.dtype().id(); + const auto connSize = n_connectivity.dtype().number_of_elements(); + + // Use the coordset to get the number of nodes. Conduit should be able to do this using only metadata. + const auto nnodes = + conduit::blueprint::mesh::utils::coordset::length(coordset); + + if(shape.is_polyhedral()) + { + using reduce_policy = + typename axom::execution_space::reduce_policy; + const auto allocatorID = axom::execution_space::allocatorID(); + + views::dispatch_unstructured_polyhedral_topology( + topo, + [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { + const auto nzones = topoView.numberOfZones(); + axom::Array sizes(nzones, nzones, allocatorID); + auto sizes_view = sizes.view(); + + // Run through the topology once to do a count of each zone's unique node ids. + RAJA::ReduceSum count(0); + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); + const auto connSize = count.get(); + + // Do a scan on the size array to build an offset array. + axom::Array offsets(nzones, nzones, allocatorID); + auto offsets_view = offsets.view(); + axom::exclusive_scan(sizes_view, offsets_view); + sizes.clear(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + conduit::Node n_conn; + n_conn.set_allocator(conduitAllocatorID); + n_conn.set(conduit::DataType(intTypeId, connSize)); + + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same( + n_conn, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, + auto zonesView, + auto sizesView, + auto offsetsView) { + // Run through the data one more time to build the nodes and zones arrays. + topoView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); + i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); + + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + details::FillZonesAndOffsets::execute( + connectivityView, + zonesView, + offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); + }); + }); + } + else if(shape.is_polygonal() || shapeType == "mixed") + { + const conduit::Node &n_topo_sizes = topo["elements/sizes"]; + const conduit::Node &n_topo_offsets = topo["elements/offsets"]; + + const auto nzones = n_topo_sizes.dtype().number_of_elements(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + // Make zones for each node + views::IndexNode_to_ArrayView_same( + n_zones, + n_topo_sizes, + n_topo_offsets, + [&](auto zonesView, auto sizesView, auto offsetsView) { + using DataType = typename decltype(zonesView)::value_type; + axom::for_all( + 0, + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + for(DataType i = 0; i < sizesView[zoneIndex]; i++) + zonesView[offsetsView[zoneIndex] + i] = zoneIndex; + }); + }); + + views::IndexNode_to_ArrayView_same( + n_connectivity, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + details::FillZonesAndOffsets::execute( + connectivityView, + zonesView, + offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); + }); + } + else + { + // Shapes are all the same size. + const auto nodesPerShape = shape.indices; + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same( + n_connectivity, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { + // Make zones for each node + axom::for_all( + 0, + connSize, + AXOM_LAMBDA(axom::IndexType index) { + zonesView[index] = index / nodesPerShape; + }); + // Make the relation, outputting into the zonesView and offsetsView. + using ViewType = decltype(connectivityView); + details::FillZonesAndOffsets::execute( + connectivityView, + zonesView, + offsetsView); + + // Compute sizes from offsets. + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (connSize - offsetsView[i]); + }); + }); + } } else { - // Shapes are all the same size. - const auto nodesPerShape = shape.indices; - - // Allocate Conduit arrays on the device in a data type that matches the connectivity. - n_zones.set(conduit::DataType(intTypeId, connSize)); - n_sizes.set(conduit::DataType(intTypeId, nnodes)); - n_offsets.set(conduit::DataType(intTypeId, nnodes)); - - views::IndexNode_to_ArrayView_same( - n_connectivity, - n_zones, - n_sizes, - n_offsets, - [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { - // Make zones for each node - axom::for_all( - 0, - connSize, - AXOM_LAMBDA(axom::IndexType index) { - zonesView[index] = index / nodesPerShape; - }); - // Make the relation, outputting into the zonesView and offsetsView. - using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute(connectivityView, - zonesView, - offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); - }); - } - } - else - { - // These are all structured topos of some sort. Make an unstructured representation and recurse. + // These are all structured topos of some sort. Make an unstructured representation and recurse. - conduit::Node mesh; - axom::mir::utilities::blueprint::to_unstructured(topo, - coordset, - "newtopo", - mesh); + conduit::Node mesh; + axom::mir::utilities::blueprint::to_unstructured(topo, + coordset, + "newtopo", + mesh); - // Recurse using the unstructured mesh. - execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); + // Recurse using the unstructured mesh. + execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); + } } -} }; } // end namespace blueprint diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 482fe30141..3a2721ab91 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -442,11 +442,12 @@ template struct Unique { static void execute(const axom::ArrayView &keys_orig_view, - axom::Array &skeys, - axom::Array &sindices) + axom::Array &skeys, + axom::Array &sindices) { using loop_policy = typename axom::execution_space::loop_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; const int allocatorID = axom::execution_space::allocatorID(); // Make a copy of the keys and make original indices. @@ -511,8 +512,8 @@ template struct Unique { static void execute(const axom::ArrayView &keys_orig_view, - axom::Array &skeys, - axom::Array &sindices) + axom::Array &skeys, + axom::Array &sindices) { std::map unique; const axom::IndexType n = keys_orig_view.size(); From b8d3c1c9c85c82b830c890bc1d3bfa38b11fe2eb Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 3 Sep 2024 18:23:53 -0700 Subject: [PATCH 201/290] Some speedup for serial NodeToZoneRelationBuilder. --- src/axom/mir/NodeToZoneRelationBuilder.hpp | 155 +++++++++--------- src/axom/mir/blueprint_utilities.hpp | 8 +- .../mir/tests/mir_blueprint_utilities.cpp | 8 +- src/axom/mir/utilities.hpp | 7 +- 4 files changed, 91 insertions(+), 87 deletions(-) diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 61ffb4cfb7..ba66c428a5 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -35,95 +35,124 @@ namespace details * node numbers to produce a list of zones for each node and an offsets array * that points to the start of each list of zones. * - * \param[in] nodes_view A view that contains the set of all of the nodes in the topology (the connectivity) - * \param[inout[ zones_view A view (same size as \a nodes_view) that contains the zone number of each node. - * \param[out] offsets_view A view that we fill with offsets so offsets_view[i] points to the start of the i'th list in \a zones_view. + * \param[in] nodesView A view that contains the set of all of the nodes in the topology (the connectivity) + * \param[inout[ zonesView A view (same size as \a nodesView) that contains the zone number of each node. + * \param[out] offsetsView A view that we fill with offsets so offsetsView[i] points to the start of the i'th list in \a zonesView. + * + * \note RAJA::sort_pairs can be slow if there are a lot of nodes (depends on ExecSpace too). */ template -struct FillZonesAndOffsets +struct BuildRelation { - static void execute(const ViewType &nodes_view, - ViewType &zones_view, - ViewType &offsets_view) + static void execute(const ViewType &nodesView, + ViewType &zonesView, + ViewType &sizesView, + ViewType &offsetsView) { AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); - assert(nodes_view.size() == zones_view.size()); + assert(nodesView.size() == zonesView.size()); using loop_policy = typename axom::execution_space::loop_policy; using value_type = typename ViewType::value_type; const int allocatorID = axom::execution_space::allocatorID(); // Make a copy of the nodes that we'll use as keys. - const auto n = nodes_view.size(); + const auto n = nodesView.size(); axom::Array keys(n, n, allocatorID); - auto keys_view = keys.view(); + auto keysView = keys.view(); axom::for_all( n, - AXOM_LAMBDA(axom::IndexType i) { keys_view[i] = nodes_view[i]; }); + AXOM_LAMBDA(axom::IndexType i) { keysView[i] = nodesView[i]; }); - // Sort the keys, zones in place. This sorts the zones_view which we want for output. - RAJA::sort_pairs(RAJA::make_span(keys_view.data(), n), - RAJA::make_span(zones_view.data(), n)); + // Sort the keys, zones in place. This sorts the zonesView which we want for output. + RAJA::sort_pairs(RAJA::make_span(keysView.data(), n), + RAJA::make_span(zonesView.data(), n)); // Make a mask array for where differences occur. axom::Array mask(n, n, allocatorID); - auto mask_view = mask.view(); + auto maskView = mask.view(); axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { - mask_view[i] = - (i >= 1) ? ((keys_view[i] != keys_view[i - 1]) ? 1 : 0) : 1; + maskView[i] = + (i >= 1) ? ((keysView[i] != keysView[i - 1]) ? 1 : 0) : 1; }); // Do a scan on the mask array to build an offset array. axom::Array dest_offsets(n, n, allocatorID); - auto dest_offsets_view = dest_offsets.view(); - axom::exclusive_scan(mask_view, dest_offsets_view); + auto dest_offsetsView = dest_offsets.view(); + axom::exclusive_scan(maskView, dest_offsetsView); // Build the offsets to each node's zone ids. axom::for_all( - offsets_view.size(), - AXOM_LAMBDA(axom::IndexType i) { offsets_view[i] = 0; }); - + offsetsView.size(), + AXOM_LAMBDA(axom::IndexType i) { offsetsView[i] = 0; }); axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { - if(mask_view[i]) + if(maskView[i]) { - offsets_view[dest_offsets_view[i]] = i; + offsetsView[dest_offsetsView[i]] = i; } }); + + // Compute sizes from offsets. + const value_type totalSize = nodesView.size(); + axom::for_all( + offsetsView.size(), + AXOM_LAMBDA(auto i) { + sizesView[i] = (i < offsetsView.size() - 1) + ? (offsetsView[i + 1] - offsetsView[i]) + : (totalSize - offsetsView[i]); + }); } }; /// Partial specialization for axom::SEQ_EXEC. template -struct FillZonesAndOffsets +struct BuildRelation { - static void execute(const ViewType &nodes_view, - ViewType &zones_view, - ViewType &offsets_view) + static void execute(const ViewType &nodesView, + ViewType &zonesView, + ViewType &sizesView, + ViewType &offsetsView) { AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); - assert(nodes_view.size() == zones_view.size()); + assert(nodesView.size() == zonesView.size()); using value_type = typename ViewType::value_type; + using ExecSpace = axom::SEQ_EXEC; + const int allocatorID = execution_space::allocatorID(); - std::map> n2z; - const auto n = nodes_view.size(); - for(axom::IndexType index = 0; index < n; index++) + // Count how many times a node is used. + const auto nnodes = offsetsView.size(); + axom::for_all(nnodes, AXOM_LAMBDA(auto index) { - n2z[nodes_view[index]].push_back(zones_view[index]); - } + sizesView[index] = 0; + }); + axom::for_all(nodesView.size(), AXOM_LAMBDA(auto index) + { + sizesView[nodesView[index]]++; // Works only because ExecSpace=SEQ_EXEC. + }); + // Make offsets + axom::exclusive_scan(sizesView, offsetsView); - // Flatten the map to fill zones_view and make offsets - axom::IndexType offset = 0, zi = 0, oi = 0; - for(auto it = n2z.begin(); it != n2z.end(); it++) + axom::for_all(nnodes, AXOM_LAMBDA(auto index) { - offsets_view[oi++] = offset; - const auto n = it->second.size(); - for(size_t i = 0; i < n; i++) zones_view[zi++] = it->second[i]; - offset += n; - } + sizesView[index] = 0; + }); + + axom::Array zcopy(zonesView.size(), zonesView.size(), allocatorID); + axom::copy(zcopy.data(), zonesView.data(), zonesView.size() * sizeof(value_type)); + auto zcopyView = zcopy.view(); + + // Fill in zonesView, sizesView with each node's zones. + axom::for_all(nodesView.size(), AXOM_LAMBDA(auto index) + { + const auto ni = nodesView[index]; + const auto destOffset = offsetsView[ni] + sizesView[ni]; + zonesView[destOffset] = zcopyView[index]; + sizesView[ni]++; // Works only because ExecSpace=SEQ_EXEC. + }); } }; @@ -234,21 +263,13 @@ class NodeToZoneRelationBuilder } }); - // Make the relation, outputting into the zonesView and offsetsView. + // Make the relation. using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute( + details::BuildRelation::execute( connectivityView, zonesView, + sizesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); }); }); } @@ -286,21 +307,13 @@ class NodeToZoneRelationBuilder n_sizes, n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { - // Make the relation, outputting into the zonesView and offsetsView. + // Make the relation. using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute( + details::BuildRelation::execute( connectivityView, zonesView, + sizesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); }); } else @@ -326,21 +339,13 @@ class NodeToZoneRelationBuilder AXOM_LAMBDA(axom::IndexType index) { zonesView[index] = index / nodesPerShape; }); - // Make the relation, outputting into the zonesView and offsetsView. + // Make the relation. using ViewType = decltype(connectivityView); - details::FillZonesAndOffsets::execute( + details::BuildRelation::execute( connectivityView, zonesView, + sizesView, offsetsView); - - // Compute sizes from offsets. - axom::for_all( - offsetsView.size(), - AXOM_LAMBDA(auto i) { - sizesView[i] = (i < offsetsView.size() - 1) - ? (offsetsView[i + 1] - offsetsView[i]) - : (connSize - offsetsView[i]); - }); }); } } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index eb162cb4cc..617f469718 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -452,10 +452,10 @@ void copy(conduit::Node &dest, const conduit::Node &src) * * \return A pair containing the min,max values in the node. */ -template -std::pair minmax(const conduit::Node &n) +template +std::pair minmax(const conduit::Node &n) { - std::pair retval {0., 0.}; + std::pair retval; axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { using value_type = typename decltype(nview)::value_type; @@ -474,7 +474,7 @@ std::pair minmax(const conduit::Node &n) vmax.max(nview[index]); }); - retval = std::pair {vmin.get(), vmax.get()}; + retval = std::pair {static_cast(vmin.get()), static_cast(vmax.get())}; }); return retval; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 5dee62d076..08e776d7e8 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -63,25 +63,25 @@ void test_copy_braid(const conduit::Node &mesh) constexpr double eps = 1.e-7; - auto x = axom::mir::utilities::blueprint::minmax( + auto x = axom::mir::utilities::blueprint::minmax( mesh["coordsets/coords/values/x"]); //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; EXPECT_NEAR(x.first, -10., eps); EXPECT_NEAR(x.second, 10., eps); - auto y = axom::mir::utilities::blueprint::minmax( + auto y = axom::mir::utilities::blueprint::minmax( mesh["coordsets/coords/values/y"]); //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; EXPECT_NEAR(y.first, -10., eps); EXPECT_NEAR(y.second, 10., eps); - auto c = axom::mir::utilities::blueprint::minmax( + auto c = axom::mir::utilities::blueprint::minmax( mesh["topologies/mesh/elements/connectivity"]); //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; EXPECT_NEAR(c.first, 0., eps); EXPECT_NEAR(c.second, 999., eps); - auto r = axom::mir::utilities::blueprint::minmax( + auto r = axom::mir::utilities::blueprint::minmax( mesh["fields/radial/values"]); //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; EXPECT_NEAR(r.first, 19.2450089729875, eps); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 3a2721ab91..e61ac24060 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -508,6 +508,8 @@ struct Unique } }; +//------------------------------------------------------------------------------ +/// Partial specialization for SEQ_EXEC. template struct Unique { @@ -521,10 +523,7 @@ struct Unique for(; index < n; index++) { const auto k = keys_orig_view[index]; - //if(unique.find(k) == unique.end()) - { - unique[k] = index; - } + unique[k] = index; } // Allocate the output arrays. const axom::IndexType newsize = unique.size(); From 85b6dc4d29018bc73eff329e06f60d1011c347f6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 4 Sep 2024 17:23:36 -0700 Subject: [PATCH 202/290] Skip a little work with 1 blend value. --- src/axom/mir/CoordsetBlender.hpp | 23 +++++++++++++++++------ src/axom/mir/FieldBlender.hpp | 27 ++++++++++++++++++--------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 9fca6dce2d..0bc7ebe065 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -88,6 +88,7 @@ class CoordsetBlender // Iterate over each blend group. const BlendData blendDevice(blend); + const CoordsetViewType viewDevice(view); axom::for_all( outputSize, AXOM_LAMBDA(auto bgid) { @@ -95,15 +96,25 @@ class CoordsetBlender const auto selectedIndex = SelectionPolicy::selectedIndex(blendDevice, bgid); const auto start = blendDevice.m_blendGroupStartView[selectedIndex]; - const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; + const auto nValues = blendDevice.m_blendGroupSizesView[selectedIndex]; - // Blend points for this blend group. VectorType blended {}; - for(IndexType i = start; i < end; i++) + if(nValues == 1) { - const auto index = blendDevice.m_blendIdsView[i]; - const auto weight = blendDevice.m_blendCoeffView[i]; - blended += (VectorType(view[index]) * static_cast(weight)); + const auto index = blendDevice.m_blendIdsView[start]; + blended = VectorType(viewDevice[index]); + } + else + { + const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; + + // Blend points for this blend group. + for(IndexType i = start; i < end; i++) + { + const auto index = blendDevice.m_blendIdsView[i]; + const auto weight = blendDevice.m_blendCoeffView[i]; + blended += (VectorType(viewDevice[index]) * static_cast(weight)); + } } // Store the point into the Conduit component arrays. diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index b1302caf8c..d653ad8637 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -164,19 +164,28 @@ class FieldBlender const auto selectedIndex = SelectionPolicy::selectedIndex(deviceBlend, bgid); const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; - const auto end = - start + deviceBlend.m_blendGroupSizesView[selectedIndex]; + const auto nValues = deviceBlend.m_blendGroupSizesView[selectedIndex]; - accum_type blended = 0; - for(IndexType i = start; i < end; i++) + if(nValues == 1) { - const auto index = deviceBlend.m_blendIdsView[i]; - const auto weight = deviceBlend.m_blendCoeffView[i]; + const auto index = deviceBlend.m_blendIdsView[start]; const auto transformedIndex = deviceIndexing[index]; - blended += - static_cast(compView[transformedIndex]) * weight; + outView[bgid] = compView[transformedIndex]; + } + else + { + const auto end = start + nValues; + accum_type blended = 0; + for(IndexType i = start; i < end; i++) + { + const auto index = deviceBlend.m_blendIdsView[i]; + const auto weight = deviceBlend.m_blendCoeffView[i]; + const auto transformedIndex = deviceIndexing[index]; + blended += + static_cast(compView[transformedIndex]) * weight; + } + outView[bgid] = static_cast(blended); } - outView[bgid] = static_cast(blended); }); }); } From 3ee18aeef8f910a0d762448dbb4b5c0e435a8f69 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 6 Sep 2024 10:52:47 -0700 Subject: [PATCH 203/290] Start of algorithm change to reduce blend group count. Not enabled right now. --- src/axom/mir/BlendGroupBuilder.hpp | 94 +++++++++++ src/axom/mir/ClipField.hpp | 248 +++++++++++++++++++++++++++-- src/axom/mir/CoordsetBlender.hpp | 50 ++++-- src/axom/mir/FieldBlender.hpp | 52 ++++-- src/axom/mir/RecenterField.hpp | 2 +- 5 files changed, 404 insertions(+), 42 deletions(-) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index cb9661c2af..9f12b885aa 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -271,6 +271,30 @@ class BlendGroupBuilder return w; } + /** + * \brief Return the index'th weight in the blend group. + * \note The blendGroup must have finished construction. + * \return The index'th weight. + */ + AXOM_HOST_DEVICE + inline float weight(int index) const + { + const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; + return m_state->m_blendCoeffView[start + index]; + } + + /** + * \brief Return the index'th weight in the blend group. + * \note The blendGroup must have finished construction. + * \return The index'th weight. + */ + AXOM_HOST_DEVICE + inline IndexType id(int index) const + { + const auto start = m_state->m_blendGroupStartView[m_blendGroupId]; + return m_state->m_blendIdsView[start + index]; + } + /** * \brief Return the name of the current blend group. * \return The name of the current blend group. @@ -403,6 +427,76 @@ class BlendGroupBuilder return groups; } + /** + * \brief Filter out single node blend groups from the unique. + * + * \param blend The BlendData that describes the blend groups. + * \param[out] newSelectedIndices An array that will contain the data for the + * new selected indices, if we need to make it. + */ + void filterUnique(axom::Array &newUniqueNames, axom::Array &newUniqueIndices) + { + AXOM_ANNOTATE_SCOPE("filterUnique"); + using reduce_policy = typename axom::execution_space::reduce_policy; + const auto nIndices = m_state.m_blendUniqueIndicesView.size(); +std::cout << "filterUnique: nIndices=" << nIndices << std::endl; + + if(nIndices > 0) + { + const int allocatorID = axom::execution_space::allocatorID(); + + // Make a mask of selected indices have more than one id in their blend group. + axom::Array mask(nIndices, nIndices, allocatorID); + auto maskView = mask.view(); + RAJA::ReduceSum mask_reduce(0); + State deviceState(m_state); + axom::for_all(nIndices, AXOM_LAMBDA(auto index) + { + const auto uniqueIndex = deviceState.m_blendUniqueIndicesView[index]; + const int m = (deviceState.m_blendGroupSizesView[uniqueIndex] > 1) ? 1 : 0; +if(m == 0) +{ +std::cout << "(" << uniqueIndex << ", " << deviceState.m_blendGroupSizesView[uniqueIndex] << ", " << m << "), "; +} + maskView[index] = m; + mask_reduce += m; + }); +std::cout << std::endl; + // If we need to filter, do it. + const int mask_count = mask_reduce.get(); +std::cout << "mask_count = " << mask_count << std::endl; + if(mask_count < nIndices) + { +std::cout << "Making new selected indices" << std::endl; + + // Make offsets. + axom::Array offset(nIndices, nIndices, allocatorID); + auto offsetView = offset.view(); + axom::exclusive_scan(maskView, offsetView); + + // Make new unique data where we compress out blend groups that had 1 node. + newUniqueNames = axom::Array(mask_count, mask_count, allocatorID); + newUniqueIndices = axom::Array(mask_count, mask_count, allocatorID); + + auto newUniqueNamesView = newUniqueNames.view(); + auto newUniqueIndicesView = newUniqueIndices.view(); + axom::for_all(nIndices, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + const auto offset = offsetView[index]; + newUniqueNamesView[offset] = deviceState.m_blendUniqueNamesView[index]; + newUniqueIndicesView[offset] = deviceState.m_blendUniqueIndicesView[index]; + } + }); + + // Replace the unique names/indices. + m_state.m_blendUniqueNamesView = newUniqueNamesView; + m_state.m_blendUniqueIndicesView = newUniqueIndicesView; + } + } + } + /** * \brief Make a BlendData object from the views in this object. * diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 4e436016ae..93ef09f200 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -24,13 +24,24 @@ #include #include #include +#include #include #include +// Enable code to save some debugging output files. //#define AXOM_DEBUG_CLIP_FIELD + +// Filter out degenerate zones in 2D. #define AXOM_CLIP_FILTER_DEGENERATES +// Enable code to reduce the number of blend groups by NOT emitting 1-node +// blend groups and instead using a node lookup field for those. This does +// seem to have some issues producing the same node id for edge blend groups +// and original nodes. +//#define AXOM_REDUCE_BLEND_GROUPS +//#define AXOM_REDUCE_BLEND_GROUPS_DEBUGGING + namespace axom { namespace mir @@ -465,6 +476,15 @@ class ClipField zoneData.m_clipCasesView = clipCases.view(); zoneData.m_pointsUsedView = pointsUsed.view(); + // Allocate some memory and store views in NodeData. + NodeData nodeData; +#if defined(AXOM_REDUCE_BLEND_GROUPS) + const auto nnodes = m_coordsetView.numberOfNodes(); + axom::Array nodeUsed(nnodes, nnodes, allocatorID); + nodeData.m_nodeUsedView = nodeUsed.view(); +#endif + + // Allocate some memory and store views in FragmentData. axom::Array fragments( nzones, nzones, @@ -509,10 +529,52 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, builder, zoneData, fragmentData, opts, selectedZones); + computeSizes(clipTableViews, builder, zoneData, nodeData, fragmentData, opts, selectedZones); computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); + // Compute original node count that we're preserving, make node maps. +#if defined(AXOM_REDUCE_BLEND_GROUPS) + const int compactSize = countOriginalNodes(nodeData); + axom::Array compactNodes(compactSize, compactSize, allocatorID); + axom::Array oldNodeToNewNode(nnodes, nnodes, allocatorID); + nodeData.m_originalIdsView = compactNodes.view(); + nodeData.m_oldNodeToNewNodeView = oldNodeToNewNode.view(); + createNodeMaps(nodeData); +#endif +#if 0 + conduit::Node tmpMesh; + tmpMesh[n_coordset.path()].set_external(n_coordset); + tmpMesh[n_topo.path()].set_external(n_topo); + tmpMesh["fields/nodeUsed/topology"] = n_topo.name(); + tmpMesh["fields/nodeUsed/association"] = "vertex"; + tmpMesh["fields/nodeUsed/values"].set_external(nodeData.m_nodeUsedView.data(), nodeData.m_nodeUsedView.size()); + + tmpMesh["fields/oldNodeToNewNode/topology"] = n_topo.name(); + tmpMesh["fields/oldNodeToNewNode/association"] = "vertex"; + tmpMesh["fields/oldNodeToNewNode/values"].set_external(nodeData.m_oldNodeToNewNodeView.data(), nodeData.m_oldNodeToNewNodeView.size()); + + // Make filename + int count = 0; + std::string path; + do + { + std::stringstream ss; + ss << "clipfield." << count; + path = ss.str(); + count++; + } while(axom::utilities::filesystem::pathExists(path + ".root")); + + // Save data. + conduit::relay::io::blueprint::save_mesh(tmpMesh, path, "hdf5"); + tmpMesh.print(); +#endif +#if defined(AXOM_REDUCE_BLEND_GROUPS) + nodeUsed.clear(); + nodeData.m_nodeUsedView = axom::ArrayView(); +#endif + + // Further initialize the blend group builder. IndexType blendGroupsSize = 0, blendGroupLenSize = 0; builder.computeBlendGroupSizes(blendGroupsSize, blendGroupLenSize); builder.setBlendGroupOffsets(blendOffset.view(), blendGroupOffsets.view()); @@ -546,6 +608,10 @@ class ClipField // Make the blend groups unique axom::Array uNames; axom::Array uIndices; +#if defined(AXOM_REDUCE_BLEND_GROUPS) + axom::Array newUniqueNames; + axom::Array newUniqueIndices; +#endif { AXOM_ANNOTATE_SCOPE("unique"); axom::mir::utilities::Unique::execute( @@ -553,19 +619,35 @@ class ClipField uNames, uIndices); builder.setUniqueNames(uNames.view(), uIndices.view()); + +// NOTE TO SELF: This is new code that I'm trying out to remove single-node +// blend groups from the selected unique blend groups. It's not +// quite working yet. +#if 0//defined(AXOM_REDUCE_BLEND_GROUPS) + // Filter the unique names/indices to remove single node blend groups. + builder.filterUnique(newUniqueNames, newUniqueIndices); + uNames.clear(); + uIndices.clear(); +#endif } + + // Make BlendData. bputils::BlendData blend = builder.makeBlendData(); + blend.m_originalIdsView = nodeData.m_originalIdsView; // Make the clipped mesh makeTopology(clipTableViews, builder, zoneData, + nodeData, fragmentData, opts, selectedZones, n_newTopo, n_newCoordset, n_newFields); + + // Make the coordset makeCoordset(blend, n_coordset, n_newCoordset); // Get the fields that we want to process. @@ -663,6 +745,16 @@ class ClipField axom::ArrayView m_pointsUsedView {}; }; + /** + * \brief Contains some per-node data that we want to hold onto between methods. + */ + struct NodeData + { + axom::ArrayView m_nodeUsedView {}; + axom::ArrayView m_oldNodeToNewNodeView {}; + axom::ArrayView m_originalIdsView {}; + }; + /** * \brief Make a bitset that indicates the parts of the selection that are selected. */ @@ -711,6 +803,7 @@ class ClipField * \param[in] clipTableViews An object that holds views of the clipping table data. * \param[in] builder This object holds views to blend group data and helps with building/access. * \param[in] zoneData This object holds views to per-zone data. + * \param[in] nodeData This object holds views to per-node data. * \param[in] fragmentData This object holds views to per-fragment data. * \param[inout] opts Clipping options. * @@ -719,6 +812,7 @@ class ClipField void computeSizes(ClipTableViews clipTableViews, BlendGroupBuilderType builder, ZoneData zoneData, + NodeData nodeData, FragmentData fragmentData, const ClipOptions &opts, const SelectedZones &selectedZones) const @@ -729,6 +823,12 @@ class ClipField auto blendGroupsView = builder.state().m_blendGroupsView; auto blendGroupsLenView = builder.state().m_blendGroupsLenView; + // Initialize nodeUsed data for nodes. + axom::for_all(nodeData.m_nodeUsedView.size(), AXOM_LAMBDA(auto index) + { + nodeData.m_nodeUsedView[index] = 0; + }); + const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( selectedZones.view(), @@ -808,6 +908,20 @@ class ClipField // Save the flags for the points that were used in this zone zoneData.m_pointsUsedView[szIndex] = ptused; +#if defined(AXOM_REDUCE_BLEND_GROUPS) + // NOTE: We are not going to emit blend groups for P0..P7 points. + + // If the zone uses a node, set that node in nodeUsedView. + for(IndexType pid = P0; pid <= P7; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto nodeId = zone.getId(pid); + // NOTE: Multiple threads may write to this node but they all write the same value. + nodeData.m_nodeUsedView[nodeId] = 1; + } + } +#else // Count which points in the original cell are used. for(IndexType pid = P0; pid <= P7; pid++) { @@ -816,6 +930,7 @@ class ClipField thisBlendGroupLen += incr; // {p0} thisBlendGroups += incr; } +#endif // Count edges that are used. for(IndexType pid = EA; pid <= EL; pid++) @@ -910,6 +1025,71 @@ class ClipField #endif } +#if defined(AXOM_REDUCE_BLEND_GROUPS) + /** + * \brief Counts the number of original nodes used by the selected fragments. + * + * \param nodeData The node data (passed by value on purpose) + * \return The number of original nodes used by selected fragments. + */ + int countOriginalNodes(NodeData nodeData) const + { + AXOM_ANNOTATE_SCOPE("countOriginalNodes"); + // Count the number of original nodes we'll use directly. + RAJA::ReduceSum nUsed_reducer(0); + const auto nodeUsedView = nodeData.m_nodeUsedView; + axom::for_all(nodeUsedView.size(), AXOM_LAMBDA(auto index) + { + nUsed_reducer += nodeUsedView[index]; + }); + return nUsed_reducer.get(); + } + + /** + * \brief Creates the node lists/maps. + * + * \param nodeData The node data that contains views where the node data is stored. + */ + void createNodeMaps(NodeData nodeData) const + { + AXOM_ANNOTATE_SCOPE("createNodeMaps"); + const int allocatorID = axom::execution_space::allocatorID(); + + // Make offsets into a compact array. + const auto nnodes = nodeData.m_nodeUsedView.size(); + axom::Array nodeOffsets(nnodes, nnodes, allocatorID); + auto nodeOffsetsView = nodeOffsets.view(); + axom::exclusive_scan(nodeData.m_nodeUsedView, nodeOffsetsView); + + // Make the compact node list and oldToNew map. + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + IndexType newId = 0; + if(nodeData.m_nodeUsedView[index] > 0) + { + nodeData.m_originalIdsView[nodeOffsetsView[index]] = index; + newId = index; + } + nodeData.m_oldNodeToNewNodeView[index] = newId; + }); + +#if defined(AXOM_DEBUG_CLIP_FIELD) + std::cout << "---------------------------- createNodeMaps " + "----------------------------" + << std::endl; + details::printHost("nodeData.m_nodeUsedView", + nodeData.m_nodeUsedView); + details::printHost("nodeData.m_originalIdsView", + nodeData.m_originalIdsView); + details::printHost("nodeData.m_oldNodeToNewNodeView", + nodeData.m_oldNodeToNewNodeView); + std::cout << "-------------------------------------------------------------" + "-----------" + << std::endl; +#endif + } +#endif + /** * \brief Fill in the data for the blend group views. * @@ -991,8 +1171,9 @@ class ClipField } } } - +#if !defined(AXOM_REDUCE_BLEND_GROUPS) // Add blend group for each original point that was used. + // NOTE - this can add a lot of blend groups with 1 node. for(IndexType pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) @@ -1002,7 +1183,7 @@ class ClipField groups.endGroup(); } } - +#endif // Add blend group for each edge point that was used. for(IndexType pid = EA; pid <= EL; pid++) { @@ -1046,6 +1227,7 @@ class ClipField * \param[in] clipTableViews An object that holds views of the clipping table data. * \param[in] builder This object holds views to blend group data and helps with building/access. * \param[in] zoneData This object holds views to per-zone data. + * \param[in] nodeData This object holds views to per-node data. * \param[in] fragmentData This object holds views to per-fragment data. * \param[in] opts Clipping options. * \param[in] selectedZones The selected zones. @@ -1058,6 +1240,7 @@ class ClipField void makeTopology(ClipTableViews clipTableViews, BlendGroupBuilderType builder, ZoneData zoneData, + NodeData nodeData, FragmentData fragmentData, const ClipOptions &opts, const SelectedZones &selectedZones, @@ -1143,6 +1326,7 @@ class ClipField #endif { AXOM_ANNOTATE_SCOPE("build"); + const auto origSize = nodeData.m_originalIdsView.size(); m_topologyView.template for_selected_zones( selectedZones.view(), AXOM_LAMBDA(auto szIndex, @@ -1153,7 +1337,9 @@ class ClipField // Seek to the start of the blend groups for this zone. auto groups = builder.blendGroupsForZone(szIndex); - +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) +std::cout << "zone " << szIndex << std::endl; +#endif // Go through the points in the order they would have been added as blend // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index @@ -1164,23 +1350,62 @@ class ClipField { if(axom::utilities::bitIsSet(ptused, pid)) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); + point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) +std::cout << "\tpoint N" << (pid - N0) << " -> " << point_2_new[pid] << std::endl; +#endif groups++; } } +#if defined(AXOM_REDUCE_BLEND_GROUPS) + // For single nodes, we did not make a blend group. We look up the new + // node id from nodeData.m_oldNodeToNewNodeView. + for(BitSet pid = P0; pid <= P7; pid++) + { + if(axom::utilities::bitIsSet(ptused, pid)) + { + const auto nodeId = zone.getId(pid); + point_2_new[pid] = nodeData.m_oldNodeToNewNodeView[nodeId]; +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) +std::cout << "\tpoint P" << pid << " -> " << point_2_new[pid] << std::endl; +#endif + } + } +#else for(BitSet pid = P0; pid <= P7; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); + point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); groups++; } } +#endif for(BitSet pid = EA; pid <= EL; pid++) { if(axom::utilities::bitIsSet(ptused, pid)) { - point_2_new[pid] = groups.uniqueBlendGroupIndex(); +#if defined(AXOM_REDUCE_BLEND_GROUPS) + // There is a chance that the edge blend group was emitted with a + // single node if the edge was really close to a corner node. + if(groups.size() == 1) + { + const auto nodeId = groups.id(0); + point_2_new[pid] = nodeData.m_oldNodeToNewNodeView[nodeId]; +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) +std::cout << "\tpoint E" << static_cast(('A' + (pid - EA))) << " -> " << point_2_new[pid] << " (size=1)" << std::endl; +#endif + } + else + { + point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) +std::cout << "\tpoint E" << static_cast(('A' + (pid - EA))) << " -> " << point_2_new[pid] << " (size>1)" << std::endl; +#endif + } +#else + point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); +#endif groups++; } } @@ -1210,12 +1435,15 @@ class ClipField { // Output the nodes used in this zone. const int fragmentSize = fragment.size(); - //#if defined(AXOM_CLIP_FILTER_DEGENERATES) - // int connStart = outputIndex; - //#endif offsetsView[sizeIndex] = outputIndex; for(int i = 2; i < fragmentSize; i++) connView[outputIndex++] = point_2_new[fragment[i]]; +#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) + std::cout << "fragment: "; + for(int i = 2; i < fragmentSize; i++) + std::cout << point_2_new[fragment[i]] << ", "; + std::cout << std::endl; +#endif const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 0bc7ebe065..6a779fff17 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -71,8 +71,12 @@ class CoordsetBlender n_output["type"] = "explicit"; conduit::Node &n_values = n_output["values"]; + // Determine output size. + const auto origSize = blend.m_originalIdsView.size(); + const auto blendSize = SelectionPolicy::size(blend); + const auto outputSize = origSize + blendSize; + // Make output nodes using axis names from the input coordset. Make array views too. - const auto outputSize = SelectionPolicy::size(blend); axom::StackArray, PointType::DIMENSION> compViews; for(size_t i = 0; i < nComponents; i++) { @@ -86,41 +90,57 @@ class CoordsetBlender compViews[i] = axom::ArrayView(comp_data, outputSize); } - // Iterate over each blend group. - const BlendData blendDevice(blend); - const CoordsetViewType viewDevice(view); + const CoordsetViewType deviceView(view); + const BlendData deviceBlend(blend); + + // Copy over some original values to the start of the array. + axom::for_all( + origSize, + AXOM_LAMBDA(auto index) { + const auto srcIndex = deviceBlend.m_originalIdsView[index]; + const auto pt = deviceView[srcIndex]; + + // Store the point into the Conduit component arrays. + for(int comp = 0; comp < PointType::DIMENSION; comp++) + { + compViews[comp][index] = pt[comp]; + } + }); + + // Append blended values to the end of the array. axom::for_all( - outputSize, + blendSize, AXOM_LAMBDA(auto bgid) { // Get the blend group index we want. const auto selectedIndex = - SelectionPolicy::selectedIndex(blendDevice, bgid); - const auto start = blendDevice.m_blendGroupStartView[selectedIndex]; - const auto nValues = blendDevice.m_blendGroupSizesView[selectedIndex]; + SelectionPolicy::selectedIndex(deviceBlend, bgid); + const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; + const auto nValues = deviceBlend.m_blendGroupSizesView[selectedIndex]; + const auto destIndex = bgid + origSize; VectorType blended {}; if(nValues == 1) { - const auto index = blendDevice.m_blendIdsView[start]; - blended = VectorType(viewDevice[index]); + const auto index = deviceBlend.m_blendIdsView[start]; + blended = VectorType(deviceView[index]); } else { - const auto end = start + blendDevice.m_blendGroupSizesView[selectedIndex]; + const auto end = start + deviceBlend.m_blendGroupSizesView[selectedIndex]; // Blend points for this blend group. for(IndexType i = start; i < end; i++) { - const auto index = blendDevice.m_blendIdsView[i]; - const auto weight = blendDevice.m_blendCoeffView[i]; - blended += (VectorType(viewDevice[index]) * static_cast(weight)); + const auto index = deviceBlend.m_blendIdsView[i]; + const auto weight = deviceBlend.m_blendCoeffView[i]; + blended += (VectorType(deviceView[index]) * static_cast(weight)); } } // Store the point into the Conduit component arrays. for(int comp = 0; comp < PointType::DIMENSION; comp++) { - compViews[comp][bgid] = blended[comp]; + compViews[comp][destIndex] = blended[comp]; } }); } diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index d653ad8637..b5831c7df7 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -21,17 +21,26 @@ namespace utilities namespace blueprint { /** - * \brief This class contains views of blend data. + * \brief This class contains views of blend data. Blend data lets is make new + * nodal fields and coordsets. The field data are sampled using m_originalIdsView + * which is a compact list of the original node ids that we want to preserve + * without any blending. This stream is followed by a second stream of data + * made using the field and the blend groups. Each blend group has + * m_blendGroupSizesView[i] elements, starts at m_blendGroupStartView[i] and + * uses values from the m_blendIdsView, m_blendCoeffView members to blend the + * data values. + * */ struct BlendData { + axom::ArrayView m_originalIdsView; // Contains indices of original node ids to be preserved. + axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. - axom::ArrayView - m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. - axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups - axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. + axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; /** @@ -140,7 +149,9 @@ class FieldBlender { // We're allowing selectedIndicesView to be used to select specific blend // groups. If the user did not provide that, use all blend groups. - const auto outputSize = SelectionPolicy::size(blend); + const auto origSize = blend.m_originalIdsView.size(); + const auto blendSize = SelectionPolicy::size(blend); + const auto outputSize = origSize + blendSize; // Allocate Conduit data through Axom. utilities::blueprint::ConduitAllocateThroughAxom c2a; @@ -155,22 +166,32 @@ class FieldBlender using accum_type = typename axom::mir::utilities::accumulation_traits::value_type; - IndexingPolicy deviceIndexing(m_indexing); + const IndexingPolicy deviceIndexing(m_indexing); const BlendData deviceBlend(blend); + + // Copy over some original values to the start of the array. axom::for_all( - outputSize, + origSize, + AXOM_LAMBDA(auto index) { + const auto srcIndex = deviceBlend.m_originalIdsView[index]; + outView[index] = compView[srcIndex]; + }); + + // Append blended values to the end of the array. + axom::for_all( + blendSize, AXOM_LAMBDA(auto bgid) { - // Get the index we want. + // Get the blend group index we want. const auto selectedIndex = SelectionPolicy::selectedIndex(deviceBlend, bgid); const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; const auto nValues = deviceBlend.m_blendGroupSizesView[selectedIndex]; - + const auto destIndex = origSize + bgid; if(nValues == 1) { const auto index = deviceBlend.m_blendIdsView[start]; - const auto transformedIndex = deviceIndexing[index]; - outView[bgid] = compView[transformedIndex]; + const auto srcIndex = deviceIndexing[index]; + outView[destIndex] = compView[srcIndex]; } else { @@ -180,11 +201,10 @@ class FieldBlender { const auto index = deviceBlend.m_blendIdsView[i]; const auto weight = deviceBlend.m_blendCoeffView[i]; - const auto transformedIndex = deviceIndexing[index]; - blended += - static_cast(compView[transformedIndex]) * weight; + const auto srcIndex = deviceIndexing[index]; + blended += static_cast(compView[srcIndex]) * weight; } - outView[bgid] = static_cast(blended); + outView[destIndex] = static_cast(blended); } }); }); diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 564e9f9803..483821c7cf 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -99,9 +99,9 @@ void RecenterField::recenterSingleComponent( n_sizes, n_offsets, [&](auto relView, auto sizesView, auto offsetsView) { - const auto relSize = sizesView.size(); // Allocate Conduit data through Axom. + const auto relSize = sizesView.size(); utilities::blueprint::ConduitAllocateThroughAxom c2a; n_out.set_allocator(c2a.getConduitAllocatorID()); n_out.set(conduit::DataType(n_comp.dtype().id(), relSize)); From 1bfbd241a6489e84a84e8312e1aafcf048830089 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 6 Sep 2024 16:24:03 -0700 Subject: [PATCH 204/290] make style --- src/axom/mir/BlendGroupBuilder.hpp | 74 +++++++++++-------- src/axom/mir/ClipField.hpp | 86 +++++++++------------- src/axom/mir/CoordsetBlender.hpp | 9 ++- src/axom/mir/FieldBlender.hpp | 16 ++-- src/axom/mir/NodeToZoneRelationBuilder.hpp | 65 ++++++++-------- src/axom/mir/RecenterField.hpp | 1 - src/axom/mir/blueprint_utilities.hpp | 4 +- src/axom/mir/tests/mir_clipfield.cpp | 8 +- 8 files changed, 129 insertions(+), 134 deletions(-) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index 9f12b885aa..6faa746fb9 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -183,7 +183,7 @@ class BlendGroupBuilder * \return The number of blend groups for this zone. */ AXOM_HOST_DEVICE - inline IndexType size() const + inline IndexType numGroups() const { return m_state->m_blendGroupsView[m_zoneIndex]; } @@ -196,7 +196,7 @@ class BlendGroupBuilder * \param blendGroupSize The size of all of the blend groups in this zone. */ AXOM_HOST_DEVICE - inline void setSize(IndexType nBlendGroups, IndexType blendGroupsSize) + inline void setNumGroups(IndexType nBlendGroups, IndexType blendGroupsSize) { m_state->m_blendGroupsView[m_zoneIndex] = nBlendGroups; m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; @@ -271,6 +271,17 @@ class BlendGroupBuilder return w; } + /** + * \brief Return the size for the current blend group. + * \note The blendGroup must have finished construction. + * \return The size of the current blend group. + */ + AXOM_HOST_DEVICE + inline IndexType size() const + { + return m_state->m_blendGroupSizesView[m_blendGroupId]; + } + /** * \brief Return the index'th weight in the blend group. * \note The blendGroup must have finished construction. @@ -434,12 +445,13 @@ class BlendGroupBuilder * \param[out] newSelectedIndices An array that will contain the data for the * new selected indices, if we need to make it. */ - void filterUnique(axom::Array &newUniqueNames, axom::Array &newUniqueIndices) + void filterUnique(axom::Array &newUniqueNames, + axom::Array &newUniqueIndices) { AXOM_ANNOTATE_SCOPE("filterUnique"); - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; const auto nIndices = m_state.m_blendUniqueIndicesView.size(); -std::cout << "filterUnique: nIndices=" << nIndices << std::endl; if(nIndices > 0) { @@ -450,45 +462,45 @@ std::cout << "filterUnique: nIndices=" << nIndices << std::endl; auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); State deviceState(m_state); - axom::for_all(nIndices, AXOM_LAMBDA(auto index) - { - const auto uniqueIndex = deviceState.m_blendUniqueIndicesView[index]; - const int m = (deviceState.m_blendGroupSizesView[uniqueIndex] > 1) ? 1 : 0; -if(m == 0) -{ -std::cout << "(" << uniqueIndex << ", " << deviceState.m_blendGroupSizesView[uniqueIndex] << ", " << m << "), "; -} - maskView[index] = m; - mask_reduce += m; - }); -std::cout << std::endl; + axom::for_all( + nIndices, + AXOM_LAMBDA(auto index) { + const auto uniqueIndex = deviceState.m_blendUniqueIndicesView[index]; + const int m = + (deviceState.m_blendGroupSizesView[uniqueIndex] > 1) ? 1 : 0; + maskView[index] = m; + mask_reduce += m; + }); // If we need to filter, do it. const int mask_count = mask_reduce.get(); -std::cout << "mask_count = " << mask_count << std::endl; + if(mask_count < nIndices) { -std::cout << "Making new selected indices" << std::endl; - // Make offsets. axom::Array offset(nIndices, nIndices, allocatorID); auto offsetView = offset.view(); axom::exclusive_scan(maskView, offsetView); // Make new unique data where we compress out blend groups that had 1 node. - newUniqueNames = axom::Array(mask_count, mask_count, allocatorID); - newUniqueIndices = axom::Array(mask_count, mask_count, allocatorID); + newUniqueNames = + axom::Array(mask_count, mask_count, allocatorID); + newUniqueIndices = + axom::Array(mask_count, mask_count, allocatorID); auto newUniqueNamesView = newUniqueNames.view(); auto newUniqueIndicesView = newUniqueIndices.view(); - axom::for_all(nIndices, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - const auto offset = offsetView[index]; - newUniqueNamesView[offset] = deviceState.m_blendUniqueNamesView[index]; - newUniqueIndicesView[offset] = deviceState.m_blendUniqueIndicesView[index]; - } - }); + axom::for_all( + nIndices, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + const auto offset = offsetView[index]; + newUniqueNamesView[offset] = + deviceState.m_blendUniqueNamesView[index]; + newUniqueIndicesView[offset] = + deviceState.m_blendUniqueIndicesView[index]; + } + }); // Replace the unique names/indices. m_state.m_blendUniqueNamesView = newUniqueNamesView; diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 93ef09f200..39967d027e 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -36,11 +36,8 @@ #define AXOM_CLIP_FILTER_DEGENERATES // Enable code to reduce the number of blend groups by NOT emitting 1-node -// blend groups and instead using a node lookup field for those. This does -// seem to have some issues producing the same node id for edge blend groups -// and original nodes. -//#define AXOM_REDUCE_BLEND_GROUPS -//#define AXOM_REDUCE_BLEND_GROUPS_DEBUGGING +// blend groups and instead using a node lookup field for those. +#define AXOM_REDUCE_BLEND_GROUPS namespace axom { @@ -529,7 +526,13 @@ class ClipField builder.setBlendGroupSizes(blendGroups.view(), blendGroupsLen.view()); // Compute sizes and offsets - computeSizes(clipTableViews, builder, zoneData, nodeData, fragmentData, opts, selectedZones); + computeSizes(clipTableViews, + builder, + zoneData, + nodeData, + fragmentData, + opts, + selectedZones); computeFragmentSizes(fragmentData, selectedZones); computeFragmentOffsets(fragmentData); @@ -623,7 +626,7 @@ class ClipField // NOTE TO SELF: This is new code that I'm trying out to remove single-node // blend groups from the selected unique blend groups. It's not // quite working yet. -#if 0//defined(AXOM_REDUCE_BLEND_GROUPS) +#if defined(AXOM_REDUCE_BLEND_GROUPS) // Filter the unique names/indices to remove single node blend groups. builder.filterUnique(newUniqueNames, newUniqueIndices); uNames.clear(); @@ -750,7 +753,7 @@ class ClipField */ struct NodeData { - axom::ArrayView m_nodeUsedView {}; + axom::ArrayView m_nodeUsedView {}; axom::ArrayView m_oldNodeToNewNodeView {}; axom::ArrayView m_originalIdsView {}; }; @@ -824,10 +827,9 @@ class ClipField auto blendGroupsLenView = builder.state().m_blendGroupsLenView; // Initialize nodeUsed data for nodes. - axom::for_all(nodeData.m_nodeUsedView.size(), AXOM_LAMBDA(auto index) - { - nodeData.m_nodeUsedView[index] = 0; - }); + axom::for_all( + nodeData.m_nodeUsedView.size(), + AXOM_LAMBDA(auto index) { nodeData.m_nodeUsedView[index] = 0; }); const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( @@ -1038,10 +1040,9 @@ class ClipField // Count the number of original nodes we'll use directly. RAJA::ReduceSum nUsed_reducer(0); const auto nodeUsedView = nodeData.m_nodeUsedView; - axom::for_all(nodeUsedView.size(), AXOM_LAMBDA(auto index) - { - nUsed_reducer += nodeUsedView[index]; - }); + axom::for_all( + nodeUsedView.size(), + AXOM_LAMBDA(auto index) { nUsed_reducer += nodeUsedView[index]; }); return nUsed_reducer.get(); } @@ -1062,31 +1063,30 @@ class ClipField axom::exclusive_scan(nodeData.m_nodeUsedView, nodeOffsetsView); // Make the compact node list and oldToNew map. - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - IndexType newId = 0; - if(nodeData.m_nodeUsedView[index] > 0) - { - nodeData.m_originalIdsView[nodeOffsetsView[index]] = index; - newId = index; - } - nodeData.m_oldNodeToNewNodeView[index] = newId; - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { + IndexType newId = 0; + if(nodeData.m_nodeUsedView[index] > 0) + { + nodeData.m_originalIdsView[nodeOffsetsView[index]] = index; + newId = index; + } + nodeData.m_oldNodeToNewNodeView[index] = newId; + }); -#if defined(AXOM_DEBUG_CLIP_FIELD) + #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout << "---------------------------- createNodeMaps " "----------------------------" << std::endl; - details::printHost("nodeData.m_nodeUsedView", - nodeData.m_nodeUsedView); - details::printHost("nodeData.m_originalIdsView", - nodeData.m_originalIdsView); + details::printHost("nodeData.m_nodeUsedView", nodeData.m_nodeUsedView); + details::printHost("nodeData.m_originalIdsView", nodeData.m_originalIdsView); details::printHost("nodeData.m_oldNodeToNewNodeView", nodeData.m_oldNodeToNewNodeView); std::cout << "-------------------------------------------------------------" "-----------" << std::endl; -#endif + #endif } #endif @@ -1337,9 +1337,7 @@ class ClipField // Seek to the start of the blend groups for this zone. auto groups = builder.blendGroupsForZone(szIndex); -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) -std::cout << "zone " << szIndex << std::endl; -#endif + // Go through the points in the order they would have been added as blend // groups, get their blendName, and then overall index of that blendName // in uNames, the unique list of new dof names. That will be their index @@ -1351,9 +1349,6 @@ std::cout << "zone " << szIndex << std::endl; if(axom::utilities::bitIsSet(ptused, pid)) { point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) -std::cout << "\tpoint N" << (pid - N0) << " -> " << point_2_new[pid] << std::endl; -#endif groups++; } } @@ -1366,9 +1361,6 @@ std::cout << "\tpoint N" << (pid - N0) << " -> " << point_2_new[pid] << std::end { const auto nodeId = zone.getId(pid); point_2_new[pid] = nodeData.m_oldNodeToNewNodeView[nodeId]; -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) -std::cout << "\tpoint P" << pid << " -> " << point_2_new[pid] << std::endl; -#endif } } #else @@ -1392,16 +1384,10 @@ std::cout << "\tpoint P" << pid << " -> " << point_2_new[pid] << std::endl; { const auto nodeId = groups.id(0); point_2_new[pid] = nodeData.m_oldNodeToNewNodeView[nodeId]; -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) -std::cout << "\tpoint E" << static_cast(('A' + (pid - EA))) << " -> " << point_2_new[pid] << " (size=1)" << std::endl; -#endif } else { point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) -std::cout << "\tpoint E" << static_cast(('A' + (pid - EA))) << " -> " << point_2_new[pid] << " (size>1)" << std::endl; -#endif } #else point_2_new[pid] = origSize + groups.uniqueBlendGroupIndex(); @@ -1438,12 +1424,6 @@ std::cout << "\tpoint E" << static_cast(('A' + (pid - EA))) << " -> " << p offsetsView[sizeIndex] = outputIndex; for(int i = 2; i < fragmentSize; i++) connView[outputIndex++] = point_2_new[fragment[i]]; -#if defined(AXOM_REDUCE_BLEND_GROUPS_DEBUGGING) - std::cout << "fragment: "; - for(int i = 2; i < fragmentSize; i++) - std::cout << point_2_new[fragment[i]] << ", "; - std::cout << std::endl; -#endif const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 6a779fff17..18ef5a752e 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -105,7 +105,7 @@ class CoordsetBlender { compViews[comp][index] = pt[comp]; } - }); + }); // Append blended values to the end of the array. axom::for_all( @@ -126,14 +126,15 @@ class CoordsetBlender } else { - const auto end = start + deviceBlend.m_blendGroupSizesView[selectedIndex]; - + const auto end = + start + deviceBlend.m_blendGroupSizesView[selectedIndex]; // Blend points for this blend group. for(IndexType i = start; i < end; i++) { const auto index = deviceBlend.m_blendIdsView[i]; const auto weight = deviceBlend.m_blendCoeffView[i]; - blended += (VectorType(deviceView[index]) * static_cast(weight)); + blended += + (VectorType(deviceView[index]) * static_cast(weight)); } } diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index b5831c7df7..148b9924fa 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -33,14 +33,16 @@ namespace blueprint */ struct BlendData { - axom::ArrayView m_originalIdsView; // Contains indices of original node ids to be preserved. + axom::ArrayView + m_originalIdsView; // Contains indices of original node ids to be preserved. axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. - axom::ArrayView m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. - axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups - axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. + axom::ArrayView + m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; /** @@ -173,9 +175,9 @@ class FieldBlender axom::for_all( origSize, AXOM_LAMBDA(auto index) { - const auto srcIndex = deviceBlend.m_originalIdsView[index]; - outView[index] = compView[srcIndex]; - }); + const auto srcIndex = deviceBlend.m_originalIdsView[index]; + outView[index] = compView[srcIndex]; + }); // Append blended values to the end of the array. axom::for_all( diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index ba66c428a5..7b9f0f006c 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -74,8 +74,7 @@ struct BuildRelation axom::for_all( n, AXOM_LAMBDA(axom::IndexType i) { - maskView[i] = - (i >= 1) ? ((keysView[i] != keysView[i - 1]) ? 1 : 0) : 1; + maskView[i] = (i >= 1) ? ((keysView[i] != keysView[i - 1]) ? 1 : 0) : 1; }); // Do a scan on the mask array to build an offset array. @@ -104,7 +103,7 @@ struct BuildRelation sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (totalSize - offsetsView[i]); - }); + }); } }; @@ -125,34 +124,36 @@ struct BuildRelation // Count how many times a node is used. const auto nnodes = offsetsView.size(); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - sizesView[index] = 0; - }); - axom::for_all(nodesView.size(), AXOM_LAMBDA(auto index) - { - sizesView[nodesView[index]]++; // Works only because ExecSpace=SEQ_EXEC. - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); + axom::for_all( + nodesView.size(), + AXOM_LAMBDA(auto index) { + sizesView[nodesView[index]]++; // Works only because ExecSpace=SEQ_EXEC. + }); // Make offsets axom::exclusive_scan(sizesView, offsetsView); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - sizesView[index] = 0; - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); axom::Array zcopy(zonesView.size(), zonesView.size(), allocatorID); - axom::copy(zcopy.data(), zonesView.data(), zonesView.size() * sizeof(value_type)); + axom::copy(zcopy.data(), + zonesView.data(), + zonesView.size() * sizeof(value_type)); auto zcopyView = zcopy.view(); // Fill in zonesView, sizesView with each node's zones. - axom::for_all(nodesView.size(), AXOM_LAMBDA(auto index) - { - const auto ni = nodesView[index]; - const auto destOffset = offsetsView[ni] + sizesView[ni]; - zonesView[destOffset] = zcopyView[index]; - sizesView[ni]++; // Works only because ExecSpace=SEQ_EXEC. - }); + axom::for_all( + nodesView.size(), + AXOM_LAMBDA(auto index) { + const auto ni = nodesView[index]; + const auto destOffset = offsetsView[ni] + sizesView[ni]; + zonesView[destOffset] = zcopyView[index]; + sizesView[ni]++; // Works only because ExecSpace=SEQ_EXEC. + }); } }; @@ -309,11 +310,10 @@ class NodeToZoneRelationBuilder [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make the relation. using ViewType = decltype(connectivityView); - details::BuildRelation::execute( - connectivityView, - zonesView, - sizesView, - offsetsView); + details::BuildRelation::execute(connectivityView, + zonesView, + sizesView, + offsetsView); }); } else @@ -341,11 +341,10 @@ class NodeToZoneRelationBuilder }); // Make the relation. using ViewType = decltype(connectivityView); - details::BuildRelation::execute( - connectivityView, - zonesView, - sizesView, - offsetsView); + details::BuildRelation::execute(connectivityView, + zonesView, + sizesView, + offsetsView); }); } } diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 483821c7cf..a04d3baee2 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -99,7 +99,6 @@ void RecenterField::recenterSingleComponent( n_sizes, n_offsets, [&](auto relView, auto sizesView, auto offsetsView) { - // Allocate Conduit data through Axom. const auto relSize = sizesView.size(); utilities::blueprint::ConduitAllocateThroughAxom c2a; diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 617f469718..effd33339f 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -474,7 +474,9 @@ std::pair minmax(const conduit::Node &n) vmax.max(nview[index]); }); - retval = std::pair {static_cast(vmin.get()), static_cast(vmax.get())}; + retval = + std::pair {static_cast(vmin.get()), + static_cast(vmax.get())}; }); return retval; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index b0cc155ad1..a3a50034b7 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -232,9 +232,9 @@ TEST(mir_clipfield, blend_group_builder) //std::cout << "-------- zone 0 --------" << std::endl; auto z0 = builder.blendGroupsForZone(0); - EXPECT_EQ(z0.size(), 8); + EXPECT_EQ(z0.numGroups(), 8); IndexType index = 0; - for(IndexType i = 0; i < z0.size(); i++, index++) + for(IndexType i = 0; i < z0.numGroups(); i++, index++) { //z0.print(std::cout); EXPECT_EQ(z0.ids().size(), blendGroupSizes[index]); @@ -244,8 +244,8 @@ TEST(mir_clipfield, blend_group_builder) //std::cout << "-------- zone 1 --------" << std::endl; auto z1 = builder.blendGroupsForZone(1); - EXPECT_EQ(z1.size(), 5); - for(IndexType i = 0; i < z1.size(); i++, index++) + EXPECT_EQ(z1.numGroups(), 5); + for(IndexType i = 0; i < z1.numGroups(); i++, index++) { //z1.print(std::cout); EXPECT_EQ(z1.ids().size(), blendGroupSizes[index]); From 064588ed30f5c02591dda28413870eeb6293ee8f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 9 Sep 2024 18:32:16 -0700 Subject: [PATCH 205/290] Adding some new components for slicing matsets and extracting zones from a topology. --- src/axom/mir/CMakeLists.txt | 4 + src/axom/mir/CoordsetSlicer.hpp | 108 +++++ src/axom/mir/ExtractZones.hpp | 385 ++++++++++++++++++ src/axom/mir/MatsetSlicer.hpp | 129 ++++++ src/axom/mir/ZoneListBuilder.hpp | 169 ++++++++ .../mir/tests/mir_blueprint_utilities.cpp | 82 ++++ 6 files changed, 877 insertions(+) create mode 100644 src/axom/mir/CoordsetSlicer.hpp create mode 100644 src/axom/mir/ExtractZones.hpp create mode 100644 src/axom/mir/MatsetSlicer.hpp create mode 100644 src/axom/mir/ZoneListBuilder.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 43f56ca42f..b942646ebf 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -34,9 +34,12 @@ set(mir_headers BlendGroupBuilder.hpp CoordsetBlender.hpp + CoordsetSlicer.hpp EquiZAlgorithm.hpp + ExtractZones.hpp FieldBlender.hpp FieldSlicer.hpp + MatsetSlicer.hpp MIRAlgorithm.hpp RecenterField.hpp utilities.hpp @@ -45,6 +48,7 @@ set(mir_headers MIROptions.hpp Options.hpp ClipOptions.hpp + ZoneListBuilder.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp views/dispatch_rectilinear_topology.hpp diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/CoordsetSlicer.hpp new file mode 100644 index 0000000000..d0686ad4f9 --- /dev/null +++ b/src/axom/mir/CoordsetSlicer.hpp @@ -0,0 +1,108 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_COORDSET_SLICER_HPP_ +#define AXOM_MIR_COORDSET_SLICER_HPP_ + +#include "axom/core.hpp" +#include "axom/mir.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \accelerated + * \class CoordsetSlicer + * + * \brief This class uses SliceData to generate a new sliced coordset (pulling out specific points from input coordset). + * + * \tparam ExecSpace The execution space where the algorithm will run. + * + */ +template +class CoordsetSlicer +{ +public: + /// Constructor + CoordsetSlicer(const CoordsetView &coordsetView) : m_coordsetView(coordsetView) { } + + /** + * \brief Execute the slice on the \a n_input coordset and store the new sliced coordset in \a n_output. + * + * \param slice The slice data that indicates how the coordset will be sliced. + * \param n_input A Conduit node containing the coordset to be sliced. + * \param n_output A node that will contain the sliced coordset. + * + * \note We assume for now that n_input != n_output. + */ + void execute(const SliceData &slice, + const conduit::Node &n_input, + conduit::Node &n_output) + { + using value_type = typename CoordsetView::value_type; + using PointType = typename CoordsetView::PointType; + namespace bputils = axom::mir::utilities::blueprint; + + const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); + const auto nComponents = axes.size(); + SLIC_ASSERT(PointType::DIMENSION == nComponents); + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + bputils::ConduitAllocateThroughAxom c2a; + + n_output.reset(); + n_output["type"] = "explicit"; + conduit::Node &n_values = n_output["values"]; + + // Determine output size. + const auto outputSize = slice.m_indicesView; + + // Make output nodes using axis names from the input coordset. Make array views too. + axom::StackArray, PointType::DIMENSION> compViews; + for(size_t i = 0; i < nComponents; i++) + { + // Allocate data in the Conduit node and make a view. + conduit::Node &comp = n_values[axes[i]]; + comp.set_allocator(c2a.getConduitAllocatorID()); + comp.set(conduit::DataType( + bputils::cpp2conduit::id, + outputSize)); + auto *comp_data = static_cast(comp.data_ptr()); + compViews[i] = axom::ArrayView(comp_data, outputSize); + } + + // Select the nodes we want in the output. + const CoordsetView deviceView(m_coordsetView); + const auto deviceIndicesView = slice.m_indicesView; + axom::for_all( + outputSize, + AXOM_LAMBDA(auto index) { + const auto srcIndex = deviceIndicesView[index]; + const auto pt = deviceView[srcIndex]; + + // Store the point into the Conduit component arrays. + for(int comp = 0; comp < PointType::DIMENSION; comp++) + { + compViews[comp][index] = pt[comp]; + } + }); + } +private: + CoordsetView m_coordsetView; +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp new file mode 100644 index 0000000000..87afb42d2a --- /dev/null +++ b/src/axom/mir/ExtractZones.hpp @@ -0,0 +1,385 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_EXTRACT_ZONES_HPP +#define AXOM_MIR_EXTRACT_ZONES_HPP + +#include +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief Make a new topology using a + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam TopologyView The topology view type on which the algorithm will run. + * \tparam MatsetView The matset view type on which the algorithm will run. + */ +template +class ExtractZones +{ + using ConnectivityType = typename TopologyView::ConnectivityType; + using reduce_policy = typename axom::execution_space::reduce_policy; +public: + using SelectedZonesView = axom::ArrayView; + + /** + * \brief Constructor + * + * \param topoView The input topology view. + * \param coordsetView The input coordset view. + * \param matsetView The input matset view. + */ + ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) : + m_topologyView(topoView), m_coordsetView(coordsetView), m_matsetView(matsetView) + { + } + + /** + * \brief Select zones from the input mesh by id and output them in the output mesh. + * + * \param + */ + void execute(const SelectedZonesView &selectedZonesView, + const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) const + { + AXOM_ANNOTATE_SCOPE("ExtractZones"); + const int allocatorID = axom::execution_space::allocatorID(); + + AXOM_ANNOTATE_BEGIN("nodeMap"); + // We need to figure out which nodes to keep. + const auto nnodes = m_coordsetView.numberOfNodes(); + axom::Array mask(nnodes, nnodes, allocatorID); + mask.fill(0); + auto maskView = mask.view(); + // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. + RAJA::ReduceSum connsize_reduce(0); + m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) + { + const int nids = zone.numberOfNodes(); + for(int i = 0; i < nids; i++) + { + maskView[zone.getId(i)] = 1; + } + connsize_reduce += nids; + }); + + // Count the used nodes. + RAJA::ReduceSum mask_reduce(0); + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + mask_reduce += maskView[index]; + }); + const int newNumNodes = mask_reduce.get(); + + // Make a compact list of nodes. + axom::Array maskOffsets(nnodes, nnodes, allocatorID); + auto maskOffsetsView = maskOffsets.view(); + axom::exclusive_scan(maskView, maskOffsetsView); + + // Make an array of original node ids that we can use to "slice" the nodal data. + axom::Array old2new(nnodes, nnodes, allocatorID); + axom::Array nodeSlice(newNumNodes, newNumNodes, allocatorID); + auto old2newView = old2new.view(); + auto nodeSliceView = nodeSlice.view(); + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + nodeSliceView[maskOffsetsView[index]] = index; + old2newView[index] = (maskView[index] > 0) ? maskOffsetsView[index] : 0; + }); + AXOM_ANNOTATE_END("nodeMap"); + + // Now make a new output topology. + const conduit::Node &n_topologies = n_input.fetch_existing("topologies"); + const std::string topoName = topologyName(n_input, n_options); + const conduit::Node &n_topo = n_topologies.fetch_existing(topoName); + + const conduit::Node &n_newTopo = n_output["topologies/" + topoName]; + const auto newConnSize = connsize_reduce.get(); + makeTopology(selectedZonesView, newConnSize, old2newView, n_topo, n_newTopo); + + // Make a new coordset. + SliceData nSlice; + nSlice.m_indicesView = nodeSliceView; + const std::string coordsetName = n_topo["coordset"].as_string(); + const conduit::Node &n_coordset = n_input["coordsets/" + coordsetName]; + conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; + makeCoordset(nSlice, n_coordset, n_newCoordset); + + // Make new fields. + if(n_input.has_child("fields")) + { + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + conduit::Node &n_newFields = n_output["fields"]; + SliceData zSlice; + zSlice.m_indicesView = selectedZonesView; + makeFields(nSlice, zSlice, n_fields, n_newFields); + } + + // Make new matset. + std::string mname = matsetName(n_input, topoName); + if(!mname.empty()) + { + const conduit::Node &n_matset = n_input.fetch_existing("matsets/" + mname); + conduit::Node &n_newMatset = n_output["matsets/" + mname]; + makeMatset(selectedZonesView, n_matset, n_newMatset); + } + } + +private: + /** + * \brief Make the output topology for just the selected zones. + * + * \param selectedZonesView A view that contains the ids of the zones to extract. + * \param newConnSize The expected size of the new connectivity. + * \param old2newView A view that lets us map old node numbers to new node numbers. + * \param n_topo The input topology. + * \param n_newTopo A node to contain the new topology. + */ + void makeTopology(const SelectedZonesView &selectedZonesView, + int newConnSize, + const axom::ArrayView &old2newView, + const conduit::Node &n_topo, + conduit::Node &n_newTopo) const + { + AXOM_ANNOTATE_SCOPE("makeTopology"); + namespace bputils = axom::mir::utilities::blueprint; + bputils::ConduitAllocateThroughAxom c2a; + + const std::string shape = outputShape(n_topo); + if(shape == "polyhedron") + { + // TODO: Handle polyhedron shape. + // NOTE: We could know whether we can have PH topos if the TopologyView handles PH zones. Maybe this method is separated out and partially specialized. + SLIC_ERROR("Polyhedron is not handled yet."); + } + else + { + // The topology is not polyhedral. We'll output unstructured. + n_newTopo["type"] = "unstructured"; + n_newTopo["coordset"] = n_topo["coordset"].as_string(); + n_newTopo["elements/shape"] = outputShape(n_topo); + + conduit::Node &n_conn = n_newTopo["elements/connectivity"]; + n_conn.set_allocator(c2a.getConduitAllocatorID()); + n_conn.set(conduit::DataType(cpp2conduit::id, newConnSize)); + auto connView = bputils::make_array_view(n_conn); + + conduit::Node &n_sizes = n_newTopo["elements/sizes"]; + n_sizes.set_allocator(c2a.getConduitAllocatorID()); + n_sizes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + auto sizesView = bputils::make_array_view(n_sizes); + + conduit::Node &n_offsets = n_newTopo["elements/offsets"]; + n_offsets.set_allocator(c2a.getConduitAllocatorID()); + n_offsets.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + auto offsetsView = bputils::make_array_view(n_offsets); + + // Fill in sizes, offsets, connectivity. + m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) + { + sizesView[szIndex] = zone.numberOfNodes(); + }); + axom::exclusive_scan(sizesView, offsetsView); + const axom::ArrayView deviceOld2NewView(old2newView); + m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) + { + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + const auto oldNodeId = zone.getId(i); + const auto newNodeId = deviceOld2NewView[oldNodeId]; + connView[offset + i] = newNodeId; + } + }); + + // Handle shapes, if present. + if(n_topo.has_path("elements/shapes")) + { + const conduit::Node &n_shapes = n_topo.fetch_existing("elements/shapes"); + auto shapesView = bputils::make_array_view(n_shapes); + + conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; + n_newShapes.set_allocator(c2a.getConduitAllocatorID()); + n_newShapes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + auto newShapesView = bputils::make_array_view(n_newShapes); + + const SelectedZonesView deviceSelectedZonesView(selectedZonesView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) + { + newShapesView[index] = shapesView[deviceSelectedZonesView[index]]; + }); + } + } + } + + /** + * \brief Make the new coordset using the blend data and the input coordset/coordsetview. + * + * \param blend The BlendData that we need to construct the new coordset. + * \param n_coordset The input coordset, which is passed for metadata. + * \param[out] n_newCoordset The new coordset. + */ + void makeCoordset(const SliceData &slice, + const conduit::Node &n_coordset, + conduit::Node &n_newCoordset) const + { + AXOM_ANNOTATE_SCOPE("makeCoordset"); + axom::mir::utilities::blueprint::CoordsetSlicer cs(m_coordsetView); + n_newCoordset.reset(); + cs.execute(slice, n_coordset, n_newCoordset); + } + + /** + * \brief Make fields for the output mesh, as needed. + * + * \param nodeSlice Node slice information. + * \param zoneSlice Zone slice information. + * \param n_fields The input fields. + * \param n_newFields The output fields. + */ + void makeFields(const SliceData &nodeSlice, const SliceData &zoneSlice, const conduit::Node &n_fields, conduit::Node &n_newFields) const + { + AXOM_ANNOTATE_SCOPE("makeFields"); + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + const conduit::Node &n_field = n_fields[i]; + const std::string association = n_field["association"].as_string(); + conduit::Node &n_newField = n_newFields[n_field.name()]; + axom::mir::utilities::blueprint::FieldSlicer fs; + if(association == "element") + { + fs.execute(zoneSlice, n_field, n_newField); + } + else if(association == "vertex") + { + fs.execute(nodeSlice, n_field, n_newField); + } + } + } + + /** + * \brief Get the topology name. + * + * \param n_input The input mesh. + * \param n_options The options node that may contain a "topology" string. + * + * \return Returns the options topology name, if present. Otherwise, it returns the first topology name. + */ + std::string topologyName(const conduit::Node &n_input, const conduit::Node &n_options) const + { + std::string name; + if(n_options.has_path("topology")) + { + name = n_options["topology"].as_string(); + } + else + { + const conduit::Node &n_topologies = n_input.fetch_existing("topologies"); + name = n_topologies[0].name(); + } + return name; + } + + /** + * \brief Return the name of the output shape type. + * + * \param n_topo The input topology node. + * + * \return The name of the output shape. + */ + std::string outputShape(const conduit::Node &n_topo) const + { + std::string shape; + if(n_topo["type"].as_string() == "unstructured") + { + shape = n_topo["elements/shape"].as_string(); + } + else + { + if(TopologyView::dimension() == 3) + { + shape = "hex"; + } + else if(TopologyView::dimension() == 2) + { + shape = "quad"; + } + else + { + shape = "line"; + } + } + return shape; + } + + /** + * \brief Return the matset for the named topology. + * + * \param n_input The input mesh. + * \param topoName The name of the topology. + * + * \return The name of the matset for the topology or an empty string if no matset was found. + */ + std::string matsetName(const conduit::Node &n_input, const std::string &topoName) const + { + std::string matset; + if(n_input.has_child("matsets")) + { + const conduit::Node &n_matsets = n_input.fetch_existing("matsets"); + for(conduit::index_t i = 0; i < n_matsets.number_of_children(); i++) + { + const conduit::Node &n_matset = n_matsets[i]; + if(n_matset["topology"].as_string() == topoName) + { + matset = n_matset.name(); + break; + } + } + } + return matset; + } + + /** + * \brief Make a new matset that covers the selected zones. + * + * \param selectedZonesView A view that contains the selected zones. + * \param n_matset The input matset. + * \param n_newMatset A node that will contain the new matset. + */ + void makeMatset(const SelectedZonesView &selectedZonesView, const conduit::Node &n_matset, conduit::Node &n_newMatset) const + { +#if 0 + AXOM_ANNOTATE_SCOPE("makeMatset"); + namespace bputils = axom::mir::utilities::blueprint; + using MatSlicer = bputils::MatsetSlicer; + MatSlicer ms; + ms.execute(m_matsetView, selectedZonesView, n_matset, n_newMatset); +#endif + } + +private: + TopologyView m_topologyView; + CoordsetView m_coordsetView; + MatsetView m_matsetView; +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp new file mode 100644 index 0000000000..b09e98e15a --- /dev/null +++ b/src/axom/mir/MatsetSlicer.hpp @@ -0,0 +1,129 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_MATSET_SLICER_HPP +#define AXOM_MIR_MATSET_SLICER_HPP + +#include +#include + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief Slices the input matset view and outputs a new matset (unibuffer flavor). + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam MatsetView The matset view type that wraps the Blueprint matset. + */ +template +class MatsetSlicer +{ + using reduce_policy = typename axom::execution_space::reduce_policy; +public: + using SelectedZonesView = axom::ArrayView; +#pragma message "***** We are building MatsetSlicer" + /** + * \brief Slice the input matset and output a new matset. + * + * \param matsetView A view that wraps the input matset. + * \param selectedZonesView A view that contains the zone ids that we're extracting from the matset. + * \param n_matset The input matset. + * \param[out] n_newMatset The output matset. + */ + static void execute(const MatsetView &matsetView, + const SelectedZonesView &selectedZonesView, + const conduit::Node &n_matset, + conduit::Node &n_newMatset) + { + using MatsetIndex = typename MatsetView::IndexType; + using MatsetFloat = typename MatsetView::FloatType; + namespace bputils = axom::mir::utilities::blueprint; + + bputils::ConduitAllocateThroughAxom c2a; + + // Allocate sizes/offsets. + conduit::Node n_sizes = n_newMatset["sizes"]; + n_sizes.set_allocator(c2a.getConduitAllocatorID()); + n_sizes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + auto sizesView = bputils::make_array_view(n_sizes); + + conduit::Node n_offsets = n_newMatset["offsets"]; + n_offsets.set_allocator(c2a.getConduitAllocatorID()); + n_offsets.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + auto offsetsView = bputils::make_array_view(n_offsets); + + // Figure out overall size of the matset zones we're keeping. + MatsetView deviceMatsetView(matsetView); + RAJA::ReduceSum size_reduce(0); + const axom::ArrayView deviceSelectedZonesView(selectedZonesView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) + { + const auto nmats = deviceMatsetView.numberOfMaterials(deviceSelectedZonesView[index]); + sizesView[index] = nmats; + size_reduce += nmats; + }); + axom::exclusive_scan(sizesView, offsetsView); + + // Allocate data for the rest of the matset. + const auto totalSize = size_reduce.get(); + + conduit::Node n_indices = n_newMatset["indices"]; + n_indices.set_allocator(c2a.getConduitAllocatorID()); + n_indices.set(conduit::DataType(cpp2conduit::id, totalSize)); + auto indicesView = bputils::make_array_view(n_indices); + + conduit::Node n_material_ids = n_newMatset["material_ids"]; + n_material_ids.set_allocator(c2a.getConduitAllocatorID()); + n_material_ids.set(conduit::DataType(cpp2conduit::id, totalSize)); + auto materialIdsView = bputils::make_array_view(n_material_ids); + + conduit::Node n_volume_fractions = n_newMatset["volume_fractions"]; + n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); + n_volume_fractions.set(conduit::DataType(cpp2conduit::id, totalSize)); + auto volumeFractionsView = bputils::make_array_view(n_volume_fractions); + + // Fill in the matset data with the zones we're keeping. + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) + { + const auto size = static_cast(sizesView[index]); + const auto offset = offsetsView[index]; + + typename MatsetView::IDList ids; + typename MatsetView::VFList vfs; + deviceMatsetView.zoneMaterials(deviceSelectedZonesView[index], ids, vfs); + + for(int i = 0; i < size; i++) + { + const auto destIndex = offset + i; + materialIdsView[destIndex] = ids[i]; + volumeFractionsView[destIndex] = vfs[i]; + indicesView[destIndex] = destIndex; + } + }); + + // Copy the material_map if it exists. + for(const auto &key : std::vector{{"topology", "material_map"}}) + { + if(n_matset.has_child(key)) + n_newMatset[key] = n_matset.fetch_existing(key); + } + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp new file mode 100644 index 0000000000..d28743e108 --- /dev/null +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -0,0 +1,169 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_MIR_ZONELIST_BUILDER_HPP +#define AXOM_MIR_ZONELIST_BUILDER_HPP + +#include +#include + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief This struct builds lists of clean and mixed zones using the input topology and matset views. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam TopologyView The topology view type on which the algorithm will run. + * \tparam MatsetView The matset view type on which the algorithm will run. + */ +template +class ZoneListBuilder +{ +public: + /** + * \brief Constructor + * + * \param topoView The topology view to use for creating the zone lists. + * \param matsetView The matset view to use for creating the zone lists. + */ + ZoneListBuilder(const TopologyView &topoView, const MatsetView &matsetView) : m_topologyView(topoView), m_matsetView(matsetView) + { + } + + /** + * \brief Build the list of clean and mixed zones. + * + * \param n2z A Conduit node containing a node to zone relation. + * \param[out] cleanIndices An array that will contain the list of clean material zone ids. + * \param[out] mixedIndices An array that will contain the list of mixed material zone ids. + * + * \note The clean/mixed index arrays are not strictly what could be determined by the matset alone. + * We figure out which nodes touch multiple materials. Then we iterate over the zones and + * those that touch only nodes that have 1 material are marked clean, otherwise they are + * considered mixed as we might have to split those zones. + */ + void execute(const conduit::Node &n2z, axom::Array &cleanIndices, axom::Array &mixedIndices) const + { + AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); + const int allocatorID = axom::execution_space::allocatorID(); + + const conduit::Node &n_zones = n2z.fetch_existing("zones"); + const conduit::Node &n_indices = n2z.fetch_existing("indices"); + const conduit::Node &n_sizes = n2z.fetch_existing("sizes"); + const conduit::Node &n_offsets = n2z.fetch_existing("offsets"); + axom::mir::views::IndexNode_to_ArrayView_same(n_zones, n_indices, n_sizes, n_offsets, [&](auto zones, auto indices, auto sizes, auto offsets) + { + using reduce_policy = typename axom::execution_space::reduce_policy; + const auto nnodes = sizes.size(); + axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); + auto nMatsPerNodeView = nMatsPerNode.view(); + + // OR to the nodes. We just take the max number of materials of adjacent + // zones and put those on the nodes lest we need an array that stores + // general N-bit values per element. + MatsetView deviceMatsetView(m_matsetView); + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + int nmats = 0; + const int size = static_cast(sizes[index]); + const auto offset = offsets[index]; + for(int i = 0; i < size; i++) + { + const auto zi = zones[indices[offset + i]]; + nmats = axom::utilities::max(nmats, static_cast(deviceMatsetView.numberOfMaterials(zi))); + } + nMatsPerNodeView[index] = nmats; + }); + + // Now, mark all zones that have 1 mat per node as clean. + const auto nzones = m_topologyView.numberOfZones(); + axom::Array mask(nzones, nzones, allocatorID); + auto maskView = mask.view(); + RAJA::ReduceSum mask_reduce(0); + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean |= nMatsPerNodeView[nodeId] == 1; + } + + const int ival = clean ? 1 : 0; + maskView[zoneIndex] = ival; + mask_reduce += ival; + }); + + const int nClean = mask_reduce.get(); + if(nClean > 0) + { + axom::Array maskOffsets(nzones, nzones, allocatorID); + auto maskOffsetsView = maskOffsets.view(); + axom::exclusive_scan(maskView, maskOffsetsView); + + // Make the output cleanIndices array. + cleanIndices = axom::Array(nClean, nClean, allocatorID); + auto cleanIndicesView = cleanIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + cleanIndicesView[maskOffsetsView[index]] = index; + } + }); + + // Make the mixedIndices array. + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + maskView[index] = ~maskView[index]; + }); + axom::exclusive_scan(maskView, maskOffsetsView); + const int nMixed = nzones - nClean; + mixedIndices = axom::Array(nMixed, nMixed, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + mixedIndicesView[maskOffsetsView[index]] = index; + } + }); + } + else + { + cleanIndices = axom::Array(); + + // There were no clean, so it must all be mixed. + mixedIndices = axom::Array(nzones, nzones, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + mixedIndicesView[index] = index; + }); + } + }); + } + +private: + TopologyView m_topologyView; + MatsetView m_matsetView; +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 08e776d7e8..6699ad4fab 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -435,6 +435,88 @@ TEST(mir_blueprint_utilities, recenterfield) #endif } +//------------------------------------------------------------------------------ +template +void test_matset_slice(const conduit::Node &hostMatset) +{ + namespace bputils = axom::mir::utilities::blueprint; + + // host->device + conduit::Node deviceMatset; + bputils::copy(deviceMatset, hostMatset); + + axom::Array ids{{1,3,5}}; + axom::Array selectedZones(3, 3, axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); + + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMatset["material_ids"]), + bputils::make_array_view(deviceMatset["volume_fractions"]), + bputils::make_array_view(deviceMatset["sizes"]), + bputils::make_array_view(deviceMatset["offsets"]), + bputils::make_array_view(deviceMatset["indices"])); + + // Slice it. + bputils::MatsetSlicer slicer; + conduit::Node newDeviceMatset; + slicer.execute(matsetView, selectedZones.view(), deviceMatset, newDeviceMatset); + + // device->host + conduit::Node newHostMatset; + bputils::copy(newHostMatset, newDeviceMatset); + + newHostMatset.print(); + + // TODO: checks +} + +TEST(mir_blueprint_utilities, matsetslice) +{ + /* + 8-------9------10------11 + | 2/1 | 1/0.1 | 2/0.8 | + | | 2/0.5 | 3/0.2 | + | | 3/0.4 | | + 4-------5-------6-------7 + | | 1/0.5 | 1/0.2 | + | 1/1 | 2/0.5 | 2/0.8 | + | | | | + 0-------1-------2-------3 + */ + const char *matsetData = R"xx( +topology: mesh +material_map: + a: 1 + b: 2 + c: 3 +material_ids: [1, 1,2, 1,2, 2, 1,2,3, 2,3] +volume_fractions: [1., 0.5,0.5, 0.2,0.8, 1., 0.1,0.5,0.4, 0.8,0.2] +indices: [0, 1, 2, 3, 4, 5, 6, 7, 8] +sizes: [1, 2, 2, 1, 3, 2] +offsets: [0, 1, 3, 5, 6, 9] +)xx"; + + conduit::Node matset; + matset.parse(matsetData); + + + + test_matset_slice(matset); +#if defined(AXOM_USE_OPENMP) + test_matset_slice(matset); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_matset_slice(matset); +#endif + +#if defined(AXOM_USE_HIP) + test_matset_slice(matset); +#endif +} + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { From 79144ccfdf5631e3006d5ab84660a8fb014ae20d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 10 Sep 2024 15:09:23 -0700 Subject: [PATCH 206/290] Added some tests --- src/axom/mir/CoordsetBlender.hpp | 22 +- src/axom/mir/CoordsetSlicer.hpp | 24 +- src/axom/mir/ExtractZones.hpp | 104 ++++-- src/axom/mir/MatsetSlicer.hpp | 29 +- .../mir/tests/mir_blueprint_utilities.cpp | 313 +++++++++++++++++- src/axom/mir/utilities.hpp | 19 +- src/axom/mir/views/MaterialView.hpp | 3 + src/axom/mir/views/Shapes.hpp | 5 +- src/axom/mir/views/dispatch_coordset.hpp | 54 +++ 9 files changed, 506 insertions(+), 67 deletions(-) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 18ef5a752e..1d1883b75f 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -59,8 +59,25 @@ class CoordsetBlender using value_type = typename CoordsetViewType::value_type; using PointType = typename CoordsetViewType::PointType; using VectorType = axom::primal::Vector; + namespace bputils = axom::mir::utilities::blueprint; + + // Get the axis names for the output coordset. For uniform, prefer x,y,z + // instead of i,j,k since we're making an explicit coordset. + std::vector axes; + if(n_input["type"].as_string() == "uniform") + { + if(n_input.has_path("dims/i")) + axes.push_back("x"); + if(n_input.has_path("dims/j")) + axes.push_back("y"); + if(n_input.has_path("dims/k")) + axes.push_back("z"); + } + else + { + axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); + } - const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); const auto nComponents = axes.size(); SLIC_ASSERT(PointType::DIMENSION == nComponents); @@ -86,8 +103,7 @@ class CoordsetBlender comp.set(conduit::DataType( axom::mir::utilities::blueprint::cpp2conduit::id, outputSize)); - auto *comp_data = static_cast(comp.data_ptr()); - compViews[i] = axom::ArrayView(comp_data, outputSize); + compViews[i] = bputils::make_array_view(comp); } const CoordsetViewType deviceView(view); diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/CoordsetSlicer.hpp index d0686ad4f9..964c0f3fef 100644 --- a/src/axom/mir/CoordsetSlicer.hpp +++ b/src/axom/mir/CoordsetSlicer.hpp @@ -52,9 +52,26 @@ class CoordsetSlicer using PointType = typename CoordsetView::PointType; namespace bputils = axom::mir::utilities::blueprint; - const auto axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); + // Get the axis names for the output coordset. For uniform, prefer x,y,z + // instead of i,j,k since we're making an explicit coordset. + std::vector axes; + if(n_input["type"].as_string() == "uniform") + { + if(n_input.has_path("dims/i")) + axes.push_back("x"); + if(n_input.has_path("dims/j")) + axes.push_back("y"); + if(n_input.has_path("dims/k")) + axes.push_back("z"); + } + else + { + axes = conduit::blueprint::mesh::utils::coordset::axes(n_input); + } + const auto nComponents = axes.size(); SLIC_ASSERT(PointType::DIMENSION == nComponents); + SLIC_ASSERT(slice.m_indicesView.size() > 0); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. bputils::ConduitAllocateThroughAxom c2a; @@ -64,7 +81,7 @@ class CoordsetSlicer conduit::Node &n_values = n_output["values"]; // Determine output size. - const auto outputSize = slice.m_indicesView; + const auto outputSize = slice.m_indicesView.size(); // Make output nodes using axis names from the input coordset. Make array views too. axom::StackArray, PointType::DIMENSION> compViews; @@ -76,8 +93,7 @@ class CoordsetSlicer comp.set(conduit::DataType( bputils::cpp2conduit::id, outputSize)); - auto *comp_data = static_cast(comp.data_ptr()); - compViews[i] = axom::ArrayView(comp_data, outputSize); + compViews[i] = bputils::make_array_view(comp); } // Select the nodes we want in the output. diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 87afb42d2a..93a65763c8 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -19,13 +19,12 @@ namespace blueprint { /** - * \brief Make a new topology using a + * \brief Make a new topology and coordset by extracting certain zones from the input mesh. * * \tparam ExecSpace The execution space where the algorithm will run. * \tparam TopologyView The topology view type on which the algorithm will run. - * \tparam MatsetView The matset view type on which the algorithm will run. */ -template +template class ExtractZones { using ConnectivityType = typename TopologyView::ConnectivityType; @@ -40,15 +39,21 @@ class ExtractZones * \param coordsetView The input coordset view. * \param matsetView The input matset view. */ - ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) : - m_topologyView(topoView), m_coordsetView(coordsetView), m_matsetView(matsetView) + ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView) : + m_topologyView(topoView), m_coordsetView(coordsetView) { } /** * \brief Select zones from the input mesh by id and output them in the output mesh. * - * \param + * \param selectedZonesView A view that contains the selected zone ids. + * \param n_input The input mesh. + * \param n_options The input options. + * \param[out] n_output The output mesh. + * + * \note The \a n_options node contains a "topology" string that is selects the + * name of the topology to extract. */ void execute(const SelectedZonesView &selectedZonesView, const conduit::Node &n_input, @@ -91,7 +96,7 @@ class ExtractZones // Make an array of original node ids that we can use to "slice" the nodal data. axom::Array old2new(nnodes, nnodes, allocatorID); - axom::Array nodeSlice(newNumNodes, newNumNodes, allocatorID); + axom::Array nodeSlice(newNumNodes, newNumNodes, allocatorID); auto old2newView = old2new.view(); auto nodeSliceView = nodeSlice.view(); axom::for_all(nnodes, AXOM_LAMBDA(auto index) @@ -106,15 +111,15 @@ class ExtractZones const std::string topoName = topologyName(n_input, n_options); const conduit::Node &n_topo = n_topologies.fetch_existing(topoName); - const conduit::Node &n_newTopo = n_output["topologies/" + topoName]; + conduit::Node &n_newTopo = n_output["topologies/" + topoName]; const auto newConnSize = connsize_reduce.get(); makeTopology(selectedZonesView, newConnSize, old2newView, n_topo, n_newTopo); // Make a new coordset. SliceData nSlice; nSlice.m_indicesView = nodeSliceView; - const std::string coordsetName = n_topo["coordset"].as_string(); - const conduit::Node &n_coordset = n_input["coordsets/" + coordsetName]; + const std::string coordsetName = n_topo.fetch_existing("coordset").as_string(); + const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; makeCoordset(nSlice, n_coordset, n_newCoordset); @@ -127,18 +132,9 @@ class ExtractZones zSlice.m_indicesView = selectedZonesView; makeFields(nSlice, zSlice, n_fields, n_newFields); } - - // Make new matset. - std::string mname = matsetName(n_input, topoName); - if(!mname.empty()) - { - const conduit::Node &n_matset = n_input.fetch_existing("matsets/" + mname); - conduit::Node &n_newMatset = n_output["matsets/" + mname]; - makeMatset(selectedZonesView, n_matset, n_newMatset); - } } -private: +protected: /** * \brief Make the output topology for just the selected zones. * @@ -326,6 +322,69 @@ class ExtractZones return shape; } +private: + TopologyView m_topologyView; + CoordsetView m_coordsetView; +}; + +/** + * \brief Make a new topology and coordset by extracting certain zones from the input mesh. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * \tparam TopologyView The topology view type on which the algorithm will run. + * \tparam MatsetView The matset view type on which the algorithm will run. + */ +template +class ExtractZonesAndMatset : public ExtractZones +{ +public: + using SelectedZonesView = axom::ArrayView; + + /** + * \brief Constructor + * + * \param topoView The input topology view. + * \param coordsetView The input coordset view. + * \param matsetView The input matset view. + */ + ExtractZonesAndMatset(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) : + ExtractZones(topoView, coordsetView), m_matsetView(matsetView) + { + } + + /** + * \brief Select zones from the input mesh by id and output them in the output mesh. + * + * \param selectedZonesView A view that contains the selected zone ids. + * \param n_input The input mesh. + * \param n_options The input options. + * \param[out] n_output The output mesh. + * + * \note The \a n_options node contains a "topology" string that is selects the + * name of the topology to extract. + */ + void execute(const SelectedZonesView &selectedZonesView, + const conduit::Node &n_input, + const conduit::Node &n_options, + conduit::Node &n_output) const + { + AXOM_ANNOTATE_SCOPE("ExtractZonesAndMatset"); + + // Call base class to handle mesh/coordset/fields + ExtractZones::execute(selectedZonesView, n_input, n_options, n_output); + + // Make new matset. + const std::string topoName = ExtractZones::topologyName(n_input, n_options); + std::string mname = matsetName(n_input, topoName); + if(!mname.empty()) + { + const conduit::Node &n_matset = n_input.fetch_existing("matsets/" + mname); + conduit::Node &n_newMatset = n_output["matsets/" + mname]; + makeMatset(selectedZonesView, n_matset, n_newMatset); + } + } + +private: /** * \brief Return the matset for the named topology. * @@ -371,12 +430,11 @@ class ExtractZones #endif } -private: - TopologyView m_topologyView; - CoordsetView m_coordsetView; MatsetView m_matsetView; }; + + } // end namespace blueprint } // end namespace utilities } // end namespace mir diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index b09e98e15a..81e8d1abf0 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -32,7 +32,7 @@ class MatsetSlicer using reduce_policy = typename axom::execution_space::reduce_policy; public: using SelectedZonesView = axom::ArrayView; -#pragma message "***** We are building MatsetSlicer" + /** * \brief Slice the input matset and output a new matset. * @@ -49,16 +49,25 @@ class MatsetSlicer using MatsetIndex = typename MatsetView::IndexType; using MatsetFloat = typename MatsetView::FloatType; namespace bputils = axom::mir::utilities::blueprint; + SLIC_ASSERT(selectedZonesView.size() > 0); + + // Copy the material_map if it exists. + const char *keys[] = {"topology", "material_map"}; + for(int i = 0; i < 2; i++) + { + if(n_matset.has_child(keys[i])) + n_newMatset[keys[i]] = n_matset.fetch_existing(keys[i]); + } bputils::ConduitAllocateThroughAxom c2a; // Allocate sizes/offsets. - conduit::Node n_sizes = n_newMatset["sizes"]; + conduit::Node &n_sizes = n_newMatset["sizes"]; n_sizes.set_allocator(c2a.getConduitAllocatorID()); n_sizes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); auto sizesView = bputils::make_array_view(n_sizes); - conduit::Node n_offsets = n_newMatset["offsets"]; + conduit::Node &n_offsets = n_newMatset["offsets"]; n_offsets.set_allocator(c2a.getConduitAllocatorID()); n_offsets.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); auto offsetsView = bputils::make_array_view(n_offsets); @@ -77,18 +86,19 @@ class MatsetSlicer // Allocate data for the rest of the matset. const auto totalSize = size_reduce.get(); + SLIC_ASSERT(totalSize > 0); - conduit::Node n_indices = n_newMatset["indices"]; + conduit::Node &n_indices = n_newMatset["indices"]; n_indices.set_allocator(c2a.getConduitAllocatorID()); n_indices.set(conduit::DataType(cpp2conduit::id, totalSize)); auto indicesView = bputils::make_array_view(n_indices); - conduit::Node n_material_ids = n_newMatset["material_ids"]; + conduit::Node &n_material_ids = n_newMatset["material_ids"]; n_material_ids.set_allocator(c2a.getConduitAllocatorID()); n_material_ids.set(conduit::DataType(cpp2conduit::id, totalSize)); auto materialIdsView = bputils::make_array_view(n_material_ids); - conduit::Node n_volume_fractions = n_newMatset["volume_fractions"]; + conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); n_volume_fractions.set(conduit::DataType(cpp2conduit::id, totalSize)); auto volumeFractionsView = bputils::make_array_view(n_volume_fractions); @@ -111,13 +121,6 @@ class MatsetSlicer indicesView[destIndex] = destIndex; } }); - - // Copy the material_map if it exists. - for(const auto &key : std::vector{{"topology", "material_map"}}) - { - if(n_matset.has_child(key)) - n_newMatset[key] = n_matset.fetch_existing(key); - } } }; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 6699ad4fab..1e3414c8e9 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -17,6 +17,7 @@ #include namespace mir = axom::mir; +namespace bputils = axom::mir::utilities::blueprint; template void test_conduit_allocate() @@ -119,8 +120,6 @@ void compareRelation(const conduit::Node &hostRelation, const axom::ArrayView &sizes, const axom::ArrayView &offsets) { - namespace bputils = axom::mir::utilities::blueprint; - const auto zonesView = bputils::make_array_view(hostRelation["zones"]); const auto sizesView = @@ -436,11 +435,20 @@ TEST(mir_blueprint_utilities, recenterfield) } //------------------------------------------------------------------------------ +template +bool compare_views(const Container1 &a, const Container2 &b) +{ + bool eq = a.size() == b.size(); + for(axom::IndexType i = 0; i < a.size() && eq; i++) + { + eq &= a[i] == b[i]; + } + return eq; +} + template void test_matset_slice(const conduit::Node &hostMatset) { - namespace bputils = axom::mir::utilities::blueprint; - // host->device conduit::Node deviceMatset; bputils::copy(deviceMatset, hostMatset); @@ -449,14 +457,14 @@ void test_matset_slice(const conduit::Node &hostMatset) axom::Array selectedZones(3, 3, axom::execution_space::allocatorID()); axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = axom::mir::views::UnibufferMaterialView; MatsetView matsetView; matsetView.set( - bputils::make_array_view(deviceMatset["material_ids"]), - bputils::make_array_view(deviceMatset["volume_fractions"]), - bputils::make_array_view(deviceMatset["sizes"]), - bputils::make_array_view(deviceMatset["offsets"]), - bputils::make_array_view(deviceMatset["indices"])); + bputils::make_array_view(deviceMatset["material_ids"]), + bputils::make_array_view(deviceMatset["volume_fractions"]), + bputils::make_array_view(deviceMatset["sizes"]), + bputils::make_array_view(deviceMatset["offsets"]), + bputils::make_array_view(deviceMatset["indices"])); // Slice it. bputils::MatsetSlicer slicer; @@ -467,9 +475,18 @@ void test_matset_slice(const conduit::Node &hostMatset) conduit::Node newHostMatset; bputils::copy(newHostMatset, newDeviceMatset); - newHostMatset.print(); - - // TODO: checks + // Expected answers. + const axom::Array sizes{{2, 1, 2}}; + const axom::Array offsets{{0, 2, 3}}; + const axom::Array indices{{0, 1, 2, 3, 4}}; + const axom::Array material_ids{{1, 2, 2, 2, 3}}; + const axom::Array volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); + EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMatset["offsets"]))); + EXPECT_TRUE(compare_views(indices.view(), bputils::make_array_view(newHostMatset["indices"]))); + EXPECT_TRUE(compare_views(material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); + EXPECT_TRUE(compare_views(volume_fractions.view(), bputils::make_array_view(newHostMatset["volume_fractions"]))); } TEST(mir_blueprint_utilities, matsetslice) @@ -493,15 +510,13 @@ topology: mesh c: 3 material_ids: [1, 1,2, 1,2, 2, 1,2,3, 2,3] volume_fractions: [1., 0.5,0.5, 0.2,0.8, 1., 0.1,0.5,0.4, 0.8,0.2] -indices: [0, 1, 2, 3, 4, 5, 6, 7, 8] +indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sizes: [1, 2, 2, 1, 3, 2] offsets: [0, 1, 3, 5, 6, 9] )xx"; conduit::Node matset; - matset.parse(matsetData); - - + matset.parse(matsetData); test_matset_slice(matset); #if defined(AXOM_USE_OPENMP) @@ -517,6 +532,270 @@ offsets: [0, 1, 3, 5, 6, 9] #endif } +//------------------------------------------------------------------------------ +template +void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) +{ + axom::Array ids{{0,1,2,4,5,6}}; + + const auto nnodes = ids.size(); + axom::Array selectedNodes(nnodes, nnodes, axom::execution_space::allocatorID()); + axom::copy(selectedNodes.data(), ids.data(), nnodes * sizeof(axom::IndexType)); + + bputils::SliceData slice; + slice.m_indicesView = selectedNodes.view(); + + // host->device + conduit::Node deviceCoordset; + bputils::copy(deviceCoordset, hostCoordset); + + // Make a view. + auto coordsetView = makeView(deviceCoordset); + using CoordsetView = decltype(coordsetView); + + // Pull out selected nodes + bputils::CoordsetSlicer slicer(coordsetView); + conduit::Node newDeviceCoordset; + slicer.execute(slice, deviceCoordset, newDeviceCoordset); + + // device->host + conduit::Node newHostCoordset; + bputils::copy(newHostCoordset, newDeviceCoordset); + + // We get an explicit coordset out of the slicer. + const axom::Array x{{0., 1., 2., 0., 1., 2.}}; + const axom::Array y{{0., 0., 0., 1., 1., 1.}}; + EXPECT_TRUE(compare_views(x.view(), bputils::make_array_view(newHostCoordset["values/x"]))); + EXPECT_TRUE(compare_views(y.view(), bputils::make_array_view(newHostCoordset["values/y"]))); +} + +TEST(mir_blueprint_utilities, coordsetslicer_explicit) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: explicit +values: + x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] + y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2.] +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_explicit_coordset::view(deviceCoordset); + }; + + test_coordsetslicer(coordset, makeView); +#if defined(AXOM_USE_OPENMP) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_HIP) + test_coordsetslicer(coordset, makeView); +#endif +} + +TEST(mir_blueprint_utilities, coordsetslicer_rectilinear) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: rectilinear +values: + x: [0., 1., 2., 3.] + y: [0., 1., 2.] +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_rectilinear_coordset::view(deviceCoordset); + }; + + test_coordsetslicer(coordset, makeView); +#if defined(AXOM_USE_OPENMP) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_HIP) + test_coordsetslicer(coordset, makeView); +#endif +} + +TEST(mir_blueprint_utilities, coordsetslicer_uniform) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: uniform +dims: + i: 4 + j: 3 +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); + }; + + test_coordsetslicer(coordset, makeView); +#if defined(AXOM_USE_OPENMP) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_coordsetslicer(coordset, makeView); +#endif + +#if defined(AXOM_USE_HIP) + test_coordsetslicer(coordset, makeView); +#endif +} + +//------------------------------------------------------------------------------ +template +void test_extractzones(const conduit::Node &hostMesh) +{ + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + axom::Array ids{{1,3,5}}; + const auto nzones = ids.size(); + axom::Array selectedZones(nzones, nzones, axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), nzones * sizeof(axom::IndexType)); + + // Wrap the data in views. + auto coordsetView = axom::mir::views::make_explicit_coordset::view(deviceMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]), + bputils::make_array_view(deviceMesh["topologies/mesh/elements/sizes"]), + bputils::make_array_view(deviceMesh["topologies/mesh/elements/offsets"])); + + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); + + // Pull out selected zones + bputils::ExtractZones extract(topoView, coordsetView); + conduit::Node options, newDeviceMesh; + options["topology"] = "mesh"; + extract.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); + + // device->host + conduit::Node newHostMesh; + bputils::copy(newHostMesh, newDeviceMesh); + + newHostMesh.print(); +} + +TEST(mir_blueprint_utilities, extractzones) +{ + /* + 8-------9------10------11 + | 2/1 | 1/0.1 | 2/0.8 | + | | 2/0.5 | 3/0.2 | + | | 3/0.4 | | + 4-------5-------6-------7 + | | 1/0.5 | 1/0.2 | + | 1/1 | 2/0.5 | 2/0.8 | + | | | | + 0-------1-------2-------3 + */ + const char *meshData = R"xx( +coordsets: + coords: + type: explicit + values: + x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] + y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2.] +topologies: + mesh: + coordset: coords + type: unstructured + elements: + shape: quad + connectivity: [0,1,5,4, 1,2,6,5, 2,3,7,6, 4,5,9,8, 5,6,10,9, 6,7,11,10] + sizes: [4,4,4,4,4,4] + offsets: [0,4,8,12,16,20] +fields: + zonal: + topology: mesh + association: element + values: [0.,1.,2.,3.,4.,5.] + nodal: + topology: mesh + association: vertex + values: [0.,1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.] +matsets: + mat1: + topology: mesh + material_map: + a: 1 + b: 2 + c: 3 + material_ids: [1, 1,2, 1,2, 2, 1,2,3, 2,3] + volume_fractions: [1., 0.5,0.5, 0.2,0.8, 1., 0.1,0.5,0.4, 0.8,0.2] + indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + sizes: [1, 2, 2, 1, 3, 2] + offsets: [0, 1, 3, 5, 6, 9] +)xx"; + + conduit::Node mesh; + mesh.parse(meshData); + + test_extractzones(mesh); +#if defined(AXOM_USE_OPENMP) + test_extractzones(mesh); +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + test_extractzones(mesh); +#endif + +#if defined(AXOM_USE_HIP) + test_extractzones(mesh); +#endif +} + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index e61ac24060..68c5538eae 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -247,7 +247,7 @@ class HashNaming AXOM_HOST_DEVICE inline KeyType make_name_1(IndexType p0) const { - assert(p0 < PayloadMask); + assert(static_cast(p0) < PayloadMask); // Store p0 in the key as a 62-bit integer KeyType k0 = (static_cast(p0) & PayloadMask); return KeyIDSingle | k0; @@ -262,7 +262,7 @@ class HashNaming AXOM_HOST_DEVICE inline KeyType make_name_2(IndexType p0, IndexType p1) const { - assert(p0 <= Max31Bit && p1 <= Max31Bit); + assert(static_cast(p0) <= Max31Bit && static_cast(p1) <= Max31Bit); // Store p0 and p1 both in the 64-bit key as 31-bit integers KeyType k0 = (static_cast(std::min(p0, p1)) & Max31Bit); KeyType k1 = (static_cast(std::max(p0, p1)) & Max31Bit); @@ -315,7 +315,10 @@ class HashNaming { // Narrow to 16-bit, sort std::uint16_t sorted[MAXIDS]; - for(int i = 0; i < n; i++) sorted[i] = static_cast(p[i]); + for(int i = 0; i < n; i++) + { + sorted[i] = static_cast(p[i]); + } axom::utilities::Sorting::sort(sorted, n); // Make a hash from the narrowed ids @@ -329,7 +332,10 @@ class HashNaming { // Narrow to 32-bit, sort std::uint32_t sorted[MAXIDS]; - for(int i = 0; i < n; i++) sorted[i] = static_cast(p[i]); + for(int i = 0; i < n; i++) + { + sorted[i] = static_cast(p[i]); + } axom::utilities::Sorting::sort(sorted, n); // Make a hash from the narrowed ids @@ -342,7 +348,10 @@ class HashNaming else { IndexType sorted[MAXIDS]; - for(int i = 0; i < n; i++) sorted[i] = p[i]; + for(int i = 0; i < n; i++) + { + sorted[i] = p[i]; + } axom::utilities::Sorting::sort(sorted, n); // Make a hash from the ids diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 39c3a45656..c9641c91b1 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -85,6 +85,9 @@ class UnibufferMaterialView const axom::ArrayView &offsets, const axom::ArrayView &indices) { + assert(material_ids.size() == volume_fractions.size()); + assert(sizes.size() == offsets.size()); + m_material_ids = material_ids; m_volume_fractions = volume_fractions; m_sizes = sizes; diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 63d100f454..6ed0c1818b 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -481,7 +481,7 @@ struct Shape : public ShapeTraits : m_idsView(ids) , m_faceIds() { - SLIC_ASSERT(m_idsView.size() == ShapeTraits::numberOfNodes()); + assert(m_idsView.size() == ShapeTraits::numberOfNodes()); } /** @@ -491,6 +491,7 @@ struct Shape : public ShapeTraits */ AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const { + assert(index < static_cast(m_idsView.size())); return m_idsView[index]; } @@ -578,7 +579,7 @@ struct VariableShape : m_shapeId(shapeId) , m_idsView(ids) { - //SLIC_ASSERT(shapeId >= Point_ShapeID && shapeID <= Hex_ShapeID); + assert(shapeId >= Point_ShapeID && shapeId <= Hex_ShapeID); } /** diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 083d980ee3..6d0c33c9f1 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -207,6 +207,60 @@ void dispatch_rectilinear_coordset(const conduit::Node &coordset, FuncType &&fun } } +/** + * \brief Base template for creating a explicit coordset view. + */ +template +struct make_explicit_coordset +{ }; + +/** + * \brief Partial specialization for creating 3D explicit coordset view. + */ +template +struct make_explicit_coordset +{ + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + namespace bputils = axom::mir::utilities::blueprint; + const conduit::Node &values = coordset.fetch_existing("values"); + auto x = bputils::make_array_view(values[0]); + auto y = bputils::make_array_view(values[1]); + auto z = bputils::make_array_view(values[2]); + return CoordsetView(x, y, z); + } +}; + +/** + * \brief Partial specialization for creating 2D explicit coordset view. + */ +template +struct make_explicit_coordset +{ + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + + /** + * \brief Create the coordset view and initialize it from the coordset. + * \param topo The node containing the coordset. + * \return The coordset view. + */ + static CoordsetView view(const conduit::Node &coordset) + { + namespace bputils = axom::mir::utilities::blueprint; + const conduit::Node &values = coordset.fetch_existing("values"); + auto x = bputils::make_array_view(values[0]); + auto y = bputils::make_array_view(values[1]); + return CoordsetView(x, y); + } +}; + /** * \brief Dispatch an explicit coordset to a function. * From bd8fe95c4da657d71f69e5e4a64207a30c4082ef Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 10 Sep 2024 18:02:40 -0700 Subject: [PATCH 207/290] Added more tests, other improvements. --- src/axom/mir/ExtractZones.hpp | 20 +- src/axom/mir/blueprint_utilities.cpp | 14 + src/axom/mir/blueprint_utilities.hpp | 14 +- .../mir/tests/mir_blueprint_utilities.cpp | 808 +++++++++++------- 4 files changed, 541 insertions(+), 315 deletions(-) diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 93a65763c8..d9ba6998cb 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -8,6 +8,7 @@ #include #include +#include // Needed to get MatsetSlicer namespace axom { @@ -67,8 +68,9 @@ class ExtractZones // We need to figure out which nodes to keep. const auto nnodes = m_coordsetView.numberOfNodes(); axom::Array mask(nnodes, nnodes, allocatorID); - mask.fill(0); auto maskView = mask.view(); + mask.fill(0); + // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. RAJA::ReduceSum connsize_reduce(0); m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) @@ -76,7 +78,8 @@ class ExtractZones const int nids = zone.numberOfNodes(); for(int i = 0; i < nids; i++) { - maskView[zone.getId(i)] = 1; + const auto nodeId = zone.getId(i); + maskView[nodeId] = 1; } connsize_reduce += nids; }); @@ -101,8 +104,11 @@ class ExtractZones auto nodeSliceView = nodeSlice.view(); axom::for_all(nnodes, AXOM_LAMBDA(auto index) { - nodeSliceView[maskOffsetsView[index]] = index; - old2newView[index] = (maskView[index] > 0) ? maskOffsetsView[index] : 0; + if(maskView[index] > 0) + { + nodeSliceView[maskOffsetsView[index]] = index; + old2newView[index] = maskOffsetsView[index]; + } }); AXOM_ANNOTATE_END("nodeMap"); @@ -421,11 +427,9 @@ class ExtractZonesAndMatset : public ExtractZones; - MatSlicer ms; + MatsetSlicer ms; ms.execute(m_matsetView, selectedZonesView, n_matset, n_newMatset); #endif } diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index a914838224..ad88ce6c11 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -16,6 +16,20 @@ namespace utilities { namespace blueprint { + +// Static data. These originally appeared as constexpr members in the header file +// but there were linker errors despite constexpr. +const char *cpp2conduit::name = "int8"; +const char *cpp2conduit::name = "int16"; +const char *cpp2conduit::name = "int32"; +const char *cpp2conduit::name = "int64"; +const char *cpp2conduit::name = "uint8"; +const char *cpp2conduit::name = "uint16"; +const char *cpp2conduit::name = "uint32"; +const char *cpp2conduit::name = "uint64"; +const char *cpp2conduit::name = "float32"; +const char *cpp2conduit::name = "float64"; + /** * \brief Turns a ShapeID to a VTK cell type value. * diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index effd33339f..781f481c77 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -40,7 +40,6 @@ namespace blueprint template struct cpp2conduit { - static constexpr conduit::index_t id = conduit::DataType::EMPTY_ID; }; template <> @@ -48,6 +47,7 @@ struct cpp2conduit { using type = conduit::int8; static constexpr conduit::index_t id = conduit::DataType::INT8_ID; + static const char *name; }; template <> @@ -55,6 +55,7 @@ struct cpp2conduit { using type = conduit::int16; static constexpr conduit::index_t id = conduit::DataType::INT16_ID; + static const char *name; }; template <> @@ -62,6 +63,7 @@ struct cpp2conduit { using type = conduit::int32; static constexpr conduit::index_t id = conduit::DataType::INT32_ID; + static const char *name; }; template <> @@ -69,6 +71,7 @@ struct cpp2conduit { using type = conduit::int64; static constexpr conduit::index_t id = conduit::DataType::INT64_ID; + static const char *name; }; template <> @@ -76,6 +79,7 @@ struct cpp2conduit { using type = conduit::uint8; static constexpr conduit::index_t id = conduit::DataType::UINT8_ID; + static const char *name; }; template <> @@ -83,6 +87,7 @@ struct cpp2conduit { using type = conduit::uint16; static constexpr conduit::index_t id = conduit::DataType::UINT16_ID; + static const char *name; }; template <> @@ -90,6 +95,7 @@ struct cpp2conduit { using type = conduit::uint32; static constexpr conduit::index_t id = conduit::DataType::UINT32_ID; + static const char *name; }; template <> @@ -97,6 +103,7 @@ struct cpp2conduit { using type = conduit::uint64; static constexpr conduit::index_t id = conduit::DataType::UINT64_ID; + static const char *name; }; template <> @@ -104,6 +111,7 @@ struct cpp2conduit { using type = conduit::float32; static constexpr conduit::index_t id = conduit::DataType::FLOAT32_ID; + static const char *name; }; template <> @@ -111,6 +119,7 @@ struct cpp2conduit { using type = conduit::float64; static constexpr conduit::index_t id = conduit::DataType::FLOAT64_ID; + static const char *name; }; //------------------------------------------------------------------------------ @@ -128,6 +137,7 @@ struct cpp2conduit template inline axom::ArrayView make_array_view(conduit::Node &n) { + SLIC_ASSERT_MSG(cpp2conduit::id == n.dtype().id(), axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", cpp2conduit::name, n.dtype().name())); return axom::ArrayView(static_cast(n.data_ptr()), n.dtype().number_of_elements()); } @@ -135,6 +145,7 @@ inline axom::ArrayView make_array_view(conduit::Node &n) template inline axom::ArrayView make_array_view(const conduit::Node &n) { + SLIC_ASSERT_MSG(cpp2conduit::id == n.dtype().id(), axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", cpp2conduit::name, n.dtype().name())); return axom::ArrayView(static_cast(const_cast(n.data_ptr())), n.dtype().number_of_elements()); } @@ -455,6 +466,7 @@ void copy(conduit::Node &dest, const conduit::Node &src) template std::pair minmax(const conduit::Node &n) { + SLIC_ASSERT(n.dtype().number_of_elements() > 0); std::pair retval; axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 1e3414c8e9..70bb55e700 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -19,100 +19,143 @@ namespace mir = axom::mir; namespace bputils = axom::mir::utilities::blueprint; +//------------------------------------------------------------------------------ template -void test_conduit_allocate() +struct test_conduit_allocate { - axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a; - EXPECT_TRUE(c2a.getConduitAllocatorID() > 0); + static void test() + { + axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a; + EXPECT_TRUE(c2a.getConduitAllocatorID() > 0); - constexpr int nValues = 100; - conduit::Node n; - n.set_allocator(c2a.getConduitAllocatorID()); - n.set(conduit::DataType::int32(nValues)); + constexpr int nValues = 100; + conduit::Node n; + n.set_allocator(c2a.getConduitAllocatorID()); + n.set(conduit::DataType::int32(nValues)); - // Make sure we can store some values into the data that were allocated. - int *ptr = static_cast(n.data_ptr()); - axom::for_all( - nValues, - AXOM_LAMBDA(auto index) { ptr[index] = index; }); + // Make sure we can store some values into the data that were allocated. + auto nview = bputils::make_array_view(n); + axom::for_all( + nValues, + AXOM_LAMBDA(auto index) { nview[index] = index; }); - EXPECT_EQ(n.dtype().number_of_elements(), nValues); -} + EXPECT_EQ(n.dtype().number_of_elements(), nValues); + + // Get the values back to the host. + std::vector hostValues(nValues); + axom::copy(hostValues.data(), n.data_ptr(), sizeof(int) * nValues); -TEST(mir_blueprint_utilities, allocate) + // Check that the values were set. + for(int i = 0; i < nValues; i++) + { + EXPECT_EQ(hostValues[i], i); + } + } +}; + +TEST(mir_blueprint_utilities, allocate_seq) { - test_conduit_allocate(); + test_conduit_allocate::test(); +} #if defined(AXOM_USE_OPENMP) - test_conduit_allocate(); +TEST(mir_blueprint_utilities, allocate_omp) +{ + test_conduit_allocate::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_conduit_allocate(); +TEST(mir_blueprint_utilities, allocate_cuda) +{ + test_conduit_allocate::test(); +} #endif #if defined(AXOM_USE_HIP) - test_conduit_allocate(); -#endif +TEST(mir_blueprint_utilities, allocate_hip) +{ + test_conduit_allocate::test(); } +#endif +//------------------------------------------------------------------------------ template -void test_copy_braid(const conduit::Node &mesh) +struct test_copy_braid { - // Copy the mesh to device. - conduit::Node mesh_dev; - axom::mir::utilities::blueprint::copy(mesh_dev, mesh); - - // Run some minmax operations on device (proves that the data was in the right place) and check the results. - - constexpr double eps = 1.e-7; - - auto x = axom::mir::utilities::blueprint::minmax( - mesh["coordsets/coords/values/x"]); - //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; - EXPECT_NEAR(x.first, -10., eps); - EXPECT_NEAR(x.second, 10., eps); - - auto y = axom::mir::utilities::blueprint::minmax( - mesh["coordsets/coords/values/y"]); - //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; - EXPECT_NEAR(y.first, -10., eps); - EXPECT_NEAR(y.second, 10., eps); - - auto c = axom::mir::utilities::blueprint::minmax( - mesh["topologies/mesh/elements/connectivity"]); - //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; - EXPECT_NEAR(c.first, 0., eps); - EXPECT_NEAR(c.second, 999., eps); + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // Copy the mesh to device. + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + + // Run some minmax operations on device (proves that the data was in the right place) and check the results. + + constexpr double eps = 1.e-7; + + auto x = axom::mir::utilities::blueprint::minmax( + deviceMesh["coordsets/coords/values/x"]); + //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; + EXPECT_NEAR(x.first, -10., eps); + EXPECT_NEAR(x.second, 10., eps); + + auto y = axom::mir::utilities::blueprint::minmax( + deviceMesh["coordsets/coords/values/y"]); + //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; + EXPECT_NEAR(y.first, -10., eps); + EXPECT_NEAR(y.second, 10., eps); + + auto c = axom::mir::utilities::blueprint::minmax( + deviceMesh["topologies/mesh/elements/connectivity"]); + //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; + EXPECT_NEAR(c.first, 0., eps); + EXPECT_NEAR(c.second, 999., eps); + + auto r = axom::mir::utilities::blueprint::minmax( + deviceMesh["fields/radial/values"]); + //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; + EXPECT_NEAR(r.first, 19.2450089729875, eps); + EXPECT_NEAR(r.second, 173.205080756888, eps); + } - auto r = axom::mir::utilities::blueprint::minmax( - mesh["fields/radial/values"]); - //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; - EXPECT_NEAR(r.first, 19.2450089729875, eps); - EXPECT_NEAR(r.second, 173.205080756888, eps); -} + static void create(conduit::Node &mesh) + { + const int d[3] = {10, 10, 10}; + conduit::blueprint::mesh::examples::braid("hexs", d[0], d[1], d[2], mesh); + } +}; -TEST(mir_blueprint_utilities, copy) +TEST(mir_blueprint_utilities, copy_seq) { - const int d[3] = {10, 10, 10}; - conduit::Node mesh; - conduit::blueprint::mesh::examples::braid("hexs", d[0], d[1], d[2], mesh); + test_copy_braid::test(); +} - test_copy_braid(mesh); #if defined(AXOM_USE_OPENMP) - test_copy_braid(mesh); +TEST(mir_blueprint_utilities, copy_omp) +{ + test_copy_braid::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_copy_braid(mesh); +TEST(mir_blueprint_utilities, copy_cuda) +{ + test_copy_braid::test(); +} #endif #if defined(AXOM_USE_HIP) - test_copy_braid(mesh); -#endif +TEST(mir_blueprint_utilities, copy_hip) +{ + test_copy_braid::test(); } +#endif +//------------------------------------------------------------------------------ TEST(mir_blueprint_utilities, to_unstructured) { // TODO: to_unstructured } -//---------------------------------------------------------------------- +//------------------------------------------------------------------------------ template void compareRelation(const conduit::Node &hostRelation, @@ -355,84 +398,102 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) //------------------------------------------------------------------------------ template -void test_recenter_field(const conduit::Node &hostMesh) +struct test_recenter_field { - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; - - // Make a node to zone relation on the device. - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceCoordset, deviceRelation); - - // Recenter a field zonal->nodal on the device - axom::mir::utilities::blueprint::RecenterField r; - r.execute(deviceMesh["fields/easy_zonal"], - deviceRelation, - deviceMesh["fields/z2n"]); - - // Recenter a field nodal->zonal on the device. (The elements are an o2m relation) - r.execute(deviceMesh["fields/z2n"], - deviceMesh["topologies/mesh/elements"], - deviceMesh["fields/n2z"]); - - // device -> host - conduit::Node hostResultMesh; - axom::mir::utilities::blueprint::copy(hostResultMesh, deviceMesh); + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; + + // Make a node to zone relation on the device. + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + + // Recenter a field zonal->nodal on the device + axom::mir::utilities::blueprint::RecenterField r; + r.execute(deviceMesh["fields/easy_zonal"], + deviceRelation, + deviceMesh["fields/z2n"]); + + // Recenter a field nodal->zonal on the device. (The elements are an o2m relation) + r.execute(deviceMesh["fields/z2n"], + deviceMesh["topologies/mesh/elements"], + deviceMesh["fields/n2z"]); + + // device -> host + conduit::Node hostResultMesh; + axom::mir::utilities::blueprint::copy(hostResultMesh, deviceMesh); #if 0 - // Print the results. - printNode(hostResultMesh); + // Print the results. + printNode(hostResultMesh); #endif - const float n2z_result[] = {1., 2., 4., 5., 4., 5., 7., 8., 7., 8., 10., 11.}; - for(size_t i = 0; i < (sizeof(n2z_result) / sizeof(float)); i++) - { - EXPECT_EQ(n2z_result[i], - hostResultMesh["fields/z2n/values"].as_float_accessor()[i]); + const float n2z_result[] = {1., 2., 4., 5., 4., 5., 7., 8., 7., 8., 10., 11.}; + for(size_t i = 0; i < (sizeof(n2z_result) / sizeof(float)); i++) + { + EXPECT_EQ(n2z_result[i], + hostResultMesh["fields/z2n/values"].as_float_accessor()[i]); + } + const float z2n_result[] = {3., 4.5, 6., 6., 7.5, 9.}; + for(size_t i = 0; i < (sizeof(z2n_result) / sizeof(float)); i++) + { + EXPECT_EQ(z2n_result[i], + hostResultMesh["fields/n2z/values"].as_float_accessor()[i]); + } } - const float z2n_result[] = {3., 4.5, 6., 6., 7.5, 9.}; - for(size_t i = 0; i < (sizeof(z2n_result) / sizeof(float)); i++) + + static void create(conduit::Node &mesh) { - EXPECT_EQ(z2n_result[i], - hostResultMesh["fields/n2z/values"].as_float_accessor()[i]); + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + mesh["topologies/mesh/elements/sizes"].set( + std::vector {{4, 4, 4, 4, 4, 4}}); + mesh["topologies/mesh/elements/offsets"].set( + std::vector {{0, 4, 8, 12, 16, 20}}); + mesh["fields/easy_zonal/topology"] = "mesh"; + mesh["fields/easy_zonal/association"] = "element"; + mesh["fields/easy_zonal/values"].set(std::vector {{1, 3, 5, 7, 9, 11}}); } -} +}; -TEST(mir_blueprint_utilities, recenterfield) +TEST(mir_blueprint_utilities, recenterfield_seq) { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - mesh["topologies/mesh/elements/sizes"].set( - std::vector {{4, 4, 4, 4, 4, 4}}); - mesh["topologies/mesh/elements/offsets"].set( - std::vector {{0, 4, 8, 12, 16, 20}}); - mesh["fields/easy_zonal/topology"] = "mesh"; - mesh["fields/easy_zonal/association"] = "element"; - mesh["fields/easy_zonal/values"].set(std::vector {{1, 3, 5, 7, 9, 11}}); - - test_recenter_field(mesh); + test_recenter_field::test(); +} + #if defined(AXOM_USE_OPENMP) - test_recenter_field(mesh); +TEST(mir_blueprint_utilities, recenterfield_omp) +{ + test_recenter_field::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_recenter_field(mesh); +TEST(mir_blueprint_utilities, recenterfield_cuda) +{ + test_recenter_field::test(); +} #endif #if defined(AXOM_USE_HIP) - test_recenter_field(mesh); -#endif +TEST(mir_blueprint_utilities, recenterfield_hip) +{ + test_recenter_field::test(); } +#endif //------------------------------------------------------------------------------ template @@ -447,62 +508,67 @@ bool compare_views(const Container1 &a, const Container2 &b) } template -void test_matset_slice(const conduit::Node &hostMatset) +struct test_matset_slice { - // host->device - conduit::Node deviceMatset; - bputils::copy(deviceMatset, hostMatset); - - axom::Array ids{{1,3,5}}; - axom::Array selectedZones(3, 3, axom::execution_space::allocatorID()); - axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); - - using MatsetView = axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMatset["material_ids"]), - bputils::make_array_view(deviceMatset["volume_fractions"]), - bputils::make_array_view(deviceMatset["sizes"]), - bputils::make_array_view(deviceMatset["offsets"]), - bputils::make_array_view(deviceMatset["indices"])); - - // Slice it. - bputils::MatsetSlicer slicer; - conduit::Node newDeviceMatset; - slicer.execute(matsetView, selectedZones.view(), deviceMatset, newDeviceMatset); - - // device->host - conduit::Node newHostMatset; - bputils::copy(newHostMatset, newDeviceMatset); - - // Expected answers. - const axom::Array sizes{{2, 1, 2}}; - const axom::Array offsets{{0, 2, 3}}; - const axom::Array indices{{0, 1, 2, 3, 4}}; - const axom::Array material_ids{{1, 2, 2, 2, 3}}; - const axom::Array volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; - - EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); - EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMatset["offsets"]))); - EXPECT_TRUE(compare_views(indices.view(), bputils::make_array_view(newHostMatset["indices"]))); - EXPECT_TRUE(compare_views(material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); - EXPECT_TRUE(compare_views(volume_fractions.view(), bputils::make_array_view(newHostMatset["volume_fractions"]))); -} + static void test() + { + conduit::Node hostMatset; + create(hostMatset); + + // host->device + conduit::Node deviceMatset; + bputils::copy(deviceMatset, hostMatset); + + axom::Array ids{{1,3,5}}; + axom::Array selectedZones(3, 3, axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); + + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMatset["material_ids"]), + bputils::make_array_view(deviceMatset["volume_fractions"]), + bputils::make_array_view(deviceMatset["sizes"]), + bputils::make_array_view(deviceMatset["offsets"]), + bputils::make_array_view(deviceMatset["indices"])); + + // Slice it. + bputils::MatsetSlicer slicer; + conduit::Node newDeviceMatset; + slicer.execute(matsetView, selectedZones.view(), deviceMatset, newDeviceMatset); + + // device->host + conduit::Node newHostMatset; + bputils::copy(newHostMatset, newDeviceMatset); + + // Expected answers. + const axom::Array sizes{{2, 1, 2}}; + const axom::Array offsets{{0, 2, 3}}; + const axom::Array indices{{0, 1, 2, 3, 4}}; + const axom::Array material_ids{{1, 2, 2, 2, 3}}; + const axom::Array volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); + EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMatset["offsets"]))); + EXPECT_TRUE(compare_views(indices.view(), bputils::make_array_view(newHostMatset["indices"]))); + EXPECT_TRUE(compare_views(material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); + EXPECT_TRUE(compare_views(volume_fractions.view(), bputils::make_array_view(newHostMatset["volume_fractions"]))); + } -TEST(mir_blueprint_utilities, matsetslice) -{ - /* - 8-------9------10------11 - | 2/1 | 1/0.1 | 2/0.8 | - | | 2/0.5 | 3/0.2 | - | | 3/0.4 | | - 4-------5-------6-------7 - | | 1/0.5 | 1/0.2 | - | 1/1 | 2/0.5 | 2/0.8 | - | | | | - 0-------1-------2-------3 - */ - const char *matsetData = R"xx( + static void create(conduit::Node &matset) + { + /* + 8-------9------10------11 + | 2/1 | 1/0.1 | 2/0.8 | + | | 2/0.5 | 3/0.2 | + | | 3/0.4 | | + 4-------5-------6-------7 + | | 1/0.5 | 1/0.2 | + | 1/1 | 2/0.5 | 2/0.8 | + | | | | + 0-------1-------2-------3 + */ + const char *yaml = R"xx( topology: mesh material_map: a: 1 @@ -515,22 +581,36 @@ sizes: [1, 2, 2, 1, 3, 2] offsets: [0, 1, 3, 5, 6, 9] )xx"; - conduit::Node matset; - matset.parse(matsetData); + matset.parse(yaml); + } +}; + +TEST(mir_blueprint_utilities, matsetslice_seq) +{ + test_matset_slice::test(); +} - test_matset_slice(matset); #if defined(AXOM_USE_OPENMP) - test_matset_slice(matset); +TEST(mir_blueprint_utilities, matsetslice_omp) +{ + test_matset_slice::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_matset_slice(matset); +TEST(mir_blueprint_utilities, matsetslice_cuda) +{ + test_matset_slice::test(); +} #endif #if defined(AXOM_USE_HIP) - test_matset_slice(matset); -#endif +TEST(mir_blueprint_utilities, matsetslice_hip) +{ + test_matset_slice::test(); } +#endif + //------------------------------------------------------------------------------ template @@ -569,178 +649,281 @@ void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) EXPECT_TRUE(compare_views(y.view(), bputils::make_array_view(newHostCoordset["values/y"]))); } -TEST(mir_blueprint_utilities, coordsetslicer_explicit) +//------------------------------------------------------------------------------ +template +struct coordsetslicer_explicit { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( type: explicit values: x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2.] )xx"; - conduit::Node coordset; - coordset.parse(yaml); + conduit::Node coordset; + coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { - return axom::mir::views::make_explicit_coordset::view(deviceCoordset); - }; + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_explicit_coordset::view(deviceCoordset); + }; + + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_blueprint_utilities, coordsetslicer_explicit_seq) +{ + coordsetslicer_explicit::test(); +} - test_coordsetslicer(coordset, makeView); #if defined(AXOM_USE_OPENMP) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_explicit_omp) +{ + coordsetslicer_explicit::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_explicit_cuda) +{ + coordsetslicer_explicit::test(); +} #endif #if defined(AXOM_USE_HIP) - test_coordsetslicer(coordset, makeView); -#endif +TEST(mir_blueprint_utilities, coordsetslicer_explicit_hip) +{ + coordsetslicer_explicit::test(); } +#endif -TEST(mir_blueprint_utilities, coordsetslicer_rectilinear) +//------------------------------------------------------------------------------ +template +struct coordsetslicer_rectilinear { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( type: rectilinear values: x: [0., 1., 2., 3.] y: [0., 1., 2.] )xx"; - conduit::Node coordset; - coordset.parse(yaml); + conduit::Node coordset; + coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { - return axom::mir::views::make_rectilinear_coordset::view(deviceCoordset); - }; + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_rectilinear_coordset::view(deviceCoordset); + }; + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_seq) +{ + coordsetslicer_rectilinear::test(); +} - test_coordsetslicer(coordset, makeView); #if defined(AXOM_USE_OPENMP) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_omp) +{ + coordsetslicer_rectilinear::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_cuda) +{ + coordsetslicer_rectilinear::test(); +} #endif #if defined(AXOM_USE_HIP) - test_coordsetslicer(coordset, makeView); -#endif +TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_hip) +{ + coordsetslicer_rectilinear::test(); } +#endif -TEST(mir_blueprint_utilities, coordsetslicer_uniform) +//------------------------------------------------------------------------------ +template +struct coordsetslicer_uniform { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( type: uniform dims: i: 4 j: 3 )xx"; - conduit::Node coordset; - coordset.parse(yaml); + conduit::Node coordset; + coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { - return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); - }; + auto makeView = [](const conduit::Node &deviceCoordset) + { + return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); + }; + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_blueprint_utilities, coordsetslicer_uniform_seq) +{ + coordsetslicer_uniform::test(); +} - test_coordsetslicer(coordset, makeView); #if defined(AXOM_USE_OPENMP) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_uniform_omp) +{ + coordsetslicer_uniform::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_coordsetslicer(coordset, makeView); +TEST(mir_blueprint_utilities, coordsetslicer_uniform_cuda) +{ + coordsetslicer_uniform::test(); +} #endif #if defined(AXOM_USE_HIP) - test_coordsetslicer(coordset, makeView); -#endif +TEST(mir_blueprint_utilities, coordsetslicer_uniform_hip) +{ + coordsetslicer_uniform::test(); } +#endif //------------------------------------------------------------------------------ template -void test_extractzones(const conduit::Node &hostMesh) +struct test_extractzones { - // host->device - conduit::Node deviceMesh; - bputils::copy(deviceMesh, hostMesh); - - axom::Array ids{{1,3,5}}; - const auto nzones = ids.size(); - axom::Array selectedZones(nzones, nzones, axom::execution_space::allocatorID()); - axom::copy(selectedZones.data(), ids.data(), nzones * sizeof(axom::IndexType)); - - // Wrap the data in views. - auto coordsetView = axom::mir::views::make_explicit_coordset::view(deviceMesh["coordsets/coords"]); - using CoordsetView = decltype(coordsetView); - - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopologyView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]), - bputils::make_array_view(deviceMesh["topologies/mesh/elements/sizes"]), - bputils::make_array_view(deviceMesh["topologies/mesh/elements/offsets"])); - - using MatsetView = axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); - - // Pull out selected zones - bputils::ExtractZones extract(topoView, coordsetView); - conduit::Node options, newDeviceMesh; - options["topology"] = "mesh"; - extract.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); - - // device->host - conduit::Node newHostMesh; - bputils::copy(newHostMesh, newDeviceMesh); - - newHostMesh.print(); -} + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + axom::Array ids{{1,3,5}}; + const auto nzones = ids.size(); + axom::Array selectedZones(nzones, nzones, axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), nzones * sizeof(axom::IndexType)); + + // Wrap the data in views. + auto coordsetView = axom::mir::views::make_explicit_coordset::view(deviceMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]), + bputils::make_array_view(deviceMesh["topologies/mesh/elements/sizes"]), + bputils::make_array_view(deviceMesh["topologies/mesh/elements/offsets"])); + + // Pull out selected zones + bputils::ExtractZones extract(topoView, coordsetView); + conduit::Node options, newDeviceMesh; + options["topology"] = "mesh"; + extract.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); + + // device->host + conduit::Node newHostMesh; + bputils::copy(newHostMesh, newDeviceMesh); + + //printNode(newHostMesh); + + // Check some of the key arrays + const axom::Array connectivity{{0, 1, 4, 3, 2, 3, 7, 6, 4, 5, 9, 8}}; + const axom::Array sizes{{4, 4, 4}}; + const axom::Array offsets{{0, 4, 8}}; + const axom::Array x{{1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0}}; + const axom::Array y{{0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0}}; + const axom::Array zonal{{1.0, 3.0, 5.0}}; + const axom::Array nodal{{1.0, 2.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0}}; + + EXPECT_TRUE(compare_views(connectivity.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/connectivity"]))); + EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/sizes"]))); + EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/offsets"]))); + EXPECT_TRUE(compare_views(x.view(), bputils::make_array_view(newHostMesh["coordsets/coords/values/x"]))); + EXPECT_TRUE(compare_views(y.view(), bputils::make_array_view(newHostMesh["coordsets/coords/values/y"]))); + EXPECT_TRUE(compare_views(zonal.view(), bputils::make_array_view(newHostMesh["fields/zonal/values"]))); + EXPECT_TRUE(compare_views(nodal.view(), bputils::make_array_view(newHostMesh["fields/nodal/values"]))); + + // Do the material too. + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); + + // Pull out selected zones + bputils::ExtractZonesAndMatset extractM(topoView, coordsetView, matsetView); + newDeviceMesh.reset(); + extractM.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); + + // device->host + newHostMesh.reset(); + bputils::copy(newHostMesh, newDeviceMesh); + + // Check some of the key arrays in the sliced material + const axom::Array mat_sizes{{2, 1, 2}}; + const axom::Array mat_offsets{{0, 2, 3}}; + const axom::Array mat_indices{{0, 1, 2, 3, 4}}; + const axom::Array mat_material_ids{{1, 2, 2, 2, 3}}; + const axom::Array mat_volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_TRUE(compare_views(mat_sizes.view(), bputils::make_array_view(newHostMesh["matsets/mat1/sizes"]))); + EXPECT_TRUE(compare_views(mat_offsets.view(), bputils::make_array_view(newHostMesh["matsets/mat1/offsets"]))); + EXPECT_TRUE(compare_views(mat_indices.view(), bputils::make_array_view(newHostMesh["matsets/mat1/indices"]))); + EXPECT_TRUE(compare_views(mat_material_ids.view(), bputils::make_array_view(newHostMesh["matsets/mat1/material_ids"]))); + EXPECT_TRUE(compare_views(mat_volume_fractions.view(), bputils::make_array_view(newHostMesh["matsets/mat1/volume_fractions"]))); + } -TEST(mir_blueprint_utilities, extractzones) -{ - /* - 8-------9------10------11 - | 2/1 | 1/0.1 | 2/0.8 | - | | 2/0.5 | 3/0.2 | - | | 3/0.4 | | - 4-------5-------6-------7 - | | 1/0.5 | 1/0.2 | - | 1/1 | 2/0.5 | 2/0.8 | - | | | | - 0-------1-------2-------3 - */ - const char *meshData = R"xx( + static void create(conduit::Node &hostMesh) + { + /* + 8-------9------10------11 + | 2/1 | 1/0.1 | 2/0.8 | + | | 2/0.5 | 3/0.2 | + | | 3/0.4 | | + 4-------5-------6-------7 + | | 1/0.5 | 1/0.2 | + | 1/1 | 2/0.5 | 2/0.8 | + | | | | + 0-------1-------2-------3 + */ + const char *yaml = R"xx( coordsets: coords: type: explicit @@ -779,22 +962,35 @@ TEST(mir_blueprint_utilities, extractzones) offsets: [0, 1, 3, 5, 6, 9] )xx"; - conduit::Node mesh; - mesh.parse(meshData); + hostMesh.parse(yaml); + } +}; + +TEST(mir_blueprint_utilities, extractzones_seq) +{ + test_extractzones::test(); +} - test_extractzones(mesh); #if defined(AXOM_USE_OPENMP) - test_extractzones(mesh); +TEST(mir_blueprint_utilities, extractzones_omp) +{ + test_extractzones::test(); +} #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - test_extractzones(mesh); +TEST(mir_blueprint_utilities, extractzones_cuda) +{ + test_extractzones::test(); +} #endif #if defined(AXOM_USE_HIP) - test_extractzones(mesh); -#endif +TEST(mir_blueprint_utilities, extractzones_hip) +{ + test_extractzones::test(); } +#endif //------------------------------------------------------------------------------ int main(int argc, char *argv[]) From 28f3c8a63a93ca9412d96679b139639d455c3b49 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 11 Sep 2024 12:15:24 -0700 Subject: [PATCH 208/290] Changed ZoneListBuilder to atomics instead of requiring node to zone relation. --- src/axom/mir/ExtractZones.hpp | 2 - src/axom/mir/ZoneListBuilder.hpp | 168 +++++++++--------- .../mir/tests/mir_blueprint_utilities.cpp | 126 +++++++++++++ 3 files changed, 207 insertions(+), 89 deletions(-) diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index d9ba6998cb..0d1f20ede8 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -427,11 +427,9 @@ class ExtractZonesAndMatset : public ExtractZones ms; ms.execute(m_matsetView, selectedZonesView, n_matset, n_newMatset); -#endif } MatsetView m_matsetView; diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index d28743e108..fc6a26ae25 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -42,9 +42,10 @@ class ZoneListBuilder } /** - * \brief Build the list of clean and mixed zones. + * \brief Build the list of clean and mixed zones using the number of materials + * per zone, maxed to the nodes. * - * \param n2z A Conduit node containing a node to zone relation. + * \param nnodes The number of nodes in the topology's coordset. * \param[out] cleanIndices An array that will contain the list of clean material zone ids. * \param[out] mixedIndices An array that will contain the list of mixed material zone ids. * @@ -53,107 +54,100 @@ class ZoneListBuilder * those that touch only nodes that have 1 material are marked clean, otherwise they are * considered mixed as we might have to split those zones. */ - void execute(const conduit::Node &n2z, axom::Array &cleanIndices, axom::Array &mixedIndices) const + void execute(axom::IndexType nnodes, axom::Array &cleanIndices, axom::Array &mixedIndices) const { + using atomic_policy = typename axom::execution_space::atomic_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); const int allocatorID = axom::execution_space::allocatorID(); - const conduit::Node &n_zones = n2z.fetch_existing("zones"); - const conduit::Node &n_indices = n2z.fetch_existing("indices"); - const conduit::Node &n_sizes = n2z.fetch_existing("sizes"); - const conduit::Node &n_offsets = n2z.fetch_existing("offsets"); - axom::mir::views::IndexNode_to_ArrayView_same(n_zones, n_indices, n_sizes, n_offsets, [&](auto zones, auto indices, auto sizes, auto offsets) + axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); + nMatsPerNode.fill(0); + auto nMatsPerNodeView = nMatsPerNode.view(); + + // Determine max number of materials a node might touch. + MatsetView deviceMatsetView(m_matsetView); + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + { + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); + + // Now, mark all zones that have 1 mat per node as clean. + const auto nzones = m_topologyView.numberOfZones(); + axom::Array mask(nzones, nzones, allocatorID); + auto maskView = mask.view(); + RAJA::ReduceSum mask_reduce(0); + m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - using reduce_policy = typename axom::execution_space::reduce_policy; - const auto nnodes = sizes.size(); - axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); - auto nMatsPerNodeView = nMatsPerNode.view(); - - // OR to the nodes. We just take the max number of materials of adjacent - // zones and put those on the nodes lest we need an array that stores - // general N-bit values per element. - MatsetView deviceMatsetView(m_matsetView); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) { - int nmats = 0; - const int size = static_cast(sizes[index]); - const auto offset = offsets[index]; - for(int i = 0; i < size; i++) + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[zoneIndex] = ival; + mask_reduce += ival; + }); + + const int nClean = mask_reduce.get(); + if(nClean > 0) + { + axom::Array maskOffsets(nzones, nzones, allocatorID); + auto maskOffsetsView = maskOffsets.view(); + axom::exclusive_scan(maskView, maskOffsetsView); + + // Make the output cleanIndices array. + cleanIndices = axom::Array(nClean, nClean, allocatorID); + auto cleanIndicesView = cleanIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) { - const auto zi = zones[indices[offset + i]]; - nmats = axom::utilities::max(nmats, static_cast(deviceMatsetView.numberOfMaterials(zi))); + cleanIndicesView[maskOffsetsView[index]] = index; } - nMatsPerNodeView[index] = nmats; }); - // Now, mark all zones that have 1 mat per node as clean. - const auto nzones = m_topologyView.numberOfZones(); - axom::Array mask(nzones, nzones, allocatorID); - auto maskView = mask.view(); - RAJA::ReduceSum mask_reduce(0); - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) + // Make the mixedIndices array. + axom::for_all(nzones, AXOM_LAMBDA(auto index) { - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + maskView[index] = (maskView[index] == 1) ? 0 : 1; + }); + axom::exclusive_scan(maskView, maskOffsetsView); + const int nMixed = nzones - nClean; + mixedIndices = axom::Array(nMixed, nMixed, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) { - const auto nodeId = zone.getId(i); - clean |= nMatsPerNodeView[nodeId] == 1; + mixedIndicesView[maskOffsetsView[index]] = index; } - - const int ival = clean ? 1 : 0; - maskView[zoneIndex] = ival; - mask_reduce += ival; }); + } + else + { + cleanIndices = axom::Array(); - const int nClean = mask_reduce.get(); - if(nClean > 0) + // There were no clean, so it must all be mixed. + mixedIndices = axom::Array(nzones, nzones, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) { - axom::Array maskOffsets(nzones, nzones, allocatorID); - auto maskOffsetsView = maskOffsets.view(); - axom::exclusive_scan(maskView, maskOffsetsView); - - // Make the output cleanIndices array. - cleanIndices = axom::Array(nClean, nClean, allocatorID); - auto cleanIndicesView = cleanIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - cleanIndicesView[maskOffsetsView[index]] = index; - } - }); - - // Make the mixedIndices array. - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - maskView[index] = ~maskView[index]; - }); - axom::exclusive_scan(maskView, maskOffsetsView); - const int nMixed = nzones - nClean; - mixedIndices = axom::Array(nMixed, nMixed, allocatorID); - auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - mixedIndicesView[maskOffsetsView[index]] = index; - } - }); - } - else - { - cleanIndices = axom::Array(); - - // There were no clean, so it must all be mixed. - mixedIndices = axom::Array(nzones, nzones, allocatorID); - auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - mixedIndicesView[index] = index; - }); - } - }); + mixedIndicesView[index] = index; + }); + } } private: diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 70bb55e700..c3af5d82b7 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -992,6 +992,132 @@ TEST(mir_blueprint_utilities, extractzones_hip) } #endif +//------------------------------------------------------------------------------ +template +struct test_zonelistbuilder +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Wrap the data in views. + auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(deviceMesh["coordsets/coords"]); + + auto topologyView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); + using TopologyView = decltype(topologyView); + + // Do the material too. + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); + + // Determine the list of clean and mixed zones (taking into account #mats at the nodes) + bputils::ZoneListBuilder zlb(topologyView, matsetView); + axom::Array clean, mixed; + zlb.execute(coordsetView.numberOfNodes(), clean, mixed); + + conduit::Node deviceData; + deviceData["clean"].set_external(clean.data(), clean.size()); + deviceData["mixed"].set_external(mixed.data(), mixed.size()); + + // device->host + conduit::Node hostData; + bputils::copy(hostData, deviceData); + + // Compare expected + const axom::Array cleanResult{{0, 1, 2, 3, 4, 8, 12}}; + const axom::Array mixedResult{{5, 6, 7, 9, 10, 11, 13, 14, 15}}; + EXPECT_TRUE(compare_views(cleanResult.view(), bputils::make_array_view(hostData["clean"]))); + EXPECT_TRUE(compare_views(mixedResult.view(), bputils::make_array_view(hostData["mixed"]))); + + //printNode(hostData); + } + + static void create(conduit::Node &hostMesh) + { + /* + 20------21-------22-------23-------24 + | 1/1 | 1/1 | 1/.5 | 2/1. | 1/1 = mat#1, vf=1.0 + | | | 2/.5 | | + |z12 |z13 |z14 |z15 | + 15------16-------17-------18-------19 + | 1/1 | 1/1 | 1/0.7 | 1/.5 | + | | | 2/0.3 | 2/.5 | + |z8 |z9 |z10 |z11 | + 10------11-------12-------13-------14 + | 1/1 | 1/1 | 1/1 | 1/1 | + | | | | | + |z4 |z5 |z6 |z7 | + 5-------6--------7--------8--------9 + | 1/1 | 1/1 | 1/1 | 1/1 | + | | | | | + |z0 |z1 |z2 |z3 | + 0-------1--------2--------3--------4 + */ + const char *yaml = R"xx( +coordsets: + coords: + type: rectilinear + values: + x: [0., 1., 2., 3., 4.] + y: [0., 1., 2., 3., 4.] +topologies: + mesh: + type: rectilinear + coordset: coords +matsets: + mat1: + topology: mesh + material_map: + a: 1 + b: 2 + material_ids: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2] + volume_fractions: [1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 0.7, 0.3, .5, 0.5, 1., 1., 0.5, 0.5, 1.] + indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] + sizes: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1] + offsets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18] +)xx"; + + hostMesh.parse(yaml); + } +}; + +TEST(mir_blueprint_utilities, zonelistbuilder_seq) +{ + test_zonelistbuilder::test(); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_blueprint_utilities, zonelistbuilder_omp) +{ + test_zonelistbuilder::test(); +} +#endif + +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_blueprint_utilities, zonelistbuilder_cuda) +{ + test_zonelistbuilder::test(); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_blueprint_utilities, zonelistbuilder_hip) +{ + test_zonelistbuilder::test(); +} +#endif + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { From 449f16a2d155da0a7151c2afc8acb2db10f7f6ba Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 11 Sep 2024 14:09:27 -0700 Subject: [PATCH 209/290] Added selected zones to zone list builder. --- src/axom/mir/ZoneListBuilder.hpp | 117 ++++++++++++++++++ .../mir/tests/mir_blueprint_utilities.cpp | 18 ++- 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index fc6a26ae25..baf69a1970 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -31,6 +31,8 @@ template class ZoneListBuilder { public: + using SelectedZonesView = axom::ArrayView; + /** * \brief Constructor * @@ -150,6 +152,121 @@ class ZoneListBuilder } } + /** + * \brief Build the list of clean and mixed zones using the number of materials + * per zone, maxed to the nodes. Limit the number of zones. + * + * \param nnodes The number of nodes in the topology's coordset. + * \param selectedZonesView A view containing the zone indices we're considering. + * \param[out] cleanIndices An array that will contain the list of clean material zone ids. + * \param[out] mixedIndices An array that will contain the list of mixed material zone ids. + * + * \note The clean/mixed index arrays are not strictly what could be determined by the matset alone. + * We figure out which nodes touch multiple materials. Then we iterate over the zones and + * those that touch only nodes that have 1 material are marked clean, otherwise they are + * considered mixed as we might have to split those zones. + */ + void execute(axom::IndexType nnodes, + const SelectedZonesView &selectedZonesView, + axom::Array &cleanIndices, + axom::Array &mixedIndices) const + { + using atomic_policy = typename axom::execution_space::atomic_policy; + using reduce_policy = typename axom::execution_space::reduce_policy; + + AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); + SLIC_ASSERT(selectedZonesView.size() > 0); + + const int allocatorID = axom::execution_space::allocatorID(); + + axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); + nMatsPerNode.fill(0); + auto nMatsPerNodeView = nMatsPerNode.view(); + + // Determine max number of materials a node might touch. + MatsetView deviceMatsetView(m_matsetView); + m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto zoneIndex, const auto &zone) + { + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); + + // Now, mark all selected zones that have 1 mat per node as clean. + const auto nzones = selectedZonesView.size(); + axom::Array mask(nzones, nzones, allocatorID); + auto maskView = mask.view(); + RAJA::ReduceSum mask_reduce(0); + m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) + { + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[szIndex] = ival; + mask_reduce += ival; + }); + + const int nClean = mask_reduce.get(); + if(nClean > 0) + { + axom::Array maskOffsets(nzones, nzones, allocatorID); + auto maskOffsetsView = maskOffsets.view(); + axom::exclusive_scan(maskView, maskOffsetsView); + + // Make the output cleanIndices array. + cleanIndices = axom::Array(nClean, nClean, allocatorID); + auto cleanIndicesView = cleanIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + cleanIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; + } + }); + + // Make the mixedIndices array. + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + maskView[index] = (maskView[index] == 1) ? 0 : 1; + }); + axom::exclusive_scan(maskView, maskOffsetsView); + const int nMixed = nzones - nClean; + mixedIndices = axom::Array(nMixed, nMixed, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + mixedIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; + } + }); + } + else + { + cleanIndices = axom::Array(); + + // There were no clean, so it must all be mixed. + mixedIndices = axom::Array(nzones, nzones, allocatorID); + auto mixedIndicesView = mixedIndices.view(); + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + mixedIndicesView[index] = selectedZonesView[index]; + }); + } + } + private: TopologyView m_topologyView; MatsetView m_matsetView; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index c3af5d82b7..33a50f6735 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -1040,7 +1040,23 @@ struct test_zonelistbuilder EXPECT_TRUE(compare_views(cleanResult.view(), bputils::make_array_view(hostData["clean"]))); EXPECT_TRUE(compare_views(mixedResult.view(), bputils::make_array_view(hostData["mixed"]))); - //printNode(hostData); + // Try selecting a subset of the zones. + axom::Array ids{{2,3,6,7,8,9,10,11,12,13,14}}; + axom::Array selectedZones(ids.size(), ids.size(), axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), ids.size() * sizeof(axom::IndexType)); + zlb.execute(coordsetView.numberOfNodes(), selectedZones.view(), clean, mixed); + + deviceData["clean"].set_external(clean.data(), clean.size()); + deviceData["mixed"].set_external(mixed.data(), mixed.size()); + + // device->host + bputils::copy(hostData, deviceData); + + // Compare expected + const axom::Array cleanResult2{{2, 3, 8, 12}}; + const axom::Array mixedResult2{{6, 7, 9, 10, 11, 13, 14}}; + EXPECT_TRUE(compare_views(cleanResult2.view(), bputils::make_array_view(hostData["clean"]))); + EXPECT_TRUE(compare_views(mixedResult2.view(), bputils::make_array_view(hostData["mixed"]))); } static void create(conduit::Node &hostMesh) From 00b53056ad8336ecc37a2fa5df6ef6f3f7322858 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 11 Sep 2024 14:11:53 -0700 Subject: [PATCH 210/290] make style --- src/axom/mir/CoordsetBlender.hpp | 9 +- src/axom/mir/CoordsetSlicer.hpp | 20 +- src/axom/mir/ExtractZones.hpp | 183 ++++++----- src/axom/mir/MatsetSlicer.hpp | 74 +++-- src/axom/mir/ZoneListBuilder.hpp | 230 +++++++------ src/axom/mir/blueprint_utilities.cpp | 1 - src/axom/mir/blueprint_utilities.hpp | 15 +- .../mir/tests/mir_blueprint_utilities.cpp | 309 +++++++++++------- src/axom/mir/utilities.hpp | 3 +- 9 files changed, 492 insertions(+), 352 deletions(-) diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 1d1883b75f..01e2a073e5 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -66,12 +66,9 @@ class CoordsetBlender std::vector axes; if(n_input["type"].as_string() == "uniform") { - if(n_input.has_path("dims/i")) - axes.push_back("x"); - if(n_input.has_path("dims/j")) - axes.push_back("y"); - if(n_input.has_path("dims/k")) - axes.push_back("z"); + if(n_input.has_path("dims/i")) axes.push_back("x"); + if(n_input.has_path("dims/j")) axes.push_back("y"); + if(n_input.has_path("dims/k")) axes.push_back("z"); } else { diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/CoordsetSlicer.hpp index 964c0f3fef..86a28facdc 100644 --- a/src/axom/mir/CoordsetSlicer.hpp +++ b/src/axom/mir/CoordsetSlicer.hpp @@ -18,7 +18,6 @@ namespace utilities { namespace blueprint { - /** * \accelerated * \class CoordsetSlicer @@ -33,7 +32,9 @@ class CoordsetSlicer { public: /// Constructor - CoordsetSlicer(const CoordsetView &coordsetView) : m_coordsetView(coordsetView) { } + CoordsetSlicer(const CoordsetView &coordsetView) + : m_coordsetView(coordsetView) + { } /** * \brief Execute the slice on the \a n_input coordset and store the new sliced coordset in \a n_output. @@ -57,12 +58,9 @@ class CoordsetSlicer std::vector axes; if(n_input["type"].as_string() == "uniform") { - if(n_input.has_path("dims/i")) - axes.push_back("x"); - if(n_input.has_path("dims/j")) - axes.push_back("y"); - if(n_input.has_path("dims/k")) - axes.push_back("z"); + if(n_input.has_path("dims/i")) axes.push_back("x"); + if(n_input.has_path("dims/j")) axes.push_back("y"); + if(n_input.has_path("dims/k")) axes.push_back("z"); } else { @@ -90,9 +88,8 @@ class CoordsetSlicer // Allocate data in the Conduit node and make a view. conduit::Node &comp = n_values[axes[i]]; comp.set_allocator(c2a.getConduitAllocatorID()); - comp.set(conduit::DataType( - bputils::cpp2conduit::id, - outputSize)); + comp.set( + conduit::DataType(bputils::cpp2conduit::id, outputSize)); compViews[i] = bputils::make_array_view(comp); } @@ -112,6 +109,7 @@ class CoordsetSlicer } }); } + private: CoordsetView m_coordsetView; }; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 0d1f20ede8..a7d9487cd3 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -8,7 +8,7 @@ #include #include -#include // Needed to get MatsetSlicer +#include // Needed to get MatsetSlicer namespace axom { @@ -18,7 +18,6 @@ namespace utilities { namespace blueprint { - /** * \brief Make a new topology and coordset by extracting certain zones from the input mesh. * @@ -30,6 +29,7 @@ class ExtractZones { using ConnectivityType = typename TopologyView::ConnectivityType; using reduce_policy = typename axom::execution_space::reduce_policy; + public: using SelectedZonesView = axom::ArrayView; @@ -40,10 +40,10 @@ class ExtractZones * \param coordsetView The input coordset view. * \param matsetView The input matset view. */ - ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView) : - m_topologyView(topoView), m_coordsetView(coordsetView) - { - } + ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView) + : m_topologyView(topoView) + , m_coordsetView(coordsetView) + { } /** * \brief Select zones from the input mesh by id and output them in the output mesh. @@ -73,23 +73,25 @@ class ExtractZones // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. RAJA::ReduceSum connsize_reduce(0); - m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) - { - const int nids = zone.numberOfNodes(); - for(int i = 0; i < nids; i++) - { - const auto nodeId = zone.getId(i); - maskView[nodeId] = 1; - } - connsize_reduce += nids; - }); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + const int nids = zone.numberOfNodes(); + for(int i = 0; i < nids; i++) + { + const auto nodeId = zone.getId(i); + maskView[nodeId] = 1; + } + connsize_reduce += nids; + }); // Count the used nodes. RAJA::ReduceSum mask_reduce(0); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - mask_reduce += maskView[index]; - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { mask_reduce += maskView[index]; }); const int newNumNodes = mask_reduce.get(); // Make a compact list of nodes. @@ -102,14 +104,15 @@ class ExtractZones axom::Array nodeSlice(newNumNodes, newNumNodes, allocatorID); auto old2newView = old2new.view(); auto nodeSliceView = nodeSlice.view(); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - nodeSliceView[maskOffsetsView[index]] = index; - old2newView[index] = maskOffsetsView[index]; - } - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + nodeSliceView[maskOffsetsView[index]] = index; + old2newView[index] = maskOffsetsView[index]; + } + }); AXOM_ANNOTATE_END("nodeMap"); // Now make a new output topology. @@ -124,8 +127,10 @@ class ExtractZones // Make a new coordset. SliceData nSlice; nSlice.m_indicesView = nodeSliceView; - const std::string coordsetName = n_topo.fetch_existing("coordset").as_string(); - const conduit::Node &n_coordset = n_input.fetch_existing("coordsets/" + coordsetName); + const std::string coordsetName = + n_topo.fetch_existing("coordset").as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; makeCoordset(nSlice, n_coordset, n_newCoordset); @@ -176,54 +181,67 @@ class ExtractZones conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(c2a.getConduitAllocatorID()); - n_conn.set(conduit::DataType(cpp2conduit::id, newConnSize)); + n_conn.set( + conduit::DataType(cpp2conduit::id, newConnSize)); auto connView = bputils::make_array_view(n_conn); conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(c2a.getConduitAllocatorID()); - n_sizes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + n_sizes.set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); auto sizesView = bputils::make_array_view(n_sizes); conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(c2a.getConduitAllocatorID()); - n_offsets.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + n_offsets.set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); auto offsetsView = bputils::make_array_view(n_offsets); // Fill in sizes, offsets, connectivity. - m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) - { - sizesView[szIndex] = zone.numberOfNodes(); - }); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto szIndex, + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + sizesView[szIndex] = zone.numberOfNodes(); + }); axom::exclusive_scan(sizesView, offsetsView); const axom::ArrayView deviceOld2NewView(old2newView); - m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) - { - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - const auto oldNodeId = zone.getId(i); - const auto newNodeId = deviceOld2NewView[oldNodeId]; - connView[offset + i] = newNodeId; - } - }); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto szIndex, + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + const auto oldNodeId = zone.getId(i); + const auto newNodeId = deviceOld2NewView[oldNodeId]; + connView[offset + i] = newNodeId; + } + }); // Handle shapes, if present. if(n_topo.has_path("elements/shapes")) { - const conduit::Node &n_shapes = n_topo.fetch_existing("elements/shapes"); + const conduit::Node &n_shapes = + n_topo.fetch_existing("elements/shapes"); auto shapesView = bputils::make_array_view(n_shapes); conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; n_newShapes.set_allocator(c2a.getConduitAllocatorID()); - n_newShapes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); - auto newShapesView = bputils::make_array_view(n_newShapes); + n_newShapes.set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); + auto newShapesView = + bputils::make_array_view(n_newShapes); const SelectedZonesView deviceSelectedZonesView(selectedZonesView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) - { - newShapesView[index] = shapesView[deviceSelectedZonesView[index]]; - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(auto index) { + newShapesView[index] = shapesView[deviceSelectedZonesView[index]]; + }); } } } @@ -240,7 +258,8 @@ class ExtractZones conduit::Node &n_newCoordset) const { AXOM_ANNOTATE_SCOPE("makeCoordset"); - axom::mir::utilities::blueprint::CoordsetSlicer cs(m_coordsetView); + axom::mir::utilities::blueprint::CoordsetSlicer cs( + m_coordsetView); n_newCoordset.reset(); cs.execute(slice, n_coordset, n_newCoordset); } @@ -253,7 +272,10 @@ class ExtractZones * \param n_fields The input fields. * \param n_newFields The output fields. */ - void makeFields(const SliceData &nodeSlice, const SliceData &zoneSlice, const conduit::Node &n_fields, conduit::Node &n_newFields) const + void makeFields(const SliceData &nodeSlice, + const SliceData &zoneSlice, + const conduit::Node &n_fields, + conduit::Node &n_newFields) const { AXOM_ANNOTATE_SCOPE("makeFields"); for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) @@ -281,7 +303,8 @@ class ExtractZones * * \return Returns the options topology name, if present. Otherwise, it returns the first topology name. */ - std::string topologyName(const conduit::Node &n_input, const conduit::Node &n_options) const + std::string topologyName(const conduit::Node &n_input, + const conduit::Node &n_options) const { std::string name; if(n_options.has_path("topology")) @@ -341,7 +364,8 @@ class ExtractZones * \tparam MatsetView The matset view type on which the algorithm will run. */ template -class ExtractZonesAndMatset : public ExtractZones +class ExtractZonesAndMatset + : public ExtractZones { public: using SelectedZonesView = axom::ArrayView; @@ -353,10 +377,12 @@ class ExtractZonesAndMatset : public ExtractZones(topoView, coordsetView), m_matsetView(matsetView) - { - } + ExtractZonesAndMatset(const TopologyView &topoView, + const CoordsetView &coordsetView, + const MatsetView &matsetView) + : ExtractZones(topoView, coordsetView) + , m_matsetView(matsetView) + { } /** * \brief Select zones from the input mesh by id and output them in the output mesh. @@ -377,15 +403,21 @@ class ExtractZonesAndMatset : public ExtractZones::execute(selectedZonesView, n_input, n_options, n_output); + ExtractZones::execute(selectedZonesView, + n_input, + n_options, + n_output); // Make new matset. - const std::string topoName = ExtractZones::topologyName(n_input, n_options); + const std::string topoName = + ExtractZones::topologyName( + n_input, + n_options); std::string mname = matsetName(n_input, topoName); if(!mname.empty()) { const conduit::Node &n_matset = n_input.fetch_existing("matsets/" + mname); - conduit::Node &n_newMatset = n_output["matsets/" + mname]; + conduit::Node &n_newMatset = n_output["matsets/" + mname]; makeMatset(selectedZonesView, n_matset, n_newMatset); } } @@ -399,7 +431,8 @@ class ExtractZonesAndMatset : public ExtractZones ms; ms.execute(m_matsetView, selectedZonesView, n_matset, n_newMatset); } - MatsetView m_matsetView; + MatsetView m_matsetView; }; - - -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 81e8d1abf0..7b49091a9e 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -19,7 +19,6 @@ namespace utilities { namespace blueprint { - /** * \brief Slices the input matset view and outputs a new matset (unibuffer flavor). * @@ -30,6 +29,7 @@ template class MatsetSlicer { using reduce_policy = typename axom::execution_space::reduce_policy; + public: using SelectedZonesView = axom::ArrayView; @@ -64,24 +64,29 @@ class MatsetSlicer // Allocate sizes/offsets. conduit::Node &n_sizes = n_newMatset["sizes"]; n_sizes.set_allocator(c2a.getConduitAllocatorID()); - n_sizes.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + n_sizes.set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); auto sizesView = bputils::make_array_view(n_sizes); conduit::Node &n_offsets = n_newMatset["offsets"]; n_offsets.set_allocator(c2a.getConduitAllocatorID()); - n_offsets.set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + n_offsets.set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); auto offsetsView = bputils::make_array_view(n_offsets); // Figure out overall size of the matset zones we're keeping. MatsetView deviceMatsetView(matsetView); RAJA::ReduceSum size_reduce(0); - const axom::ArrayView deviceSelectedZonesView(selectedZonesView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) - { - const auto nmats = deviceMatsetView.numberOfMaterials(deviceSelectedZonesView[index]); - sizesView[index] = nmats; - size_reduce += nmats; - }); + const axom::ArrayView deviceSelectedZonesView( + selectedZonesView); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(auto index) { + const auto nmats = + deviceMatsetView.numberOfMaterials(deviceSelectedZonesView[index]); + sizesView[index] = nmats; + size_reduce += nmats; + }); axom::exclusive_scan(sizesView, offsetsView); // Allocate data for the rest of the matset. @@ -100,33 +105,36 @@ class MatsetSlicer conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); - n_volume_fractions.set(conduit::DataType(cpp2conduit::id, totalSize)); - auto volumeFractionsView = bputils::make_array_view(n_volume_fractions); + n_volume_fractions.set( + conduit::DataType(cpp2conduit::id, totalSize)); + auto volumeFractionsView = + bputils::make_array_view(n_volume_fractions); // Fill in the matset data with the zones we're keeping. - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(auto index) - { - const auto size = static_cast(sizesView[index]); - const auto offset = offsetsView[index]; - - typename MatsetView::IDList ids; - typename MatsetView::VFList vfs; - deviceMatsetView.zoneMaterials(deviceSelectedZonesView[index], ids, vfs); - - for(int i = 0; i < size; i++) - { - const auto destIndex = offset + i; - materialIdsView[destIndex] = ids[i]; - volumeFractionsView[destIndex] = vfs[i]; - indicesView[destIndex] = destIndex; - } - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(auto index) { + const auto size = static_cast(sizesView[index]); + const auto offset = offsetsView[index]; + + typename MatsetView::IDList ids; + typename MatsetView::VFList vfs; + deviceMatsetView.zoneMaterials(deviceSelectedZonesView[index], ids, vfs); + + for(int i = 0; i < size; i++) + { + const auto destIndex = offset + i; + materialIdsView[destIndex] = ids[i]; + volumeFractionsView[destIndex] = vfs[i]; + indicesView[destIndex] = destIndex; + } + }); } }; -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index baf69a1970..bc8a9dd9a1 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -19,7 +19,6 @@ namespace utilities { namespace blueprint { - /** * \brief This struct builds lists of clean and mixed zones using the input topology and matset views. * @@ -39,9 +38,10 @@ class ZoneListBuilder * \param topoView The topology view to use for creating the zone lists. * \param matsetView The matset view to use for creating the zone lists. */ - ZoneListBuilder(const TopologyView &topoView, const MatsetView &matsetView) : m_topologyView(topoView), m_matsetView(matsetView) - { - } + ZoneListBuilder(const TopologyView &topoView, const MatsetView &matsetView) + : m_topologyView(topoView) + , m_matsetView(matsetView) + { } /** * \brief Build the list of clean and mixed zones using the number of materials @@ -56,10 +56,14 @@ class ZoneListBuilder * those that touch only nodes that have 1 material are marked clean, otherwise they are * considered mixed as we might have to split those zones. */ - void execute(axom::IndexType nnodes, axom::Array &cleanIndices, axom::Array &mixedIndices) const + void execute(axom::IndexType nnodes, + axom::Array &cleanIndices, + axom::Array &mixedIndices) const { - using atomic_policy = typename axom::execution_space::atomic_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; + using atomic_policy = + typename axom::execution_space::atomic_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); const int allocatorID = axom::execution_space::allocatorID(); @@ -70,38 +74,38 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); + m_topologyView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); // Now, mark all zones that have 1 mat per node as clean. const auto nzones = m_topologyView.numberOfZones(); axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); - m_topologyView.template for_all_zones(AXOM_LAMBDA(auto zoneIndex, const auto &zone) - { - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } + m_topologyView.template for_all_zones( + AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } - const int ival = clean ? 1 : 0; - maskView[zoneIndex] = ival; - mask_reduce += ival; - }); + const int ival = clean ? 1 : 0; + maskView[zoneIndex] = ival; + mask_reduce += ival; + }); const int nClean = mask_reduce.get(); if(nClean > 0) @@ -113,30 +117,33 @@ class ZoneListBuilder // Make the output cleanIndices array. cleanIndices = axom::Array(nClean, nClean, allocatorID); auto cleanIndicesView = cleanIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - cleanIndicesView[maskOffsetsView[index]] = index; - } - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + cleanIndicesView[maskOffsetsView[index]] = index; + } + }); // Make the mixedIndices array. - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - maskView[index] = (maskView[index] == 1) ? 0 : 1; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + maskView[index] = (maskView[index] == 1) ? 0 : 1; + }); axom::exclusive_scan(maskView, maskOffsetsView); const int nMixed = nzones - nClean; mixedIndices = axom::Array(nMixed, nMixed, allocatorID); auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - mixedIndicesView[maskOffsetsView[index]] = index; - } - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + mixedIndicesView[maskOffsetsView[index]] = index; + } + }); } else { @@ -145,10 +152,9 @@ class ZoneListBuilder // There were no clean, so it must all be mixed. mixedIndices = axom::Array(nzones, nzones, allocatorID); auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - mixedIndicesView[index] = index; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { mixedIndicesView[index] = index; }); } } @@ -171,8 +177,10 @@ class ZoneListBuilder axom::Array &cleanIndices, axom::Array &mixedIndices) const { - using atomic_policy = typename axom::execution_space::atomic_policy; - using reduce_policy = typename axom::execution_space::reduce_policy; + using atomic_policy = + typename axom::execution_space::atomic_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); SLIC_ASSERT(selectedZonesView.size() > 0); @@ -185,38 +193,40 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); - m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto zoneIndex, const auto &zone) - { - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto zoneIndex, const auto &zone) { + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); // Now, mark all selected zones that have 1 mat per node as clean. const auto nzones = selectedZonesView.size(); axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); - m_topologyView.template for_selected_zones(selectedZonesView, AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) - { - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } - const int ival = clean ? 1 : 0; - maskView[szIndex] = ival; - mask_reduce += ival; - }); + const int ival = clean ? 1 : 0; + maskView[szIndex] = ival; + mask_reduce += ival; + }); const int nClean = mask_reduce.get(); if(nClean > 0) @@ -228,30 +238,33 @@ class ZoneListBuilder // Make the output cleanIndices array. cleanIndices = axom::Array(nClean, nClean, allocatorID); auto cleanIndicesView = cleanIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - cleanIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; - } - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + cleanIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; + } + }); // Make the mixedIndices array. - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - maskView[index] = (maskView[index] == 1) ? 0 : 1; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + maskView[index] = (maskView[index] == 1) ? 0 : 1; + }); axom::exclusive_scan(maskView, maskOffsetsView); const int nMixed = nzones - nClean; mixedIndices = axom::Array(nMixed, nMixed, allocatorID); auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - mixedIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; - } - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + mixedIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; + } + }); } else { @@ -260,21 +273,22 @@ class ZoneListBuilder // There were no clean, so it must all be mixed. mixedIndices = axom::Array(nzones, nzones, allocatorID); auto mixedIndicesView = mixedIndices.view(); - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - mixedIndicesView[index] = selectedZonesView[index]; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + mixedIndicesView[index] = selectedZonesView[index]; + }); } } private: TopologyView m_topologyView; - MatsetView m_matsetView; + MatsetView m_matsetView; }; -} // end namespace blueprint -} // end namespace utilities -} // end namespace mir -} // end namespace axom +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom #endif diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/blueprint_utilities.cpp index ad88ce6c11..2ee8e13179 100644 --- a/src/axom/mir/blueprint_utilities.cpp +++ b/src/axom/mir/blueprint_utilities.cpp @@ -16,7 +16,6 @@ namespace utilities { namespace blueprint { - // Static data. These originally appeared as constexpr members in the header file // but there were linker errors despite constexpr. const char *cpp2conduit::name = "int8"; diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 781f481c77..bd66f5cb92 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -39,8 +39,7 @@ namespace blueprint */ template struct cpp2conduit -{ -}; +{ }; template <> struct cpp2conduit @@ -137,7 +136,11 @@ struct cpp2conduit template inline axom::ArrayView make_array_view(conduit::Node &n) { - SLIC_ASSERT_MSG(cpp2conduit::id == n.dtype().id(), axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", cpp2conduit::name, n.dtype().name())); + SLIC_ASSERT_MSG( + cpp2conduit::id == n.dtype().id(), + axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", + cpp2conduit::name, + n.dtype().name())); return axom::ArrayView(static_cast(n.data_ptr()), n.dtype().number_of_elements()); } @@ -145,7 +148,11 @@ inline axom::ArrayView make_array_view(conduit::Node &n) template inline axom::ArrayView make_array_view(const conduit::Node &n) { - SLIC_ASSERT_MSG(cpp2conduit::id == n.dtype().id(), axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", cpp2conduit::name, n.dtype().name())); + SLIC_ASSERT_MSG( + cpp2conduit::id == n.dtype().id(), + axom::fmt::format("Cannot create ArrayView<{}> for Conduit {} data.", + cpp2conduit::name, + n.dtype().name())); return axom::ArrayView(static_cast(const_cast(n.data_ptr())), n.dtype().number_of_elements()); } diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 33a50f6735..b5b3b54a8b 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -125,28 +125,16 @@ struct test_copy_braid } }; -TEST(mir_blueprint_utilities, copy_seq) -{ - test_copy_braid::test(); -} +TEST(mir_blueprint_utilities, copy_seq) { test_copy_braid::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, copy_omp) -{ - test_copy_braid::test(); -} +TEST(mir_blueprint_utilities, copy_omp) { test_copy_braid::test(); } #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) -TEST(mir_blueprint_utilities, copy_cuda) -{ - test_copy_braid::test(); -} +TEST(mir_blueprint_utilities, copy_cuda) { test_copy_braid::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, copy_hip) -{ - test_copy_braid::test(); -} +TEST(mir_blueprint_utilities, copy_hip) { test_copy_braid::test(); } #endif //------------------------------------------------------------------------------ @@ -505,7 +493,7 @@ bool compare_views(const Container1 &a, const Container2 &b) eq &= a[i] == b[i]; } return eq; -} +} template struct test_matset_slice @@ -519,15 +507,20 @@ struct test_matset_slice conduit::Node deviceMatset; bputils::copy(deviceMatset, hostMatset); - axom::Array ids{{1,3,5}}; - axom::Array selectedZones(3, 3, axom::execution_space::allocatorID()); + axom::Array ids {{1, 3, 5}}; + axom::Array selectedZones( + 3, + 3, + axom::execution_space::allocatorID()); axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = + axom::mir::views::UnibufferMaterialView; MatsetView matsetView; matsetView.set( bputils::make_array_view(deviceMatset["material_ids"]), - bputils::make_array_view(deviceMatset["volume_fractions"]), + bputils::make_array_view( + deviceMatset["volume_fractions"]), bputils::make_array_view(deviceMatset["sizes"]), bputils::make_array_view(deviceMatset["offsets"]), bputils::make_array_view(deviceMatset["indices"])); @@ -542,17 +535,28 @@ struct test_matset_slice bputils::copy(newHostMatset, newDeviceMatset); // Expected answers. - const axom::Array sizes{{2, 1, 2}}; - const axom::Array offsets{{0, 2, 3}}; - const axom::Array indices{{0, 1, 2, 3, 4}}; - const axom::Array material_ids{{1, 2, 2, 2, 3}}; - const axom::Array volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; - - EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); - EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMatset["offsets"]))); - EXPECT_TRUE(compare_views(indices.view(), bputils::make_array_view(newHostMatset["indices"]))); - EXPECT_TRUE(compare_views(material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); - EXPECT_TRUE(compare_views(volume_fractions.view(), bputils::make_array_view(newHostMatset["volume_fractions"]))); + const axom::Array sizes {{2, 1, 2}}; + const axom::Array offsets {{0, 2, 3}}; + const axom::Array indices {{0, 1, 2, 3, 4}}; + const axom::Array material_ids {{1, 2, 2, 2, 3}}; + const axom::Array volume_fractions { + {0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_TRUE(compare_views( + sizes.view(), + bputils::make_array_view(newHostMatset["sizes"]))); + EXPECT_TRUE(compare_views( + offsets.view(), + bputils::make_array_view(newHostMatset["offsets"]))); + EXPECT_TRUE(compare_views( + indices.view(), + bputils::make_array_view(newHostMatset["indices"]))); + EXPECT_TRUE(compare_views( + material_ids.view(), + bputils::make_array_view(newHostMatset["material_ids"]))); + EXPECT_TRUE(compare_views(volume_fractions.view(), + bputils::make_array_view( + newHostMatset["volume_fractions"]))); } static void create(conduit::Node &matset) @@ -581,7 +585,7 @@ sizes: [1, 2, 2, 1, 3, 2] offsets: [0, 1, 3, 5, 6, 9] )xx"; - matset.parse(yaml); + matset.parse(yaml); } }; @@ -611,15 +615,17 @@ TEST(mir_blueprint_utilities, matsetslice_hip) } #endif - //------------------------------------------------------------------------------ template void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) { - axom::Array ids{{0,1,2,4,5,6}}; + axom::Array ids {{0, 1, 2, 4, 5, 6}}; const auto nnodes = ids.size(); - axom::Array selectedNodes(nnodes, nnodes, axom::execution_space::allocatorID()); + axom::Array selectedNodes( + nnodes, + nnodes, + axom::execution_space::allocatorID()); axom::copy(selectedNodes.data(), ids.data(), nnodes * sizeof(axom::IndexType)); bputils::SliceData slice; @@ -643,10 +649,14 @@ void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) bputils::copy(newHostCoordset, newDeviceCoordset); // We get an explicit coordset out of the slicer. - const axom::Array x{{0., 1., 2., 0., 1., 2.}}; - const axom::Array y{{0., 0., 0., 1., 1., 1.}}; - EXPECT_TRUE(compare_views(x.view(), bputils::make_array_view(newHostCoordset["values/x"]))); - EXPECT_TRUE(compare_views(y.view(), bputils::make_array_view(newHostCoordset["values/y"]))); + const axom::Array x {{0., 1., 2., 0., 1., 2.}}; + const axom::Array y {{0., 0., 0., 1., 1., 1.}}; + EXPECT_TRUE(compare_views( + x.view(), + bputils::make_array_view(newHostCoordset["values/x"]))); + EXPECT_TRUE(compare_views( + y.view(), + bputils::make_array_view(newHostCoordset["values/y"]))); } //------------------------------------------------------------------------------ @@ -672,9 +682,9 @@ type: explicit conduit::Node coordset; coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { - return axom::mir::views::make_explicit_coordset::view(deviceCoordset); + auto makeView = [](const conduit::Node &deviceCoordset) { + return axom::mir::views::make_explicit_coordset::view( + deviceCoordset); }; test_coordsetslicer(coordset, makeView); @@ -730,9 +740,9 @@ type: rectilinear conduit::Node coordset; coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { - return axom::mir::views::make_rectilinear_coordset::view(deviceCoordset); + auto makeView = [](const conduit::Node &deviceCoordset) { + return axom::mir::views::make_rectilinear_coordset::view( + deviceCoordset); }; test_coordsetslicer(coordset, makeView); } @@ -787,8 +797,7 @@ type: uniform conduit::Node coordset; coordset.parse(yaml); - auto makeView = [](const conduit::Node &deviceCoordset) - { + auto makeView = [](const conduit::Node &deviceCoordset) { return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); }; test_coordsetslicer(coordset, makeView); @@ -834,22 +843,34 @@ struct test_extractzones conduit::Node deviceMesh; bputils::copy(deviceMesh, hostMesh); - axom::Array ids{{1,3,5}}; + axom::Array ids {{1, 3, 5}}; const auto nzones = ids.size(); - axom::Array selectedZones(nzones, nzones, axom::execution_space::allocatorID()); + axom::Array selectedZones( + nzones, + nzones, + axom::execution_space::allocatorID()); axom::copy(selectedZones.data(), ids.data(), nzones * sizeof(axom::IndexType)); // Wrap the data in views. - auto coordsetView = axom::mir::views::make_explicit_coordset::view(deviceMesh["coordsets/coords"]); + auto coordsetView = + axom::mir::views::make_explicit_coordset::view( + deviceMesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopologyView topoView(bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]), - bputils::make_array_view(deviceMesh["topologies/mesh/elements/sizes"]), - bputils::make_array_view(deviceMesh["topologies/mesh/elements/offsets"])); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopologyView topoView( + bputils::make_array_view( + deviceMesh["topologies/mesh/elements/connectivity"]), + bputils::make_array_view( + deviceMesh["topologies/mesh/elements/sizes"]), + bputils::make_array_view( + deviceMesh["topologies/mesh/elements/offsets"])); // Pull out selected zones - bputils::ExtractZones extract(topoView, coordsetView); + bputils::ExtractZones extract( + topoView, + coordsetView); conduit::Node options, newDeviceMesh; options["topology"] = "mesh"; extract.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); @@ -861,34 +882,61 @@ struct test_extractzones //printNode(newHostMesh); // Check some of the key arrays - const axom::Array connectivity{{0, 1, 4, 3, 2, 3, 7, 6, 4, 5, 9, 8}}; - const axom::Array sizes{{4, 4, 4}}; - const axom::Array offsets{{0, 4, 8}}; - const axom::Array x{{1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0}}; - const axom::Array y{{0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0}}; - const axom::Array zonal{{1.0, 3.0, 5.0}}; - const axom::Array nodal{{1.0, 2.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0}}; - - EXPECT_TRUE(compare_views(connectivity.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/connectivity"]))); - EXPECT_TRUE(compare_views(sizes.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/sizes"]))); - EXPECT_TRUE(compare_views(offsets.view(), bputils::make_array_view(newHostMesh["topologies/mesh/elements/offsets"]))); - EXPECT_TRUE(compare_views(x.view(), bputils::make_array_view(newHostMesh["coordsets/coords/values/x"]))); - EXPECT_TRUE(compare_views(y.view(), bputils::make_array_view(newHostMesh["coordsets/coords/values/y"]))); - EXPECT_TRUE(compare_views(zonal.view(), bputils::make_array_view(newHostMesh["fields/zonal/values"]))); - EXPECT_TRUE(compare_views(nodal.view(), bputils::make_array_view(newHostMesh["fields/nodal/values"]))); + const axom::Array connectivity { + {0, 1, 4, 3, 2, 3, 7, 6, 4, 5, 9, 8}}; + const axom::Array sizes {{4, 4, 4}}; + const axom::Array offsets {{0, 4, 8}}; + const axom::Array x { + {1.0, 2.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0}}; + const axom::Array y { + {0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0}}; + const axom::Array zonal {{1.0, 3.0, 5.0}}; + const axom::Array nodal { + {1.0, 2.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0}}; + + EXPECT_TRUE( + compare_views(connectivity.view(), + bputils::make_array_view( + newHostMesh["topologies/mesh/elements/connectivity"]))); + EXPECT_TRUE( + compare_views(sizes.view(), + bputils::make_array_view( + newHostMesh["topologies/mesh/elements/sizes"]))); + EXPECT_TRUE( + compare_views(offsets.view(), + bputils::make_array_view( + newHostMesh["topologies/mesh/elements/offsets"]))); + EXPECT_TRUE(compare_views(x.view(), + bputils::make_array_view( + newHostMesh["coordsets/coords/values/x"]))); + EXPECT_TRUE(compare_views(y.view(), + bputils::make_array_view( + newHostMesh["coordsets/coords/values/y"]))); + EXPECT_TRUE(compare_views(zonal.view(), + bputils::make_array_view( + newHostMesh["fields/zonal/values"]))); + EXPECT_TRUE(compare_views(nodal.view(), + bputils::make_array_view( + newHostMesh["fields/nodal/values"]))); // Do the material too. - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = + axom::mir::views::UnibufferMaterialView; MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); + matsetView.set(bputils::make_array_view( + deviceMesh["matsets/mat1/material_ids"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/volume_fractions"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/sizes"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/offsets"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/indices"])); // Pull out selected zones - bputils::ExtractZonesAndMatset extractM(topoView, coordsetView, matsetView); + bputils::ExtractZonesAndMatset + extractM(topoView, coordsetView, matsetView); newDeviceMesh.reset(); extractM.execute(selectedZones.view(), deviceMesh, options, newDeviceMesh); @@ -897,17 +945,28 @@ struct test_extractzones bputils::copy(newHostMesh, newDeviceMesh); // Check some of the key arrays in the sliced material - const axom::Array mat_sizes{{2, 1, 2}}; - const axom::Array mat_offsets{{0, 2, 3}}; - const axom::Array mat_indices{{0, 1, 2, 3, 4}}; - const axom::Array mat_material_ids{{1, 2, 2, 2, 3}}; - const axom::Array mat_volume_fractions{{0.5, 0.5, 1.0, 0.8, 0.2}}; - - EXPECT_TRUE(compare_views(mat_sizes.view(), bputils::make_array_view(newHostMesh["matsets/mat1/sizes"]))); - EXPECT_TRUE(compare_views(mat_offsets.view(), bputils::make_array_view(newHostMesh["matsets/mat1/offsets"]))); - EXPECT_TRUE(compare_views(mat_indices.view(), bputils::make_array_view(newHostMesh["matsets/mat1/indices"]))); - EXPECT_TRUE(compare_views(mat_material_ids.view(), bputils::make_array_view(newHostMesh["matsets/mat1/material_ids"]))); - EXPECT_TRUE(compare_views(mat_volume_fractions.view(), bputils::make_array_view(newHostMesh["matsets/mat1/volume_fractions"]))); + const axom::Array mat_sizes {{2, 1, 2}}; + const axom::Array mat_offsets {{0, 2, 3}}; + const axom::Array mat_indices {{0, 1, 2, 3, 4}}; + const axom::Array mat_material_ids {{1, 2, 2, 2, 3}}; + const axom::Array mat_volume_fractions { + {0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_TRUE(compare_views(mat_sizes.view(), + bputils::make_array_view( + newHostMesh["matsets/mat1/sizes"]))); + EXPECT_TRUE(compare_views(mat_offsets.view(), + bputils::make_array_view( + newHostMesh["matsets/mat1/offsets"]))); + EXPECT_TRUE(compare_views(mat_indices.view(), + bputils::make_array_view( + newHostMesh["matsets/mat1/indices"]))); + EXPECT_TRUE(compare_views(mat_material_ids.view(), + bputils::make_array_view( + newHostMesh["matsets/mat1/material_ids"]))); + EXPECT_TRUE(compare_views(mat_volume_fractions.view(), + bputils::make_array_view( + newHostMesh["matsets/mat1/volume_fractions"]))); } static void create(conduit::Node &hostMesh) @@ -962,7 +1021,7 @@ struct test_extractzones offsets: [0, 1, 3, 5, 6, 9] )xx"; - hostMesh.parse(yaml); + hostMesh.parse(yaml); } }; @@ -1006,23 +1065,33 @@ struct test_zonelistbuilder bputils::copy(deviceMesh, hostMesh); // Wrap the data in views. - auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(deviceMesh["coordsets/coords"]); + auto coordsetView = + axom::mir::views::make_rectilinear_coordset::view( + deviceMesh["coordsets/coords"]); - auto topologyView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); + auto topologyView = axom::mir::views::make_rectilinear<2>::view( + deviceMesh["topologies/mesh"]); using TopologyView = decltype(topologyView); // Do the material too. - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = + axom::mir::views::UnibufferMaterialView; MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMesh["matsets/mat1/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat1/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat1/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat1/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat1/indices"])); + matsetView.set(bputils::make_array_view( + deviceMesh["matsets/mat1/material_ids"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/volume_fractions"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/sizes"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/offsets"]), + bputils::make_array_view( + deviceMesh["matsets/mat1/indices"])); // Determine the list of clean and mixed zones (taking into account #mats at the nodes) - bputils::ZoneListBuilder zlb(topologyView, matsetView); + bputils::ZoneListBuilder zlb( + topologyView, + matsetView); axom::Array clean, mixed; zlb.execute(coordsetView.numberOfNodes(), clean, mixed); @@ -1035,15 +1104,25 @@ struct test_zonelistbuilder bputils::copy(hostData, deviceData); // Compare expected - const axom::Array cleanResult{{0, 1, 2, 3, 4, 8, 12}}; - const axom::Array mixedResult{{5, 6, 7, 9, 10, 11, 13, 14, 15}}; - EXPECT_TRUE(compare_views(cleanResult.view(), bputils::make_array_view(hostData["clean"]))); - EXPECT_TRUE(compare_views(mixedResult.view(), bputils::make_array_view(hostData["mixed"]))); + const axom::Array cleanResult {{0, 1, 2, 3, 4, 8, 12}}; + const axom::Array mixedResult { + {5, 6, 7, 9, 10, 11, 13, 14, 15}}; + EXPECT_TRUE(compare_views( + cleanResult.view(), + bputils::make_array_view(hostData["clean"]))); + EXPECT_TRUE(compare_views( + mixedResult.view(), + bputils::make_array_view(hostData["mixed"]))); // Try selecting a subset of the zones. - axom::Array ids{{2,3,6,7,8,9,10,11,12,13,14}}; - axom::Array selectedZones(ids.size(), ids.size(), axom::execution_space::allocatorID()); - axom::copy(selectedZones.data(), ids.data(), ids.size() * sizeof(axom::IndexType)); + axom::Array ids {{2, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14}}; + axom::Array selectedZones( + ids.size(), + ids.size(), + axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), + ids.data(), + ids.size() * sizeof(axom::IndexType)); zlb.execute(coordsetView.numberOfNodes(), selectedZones.view(), clean, mixed); deviceData["clean"].set_external(clean.data(), clean.size()); @@ -1053,10 +1132,14 @@ struct test_zonelistbuilder bputils::copy(hostData, deviceData); // Compare expected - const axom::Array cleanResult2{{2, 3, 8, 12}}; - const axom::Array mixedResult2{{6, 7, 9, 10, 11, 13, 14}}; - EXPECT_TRUE(compare_views(cleanResult2.view(), bputils::make_array_view(hostData["clean"]))); - EXPECT_TRUE(compare_views(mixedResult2.view(), bputils::make_array_view(hostData["mixed"]))); + const axom::Array cleanResult2 {{2, 3, 8, 12}}; + const axom::Array mixedResult2 {{6, 7, 9, 10, 11, 13, 14}}; + EXPECT_TRUE(compare_views( + cleanResult2.view(), + bputils::make_array_view(hostData["clean"]))); + EXPECT_TRUE(compare_views( + mixedResult2.view(), + bputils::make_array_view(hostData["mixed"]))); } static void create(conduit::Node &hostMesh) @@ -1104,7 +1187,7 @@ struct test_zonelistbuilder offsets: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 15, 16, 18] )xx"; - hostMesh.parse(yaml); + hostMesh.parse(yaml); } }; diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 68c5538eae..4f3a320ccf 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -262,7 +262,8 @@ class HashNaming AXOM_HOST_DEVICE inline KeyType make_name_2(IndexType p0, IndexType p1) const { - assert(static_cast(p0) <= Max31Bit && static_cast(p1) <= Max31Bit); + assert(static_cast(p0) <= Max31Bit && + static_cast(p1) <= Max31Bit); // Store p0 and p1 both in the 64-bit key as 31-bit integers KeyType k0 = (static_cast(std::min(p0, p1)) & Max31Bit); KeyType k1 = (static_cast(std::max(p0, p1)) & Max31Bit); From 98fcf664488a98ea810132189ff4d0d4a2af62b5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 12 Sep 2024 12:25:57 -0700 Subject: [PATCH 211/290] Fixed problem in ClipField --- src/axom/mir/ClipField.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 39967d027e..1f895d862d 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -30,7 +30,7 @@ #include // Enable code to save some debugging output files. -//#define AXOM_DEBUG_CLIP_FIELD +// #define AXOM_DEBUG_CLIP_FIELD // Filter out degenerate zones in 2D. #define AXOM_CLIP_FILTER_DEGENERATES @@ -442,6 +442,11 @@ class ClipField const auto allocatorID = axom::execution_space::allocatorID(); AXOM_ANNOTATE_SCOPE("ClipField"); + // Reset the output nodes just in case they've been reused. + n_newTopo.reset(); + n_newCoordset.reset(); + n_newFields.reset(); + // Make the selected zones and get the size. ClipOptions opts(n_options); axom::mir::SelectedZones selectedZones( @@ -717,6 +722,7 @@ class ClipField fieldsToProcess, n_fields, n_newFields); + makeOriginalElements(fragmentData, opts, selectedZones, @@ -1070,7 +1076,7 @@ class ClipField if(nodeData.m_nodeUsedView[index] > 0) { nodeData.m_originalIdsView[nodeOffsetsView[index]] = index; - newId = index; + newId = nodeOffsetsView[index]; } nodeData.m_oldNodeToNewNodeView[index] = newId; }); From 17ec665104e0c06077c50ee5182bb48521dc990d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 12 Sep 2024 12:27:17 -0700 Subject: [PATCH 212/290] Reset node --- src/axom/mir/blueprint_utilities.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index bd66f5cb92..361eb36908 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -426,7 +426,7 @@ template void copy(conduit::Node &dest, const conduit::Node &src) { ConduitAllocateThroughAxom c2a; - + dest.reset(); if(src.number_of_children() > 0) { for(conduit::index_t i = 0; i < src.number_of_children(); i++) From 4f43bbe09300d999498d43df17ddb52d9c40c7de Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 12 Sep 2024 12:27:49 -0700 Subject: [PATCH 213/290] Added test for selecting zones in clipfield --- src/axom/mir/tests/mir_clipfield.cpp | 242 +++++++++++++++++++++++---- 1 file changed, 210 insertions(+), 32 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index a3a50034b7..997354e2f3 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -21,7 +21,7 @@ namespace bputils = axom::mir::utilities::blueprint; //#define DEBUGGING_TEST_CASES // Uncomment to generate baselines -//#define AXOM_TESTING_GENERATE_BASELINES +// #define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) //#define AXOM_TESTING_SAVE_VISUALIZATION @@ -34,7 +34,6 @@ std::string baselineDirectory() "mir_clipfield"); } //------------------------------------------------------------------------------ - TEST(mir_clipfield, options) { int nzones = 6; @@ -317,29 +316,67 @@ TEST(mir_clipfield, sort_values) } } //------------------------------------------------------------------------------ -TEST(mir_clipfield, unique) +template +struct test_unique { - /* - 8---9---10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - - */ - axom::Array ids {{0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, - 4, 5, 9, 8, 5, 6, 10, 9, 6, 7, 11, 10}}; - axom::Array uIds, uIndices; - - axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); - EXPECT_EQ(uIds.size(), 12); - EXPECT_EQ(uIndices.size(), 12); - for(axom::IndexType i = 0; i < uIds.size(); i++) + static void test() { - EXPECT_EQ(uIds[i], i); - EXPECT_EQ(uIds[i], ids[uIndices[i]]); + /* + 8---9---10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array ids {{0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, + 4, 5, 9, 8, 5, 6, 10, 9, 6, 7, 11, 10}}; + // host->device + axom::Array deviceIds(ids.size(), ids.size(), allocatorID); + axom::copy(deviceIds.data(), ids.data(), sizeof(int) * ids.size()); + + // Make unique ids. + axom::Array uIds, uIndices; + axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); + + // device->host + axom::Array hostuIds(uIds.size()), hostuIndices(uIndices.size()); + axom::copy(hostuIds.data(), uIds.data(), sizeof(int) * uIds.size()); + axom::copy(hostuIndices.data(), uIndices.data(), sizeof(int) * uIndices.size()); + + // compare results + EXPECT_EQ(hostuIds.size(), 12); + EXPECT_EQ(hostuIndices.size(), 12); + for(axom::IndexType i = 0; i < hostuIds.size(); i++) + { + EXPECT_EQ(hostuIds[i], i); + EXPECT_EQ(hostuIds[i], ids[hostuIndices[i]]); + } } +}; + +TEST(mir_clipfield, unique_seq) +{ + test_unique::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_clipfield, unique_omp) +{ + test_unique::test(); } +#endif +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_clipfield, unique_cuda) +{ + test_unique::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_clipfield, unique_hip) +{ + test_unique::test(); +} +#endif //------------------------------------------------------------------------------ TEST(mir_clipfield, make_name) @@ -366,6 +403,7 @@ TEST(mir_clipfield, make_name) } } +//------------------------------------------------------------------------------ template void test_one_shape(const conduit::Node &hostMesh, const std::string &name) { @@ -641,6 +679,7 @@ TEST(mir_clipfield, uniform2d) #endif } +//------------------------------------------------------------------------------ template void braid_rectilinear_clip_test(const std::string &name) { @@ -818,6 +857,7 @@ TEST(mir_clipfield, strided_structured_2d) strided_structured_clip_test_exec("strided_structured_2d_sel", options); } +//------------------------------------------------------------------------------ template void braid3d_clip_test(const std::string &type, const std::string &name) { @@ -918,6 +958,7 @@ TEST(mir_clipfield, hex) braid3d_clip_test_exec>("hexs", "hex"); } +//------------------------------------------------------------------------------ template void braid3d_mixed_clip_test(const std::string &name) { @@ -991,26 +1032,31 @@ void braid3d_mixed_clip_test(const std::string &name) #endif } -TEST(mir_clipfield, mixed) +TEST(mir_clipfield, mixed_seq) { - const std::string name("mixed"); - braid3d_mixed_clip_test(name); - + braid3d_mixed_clip_test("mixed"); +} #if defined(AXOM_USE_OPENMP) - braid3d_mixed_clip_test(name); +TEST(mir_clipfield, mixed_omp) +{ + braid3d_mixed_clip_test("mixed"); +} #endif - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - braid3d_mixed_clip_test(name); +TEST(mir_clipfield, mixed_cuda) +{ + braid3d_mixed_clip_test("mixed"); +} #endif - #if defined(AXOM_USE_HIP) - braid3d_mixed_clip_test(name); -#endif +TEST(mir_clipfield, mixed_hip) +{ + braid3d_mixed_clip_test("mixed"); } +#endif //------------------------------------------------------------------------------ - +#if 0 template void compare_values(const Container1 &c1, const Container2 &c2) { @@ -1119,6 +1165,138 @@ TEST(mir_clipfield, pointmerging) point_merge_test(); #endif } +#endif +//------------------------------------------------------------------------------ + +template +struct test_selectedzones +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Wrap the data in views. + auto coordsetView = + axom::mir::views::make_rectilinear_coordset::view( + deviceMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + + auto topologyView = axom::mir::views::make_rectilinear<2>::view( + deviceMesh["topologies/mesh"]); + using TopologyView = decltype(topologyView); + + conduit::Node hostOptions; + hostOptions["selectedZones"].set(std::vector{{1,3,4,5,7}}); + hostOptions["inside"] = 1; + hostOptions["outside"] = 1; + hostOptions["clipField"] = "zero"; + + conduit::Node deviceOptions, deviceResult; + bputils::copy(deviceOptions, hostOptions); + + axom::mir::clipping::ClipField clip(topologyView, coordsetView); + clip.execute(deviceMesh, deviceOptions, deviceResult); + + // device->host + conduit::Node hostResult; + bputils::copy(hostResult, deviceResult); + //printNode(hostResult); + + // Handle baseline comparison. + const auto paths = baselinePaths(); + std::string baselineName(yamlRoot("selectedzones1")); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostResult); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostResult)); +#endif + + //--------------------- + // Try a different clip + hostOptions["outside"] = 0; + hostOptions["clipField"] = "radial"; + hostOptions["clipValue"] = 3.2; + bputils::copy(deviceOptions, hostOptions); + deviceResult.reset(); + clip.execute(deviceMesh, deviceOptions, deviceResult); + + // device->host + bputils::copy(hostResult, deviceResult); + //printNode(hostResult); + + baselineName = yamlRoot("selectedzones2"); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostResult); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostResult)); +#endif + + } + + static void create(conduit::Node &mesh) + { + /* + 12--13--14--15 + | | x | | + 8---9--10---11 + | x | x | x | x=selected zones + 4---5---6---7 + | | x | | + 0---1---2---3 + */ + const char *yaml = R"xx( +coordsets: + coords: + type: rectilinear + values: + x: [0., 1., 2., 3.] + y: [0., 1., 2., 3.] +topologies: + mesh: + type: rectilinear + coordset: coords +fields: + zero: + topology: mesh + association: vertex + values: [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.] + radial: + topology: mesh + association: vertex + values: [0.0, 1.0, 2.0, 3.0, 1.0, 1.414, 2.236, 3.162, 2.0, 2.236, 2.828, 3.605, 3.0, 3.162, 3.605, 4.242] +)xx"; + + mesh.parse(yaml); + } +}; + +TEST(mir_clipfield, selectedzones_seq) +{ + test_selectedzones::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_clipfield, selectedzones_omp) +{ + test_selectedzones::test(); +} +#endif +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_clipfield, selectedzones_cuda) +{ + test_selectedzones::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_clipfield, selectedzones_hip) +{ + test_selectedzones::test(); +} +#endif //------------------------------------------------------------------------------ #if defined(DEBUGGING_TEST_CASES) From 5a0a8ec2c0e33718a4f95321ae77d500f44cdd43 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 12 Sep 2024 18:43:32 -0700 Subject: [PATCH 214/290] Fixed some tests on HIP --- src/axom/mir/tests/mir_clipfield.cpp | 227 +++++++++++---------- src/axom/mir/tests/mir_testing_helpers.hpp | 2 +- 2 files changed, 123 insertions(+), 106 deletions(-) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 997354e2f3..43dfc1d8b2 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -21,7 +21,7 @@ namespace bputils = axom::mir::utilities::blueprint; //#define DEBUGGING_TEST_CASES // Uncomment to generate baselines -// #define AXOM_TESTING_GENERATE_BASELINES +//#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) //#define AXOM_TESTING_SAVE_VISUALIZATION @@ -413,7 +413,7 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) // Copy mesh to device conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + bputils::copy(deviceMesh, hostMesh); // Make views for the device mesh. conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); @@ -446,7 +446,7 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) // Copy device->host conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); // Handle baseline comparison. std::string baselineName(yamlRoot(name)); @@ -518,7 +518,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::braid(type, dims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + bputils::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif @@ -557,7 +557,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Copy device->host conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); #if defined(DEBUGGING_TEST_CASES) std::cout << "---------------------------------- clipped uniform " @@ -638,7 +638,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Copy device->host conduit::Node hostClipMixedMesh; - axom::mir::utilities::blueprint::copy(hostClipMixedMesh, + bputils::copy(hostClipMixedMesh, deviceClipMixedMesh); #if defined(DEBUGGING_TEST_CASES) std::cout << "---------------------------------- clipped mixed " @@ -696,15 +696,17 @@ void braid_rectilinear_clip_test(const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::braid("rectilinear", dims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif + // host->device + bputils::copy(deviceMesh, hostMesh); + // Create views auto coordsetView = axom::mir::views::make_rectilinear_coordset::view( - hostMesh["coordsets/coords"]); + deviceMesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); TopoView topoView(Indexing {zoneDims}); @@ -723,7 +725,7 @@ void braid_rectilinear_clip_test(const std::string &name) // Copy device->host conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); // Handle baseline comparison. { @@ -780,8 +782,6 @@ void strided_structured_clip_test(const std::string &name, conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::strided_structured(hostMesh); //hostMesh.print(); - - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); conduit::relay::io::blueprint::save_mesh(hostMesh, @@ -789,7 +789,11 @@ void strided_structured_clip_test(const std::string &name, "yaml"); #endif - conduit::Node deviceClipMesh, hostClipMesh; + conduit::Node deviceOptions, deviceClipMesh, hostClipMesh; + + // host->device + bputils::copy(deviceMesh, hostMesh); + bputils::copy(deviceOptions, options); // Create views axom::mir::views::dispatch_explicit_coordset( @@ -805,10 +809,10 @@ void strided_structured_clip_test(const std::string &name, axom::mir::clipping::ClipField clipper( topoView, coordsetView); - clipper.execute(deviceMesh, options, deviceClipMesh); + clipper.execute(deviceMesh, deviceOptions, deviceClipMesh); - // Copy device->host - axom::mir::utilities::blueprint::copy(hostClipMesh, + // device->host + bputils::copy(hostClipMesh, deviceClipMesh); }); @@ -869,7 +873,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) const axom::StackArray dims {10, 10, 10}; conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::braid(type, dims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + bputils::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif @@ -907,7 +911,7 @@ void braid3d_clip_test(const std::string &type, const std::string &name) // Copy device->host conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); // Handle baseline comparison. std::string baselineName(yamlRoot(name)); @@ -970,7 +974,7 @@ void braid3d_mixed_clip_test(const std::string &name) // Create the data conduit::Node hostMesh, deviceMesh; axom::mir::testing::data::mixed3d(hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + bputils::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif @@ -1020,7 +1024,7 @@ void braid3d_mixed_clip_test(const std::string &name) // Copy device->host conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); // Handle baseline comparison. std::string baselineName(yamlRoot(name)); @@ -1056,7 +1060,6 @@ TEST(mir_clipfield, mixed_hip) #endif //------------------------------------------------------------------------------ -#if 0 template void compare_values(const Container1 &c1, const Container2 &c2) { @@ -1068,104 +1071,118 @@ void compare_values(const Container1 &c1, const Container2 &c2) } template -void point_merge_test() +struct point_merge_test { - conduit::Node hostMesh; - hostMesh["coordsets/coords/type"] = "explicit"; - hostMesh["coordsets/coords/values/x"].set( - std::vector {{0., 1., 2., 0., 1., 2., 0., 1., 2.}}); - hostMesh["coordsets/coords/values/y"].set( - std::vector {{0., 0., 0., 1., 1., 1., 2., 2., 2.}}); - hostMesh["topologies/mesh/type"] = "unstructured"; - hostMesh["topologies/mesh/coordset"] = "coords"; - hostMesh["topologies/mesh/elements/shape"] = "quad"; - hostMesh["topologies/mesh/elements/connectivity"].set( - std::vector {{0, 1, 4, 3, 1, 2, 5, 4, 3, 4, 7, 6, 4, 5, 8, 7}}); - hostMesh["topologies/mesh/elements/sizes"].set(std::vector {{4, 4, 4, 4}}); - hostMesh["topologies/mesh/elements/offsets"].set( - std::vector {{0, 4, 8, 12}}); - hostMesh["fields/clip/topology"] = "mesh"; - hostMesh["fields/clip/association"] = "vertex"; - hostMesh["fields/clip/values"].set( - std::vector {{1., 1., 0.5, 1., 1., 0., 0.5, 0., 0.5}}); - - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - - // Set up views for the mesh. - using CoordsetView = axom::mir::views::ExplicitCoordsetView; - CoordsetView coordsetView( - bputils::make_array_view( - deviceMesh.fetch_existing("coordsets/coords/values/x")), - bputils::make_array_view( - deviceMesh.fetch_existing("coordsets/coords/values/y"))); - - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< - axom::mir::views::QuadShape>; - TopologyView topologyView(bputils::make_array_view( - deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"))); - - // Clip - conduit::Node options, deviceClipMesh; - options["clipField"] = "clip"; - options["clipValue"] = 0.5; - using Clip = - axom::mir::clipping::ClipField; - Clip clip(topologyView, coordsetView); - clip.execute(deviceMesh, options, deviceClipMesh); - - conduit::Node hostClipMesh; - axom::mir::utilities::blueprint::copy(hostClipMesh, - deviceClipMesh); - //printNode(hostClipMesh); - - // Check that the points were merged when making the new mesh. - std::vector x {{2.0, 1.0, 2.0, 0.0, 1.5, 2.0, 1.0}}; - std::vector y {{1.0, 1.5, 2.0, 2.0, 1.0, 0.0, 2.0}}; - EXPECT_EQ(x.size(), 7); - EXPECT_EQ(y.size(), 7); - for(size_t i = 0; i < x.size(); i++) + static void create(conduit::Node &hostMesh) { - EXPECT_FLOAT_EQ( - hostClipMesh["coordsets/coords/values/x"].as_float_accessor()[i], - x[i]); - EXPECT_FLOAT_EQ( - hostClipMesh["coordsets/coords/values/y"].as_float_accessor()[i], - y[i]); + hostMesh["coordsets/coords/type"] = "explicit"; + hostMesh["coordsets/coords/values/x"].set( + std::vector {{0., 1., 2., 0., 1., 2., 0., 1., 2.}}); + hostMesh["coordsets/coords/values/y"].set( + std::vector {{0., 0., 0., 1., 1., 1., 2., 2., 2.}}); + hostMesh["topologies/mesh/type"] = "unstructured"; + hostMesh["topologies/mesh/coordset"] = "coords"; + hostMesh["topologies/mesh/elements/shape"] = "quad"; + hostMesh["topologies/mesh/elements/connectivity"].set( + std::vector {{0, 1, 4, 3, 1, 2, 5, 4, 3, 4, 7, 6, 4, 5, 8, 7}}); + hostMesh["topologies/mesh/elements/sizes"].set(std::vector {{4, 4, 4, 4}}); + hostMesh["topologies/mesh/elements/offsets"].set( + std::vector {{0, 4, 8, 12}}); + hostMesh["fields/clip/topology"] = "mesh"; + hostMesh["fields/clip/association"] = "vertex"; + hostMesh["fields/clip/values"].set( + std::vector {{1., 1., 0.5, 1., 1., 0., 0.5, 0., 0.5}}); } - // Check that the degenerate quads were turned into triangles. - std::vector shapes {{2, 2, 3, 2}}; - std::vector sizes {{3, 3, 4, 3}}; - std::vector offsets {{0, 4, 8, 12}}; - compare_values( - shapes, - hostClipMesh["topologies/mesh/elements/shapes"].as_int_accessor()); - compare_values( - sizes, - hostClipMesh["topologies/mesh/elements/sizes"].as_int_accessor()); - compare_values( - offsets, - hostClipMesh["topologies/mesh/elements/offsets"].as_int_accessor()); -} + static void test() + { + conduit::Node hostMesh; + create(hostMesh); -TEST(mir_clipfield, pointmerging) -{ - point_merge_test(); + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + // Set up views for the mesh. + using CoordsetView = axom::mir::views::ExplicitCoordsetView; + CoordsetView coordsetView( + bputils::make_array_view( + deviceMesh.fetch_existing("coordsets/coords/values/x")), + bputils::make_array_view( + deviceMesh.fetch_existing("coordsets/coords/values/y"))); + + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopologyView topologyView(bputils::make_array_view( + deviceMesh.fetch_existing("topologies/mesh/elements/connectivity"))); + + // Clip + conduit::Node options, deviceClipMesh; + options["clipField"] = "clip"; + options["clipValue"] = 0.5; + using Clip = + axom::mir::clipping::ClipField; + Clip clip(topologyView, coordsetView); + clip.execute(deviceMesh, options, deviceClipMesh); + + conduit::Node hostClipMesh; + bputils::copy(hostClipMesh, + deviceClipMesh); + //printNode(hostClipMesh); + + // Check that the points were merged when making the new mesh. + std::vector x {{2.0, 2.0, 0.0, 1.0, 2.0, 1.5, 1.0}}; + std::vector y {{0.0, 1.0, 2.0, 2.0, 2.0, 1.0, 1.5}}; + EXPECT_EQ(x.size(), 7); + EXPECT_EQ(y.size(), 7); + for(size_t i = 0; i < x.size(); i++) + { + EXPECT_FLOAT_EQ( + hostClipMesh["coordsets/coords/values/x"].as_float_accessor()[i], + x[i]); + EXPECT_FLOAT_EQ( + hostClipMesh["coordsets/coords/values/y"].as_float_accessor()[i], + y[i]); + } + + // Check that the degenerate quads were turned into triangles. + std::vector shapes {{2, 2, 3, 2}}; + std::vector sizes {{3, 3, 4, 3}}; + std::vector offsets {{0, 4, 8, 12}}; + compare_values( + shapes, + hostClipMesh["topologies/mesh/elements/shapes"].as_int_accessor()); + compare_values( + sizes, + hostClipMesh["topologies/mesh/elements/sizes"].as_int_accessor()); + compare_values( + offsets, + hostClipMesh["topologies/mesh/elements/offsets"].as_int_accessor()); + } +}; + +TEST(mir_clipfield, pointmerging_seq) +{ + point_merge_test::test(); +} #if defined(AXOM_USE_OPENMP) - point_merge_test(); +TEST(mir_clipfield, pointmerging_omp) +{ + point_merge_test::test(); +} #endif - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - point_merge_test(); +TEST(mir_clipfield, pointmerging_cuda) +{ + point_merge_test::test(); +} #endif - #if defined(AXOM_USE_HIP) - point_merge_test(); -#endif +TEST(mir_clipfield, pointmerging_hip) +{ + point_merge_test::test(); } #endif + //------------------------------------------------------------------------------ template diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 417cf63a2e..926e022de9 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -221,7 +221,7 @@ std::vector baselinePaths() bool compareBaseline(const std::vector &baselinePaths, const std::string &baselineName, const conduit::Node ¤t, - double tolerance = 1.e-10) + double tolerance = 1.e-6) { bool success = false; int count = 0; From d86f2f28ea46f65e6b0f5c22e7d91638971b9382 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 12 Sep 2024 18:43:48 -0700 Subject: [PATCH 215/290] Updated data --- data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data b/data index 400c4e5066..12a6d6569b 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 400c4e5066620c653b770bd84b77c326da588567 +Subproject commit 12a6d6569b7a2369e6429eaae6aaa2cd473b00df From 6250b37643b2f308816fc41e932eea4e08f4b252 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 13 Sep 2024 19:00:56 -0700 Subject: [PATCH 216/290] Broken topo merging - WIP --- src/axom/mir/ClipField.hpp | 65 ++++ src/axom/mir/ClipOptions.hpp | 13 + src/axom/mir/EquiZAlgorithm.hpp | 623 +++++++++++++++++++++++++++++++- 3 files changed, 695 insertions(+), 6 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 1f895d862d..529d811c3c 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -691,6 +691,11 @@ class ClipField } } } + const std::string newNodes = opts.newNodesField(); + if(!newNodes.empty() && n_fields.has_child(newNodes)) + { + fieldsToProcess[newNodes] = newNodes; + } // Make slice indices if we have element fields. bputils::SliceData slice; @@ -729,6 +734,8 @@ class ClipField n_fields, n_newTopo, n_newFields); + + markNewNodes(blend, newNodes, opts.topologyName(n_topo.name()), n_newFields); } private: @@ -1932,6 +1939,64 @@ class ClipField return sm; } + /** + * \brief If we're making a field that marks the new nodes that were created as + * a result of clipping, update those nodes now. + * + * \param blend The blend data used to create nodal fields. + * \param newNodes The name of the new nodes field. + * \param topoName The name of the output topology. + * \param[inout] n_newFields The fields node for the output mesh. + */ + void markNewNodes(const BlendData &blend, const std::string &newNodes, const std::string &topoName, conduit::Node &n_newFields) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("markNewNodes"); + if(!newNodes.empty()) + { + const auto origSize = blend.m_originalIdsView.size(); + const auto blendSize = blend.m_selectedIndicesView.size(); + const auto outputSize = origSize + blendSize; + + if(n_newFields.has_child(newNodes)) + { + // Update the field. The field would have gone through field blending. + // We can mark the new nodes with fresh values. This comes up in + // applications that call Clip multiple times. + + conduit::Node &n_new_nodes = n_newFields.fetch_existing(newNodes); + conduit::Node &n_new_nodes_values = n_new_nodes["values"]; + auto valuesView = bputils::make_array_view(n_new_nodes_values); + + // Update values for the blend groups only. + axom::for_all(blendSize, AXOM_LAMBDA(auto bgid) + { + valuesView[origSize + bgid] = 1.f; + }); + } + else + { + // Make the field for the first time. + // Allocate Conduit data through Axom. + bputils::ConduitAllocateThroughAxom c2a; + conduit::Node &n_new_nodes = n_newFields[newNodes]; + n_new_nodes["topology"] = topoName; + n_new_nodes["association"] = "vertex"; + conduit::Node &n_new_nodes_values = n_new_nodes["values"]; + n_new_nodes_values.set_allocator(c2a.getConduitAllocatorID()); + n_new_nodes_values.set(conduit::DataType::float32(outputSize)); + auto valuesView = bputils::make_array_view(n_new_nodes_values); + + // Fill in values. Everything below origSize is an original node. + // Everything above is a blended node. + axom::for_all(outputSize, AXOM_LAMBDA(auto index) + { + valuesView[index] = (index < origSize) ? 0.f : 1.f; + }); + } + } + } + private: TopologyView m_topologyView {}; CoordsetView m_coordsetView {}; diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/ClipOptions.hpp index 03945551a8..f1f29d7a5d 100644 --- a/src/axom/mir/ClipOptions.hpp +++ b/src/axom/mir/ClipOptions.hpp @@ -71,6 +71,19 @@ class ClipOptions : public axom::mir::Options return name; } + /** + * \brief Return the name of the new nodes field to be created. If the name + * is not set then it will not be created. + * \return The name of the new field to be created. + */ + std::string newNodesField() const + { + std::string name; + if(options().has_child("newNodesField")) + name = options().fetch_existing("newNodesField").as_string(); + return name; + } + /** * \brief Whether the "inside" of the clipping field is selected. * \return 1 of the inside clipping is selected, false otherwise. diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 86ce22e682..c5520c4e74 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -15,6 +15,8 @@ #include "axom/mir/MIRAlgorithm.hpp" #include "axom/mir/RecenterField.hpp" #include "axom/mir/NodeToZoneRelationBuilder.hpp" +#include "axom/mir/ZoneListBuilder.hpp" +#include "axom/mir/ExtractZones.hpp" #include @@ -26,7 +28,7 @@ #endif // Uncomment to save inputs and outputs. -// #define AXOM_DEBUG_EQUIZ +#define AXOM_DEBUG_EQUIZ // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It @@ -280,6 +282,370 @@ class MaterialIntersector View m_view {}; }; +/** + * \brief Merge multiple unstructured Blueprint meshes + * + * \note The input meshes must currently contain a single coordset/topology/matset. + */ +template +class MergeMeshes +{ +public: + struct MeshInput + { + conduit::Node *m_input{nullptr}; + axom::ArrayView m_nodeMapView{}; + axom::ArrayView m_nodeSliceView{}; + }; + + void execute(const std::vector &inputs, conduit::Node &output) const + { + AXOM_ANNOTATE_SCOPE("MergeMeshes"); + SLIC_ASSERT(validInputs(inputs)); + + if(inputs.size() == 1) + { + singleInput(inputs, output); + } + else if(inputs.size() > 1) + { + mergeInputs(inputs, output); + } + } +private: + bool validInputs(const std::vector &inputs) const + { + for(size_t i = 0; i < inputs.size(); i++) + { + if(inputs[i].m_input == nullptr) + return false; + + const char *keys[] = {"coordsets", "topologies", "matsets"}; + for(int k = 0; k < 3; k++) + { + if(inputs[i].m_input->has_path(keys[k])) + { + const conduit::Node &n = inputs[i].m_input->fetch_existing(keys[k]); + if(n.number_of_children() > 1) + return false; + } + } + + inputs[i].m_input->fetch_existing("topologies"); + } + return true; + } + + void singleInput(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + bputils::copy(output, *(inputs[0].m_input)); + } + + void mergeInputs(const std::vector &inputs, conduit::Node &output) const + { + mergeCoordset(inputs, output); + mergeTopology(inputs, output); + //mergeFields(inputs, output); + //mergeMatset(inputs, output); + } + + void mergeCoordset(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("mergeCoordset"); + const axom::IndexType totalNodes = countNodes(inputs); + conduit::Node &n_newCoordsets = output["coordsets"]; + conduit::Node *n_newValuesPtr = nullptr; + int nComps = 2; + + axom::IndexType offsets[3] = {0, 0, 0}; + const axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &n_srcCoordset = coordsets[0]; + const conduit::Node &n_srcValues = n_srcCoordset.fetch_existing("values"); + + const auto type = n_srcCoordset.fetch_existing("type").as_string(); + SLIC_ASSERT(type == "explicit"); + + // Make all of the components the first time. + if(i == 0) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_newCoordset = n_newCoordsets[n_srcCoordset.name()]; + n_newCoordset["type"] = "explicit"; + conduit::Node &n_newValues = n_newCoordset["values"]; + n_newValuesPtr = n_newCoordset.fetch_ptr("values"); + + nComps = n_srcValues.number_of_children(); + for(int c = 0; c < nComps; c++) + { + const conduit::Node &n_srcComp = n_srcValues[c]; + conduit::Node &n_comp = n_newValues[n_srcComp.name()]; + n_comp.set_allocator(c2a.getConduitAllocatorID()); + n_comp.set(conduit::DataType(n_srcComp.dtype().id(), totalNodes)); + } + } + + // Copy this input's coordinates into the new coordset. + axom::mir::views::FloatNode_to_ArrayView(n_srcValues[0], [&](auto comp0) + { + using FloatType = typename decltype(comp0)::value_type; + for(int c = 0; c < nComps; c++) + { + axom::IndexType size = 0, offset = offsets[c]; + + const conduit::Node &n_srcComp = n_srcValues[c]; + conduit::Node &n_comp = n_newValuesPtr->child(c); + auto srcCompView = bputils::make_array_view(n_srcComp); + auto compView = bputils::make_array_view(n_comp); + + const auto nodeSliceView = inputs[i].m_nodeSliceView; + if(nodeSliceView.size() > 0) + { + // Pull out specific nodes from the input. + axom::for_all(nodeSliceView.size(), AXOM_LAMBDA(auto index) + { + const auto sliceIndex = nodeSliceView[index]; + compView[offset + index] = srcCompView[sliceIndex]; + }); + size = nodeSliceView.size(); + } + else + { + // Pull out all nodes from the input. + axom::for_all(srcCompView.size(), AXOM_LAMBDA(auto index) + { + compView[offset + index] = srcCompView[index]; + }); + size = srcCompView.size(); + } + + offsets[c] += size; + } + }); + } + } + + axom::IndexType countNodes(const std::vector &inputs) const + { + axom::IndexType nodeTotal = 0; + axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &coordset = coordsets[0]; + const auto type = coordset.fetch_existing("type").as_string(); + + axom::IndexType nnodes = 0; + if(inputs[i].m_nodeSliceView.size() > 0) + nnodes = inputs[i].m_nodeSliceView.size(); + else + nnodes = conduit::blueprint::mesh::utils::coordset::length(coordset); + + nodeTotal += nnodes; + } + return nodeTotal; + } + + void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const + { + totalConnLength = 0; + totalZones = 0; + axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topo = n_topologies[0]; + const auto type = n_topo.fetch_existing("type").as_string(); + SLIC_ASSERT(type == "unstructured"); + + const conduit::Node &n_conn = n_topo.fetch_existing("elements/connectivity"); + totalConnLength += n_conn.dtype().number_of_elements(); + + const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); + totalZones += n_size.dtype().number_of_elements(); + } + } + + void mergeTopology(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("mergeTopology"); + axom::IndexType totalConnLen = 0, totalZones = 0; + countZones(inputs, totalConnLen, totalZones); + conduit::Node &n_newTopologies = output["topologies"]; + + // Check whether there are mixed shapes. + std::map shape_map; + const axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_srcTopo = n_topologies[0]; + const auto type = n_srcTopo.fetch_existing("type").as_string(); + const auto shape = n_srcTopo.fetch_existing("elements/shape").as_string(); + SLIC_ASSERT(type == "unstructured"); + if(shape == "mixed") + { + const conduit::Node &n_shape_map = n_srcTopo.fetch_existing("elements/shape_map"); + for(int s = 0; s < n_shape_map.number_of_children(); s++) + { + const std::string sname = n_shape_map[i].name(); + const auto id = axom::mir::views::shapeNameToID(sname); + SLIC_ASSERT(id == n_shape_map[i].to_int()); + shape_map[sname] = id; + } + } + else + { + shape_map[shape] = axom::mir::views::shapeNameToID(shape); + } + } + + conduit::Node *n_newTopoPtr = nullptr; + axom::IndexType connOffset = 0, sizesOffset = 0, shapesOffset = 0; + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_srcTopo = n_topologies[0]; + + const std::string srcShape = n_srcTopo.fetch_existing("elements/shape").as_string(); + const conduit::Node &n_srcConn = n_srcTopo.fetch_existing("elements/connectivity"); + const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); + + // Make all of the elements the first time. + if(i == 0) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_newTopo = n_newTopologies[n_srcTopo.name()]; + n_newTopoPtr = n_newTopologies.fetch_ptr(n_srcTopo.name()); + n_newTopo["type"] = "unstructured"; + n_newTopo["coordset"] = n_srcTopo["coordset"].as_string(); + + conduit::Node &n_newConn = n_newTopo["elements/connectivity"]; + n_newConn.set_allocator(c2a.getConduitAllocatorID()); + n_newConn.set(conduit::DataType(n_srcConn.dtype().id(), totalConnLen)); + + conduit::Node &n_newSizes = n_newTopo["elements/sizes"]; + n_newSizes.set_allocator(c2a.getConduitAllocatorID()); + n_newSizes.set(conduit::DataType(n_srcSizes.dtype().id(), totalZones)); + + conduit::Node &n_newOffsets = n_newTopo["elements/offsets"]; + n_newOffsets.set_allocator(c2a.getConduitAllocatorID()); + n_newOffsets.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); + + if(shape_map.size() > 1) + { + // Build a new shape map in the new topology. + conduit::Node &n_shape_map = n_newTopo["shape_map"]; + for(auto it = shape_map.begin(); it != shape_map.end(); it++) + n_shape_map[it->first] = it->second; + + conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; + n_newShapes.set_allocator(c2a.getConduitAllocatorID()); + n_newShapes.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); + } + } + + // Copy this input's connectivity into the new topology. + axom::mir::views::IndexNode_to_ArrayView(n_srcConn, [&](auto srcConnView) + { + using ConnType = typename decltype(srcConnView)::value_type; + conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); + auto connView = bputils::make_array_view(n_newConn); + + if(inputs[i].m_nodeMapView.size() > 0) + { + // Copy all zones from the input but map the nodes to new values. + const auto nodeMapView = inputs[i].m_nodeMapView; + axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) + { + const auto nodeId = srcConnView[index]; + const auto newNodeId = nodeMapView[nodeId]; + connView[connOffset + index] = newNodeId; + }); + } + else + { + // Copy all zones from the input. + axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) + { + connView[connOffset + index] = srcConnView[index]; + }); + } + connOffset += srcConnView.size(); + }); + + // Copy this input's sizes into the new topology. + axom::mir::views::IndexNode_to_ArrayView(n_srcSizes, [&](auto srcSizesView) + { + using ConnType = typename decltype(srcSizesView)::value_type; + conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); + auto sizesView = bputils::make_array_view(n_newSizes); + + // Copy all sizes from the input. + axom::for_all(srcSizesView.size(), AXOM_LAMBDA(auto index) + { + sizesView[sizesOffset + index] = srcSizesView[index]; + }); + + sizesOffset += srcSizesView.size(); + }); + + // Copy shape information if it exists. + if(n_srcTopo.has_path("elements/shapes")) + { + const conduit::Node &n_srcShapes = n_srcTopo.fetch_existing("elements/shapes"); + + axom::mir::views::IndexNode_to_ArrayView(n_srcShapes, [&](auto srcShapesView) + { + using ConnType = typename decltype(srcShapesView)::value_type; + conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/sizes"); + auto shapesView = bputils::make_array_view(n_newShapes); + + // Copy all sizes from the input. + axom::for_all(srcShapesView.size(), AXOM_LAMBDA(auto index) + { + shapesView[shapesOffset + index] = srcShapesView[index]; + }); + + shapesOffset += srcShapesView.size(); + }); + } + else + { + // Fill in shape information. + conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/sizes"); + axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) + { + const int shapeId = axom::mir::views::shapeNameToID(srcShape); + axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) + { + shapesView[shapesOffset + index] = shapeId; + }); + shapesOffset += shapesView.size(); + }); + } + } + + // Make new offsets from the sizes. + conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); + axom::mir::views::IndexNode_to_ArrayView(n_newSizes, [&](auto sizesView) + { + using ConnType = typename decltype(sizesView)::value_type; + conduit::Node &n_newOffsets = n_newTopoPtr->fetch_existing("elements/offsets"); + auto offsetsView = bputils::make_array_view(n_newOffsets); + axom::exclusive_scan(sizesView, offsetsView); + }); + } +}; + + } // end namespace detail /** @@ -368,6 +734,244 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm "hdf5"); #endif +#if 1 + // Come up with lists of clean/mixed zones. + axom::Array cleanZones, mixedZones; + bputils::ZoneListBuilder zlb(m_topologyView, m_matsetView); + if(n_options.has_child("selectedZones")) + { + auto selectedZonesView = bputils::make_array_view(n_options.fetch_existing("selectedZones")); + zlb.execute(m_coordsetView.numberOfNodes(), selectedZonesView, cleanZones, mixedZones); + } + else + { + zlb.execute(m_coordsetView.numberOfNodes(), cleanZones, mixedZones); + } + SLIC_ASSERT((cleanZones.size() + mixedZones.size()) == m_topologyView.numberOfZones()); +std::cout << "cleanZones.size: " << cleanZones.size() << std::endl; +std::cout << "mixedZones.size: " << mixedZones.size() << std::endl; + + if(cleanZones.size() > 0 && mixedZones.size() > 0) + { + // Bundle the inputs so we can make the clean mesh + conduit::Node n_ezInput; + n_ezInput[n_topo.path()].set_external(n_topo); + n_ezInput[n_coordset.path()].set_external(n_coordset); + n_ezInput[n_fields.path()].set_external(n_fields); + n_ezInput[n_matset.path()].set_external(n_matset); + +std::cout << "Making clean mesh" << std::endl; + // Make the clean mesh. + bputils::ExtractZonesAndMatset ez(m_topologyView, m_coordsetView, m_matsetView); + conduit::Node n_ezopts, n_cleanOutput; + n_ezopts["topology"] = n_topo.name(); + ez.execute(cleanZones, n_ezInput, n_ezopts, n_cleanOutput); + + // Get the number of nodes in the clean mesh. + const conduit::Node &n_cleanCoordset = n_cleanOutput[n_coordset.path()]; + axom::IndexType numCleanNodes = n_cleanCoordset["values/x"].dtype().number_of_elements(); + +#if 1 +std::cout << "Saving clean mesh" << std::endl; + bputils::ConduitAllocateThroughAxom c2a; + + AXOM_ANNOTATE_BEGIN("saveClean"); + conduit::relay::io::blueprint::save_mesh(n_cleanOutput, "clean", "hdf5"); + AXOM_ANNOTATE_END("saveClean"); + + // Add a field to the original nodes. + AXOM_ANNOTATE_BEGIN("origNodes"); + // Gather the inputs into a single root but replace the fields with + // a new node to which we can add additional fields. + conduit::Node n_root; + n_root[n_coordset.path()].set_external(n_coordset); + n_root[n_topo.path()].set_external(n_topo); + n_root[n_matset.path()].set_external(n_matset); + conduit::Node &n_root_coordset = n_root[n_coordset.path()]; + conduit::Node &n_root_topo = n_root[n_topo.path()]; + conduit::Node &n_root_matset = n_root[n_matset.path()]; + // Link in original fields. + conduit::Node &n_root_fields = n_root["fields"]; + for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) + { + n_root_fields[n_fields[i].name()].set_external(n_fields[i]); + } + // Add a new field. + conduit::Node &n_orig_nodes = n_root_fields["__equiz_original_node"]; + n_orig_nodes["topology"] = n_topo.name(); + n_orig_nodes["association"] = "vertex"; + n_orig_nodes["values"].set_allocator(c2a.getConduitAllocatorID()); + n_orig_nodes["values"].set(conduit::DataType::float32(m_coordsetView.numberOfNodes())); + auto origNodesView = bputils::make_array_view(n_orig_nodes["values"]); + axom::for_all(m_coordsetView.numberOfNodes(), AXOM_LAMBDA(auto index) + { + origNodesView[index] = static_cast(index); + }); +//printNode(n_root); + // If there are fields in the options, make sure the new field is handled too. + if(n_options_copy.has_child("fields")) + n_options_copy["fields/__equiz_original_node"] = "__equiz_original_node"; + AXOM_ANNOTATE_END("origNodes"); + +//std::cout << "Making mixed mesh" << std::endl; +// // Make a mixed mesh. +// conduit::Node n_mixedOutput; +// n_ezopts["topology"] = n_topo.name(); +// ez.execute(mixedZones, n_ezInput, n_ezopts, n_mixedOutput); +//std::cout << "Saving mixed mesh" << std::endl; +// conduit::relay::io::blueprint::save_mesh(n_mixedOutput, "mixed", "hdf5"); + +#endif + +std::cout << "Process mixed zones..." << std::endl; + + // Process the mixed part of the mesh. We select just the mixed zones. + n_options_copy["selectedZones"].set_external(mixedZones.data(), mixedZones.size()); + n_options_copy["newNodesField"] = "__equiz_new_nodes"; +printNode(n_options_copy); + processMixedZones(n_root_topo, n_root_coordset, n_root_fields, n_root_matset, + n_options_copy, + n_newTopo, n_newCoordset, n_newFields, n_newMatset); +#if 1 + // Look over the nodes in the output and find the new ones. + SLIC_ASSERT(n_newFields.has_child("__equiz_original_node")); + AXOM_ANNOTATE_BEGIN("identifyOriginalNodes"); + const conduit::Node &n_output_orig_nodes = n_newFields["__equiz_original_node/values"]; + auto numOutputNodes = n_output_orig_nodes.dtype().number_of_elements(); +std::cout << "numOutputNodes: " << numOutputNodes << std::endl; + auto outputOrigNodesView = bputils::make_array_view(n_output_orig_nodes); + + const conduit::Node &n_new_nodes_values = n_newFields["__equiz_new_nodes/values"]; + auto newNodesView = bputils::make_array_view(n_new_nodes_values); + + // Make mask/offset for which nodes we need to keep from the mixed output. + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array mask(numOutputNodes, numOutputNodes, allocatorID); + axom::Array maskOffset(numOutputNodes, numOutputNodes, allocatorID); + auto maskView = mask.view(); + auto maskOffsetsView = mask.view(); + using reduce_policy = typename axom::execution_space::reduce_policy; + RAJA::ReduceSum mask_reduce(0); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + const int ival = (newNodesView[index] > 0.f) ? 1 : 0; + maskView[index] = ival; + mask_reduce += ival; + }); + axom::exclusive_scan(maskView, maskOffsetsView); + const auto numNewNodes = mask_reduce.get(); + + // Make a list of indices that we need to slice out of the node arrays. + axom::Array nodeSlice(numNewNodes, numNewNodes, allocatorID); + auto nodeSliceView = nodeSlice.view(); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + nodeSliceView[maskOffsetsView[index]] = index; + } + }); + + // Make a node map for mapping mixed connectivity into combined node numbering. + axom::Array nodeMap(numOutputNodes, numOutputNodes, allocatorID); + auto nodeMapView = nodeMap.view(); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + if(maskView[index] == 0) + { + nodeMapView[index] = static_cast(outputOrigNodesView[index]); + } + else + { + nodeMapView[index] = numCleanNodes + maskOffsetsView[index]; + } + }); + +std::cout << "Nodes that are unique to mixed output: " << numNewNodes << std::endl; + AXOM_ANNOTATE_END("identifyOriginalNodes"); + + //-------------------------------------------------------------------------- + // + // Save the MIR output with the mask. + // + //-------------------------------------------------------------------------- + conduit::Node n_tmpMask; + n_tmpMask[n_topo.path()].set_external(n_newTopo); + n_tmpMask[n_coordset.path()].set_external(n_newCoordset); + n_tmpMask[n_fields.path()].set_external(n_newFields); + n_tmpMask[n_matset.path()].set_external(n_newMatset); +// printNode(n_tmpMask); + conduit::relay::io::blueprint::save_mesh(n_tmpMask, + "debug_equiz_mask", + "hdf5"); + +// n_newFields.remove("__equiz_original_node"); +// n_newFields.remove("__equiz_mask"); + +#if 1 + conduit::Node n_merged; + { + AXOM_ANNOTATE_SCOPE("merge"); + // Merge the clean and mixed. + using MeshInput = typename detail::MergeMeshes::MeshInput; + std::vector inputs; + MeshInput i0; + i0.m_input = &n_cleanOutput; + inputs.push_back(i0); + + MeshInput i1; + i1.m_input = &n_tmpMask; + i1.m_nodeMapView = nodeMapView; + i1.m_nodeSliceView = nodeSliceView; + inputs.push_back(i1); + + detail::MergeMeshes mm; + mm.execute(inputs, n_merged); + } + printNode(n_merged); + conduit::relay::io::blueprint::save_mesh(n_merged, + "debug_equiz_merged", + "hdf5"); +#endif + + +#endif + // Merge the 2 parts together. + } + else if(cleanZones.size() == 0 && mixedZones.size() > 0) + { +std::cout << "All zones are mixed..." << std::endl; + // Only mixed zones. + processMixedZones(n_topo, n_coordset, n_fields, n_matset, + n_options_copy, + n_newTopo, n_newCoordset, n_newFields, n_newMatset); + } + else if(cleanZones.size() > 0 && mixedZones.size() == 0) + { +std::cout << "All zones are clean..." << std::endl; + // There were no mixed zones. We can copy the input to the output. + + // Add an originalZones array. + } +#else +std::cout << "Normal handling" << std::endl; + // Handle all zones. + processMixedZones(n_topo, n_coordset, n_fields, n_matset, + n_options_copy, + n_newTopo, n_newCoordset, n_newFields, n_newMatset); +#endif + } + + void processMixedZones(const conduit::Node &n_topo, + const conduit::Node &n_coordset, + const conduit::Node &n_fields, + const conduit::Node &n_matset, + conduit::Node &n_options, + conduit::Node &n_newTopo, + conduit::Node &n_newCoordset, + conduit::Node &n_newFields, + conduit::Node &n_newMatset) const + { // Make some nodes that will contain the inputs to subsequent iterations. // Store them under a single node so the nodes will have names. conduit::Node n_Input; @@ -418,7 +1022,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_coordset, n_InputFields, - n_options_copy, + n_options, n_newTopo, n_newCoordset, @@ -427,9 +1031,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // In later iterations, we do not want to pass selectedZones through // since they are only valid on the current input topology. Also, if they // were passed then the new topology only has those selected zones. - if(n_options_copy.has_child("selectedZones")) + if(n_options.has_child("selectedZones")) { - n_options_copy.remove("selectedZones"); + n_options.remove("selectedZones"); } } else @@ -471,7 +1075,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputCoordset, n_InputFields, - n_options_copy, + n_options, n_newTopo, n_newCoordset, @@ -611,6 +1215,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Make nodal VFs for each mixed material. const auto nzones = m_topologyView.numberOfZones(); const auto nnodes = m_coordsetView.numberOfNodes(); +std::cout << "makeNodeCenteredVFs: nzones=" << nzones << std::endl; +std::cout << "makeNodeCenteredVFs: nnodes=" << nnodes << std::endl; { AXOM_ANNOTATE_SCOPE("zonal"); for(const auto &mat : mixedMats) @@ -794,7 +1400,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm conduit::Node &n_newTopo, conduit::Node &n_newCoordset, - conduit::Node &n_newFields) + conduit::Node &n_newFields) const { namespace bputils = axom::mir::utilities::blueprint; namespace bpmeshutils = conduit::blueprint::mesh::utils; @@ -901,6 +1507,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Pass along fields, if present. options["fields"].set_external(n_options.fetch_existing("fields")); } + if(n_options.has_child("newNodesField")) + { + // Pass along newNodesField, if present. + options["newNodesField"] = n_options.fetch_existing("newNodesField").as_string(); + } options["topology"] = n_options["topology"]; //-------------------------------------------------------------------------- From d23744888b587cdc74fe58478107cf6897f506fd Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 16 Sep 2024 17:54:04 -0700 Subject: [PATCH 217/290] Add Ninja --- config-build.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config-build.py b/config-build.py index e87a575928..54d4c3b61f 100755 --- a/config-build.py +++ b/config-build.py @@ -84,6 +84,10 @@ def parse_arguments(): help="Create an eclipse project file.", ) + parser.add_argument( + "-n", "--ninja", action="store_true", help="Create a Ninja project." + ) + parser.add_argument( "-x", "--xcode", action="store_true", help="Create an xcode project." ) @@ -270,6 +274,9 @@ def create_cmake_command_line( if args.eclipse: cmakeline += ' -G "Eclipse CDT4 - Unix Makefiles"' + if args.ninja: + cmakeline += ' -G "Ninja"' + if args.xcode: cmakeline += ' -G "Xcode"' From c8c00c42cba4bdea94135f84f36b513091d4e7e9 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 16 Sep 2024 17:54:29 -0700 Subject: [PATCH 218/290] Progress on MergeMeshes --- src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/EquiZAlgorithm.hpp | 401 +------------- src/axom/mir/ExtractZones.hpp | 18 +- src/axom/mir/MergeMeshes.hpp | 494 ++++++++++++++++++ .../mir/tests/mir_blueprint_utilities.cpp | 169 +++++- 5 files changed, 699 insertions(+), 384 deletions(-) create mode 100644 src/axom/mir/MergeMeshes.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index b942646ebf..d6c6b051f4 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -40,6 +40,7 @@ set(mir_headers FieldBlender.hpp FieldSlicer.hpp MatsetSlicer.hpp + MergeMeshes.hpp MIRAlgorithm.hpp RecenterField.hpp utilities.hpp diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index c5520c4e74..93909c12b6 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -17,6 +17,7 @@ #include "axom/mir/NodeToZoneRelationBuilder.hpp" #include "axom/mir/ZoneListBuilder.hpp" #include "axom/mir/ExtractZones.hpp" +#include "axom/mir/MergeMeshes.hpp" #include @@ -282,370 +283,6 @@ class MaterialIntersector View m_view {}; }; -/** - * \brief Merge multiple unstructured Blueprint meshes - * - * \note The input meshes must currently contain a single coordset/topology/matset. - */ -template -class MergeMeshes -{ -public: - struct MeshInput - { - conduit::Node *m_input{nullptr}; - axom::ArrayView m_nodeMapView{}; - axom::ArrayView m_nodeSliceView{}; - }; - - void execute(const std::vector &inputs, conduit::Node &output) const - { - AXOM_ANNOTATE_SCOPE("MergeMeshes"); - SLIC_ASSERT(validInputs(inputs)); - - if(inputs.size() == 1) - { - singleInput(inputs, output); - } - else if(inputs.size() > 1) - { - mergeInputs(inputs, output); - } - } -private: - bool validInputs(const std::vector &inputs) const - { - for(size_t i = 0; i < inputs.size(); i++) - { - if(inputs[i].m_input == nullptr) - return false; - - const char *keys[] = {"coordsets", "topologies", "matsets"}; - for(int k = 0; k < 3; k++) - { - if(inputs[i].m_input->has_path(keys[k])) - { - const conduit::Node &n = inputs[i].m_input->fetch_existing(keys[k]); - if(n.number_of_children() > 1) - return false; - } - } - - inputs[i].m_input->fetch_existing("topologies"); - } - return true; - } - - void singleInput(const std::vector &inputs, conduit::Node &output) const - { - namespace bputils = axom::mir::utilities::blueprint; - bputils::copy(output, *(inputs[0].m_input)); - } - - void mergeInputs(const std::vector &inputs, conduit::Node &output) const - { - mergeCoordset(inputs, output); - mergeTopology(inputs, output); - //mergeFields(inputs, output); - //mergeMatset(inputs, output); - } - - void mergeCoordset(const std::vector &inputs, conduit::Node &output) const - { - namespace bputils = axom::mir::utilities::blueprint; - AXOM_ANNOTATE_SCOPE("mergeCoordset"); - const axom::IndexType totalNodes = countNodes(inputs); - conduit::Node &n_newCoordsets = output["coordsets"]; - conduit::Node *n_newValuesPtr = nullptr; - int nComps = 2; - - axom::IndexType offsets[3] = {0, 0, 0}; - const axom::IndexType n = static_cast(inputs.size()); - for(axom::IndexType i = 0; i < n; i++) - { - const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); - const conduit::Node &n_srcCoordset = coordsets[0]; - const conduit::Node &n_srcValues = n_srcCoordset.fetch_existing("values"); - - const auto type = n_srcCoordset.fetch_existing("type").as_string(); - SLIC_ASSERT(type == "explicit"); - - // Make all of the components the first time. - if(i == 0) - { - bputils::ConduitAllocateThroughAxom c2a; - - conduit::Node &n_newCoordset = n_newCoordsets[n_srcCoordset.name()]; - n_newCoordset["type"] = "explicit"; - conduit::Node &n_newValues = n_newCoordset["values"]; - n_newValuesPtr = n_newCoordset.fetch_ptr("values"); - - nComps = n_srcValues.number_of_children(); - for(int c = 0; c < nComps; c++) - { - const conduit::Node &n_srcComp = n_srcValues[c]; - conduit::Node &n_comp = n_newValues[n_srcComp.name()]; - n_comp.set_allocator(c2a.getConduitAllocatorID()); - n_comp.set(conduit::DataType(n_srcComp.dtype().id(), totalNodes)); - } - } - - // Copy this input's coordinates into the new coordset. - axom::mir::views::FloatNode_to_ArrayView(n_srcValues[0], [&](auto comp0) - { - using FloatType = typename decltype(comp0)::value_type; - for(int c = 0; c < nComps; c++) - { - axom::IndexType size = 0, offset = offsets[c]; - - const conduit::Node &n_srcComp = n_srcValues[c]; - conduit::Node &n_comp = n_newValuesPtr->child(c); - auto srcCompView = bputils::make_array_view(n_srcComp); - auto compView = bputils::make_array_view(n_comp); - - const auto nodeSliceView = inputs[i].m_nodeSliceView; - if(nodeSliceView.size() > 0) - { - // Pull out specific nodes from the input. - axom::for_all(nodeSliceView.size(), AXOM_LAMBDA(auto index) - { - const auto sliceIndex = nodeSliceView[index]; - compView[offset + index] = srcCompView[sliceIndex]; - }); - size = nodeSliceView.size(); - } - else - { - // Pull out all nodes from the input. - axom::for_all(srcCompView.size(), AXOM_LAMBDA(auto index) - { - compView[offset + index] = srcCompView[index]; - }); - size = srcCompView.size(); - } - - offsets[c] += size; - } - }); - } - } - - axom::IndexType countNodes(const std::vector &inputs) const - { - axom::IndexType nodeTotal = 0; - axom::IndexType n = static_cast(inputs.size()); - for(axom::IndexType i = 0; i < n; i++) - { - const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); - const conduit::Node &coordset = coordsets[0]; - const auto type = coordset.fetch_existing("type").as_string(); - - axom::IndexType nnodes = 0; - if(inputs[i].m_nodeSliceView.size() > 0) - nnodes = inputs[i].m_nodeSliceView.size(); - else - nnodes = conduit::blueprint::mesh::utils::coordset::length(coordset); - - nodeTotal += nnodes; - } - return nodeTotal; - } - - void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const - { - totalConnLength = 0; - totalZones = 0; - axom::IndexType n = static_cast(inputs.size()); - for(axom::IndexType i = 0; i < n; i++) - { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); - const conduit::Node &n_topo = n_topologies[0]; - const auto type = n_topo.fetch_existing("type").as_string(); - SLIC_ASSERT(type == "unstructured"); - - const conduit::Node &n_conn = n_topo.fetch_existing("elements/connectivity"); - totalConnLength += n_conn.dtype().number_of_elements(); - - const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); - totalZones += n_size.dtype().number_of_elements(); - } - } - - void mergeTopology(const std::vector &inputs, conduit::Node &output) const - { - namespace bputils = axom::mir::utilities::blueprint; - AXOM_ANNOTATE_SCOPE("mergeTopology"); - axom::IndexType totalConnLen = 0, totalZones = 0; - countZones(inputs, totalConnLen, totalZones); - conduit::Node &n_newTopologies = output["topologies"]; - - // Check whether there are mixed shapes. - std::map shape_map; - const axom::IndexType n = static_cast(inputs.size()); - for(axom::IndexType i = 0; i < n; i++) - { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); - const conduit::Node &n_srcTopo = n_topologies[0]; - const auto type = n_srcTopo.fetch_existing("type").as_string(); - const auto shape = n_srcTopo.fetch_existing("elements/shape").as_string(); - SLIC_ASSERT(type == "unstructured"); - if(shape == "mixed") - { - const conduit::Node &n_shape_map = n_srcTopo.fetch_existing("elements/shape_map"); - for(int s = 0; s < n_shape_map.number_of_children(); s++) - { - const std::string sname = n_shape_map[i].name(); - const auto id = axom::mir::views::shapeNameToID(sname); - SLIC_ASSERT(id == n_shape_map[i].to_int()); - shape_map[sname] = id; - } - } - else - { - shape_map[shape] = axom::mir::views::shapeNameToID(shape); - } - } - - conduit::Node *n_newTopoPtr = nullptr; - axom::IndexType connOffset = 0, sizesOffset = 0, shapesOffset = 0; - for(axom::IndexType i = 0; i < n; i++) - { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); - const conduit::Node &n_srcTopo = n_topologies[0]; - - const std::string srcShape = n_srcTopo.fetch_existing("elements/shape").as_string(); - const conduit::Node &n_srcConn = n_srcTopo.fetch_existing("elements/connectivity"); - const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); - - // Make all of the elements the first time. - if(i == 0) - { - bputils::ConduitAllocateThroughAxom c2a; - - conduit::Node &n_newTopo = n_newTopologies[n_srcTopo.name()]; - n_newTopoPtr = n_newTopologies.fetch_ptr(n_srcTopo.name()); - n_newTopo["type"] = "unstructured"; - n_newTopo["coordset"] = n_srcTopo["coordset"].as_string(); - - conduit::Node &n_newConn = n_newTopo["elements/connectivity"]; - n_newConn.set_allocator(c2a.getConduitAllocatorID()); - n_newConn.set(conduit::DataType(n_srcConn.dtype().id(), totalConnLen)); - - conduit::Node &n_newSizes = n_newTopo["elements/sizes"]; - n_newSizes.set_allocator(c2a.getConduitAllocatorID()); - n_newSizes.set(conduit::DataType(n_srcSizes.dtype().id(), totalZones)); - - conduit::Node &n_newOffsets = n_newTopo["elements/offsets"]; - n_newOffsets.set_allocator(c2a.getConduitAllocatorID()); - n_newOffsets.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); - - if(shape_map.size() > 1) - { - // Build a new shape map in the new topology. - conduit::Node &n_shape_map = n_newTopo["shape_map"]; - for(auto it = shape_map.begin(); it != shape_map.end(); it++) - n_shape_map[it->first] = it->second; - - conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; - n_newShapes.set_allocator(c2a.getConduitAllocatorID()); - n_newShapes.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); - } - } - - // Copy this input's connectivity into the new topology. - axom::mir::views::IndexNode_to_ArrayView(n_srcConn, [&](auto srcConnView) - { - using ConnType = typename decltype(srcConnView)::value_type; - conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); - auto connView = bputils::make_array_view(n_newConn); - - if(inputs[i].m_nodeMapView.size() > 0) - { - // Copy all zones from the input but map the nodes to new values. - const auto nodeMapView = inputs[i].m_nodeMapView; - axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) - { - const auto nodeId = srcConnView[index]; - const auto newNodeId = nodeMapView[nodeId]; - connView[connOffset + index] = newNodeId; - }); - } - else - { - // Copy all zones from the input. - axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) - { - connView[connOffset + index] = srcConnView[index]; - }); - } - connOffset += srcConnView.size(); - }); - - // Copy this input's sizes into the new topology. - axom::mir::views::IndexNode_to_ArrayView(n_srcSizes, [&](auto srcSizesView) - { - using ConnType = typename decltype(srcSizesView)::value_type; - conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); - auto sizesView = bputils::make_array_view(n_newSizes); - - // Copy all sizes from the input. - axom::for_all(srcSizesView.size(), AXOM_LAMBDA(auto index) - { - sizesView[sizesOffset + index] = srcSizesView[index]; - }); - - sizesOffset += srcSizesView.size(); - }); - - // Copy shape information if it exists. - if(n_srcTopo.has_path("elements/shapes")) - { - const conduit::Node &n_srcShapes = n_srcTopo.fetch_existing("elements/shapes"); - - axom::mir::views::IndexNode_to_ArrayView(n_srcShapes, [&](auto srcShapesView) - { - using ConnType = typename decltype(srcShapesView)::value_type; - conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/sizes"); - auto shapesView = bputils::make_array_view(n_newShapes); - - // Copy all sizes from the input. - axom::for_all(srcShapesView.size(), AXOM_LAMBDA(auto index) - { - shapesView[shapesOffset + index] = srcShapesView[index]; - }); - - shapesOffset += srcShapesView.size(); - }); - } - else - { - // Fill in shape information. - conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/sizes"); - axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) - { - const int shapeId = axom::mir::views::shapeNameToID(srcShape); - axom::for_all(shapesView.size(), AXOM_LAMBDA(auto index) - { - shapesView[shapesOffset + index] = shapeId; - }); - shapesOffset += shapesView.size(); - }); - } - } - - // Make new offsets from the sizes. - conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); - axom::mir::views::IndexNode_to_ArrayView(n_newSizes, [&](auto sizesView) - { - using ConnType = typename decltype(sizesView)::value_type; - conduit::Node &n_newOffsets = n_newTopoPtr->fetch_existing("elements/offsets"); - auto offsetsView = bputils::make_array_view(n_newOffsets); - axom::exclusive_scan(sizesView, offsetsView); - }); - } -}; - - } // end namespace detail /** @@ -913,23 +550,27 @@ std::cout << "Nodes that are unique to mixed output: " << numNewNodes << std::en { AXOM_ANNOTATE_SCOPE("merge"); // Merge the clean and mixed. - using MeshInput = typename detail::MergeMeshes::MeshInput; - std::vector inputs; - MeshInput i0; - i0.m_input = &n_cleanOutput; - inputs.push_back(i0); - - MeshInput i1; - i1.m_input = &n_tmpMask; - i1.m_nodeMapView = nodeMapView; - i1.m_nodeSliceView = nodeSliceView; - inputs.push_back(i1); - - detail::MergeMeshes mm; - mm.execute(inputs, n_merged); + std::vector inputs(2); + inputs[0].m_input = &n_cleanOutput; + +std::cout << "--- clean ---\n"; +printNode(n_cleanOutput); +std::cout << "--- mixed ---\n"; +printNode(n_tmpMask); + + inputs[1].m_input = &n_tmpMask; + inputs[1].m_nodeMapView = nodeMapView; + inputs[1].m_nodeSliceView = nodeSliceView; + + conduit::Node mmOpts; + mmOpts["topology"] = n_topo.name(); + bputils::MergeMeshes mm; + mm.execute(inputs, mmOpts, n_merged); } - printNode(n_merged); - conduit::relay::io::blueprint::save_mesh(n_merged, + +std::cout << "--- merged ---\n"; +printNode(n_merged); + conduit::relay::io::blueprint::save_mesh(n_merged, "debug_equiz_merged", "hdf5"); #endif diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index a7d9487cd3..1be1c388d1 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -62,6 +62,7 @@ class ExtractZones conduit::Node &n_output) const { AXOM_ANNOTATE_SCOPE("ExtractZones"); + namespace bputils = axom::mir::utilities::blueprint; const int allocatorID = axom::execution_space::allocatorID(); AXOM_ANNOTATE_BEGIN("nodeMap"); @@ -134,15 +135,30 @@ class ExtractZones conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; makeCoordset(nSlice, n_coordset, n_newCoordset); - // Make new fields. + // Make new fields. If originalZones are present, they'll be sliced there. + bool makeOriginalZones = true; if(n_input.has_child("fields")) { const conduit::Node &n_fields = n_input.fetch_existing("fields"); conduit::Node &n_newFields = n_output["fields"]; SliceData zSlice; zSlice.m_indicesView = selectedZonesView; + makeOriginalZones = !n_fields.has_child("originalZones"); makeFields(nSlice, zSlice, n_fields, n_newFields); } + + // Make originalElements. + if(makeOriginalZones) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_origZones = n_output["fields/originalElements"]; + n_origZones["topology"] = topoName; + n_origZones["association"] = "element"; + n_origZones["values"].set_allocator(c2a.getConduitAllocatorID()); + n_origZones["values"].set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + axom::copy(n_origZones["values"].data_ptr(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); + } } protected: diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp new file mode 100644 index 0000000000..09f2e27a89 --- /dev/null +++ b/src/axom/mir/MergeMeshes.hpp @@ -0,0 +1,494 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_MERGE_MESHES_HPP_ +#define AXOM_MIR_MERGE_MESHES_HPP_ + +#include "axom/config.hpp" +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/slic.hpp" + +#include + +#include + +#if defined(AXOM_USE_RAJA) + #include +#endif + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \brief A mesh input containing a Blueprint mesh and some mapping array views. + */ +struct MeshInput +{ + conduit::Node *m_input{nullptr}; //!< Pointer to Blueprint mesh. + axom::ArrayView m_nodeMapView{}; //!< Map for mesh nodeIds to nodeIds in final mesh. + axom::ArrayView m_nodeSliceView{};//!< Node ids to be extracted and added to final mesh. +}; + +/** + * \brief Merge multiple unstructured Blueprint meshes through MeshInput. + * + * \note The input meshes must currently contain a single coordset/topology/matset. + */ +template +class MergeMeshes +{ +public: + /** + * \brief Merge the input Blueprint meshes into a single Blueprint mesh. + * + * \param inputs A vector of inputs to be merged. + * \param options A Node containing algorithm options. + * \param[out] output The node that will contain the merged mesh. + */ + void execute(const std::vector &inputs, const conduit::Node &options, conduit::Node &output) const + { + AXOM_ANNOTATE_SCOPE("MergeMeshes"); + bool ok = validInputs(inputs, options); + if(!ok) + { + SLIC_ASSERT_MSG(ok, "Unsupported inputs were provided."); + return; + } + + if(inputs.size() == 1) + { + singleInput(inputs, output); + } + else if(inputs.size() > 1) + { + mergeInputs(inputs, output); + } + } +private: + /** + * \brief Check that the mesh inputs are valid and meet constraints. There must + * be 1 coordset/topology/matset. The coordset must be explicit and the + * topology must be unstructured and for now, non-polyhedral. + * + * \param inputs The mesh inputs. + * + * \return True if the inputs appear to be valid; False otherwise. + */ + bool validInputs(const std::vector &inputs, const conduit::Node &options) const + { + std::string topoName; + if(options.has_child("topology")) + topoName = options.fetch_existing("topology").as_string(); + + for(size_t i = 0; i < inputs.size(); i++) + { + if(inputs[i].m_input == nullptr) + return false; + + // If we did not specify which topology, make sure that there is only 1. + const char *keys[] = {"coordsets", "topologies", "matsets"}; + if(topoName.empty()) + { + for(int k = 0; k < 3; k++) + { + if(inputs[i].m_input->has_path(keys[k])) + { + const conduit::Node &n = inputs[i].m_input->fetch_existing(keys[k]); + if(n.number_of_children() > 1) + return false; + } + } + } + + const conduit::Node &n_coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &n_coordset = n_coordsets[0]; + if(n_coordset["type"].as_string() != "explicit") + { + return false; + } + + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node *n_topo = nullptr; + if(topoName.empty()) + n_topo = n_topologies.child_ptr(0); + else + n_topo = n_topologies.fetch_ptr(topoName); + if(n_topo->operator[]("type").as_string() != "unstructured") + { + return false; + } + // For now + if(n_topo->operator[]("elements/shape").as_string() == "polyhedral") + { + return false; + } + + // Require no nodeMap/nodeSlice or that they both be present. + if(!((inputs[i].m_nodeMapView.size() == 0 && inputs[i].m_nodeSliceView.size() == 0) || + (inputs[i].m_nodeMapView.size() > 0 && inputs[i].m_nodeSliceView.size() > 0))) + { + return false; + } + } + return true; + } + + /** + * \brief Merge a single input (copy it to the output). + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the merged mesh. + */ + void singleInput(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + bputils::copy(output, *(inputs[0].m_input)); + } + + /** + * \brief Merge a multiple inputs. + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the merged mesh. + */ + void mergeInputs(const std::vector &inputs, conduit::Node &output) const + { + mergeCoordset(inputs, output); + mergeTopology(inputs, output); + //mergeFields(inputs, output); + //mergeMatset(inputs, output); + } + + /** + * \brief Merge multiple coordsets into a single coordset. No node merging takes place. + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the merged coordset. + */ + void mergeCoordset(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("mergeCoordset"); + const axom::IndexType totalNodes = countNodes(inputs); + conduit::Node &n_newCoordsets = output["coordsets"]; + conduit::Node *n_newValuesPtr = nullptr; + int nComps = 2; + + axom::IndexType offsets[3] = {0, 0, 0}; + const axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &n_srcCoordset = coordsets[0]; + const conduit::Node &n_srcValues = n_srcCoordset.fetch_existing("values"); + + const auto type = n_srcCoordset.fetch_existing("type").as_string(); + SLIC_ASSERT(type == "explicit"); + + // Make all of the components the first time. + if(i == 0) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_newCoordset = n_newCoordsets[n_srcCoordset.name()]; + n_newCoordset["type"] = "explicit"; + conduit::Node &n_newValues = n_newCoordset["values"]; + n_newValuesPtr = n_newCoordset.fetch_ptr("values"); + + nComps = n_srcValues.number_of_children(); + for(int c = 0; c < nComps; c++) + { + const conduit::Node &n_srcComp = n_srcValues[c]; + conduit::Node &n_comp = n_newValues[n_srcComp.name()]; + n_comp.set_allocator(c2a.getConduitAllocatorID()); + n_comp.set(conduit::DataType(n_srcComp.dtype().id(), totalNodes)); + } + } + + // Copy this input's coordinates into the new coordset. + axom::mir::views::FloatNode_to_ArrayView(n_srcValues[0], [&](auto comp0) + { + using FloatType = typename decltype(comp0)::value_type; + for(int c = 0; c < nComps; c++) + { + axom::IndexType size = 0, offset = offsets[c]; + + const conduit::Node &n_srcComp = n_srcValues[c]; + conduit::Node &n_comp = n_newValuesPtr->child(c); + auto srcCompView = bputils::make_array_view(n_srcComp); + auto compView = bputils::make_array_view(n_comp); + + const auto nodeSliceView = inputs[i].m_nodeSliceView; + if(nodeSliceView.size() > 0) + { + // Pull out specific nodes from the input. + axom::for_all(nodeSliceView.size(), AXOM_LAMBDA(auto index) + { + const auto sliceIndex = nodeSliceView[index]; + compView[offset + index] = srcCompView[sliceIndex]; + }); + size = nodeSliceView.size(); + } + else + { + // Pull out all nodes from the input. + axom::for_all(srcCompView.size(), AXOM_LAMBDA(auto index) + { + compView[offset + index] = srcCompView[index]; + }); + size = srcCompView.size(); + } + + offsets[c] += size; + } + }); + } + } + + axom::IndexType countNodes(const std::vector &inputs, size_t index) const + { + SLIC_ASSERT(index < inputs.size()); + + const conduit::Node &coordsets = inputs[index].m_input->fetch_existing("coordsets"); + const conduit::Node &coordset = coordsets[0]; + const auto type = coordset.fetch_existing("type").as_string(); + + axom::IndexType nnodes = 0; + if(inputs[index].m_nodeSliceView.size() > 0) + nnodes = inputs[index].m_nodeSliceView.size(); + else + nnodes = conduit::blueprint::mesh::utils::coordset::length(coordset); + + return nnodes; + } + + axom::IndexType countNodes(const std::vector &inputs) const + { + axom::IndexType nodeTotal = 0; + for(size_t i = 0; i < inputs.size(); i++) + { + nodeTotal += countNodes(inputs, i); + } + return nodeTotal; + } + + void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const + { + totalConnLength = 0; + totalZones = 0; + axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topo = n_topologies[0]; + const auto type = n_topo.fetch_existing("type").as_string(); + SLIC_ASSERT(type == "unstructured"); + + const conduit::Node &n_conn = n_topo.fetch_existing("elements/connectivity"); + totalConnLength += n_conn.dtype().number_of_elements(); + + const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); + totalZones += n_size.dtype().number_of_elements(); + } + } + + void mergeTopology(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("mergeTopology"); + axom::IndexType totalConnLen = 0, totalZones = 0; + countZones(inputs, totalConnLen, totalZones); + conduit::Node &n_newTopologies = output["topologies"]; + + // Check whether there are mixed shapes. + std::map shape_map; + const axom::IndexType n = static_cast(inputs.size()); + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_srcTopo = n_topologies[0]; + const auto type = n_srcTopo.fetch_existing("type").as_string(); + const auto shape = n_srcTopo.fetch_existing("elements/shape").as_string(); + SLIC_ASSERT(type == "unstructured"); + if(shape == "mixed") + { + const conduit::Node &n_shape_map = n_srcTopo.fetch_existing("elements/shape_map"); + for(int s = 0; s < n_shape_map.number_of_children(); s++) + { + const std::string sname = n_shape_map[s].name(); + const auto id = axom::mir::views::shapeNameToID(sname); + SLIC_ASSERT(id == n_shape_map[s].to_int()); + shape_map[sname] = id; + } + } + else + { + shape_map[shape] = axom::mir::views::shapeNameToID(shape); + } + } + + conduit::Node *n_newTopoPtr = nullptr; + axom::IndexType connOffset = 0, sizesOffset = 0, shapesOffset = 0, coordOffset = 0; + for(axom::IndexType i = 0; i < n; i++) + { + const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_srcTopo = n_topologies[0]; + + const std::string srcShape = n_srcTopo.fetch_existing("elements/shape").as_string(); + const conduit::Node &n_srcConn = n_srcTopo.fetch_existing("elements/connectivity"); + const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); + + // Make all of the elements the first time. + if(i == 0) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_newTopo = n_newTopologies[n_srcTopo.name()]; + n_newTopoPtr = n_newTopologies.fetch_ptr(n_srcTopo.name()); + n_newTopo["type"] = "unstructured"; + n_newTopo["coordset"] = n_srcTopo["coordset"].as_string(); + + conduit::Node &n_newConn = n_newTopo["elements/connectivity"]; + n_newConn.set_allocator(c2a.getConduitAllocatorID()); + n_newConn.set(conduit::DataType(n_srcConn.dtype().id(), totalConnLen)); + + conduit::Node &n_newSizes = n_newTopo["elements/sizes"]; + n_newSizes.set_allocator(c2a.getConduitAllocatorID()); + n_newSizes.set(conduit::DataType(n_srcSizes.dtype().id(), totalZones)); + + conduit::Node &n_newOffsets = n_newTopo["elements/offsets"]; + n_newOffsets.set_allocator(c2a.getConduitAllocatorID()); + n_newOffsets.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); + + if(shape_map.size() > 1) + { + n_newTopo["elements/shape"] = "mixed"; + + // Build a new shape map in the new topology. + conduit::Node &n_shape_map = n_newTopo["elements/shape_map"]; + for(auto it = shape_map.begin(); it != shape_map.end(); it++) + n_shape_map[it->first] = it->second; + + conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; + n_newShapes.set_allocator(c2a.getConduitAllocatorID()); + n_newShapes.set(conduit::DataType(n_srcConn.dtype().id(), totalZones)); + } + else + { + n_newTopo["elements/shape"] = shape_map.begin()->first; + } + } + + // Copy this input's connectivity into the new topology. + axom::mir::views::IndexNode_to_ArrayView(n_srcConn, [&](auto srcConnView) + { + using ConnType = typename decltype(srcConnView)::value_type; + conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); + auto connView = bputils::make_array_view(n_newConn); + + if(inputs[i].m_nodeMapView.size() > 0) + { + // Copy all zones from the input but map the nodes to new values. + // The supplied nodeMap is assumed to be a mapping from the current + // node connectivity to the merged node connectivity. + const auto nodeMapView = inputs[i].m_nodeMapView; + axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) + { + const auto nodeId = srcConnView[index]; + const auto newNodeId = nodeMapView[nodeId]; + connView[connOffset + index] = newNodeId; + }); + } + else + { + // Copy all zones from the input. Map the nodes to the new values. + axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) + { + connView[connOffset + index] = coordOffset + srcConnView[index]; + }); + } + connOffset += srcConnView.size(); + coordOffset += countNodes(inputs, static_cast(i)); + }); + + // Copy this input's sizes into the new topology. + axom::mir::views::IndexNode_to_ArrayView(n_srcSizes, [&](auto srcSizesView) + { + using ConnType = typename decltype(srcSizesView)::value_type; + conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); + auto sizesView = bputils::make_array_view(n_newSizes); + + // Copy all sizes from the input. + axom::for_all(srcSizesView.size(), AXOM_LAMBDA(auto index) + { + sizesView[sizesOffset + index] = srcSizesView[index]; + }); + + sizesOffset += srcSizesView.size(); + }); + + // Copy shape information if it exists. + if(n_srcTopo.has_path("elements/shapes")) + { + const conduit::Node &n_srcShapes = n_srcTopo.fetch_existing("elements/shapes"); + + axom::mir::views::IndexNode_to_ArrayView(n_srcShapes, [&](auto srcShapesView) + { + using ConnType = typename decltype(srcShapesView)::value_type; + conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/shapes"); + auto shapesView = bputils::make_array_view(n_newShapes); + + // Copy all sizes from the input. + axom::for_all(srcShapesView.size(), AXOM_LAMBDA(auto index) + { + shapesView[shapesOffset + index] = srcShapesView[index]; + }); + + shapesOffset += srcShapesView.size(); + }); + } + else + { + // Fill in shape information. There is no source shape information. Use + // sizes to get the number of zones. + const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); + axom::IndexType nz = n_srcSizes.dtype().number_of_elements(); + conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/shapes"); + axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) + { + const int shapeId = axom::mir::views::shapeNameToID(srcShape); + axom::for_all(nz, AXOM_LAMBDA(auto index) + { + shapesView[shapesOffset + index] = shapeId; + }); + shapesOffset += nz; + }); + } + } + + // Make new offsets from the sizes. + conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); + axom::mir::views::IndexNode_to_ArrayView(n_newSizes, [&](auto sizesView) + { + using ConnType = typename decltype(sizesView)::value_type; + conduit::Node &n_newOffsets = n_newTopoPtr->fetch_existing("elements/offsets"); + auto offsetsView = bputils::make_array_view(n_newOffsets); + axom::exclusive_scan(sizesView, offsetsView); + }); + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index b5b3b54a8b..46a90ee0b7 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -1195,21 +1195,18 @@ TEST(mir_blueprint_utilities, zonelistbuilder_seq) { test_zonelistbuilder::test(); } - #if defined(AXOM_USE_OPENMP) TEST(mir_blueprint_utilities, zonelistbuilder_omp) { test_zonelistbuilder::test(); } #endif - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) TEST(mir_blueprint_utilities, zonelistbuilder_cuda) { test_zonelistbuilder::test(); } #endif - #if defined(AXOM_USE_HIP) TEST(mir_blueprint_utilities, zonelistbuilder_hip) { @@ -1217,6 +1214,172 @@ TEST(mir_blueprint_utilities, zonelistbuilder_hip) } #endif +//------------------------------------------------------------------------------ +template +struct test_mergemeshes +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Set up inputs. + std::vector inputs(2); + inputs[0].m_input = deviceMesh.fetch_ptr("domain0000"); + + inputs[1].m_input = deviceMesh.fetch_ptr("domain0001"); + // The node names for input 1 in the final merged mesh. + const axom::IndexType nodeMap[] = {1,2,5,6,9,10,13,14,16,17}; + // The 2 nodes in input 1 that do not appear in input 0 + const axom::IndexType nodeSlice[] = {8, 9}; + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array deviceNodeMap(10, 10, allocatorID); + axom::Array deviceNodeSlice(2, 2, allocatorID); + axom::copy(deviceNodeMap.data(), nodeMap, 10 * sizeof(axom::IndexType)); + axom::copy(deviceNodeSlice.data(), nodeSlice, 2 * sizeof(axom::IndexType)); + inputs[1].m_nodeMapView = deviceNodeMap.view(); + inputs[1].m_nodeSliceView = deviceNodeSlice.view(); + + // Execute + conduit::Node opts, deviceResult; + opts["topology"] = "mesh"; + bputils::MergeMeshes mm; + mm.execute(inputs, opts, deviceResult); + + // device->host + conduit::Node hostResult; + bputils::copy(hostResult, deviceResult); + + printNode(hostResult); + conduit::relay::io::blueprint::save_mesh(hostResult, "mergemeshes", "hdf5"); + + constexpr double tolerance = 1.e-7; + conduit::Node expectedResult, info; + result(expectedResult); + bool success = compareConduit(expectedResult, hostResult, tolerance, info); + if(!success) + { + info.print(); + } + EXPECT_TRUE(success); + } + + static void create(conduit::Node &mesh) + { + const char *yaml = R"xx( +domain0000: + coordsets: + coords: + type: explicit + values: + x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] + y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.] + topologies: + mesh: + type: unstructured + coordset: coords + elements: + shape: quad + connectivity: [0,1,5,4, 4,5,9,8, 8,9,13,12, 2,3,7,6, 6,7,11,10, 10,11,15,14] + sizes: [4,4,4, 4,4,4] + offsets: [0,4,8,12,16,20] + fields: + nodal: + topology: mesh + association: vertex + values: [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0] + zonal: + topology: mesh + association: element + values: [0,1,2, 3,4,5] +domain0001: + coordsets: + coords: + type: explicit + values: + x: [1., 2., 1., 2., 1., 2., 1., 2., 1.5, 1.5] + y: [0., 0., 1., 1., 2., 2., 3., 3., 0.5, 1.5] + topologies: + mesh: + type: unstructured + coordset: coords + elements: + shape: mixed + shape_map: + quad: 3 + tri: 2 + connectivity: [0,8,2, 0,1,8, 1,3,8, 8,3,2, 2,9,4, 2,3,9, 3,5,9, 5,4,9, 4,5,7,6] + sizes: [3,3,3,3, 3,3,3,3, 4] + offsets: [0,3,6,9, 12,15,18,21, 24] + shapes: [2,2,2,2, 2,2,2,2, 3] + fields: + nodal: + topology: mesh + association: vertex + values: [1,1,1,1,1,1,1,1, 2] + zonal: + topology: mesh + association: element + values: [0,1,2,3, 4,5,6,7, 8] +)xx"; + mesh.parse(yaml); + } + + static void result(conduit::Node &mesh) + { + const char *yaml = R"xx( +coordsets: + coords: + type: "explicit" + values: + x: [0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 1.5, 1.5] + y: [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 0.5, 1.5] +topologies: + mesh: + type: "unstructured" + coordset: "coords" + elements: + connectivity: [0, 1, 5, 4, 4, 5, 9, 8, 8, 9, 13, 12, 2, 3, 7, 6, 6, 7, 11, 10, 10, 11, 15, 14, 1, 16, 5, 1, 2, 16, 2, 6, 16, 16, 6, 5, 5, 17, 9, 5, 6, 17, 6, 10, 17, 10, 9, 17, 9, 10, 14, 13] + sizes: [4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4] + offsets: [0, 4, 8, 12, 16, 20, 24, 27, 30, 33, 36, 39, 42, 45, 48] + shape: "mixed" + shape_map: + quad: 3 + tri: 2 + shapes: [3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3] +)xx"; + mesh.parse(yaml); + } + +}; + +TEST(mir_blueprint_utilities, mergemeshes_seq) +{ + test_mergemeshes::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_blueprint_utilities, mergemeshes_omp) +{ + test_mergemeshes::test(); +} +#endif +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_blueprint_utilities, mergemeshes_cuda) +{ + test_mergemeshes::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_blueprint_utilities, mergemeshes_hip) +{ + test_mergemeshes::test(); +} +#endif + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { From 6b3468b65d1322c50d96a18a45e737c719adb98a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 18 Sep 2024 15:58:05 -0700 Subject: [PATCH 219/290] Progress on MergeMeshes --- src/axom/mir/EquiZAlgorithm.hpp | 40 +- src/axom/mir/ExtractZones.hpp | 408 ++++++++++++++---- src/axom/mir/MatsetSlicer.hpp | 21 +- src/axom/mir/MergeMeshes.hpp | 22 +- .../mir/tests/mir_blueprint_utilities.cpp | 6 +- src/axom/mir/views/StructuredTopologyView.hpp | 24 +- .../UnstructuredTopologyMixedShapeView.hpp | 25 +- .../UnstructuredTopologyPolyhedralView.hpp | 46 +- .../UnstructuredTopologySingleShapeView.hpp | 10 + 9 files changed, 498 insertions(+), 104 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 93909c12b6..c56b7b84d8 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -402,12 +402,13 @@ std::cout << "Making clean mesh" << std::endl; bputils::ExtractZonesAndMatset ez(m_topologyView, m_coordsetView, m_matsetView); conduit::Node n_ezopts, n_cleanOutput; n_ezopts["topology"] = n_topo.name(); + n_ezopts["compact"] = 0; ez.execute(cleanZones, n_ezInput, n_ezopts, n_cleanOutput); // Get the number of nodes in the clean mesh. const conduit::Node &n_cleanCoordset = n_cleanOutput[n_coordset.path()]; axom::IndexType numCleanNodes = n_cleanCoordset["values/x"].dtype().number_of_elements(); - +std::cout << "numCleanNodes: " << numCleanNodes << std::endl; #if 1 std::cout << "Saving clean mesh" << std::endl; bputils::ConduitAllocateThroughAxom c2a; @@ -486,18 +487,23 @@ std::cout << "numOutputNodes: " << numOutputNodes << std::endl; axom::Array mask(numOutputNodes, numOutputNodes, allocatorID); axom::Array maskOffset(numOutputNodes, numOutputNodes, allocatorID); auto maskView = mask.view(); - auto maskOffsetsView = mask.view(); + auto maskOffsetsView = maskOffset.view(); using reduce_policy = typename axom::execution_space::reduce_policy; RAJA::ReduceSum mask_reduce(0); axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) { const int ival = (newNodesView[index] > 0.f) ? 1 : 0; maskView[index] = ival; +if(ival > 0) +{ + std::cout << "maskView[" << index << "] = " << maskView[index] << std::endl; +} mask_reduce += ival; }); + axom::exclusive_scan(maskView, maskOffsetsView); const auto numNewNodes = mask_reduce.get(); - +std::cout << "numNewNodes: " << numNewNodes << std::endl; // Make a list of indices that we need to slice out of the node arrays. axom::Array nodeSlice(numNewNodes, numNewNodes, allocatorID); auto nodeSliceView = nodeSlice.view(); @@ -506,6 +512,7 @@ std::cout << "numOutputNodes: " << numOutputNodes << std::endl; if(maskView[index] > 0) { nodeSliceView[maskOffsetsView[index]] = index; +std::cout << "nodeSliceView[" << maskOffsetsView[index] << "] = " << index << std::endl; } }); @@ -517,13 +524,38 @@ std::cout << "numOutputNodes: " << numOutputNodes << std::endl; if(maskView[index] == 0) { nodeMapView[index] = static_cast(outputOrigNodesView[index]); +std::cout << "nodeMapView[" < fmask(mask.size()); + for(int i = 0; i < mask.size(); i++) + fmask[i] = mask[i]; + n_newFields["mask/values"].set(fmask); + #else + n_newFields["mask/values"].set_external(mask.data(), mask.size()); + #endif + + n_newFields["maskOffset/topology"] = "mesh"; + n_newFields["maskOffset/association"] = "vertex"; + n_newFields["maskOffset/values"].set_external(maskOffsetsView.data(), maskOffsetsView.size()); + + n_newFields["nodeMap/topology"] = "mesh"; + n_newFields["nodeMap/association"] = "vertex"; + n_newFields["nodeMap/values"].set_external(nodeMapView.data(), nodeMapView.size()); +#endif std::cout << "Nodes that are unique to mixed output: " << numNewNodes << std::endl; AXOM_ANNOTATE_END("identifyOriginalNodes"); diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 1be1c388d1..39d1bd5a82 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -43,6 +43,7 @@ class ExtractZones ExtractZones(const TopologyView &topoView, const CoordsetView &coordsetView) : m_topologyView(topoView) , m_coordsetView(coordsetView) + , m_zoneSlice() { } /** @@ -53,19 +54,238 @@ class ExtractZones * \param n_options The input options. * \param[out] n_output The output mesh. * - * \note The \a n_options node contains a "topology" string that is selects the - * name of the topology to extract. + * The \a n_options node controls how the algorithm works. + * + * \code{.yaml} + * topology: mesh + * compact: 1 + * extra: + * nodes: 0 + * zones: 0 + * connectivity: 0 + * \endcode + * + * The "topology" node contains a string that selects the name of the topology + * to extract. The "compact" node causes the algorithm to restrict the output + * coordset and vertex fields to only the nodes used by the selected zones. If + * "compact" is set to 0 then the original coordset and vertex fields will retain + * their size in the output mesh. The "extra" node is optional and it contains + * 3 integer values for extra allocation to be made for nodes, zones, and connectivity. + * This extra space can be filled in later by the application. */ void execute(const SelectedZonesView &selectedZonesView, const conduit::Node &n_input, const conduit::Node &n_options, - conduit::Node &n_output) const + conduit::Node &n_output) { AXOM_ANNOTATE_SCOPE("ExtractZones"); namespace bputils = axom::mir::utilities::blueprint; - const int allocatorID = axom::execution_space::allocatorID(); AXOM_ANNOTATE_BEGIN("nodeMap"); + + // Determine the dataSizes and map/slice information for nodes. + axom::Array old2new; + axom::Array nodeSlice; + const auto extra = getExtra(n_options); + Sizes dataSizes {}; + if(compact(n_options)) + { + dataSizes = compactNodeMap(selectedZonesView, extra, old2new, nodeSlice); + } + else + { + dataSizes = nodeMap(selectedZonesView, extra, old2new, nodeSlice); + } + + // Make a new output topology. + const conduit::Node &n_topologies = n_input.fetch_existing("topologies"); + const std::string topoName = topologyName(n_input, n_options); + const conduit::Node &n_topo = n_topologies.fetch_existing(topoName); + conduit::Node &n_newTopo = n_output["topologies/" + topoName]; + makeTopology(selectedZonesView, dataSizes, extra, old2new.view(), n_topo, n_options, n_newTopo); + + // Make a new coordset. + SliceData nSlice; + nSlice.m_indicesView = nodeSlice.view(); + const std::string coordsetName = + n_topo.fetch_existing("coordset").as_string(); + const conduit::Node &n_coordset = + n_input.fetch_existing("coordsets/" + coordsetName); + conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; + makeCoordset(nSlice, n_coordset, n_newCoordset); + + // Make new fields. If originalZones are present, they'll be sliced there. + bool makeOriginalZones = true; + if(n_input.has_child("fields")) + { + const conduit::Node &n_fields = n_input.fetch_existing("fields"); + conduit::Node &n_newFields = n_output["fields"]; + SliceData zSlice; + zSlice.m_indicesView = zoneSliceView(selectedZonesView, extra); + makeOriginalZones = !n_fields.has_child("originalZones"); + makeFields(nSlice, zSlice, n_fields, n_newFields); + } + + // Make originalElements. + if(makeOriginalZones) + { + bputils::ConduitAllocateThroughAxom c2a; + + conduit::Node &n_origZones = n_output["fields/originalElements"]; + n_origZones["topology"] = topoName; + n_origZones["association"] = "element"; + n_origZones["values"].set_allocator(c2a.getConduitAllocatorID()); + n_origZones["values"].set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); + axom::copy(n_origZones["values"].data_ptr(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); + } + } + +protected: + /** + * \brief This struct contains extra amounts of storage that we might want to overallocate. + */ + struct Sizes + { + axom::IndexType nodes{0}; + axom::IndexType zones{0}; + axom::IndexType connectivity{0}; + }; + + /** + * \brief Create a zone slice view, building m_zoneSlice if needed. + * + * \param selectedZonesView A view that contains the selected zone ids. + * \param extra A Sizes object containing any extra size that needs to be allocated. + * + * \return An array view containing the zone slice. + */ + axom::ArrayView zoneSliceView(const SelectedZonesView &selectedZonesView, const Sizes &extra) + { + axom::ArrayView view; + if(extra.zones > 0) + { + // We need to make a zone slice array that contains selectedZonesView, plus extra indices. + if(m_zoneSlice.size() == 0) + { + const int allocatorID = axom::execution_space::allocatorID(); + const auto n = selectedZonesView.size() + extra.zones; + m_zoneSlice = axom::Array(n, n, allocatorID); + view = m_zoneSlice.view(); + axom::copy(view.data(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); + axom::for_all(selectedZonesView.size(), + n, + AXOM_LAMBDA(auto index) + { + view[index] = 0; + }); + } + view = m_zoneSlice.view(); + } + else + { + view = selectedZonesView; + } + return view; + } + + /** + * \brief Return a Sizes object initialized from the options. + * + * \param n_options The options node that contains extra sizes. + * + * \return A Sizes object that contains extra sizes. Values not present in the options will be 0. + */ + Sizes getExtra(const conduit::Node &n_options) const + { + Sizes extra {}; + if(n_options.has_path("extra/nodes")) + { + extra.nodes = std::max(0, n_options["extra/nodes"].to_int()); + } + if(n_options.has_path("extra/zones")) + { + extra.zones = std::max(0, n_options["extra/zones"].to_int()); + } + if(n_options.has_path("extra/connectivity")) + { + extra.connectivity = std::max(0, n_options["extra/connectivity"].to_int()); + } + return extra; + } + + /** + * \brief Make node map and node slice information for the selected zones but + * do not limit the selection to only the used nodes. + * + * \param selectedZonesView The selected zones. + * \param extra A Sizes object containing any extra sizes to use for allocation. + * \param[out] old2new An array that contains the new node id for each node in the mesh. + * \param[out] nodeSlice An array that contains the node ids that will be selected + * from the input mesh when making coordsets and vertex fields. + * + * \return A Sizes object that contains the size of the nodes,zones,connectivity + * (excluding extra) for the output mesh. + * + * \note old2new is not used in this method. + */ + Sizes nodeMap(const SelectedZonesView &selectedZonesView, + const Sizes &extra, + axom::Array &AXOM_UNUSED_PARAM(old2new), + axom::Array &nodeSlice) const + { + AXOM_ANNOTATE_SCOPE("nodeMap"); + const int allocatorID = axom::execution_space::allocatorID(); + + const auto nnodes = m_coordsetView.numberOfNodes(); + + // Figure out the topology size based on selected zones. + RAJA::ReduceSum connsize_reduce(0); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + connsize_reduce += zone.numberOfNodes(); + }); + const auto newConnSize = connsize_reduce.get(); + + Sizes sizes {}; + sizes.nodes = nnodes; + sizes.zones = selectedZonesView.size(); + sizes.connectivity = newConnSize; + + nodeSlice = axom::Array(sizes.nodes + extra.nodes, sizes.nodes + extra.nodes, allocatorID); + auto nodeSliceView = nodeSlice.view(); + axom::for_all( + sizes.nodes + extra.nodes, + AXOM_LAMBDA(auto index) { + nodeSliceView[index] = (index < sizes.nodes) ? index : 0; + }); + + return sizes; + } + + /** + * \brief Make node map and node slice information for the selected zones + * selecting only the used nodes. + * + * \param selectedZonesView The selected zones. + * \param extra A Sizes object containing any extra sizes to use for allocation. + * \param[out] old2new An array that contains the new node id for each node in the mesh. + * \param[out] nodeSlice An array that contains the node ids that will be selected + * from the input mesh when making coordsets and vertex fields. + * + * \return A Sizes object that contains the size of the nodes,zones,connectivity + * (excluding extra) for the output mesh. + */ + Sizes compactNodeMap(const SelectedZonesView &selectedZonesView, + const Sizes &extra, + axom::Array &old2new, + axom::Array &nodeSlice) const + { + AXOM_ANNOTATE_SCOPE("compactNodeMap"); + const int allocatorID = axom::execution_space::allocatorID(); + // We need to figure out which nodes to keep. const auto nnodes = m_coordsetView.numberOfNodes(); axom::Array mask(nnodes, nnodes, allocatorID); @@ -87,6 +307,7 @@ class ExtractZones } connsize_reduce += nids; }); + const auto newConnSize = connsize_reduce.get(); // Count the used nodes. RAJA::ReduceSum mask_reduce(0); @@ -101,8 +322,8 @@ class ExtractZones axom::exclusive_scan(maskView, maskOffsetsView); // Make an array of original node ids that we can use to "slice" the nodal data. - axom::Array old2new(nnodes, nnodes, allocatorID); - axom::Array nodeSlice(newNumNodes, newNumNodes, allocatorID); + old2new = axom::Array(nnodes, nnodes, allocatorID); + nodeSlice = axom::Array(newNumNodes + extra.nodes, newNumNodes + extra.nodes, allocatorID); auto old2newView = old2new.view(); auto nodeSliceView = nodeSlice.view(); axom::for_all( @@ -114,67 +335,40 @@ class ExtractZones old2newView[index] = maskOffsetsView[index]; } }); - AXOM_ANNOTATE_END("nodeMap"); - - // Now make a new output topology. - const conduit::Node &n_topologies = n_input.fetch_existing("topologies"); - const std::string topoName = topologyName(n_input, n_options); - const conduit::Node &n_topo = n_topologies.fetch_existing(topoName); - - conduit::Node &n_newTopo = n_output["topologies/" + topoName]; - const auto newConnSize = connsize_reduce.get(); - makeTopology(selectedZonesView, newConnSize, old2newView, n_topo, n_newTopo); - - // Make a new coordset. - SliceData nSlice; - nSlice.m_indicesView = nodeSliceView; - const std::string coordsetName = - n_topo.fetch_existing("coordset").as_string(); - const conduit::Node &n_coordset = - n_input.fetch_existing("coordsets/" + coordsetName); - conduit::Node &n_newCoordset = n_output["coordsets/" + coordsetName]; - makeCoordset(nSlice, n_coordset, n_newCoordset); - - // Make new fields. If originalZones are present, they'll be sliced there. - bool makeOriginalZones = true; - if(n_input.has_child("fields")) + if(extra.nodes > 0) { - const conduit::Node &n_fields = n_input.fetch_existing("fields"); - conduit::Node &n_newFields = n_output["fields"]; - SliceData zSlice; - zSlice.m_indicesView = selectedZonesView; - makeOriginalZones = !n_fields.has_child("originalZones"); - makeFields(nSlice, zSlice, n_fields, n_newFields); + axom::for_all( + nnodes, + nnodes + extra.nodes, + AXOM_LAMBDA(auto index) { + nodeSliceView[index] = 0; + }); } - // Make originalElements. - if(makeOriginalZones) - { - bputils::ConduitAllocateThroughAxom c2a; + Sizes sizes {}; + sizes.nodes = newNumNodes; + sizes.zones = selectedZonesView.size(); + sizes.connectivity = newConnSize; - conduit::Node &n_origZones = n_output["fields/originalElements"]; - n_origZones["topology"] = topoName; - n_origZones["association"] = "element"; - n_origZones["values"].set_allocator(c2a.getConduitAllocatorID()); - n_origZones["values"].set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); - axom::copy(n_origZones["values"].data_ptr(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); - } + return sizes; } -protected: /** * \brief Make the output topology for just the selected zones. * * \param selectedZonesView A view that contains the ids of the zones to extract. - * \param newConnSize The expected size of the new connectivity. + * \param dataSizes Array sizes for connectivity, sizes, etc. + * \param extra Extra sizes for connectivity, sizes, etc. * \param old2newView A view that lets us map old node numbers to new node numbers. * \param n_topo The input topology. * \param n_newTopo A node to contain the new topology. */ void makeTopology(const SelectedZonesView &selectedZonesView, - int newConnSize, + const Sizes &dataSizes, + const Sizes &extra, const axom::ArrayView &old2newView, const conduit::Node &n_topo, + const conduit::Node &n_options, conduit::Node &n_newTopo) const { AXOM_ANNOTATE_SCOPE("makeTopology"); @@ -198,22 +392,22 @@ class ExtractZones conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(c2a.getConduitAllocatorID()); n_conn.set( - conduit::DataType(cpp2conduit::id, newConnSize)); + conduit::DataType(cpp2conduit::id, dataSizes.connectivity + extra.connectivity)); auto connView = bputils::make_array_view(n_conn); conduit::Node &n_sizes = n_newTopo["elements/sizes"]; n_sizes.set_allocator(c2a.getConduitAllocatorID()); n_sizes.set(conduit::DataType(cpp2conduit::id, - selectedZonesView.size())); + dataSizes.zones + extra.zones)); auto sizesView = bputils::make_array_view(n_sizes); conduit::Node &n_offsets = n_newTopo["elements/offsets"]; n_offsets.set_allocator(c2a.getConduitAllocatorID()); n_offsets.set(conduit::DataType(cpp2conduit::id, - selectedZonesView.size())); + dataSizes.zones + extra.zones)); auto offsetsView = bputils::make_array_view(n_offsets); - // Fill in sizes, offsets, connectivity. + // Fill sizes, offsets m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(auto szIndex, @@ -221,22 +415,57 @@ class ExtractZones const auto &zone) { sizesView[szIndex] = zone.numberOfNodes(); }); + if(extra.zones > 0) + { + axom::for_all(dataSizes.zones, dataSizes.zones + extra.zones, AXOM_LAMBDA(auto index) + { + sizesView[index] = 0; + }); + } axom::exclusive_scan(sizesView, offsetsView); - const axom::ArrayView deviceOld2NewView(old2newView); - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(auto szIndex, - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - const auto oldNodeId = zone.getId(i); - const auto newNodeId = deviceOld2NewView[oldNodeId]; - connView[offset + i] = newNodeId; - } + + // Fill connectivity + if(compact(n_options)) + { + const axom::ArrayView deviceOld2NewView(old2newView); + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto szIndex, + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + const auto oldNodeId = zone.getId(i); + // When compact, we map node ids to the compact node ids. + const auto newNodeId = deviceOld2NewView[oldNodeId]; + connView[offset + i] = newNodeId; + } + }); + } + else + { + m_topologyView.template for_selected_zones( + selectedZonesView, + AXOM_LAMBDA(auto szIndex, + auto AXOM_UNUSED_PARAM(zoneIndex), + const auto &zone) { + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + connView[offset + i] = zone.getId(i); + } + }); + } + if(extra.connectivity > 0) + { + axom::for_all(dataSizes.connectivity, dataSizes.connectivity + extra.connectivity, AXOM_LAMBDA(auto index) + { + connView[index] = 0; }); + } // Handle shapes, if present. if(n_topo.has_path("elements/shapes")) @@ -248,16 +477,23 @@ class ExtractZones conduit::Node &n_newShapes = n_newTopo["elements/shapes"]; n_newShapes.set_allocator(c2a.getConduitAllocatorID()); n_newShapes.set(conduit::DataType(cpp2conduit::id, - selectedZonesView.size())); + dataSizes.zones + extra.zones)); auto newShapesView = bputils::make_array_view(n_newShapes); const SelectedZonesView deviceSelectedZonesView(selectedZonesView); axom::for_all( - selectedZonesView.size(), + dataSizes.zones, AXOM_LAMBDA(auto index) { newShapesView[index] = shapesView[deviceSelectedZonesView[index]]; }); + if(extra.zones > 0) + { + axom::for_all(dataSizes.zones, dataSizes.zones + extra.zones, AXOM_LAMBDA(auto index) + { + newShapesView[index] = 0; + }); + } } } } @@ -265,11 +501,11 @@ class ExtractZones /** * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * - * \param blend The BlendData that we need to construct the new coordset. + * \param nodeSlice Node slice information. * \param n_coordset The input coordset, which is passed for metadata. * \param[out] n_newCoordset The new coordset. */ - void makeCoordset(const SliceData &slice, + void makeCoordset(const SliceData &nodeSlice, const conduit::Node &n_coordset, conduit::Node &n_newCoordset) const { @@ -277,7 +513,7 @@ class ExtractZones axom::mir::utilities::blueprint::CoordsetSlicer cs( m_coordsetView); n_newCoordset.reset(); - cs.execute(slice, n_coordset, n_newCoordset); + cs.execute(nodeSlice, n_coordset, n_newCoordset); } /** @@ -335,6 +571,23 @@ class ExtractZones return name; } + /** + * \brief Return whether coordset/vertex compaction is desired. + * + * \param n_options The options node that may contain a "topology" string. + * + * \return True if compaction is on (the default), false otherwise. + */ + bool compact(const conduit::Node &n_options) const + { + bool retval = true; + if(n_options.has_path("compact")) + { + retval = n_options["compact"].to_int() != 0; + } + return retval; + } + /** * \brief Return the name of the output shape type. * @@ -370,6 +623,7 @@ class ExtractZones private: TopologyView m_topologyView; CoordsetView m_coordsetView; + axom::Array m_zoneSlice; }; /** @@ -414,7 +668,7 @@ class ExtractZonesAndMatset void execute(const SelectedZonesView &selectedZonesView, const conduit::Node &n_input, const conduit::Node &n_options, - conduit::Node &n_output) const + conduit::Node &n_output) { AXOM_ANNOTATE_SCOPE("ExtractZonesAndMatset"); @@ -479,8 +733,10 @@ class ExtractZonesAndMatset conduit::Node &n_newMatset) const { AXOM_ANNOTATE_SCOPE("makeMatset"); - MatsetSlicer ms; - ms.execute(m_matsetView, selectedZonesView, n_matset, n_newMatset); + MatsetSlicer ms(m_matsetView); + SliceData zSlice; + zSlice.m_indicesView = selectedZonesView; + ms.execute(zSlice, n_matset, n_newMatset); } MatsetView m_matsetView; diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 7b49091a9e..a608c25cfa 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -33,22 +33,28 @@ class MatsetSlicer public: using SelectedZonesView = axom::ArrayView; + /** + * \brief Constructor. + */ + MatsetSlicer(const MatsetView &matsetView) : m_matsetView(matsetView) + { } + /** * \brief Slice the input matset and output a new matset. * * \param matsetView A view that wraps the input matset. - * \param selectedZonesView A view that contains the zone ids that we're extracting from the matset. + * \param slice Slice data that contains the zone ids that we're extracting from the matset. * \param n_matset The input matset. * \param[out] n_newMatset The output matset. */ - static void execute(const MatsetView &matsetView, - const SelectedZonesView &selectedZonesView, - const conduit::Node &n_matset, - conduit::Node &n_newMatset) + void execute(const SliceData &slice, + const conduit::Node &n_matset, + conduit::Node &n_newMatset) { using MatsetIndex = typename MatsetView::IndexType; using MatsetFloat = typename MatsetView::FloatType; namespace bputils = axom::mir::utilities::blueprint; + const axom::ArrayView &selectedZonesView = slice.m_indicesView; SLIC_ASSERT(selectedZonesView.size() > 0); // Copy the material_map if it exists. @@ -75,7 +81,7 @@ class MatsetSlicer auto offsetsView = bputils::make_array_view(n_offsets); // Figure out overall size of the matset zones we're keeping. - MatsetView deviceMatsetView(matsetView); + MatsetView deviceMatsetView(m_matsetView); RAJA::ReduceSum size_reduce(0); const axom::ArrayView deviceSelectedZonesView( selectedZonesView); @@ -130,6 +136,9 @@ class MatsetSlicer } }); } + +private: + MatsetView m_matsetView; }; } // end namespace blueprint diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index 09f2e27a89..ce857b0aa4 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -275,7 +275,9 @@ class MergeMeshes axom::IndexType nodeTotal = 0; for(size_t i = 0; i < inputs.size(); i++) { - nodeTotal += countNodes(inputs, i); + const auto nnodes = countNodes(inputs, i); +std::cout << "countNodes " << i << ": nnodes=" << nnodes << std::endl; + nodeTotal += nnodes; } return nodeTotal; } @@ -293,10 +295,13 @@ class MergeMeshes SLIC_ASSERT(type == "unstructured"); const conduit::Node &n_conn = n_topo.fetch_existing("elements/connectivity"); - totalConnLength += n_conn.dtype().number_of_elements(); + const auto connLength = n_conn.dtype().number_of_elements(); + totalConnLength += connLength; const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); - totalZones += n_size.dtype().number_of_elements(); + const auto nzones = n_size.dtype().number_of_elements(); + totalZones += nzones; +std::cout << "countZones " << i << ": connLength=" << connLength << ", totalZones=" << totalZones << std::endl; } } @@ -393,7 +398,7 @@ class MergeMeshes using ConnType = typename decltype(srcConnView)::value_type; conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); auto connView = bputils::make_array_view(n_newConn); - +std::cout << "-------------- input " << i << " ----------------" << std::endl; if(inputs[i].m_nodeMapView.size() > 0) { // Copy all zones from the input but map the nodes to new values. @@ -404,14 +409,23 @@ class MergeMeshes { const auto nodeId = srcConnView[index]; const auto newNodeId = nodeMapView[nodeId]; + +std::cout << index << ":A nodeId=" << nodeId << ", newNodeId=" << newNodeId << " -> " << (connOffset + index) << std::endl; + connView[connOffset + index] = newNodeId; }); +std::cout << "coordOffset=" << coordOffset << std::endl; } else { // Copy all zones from the input. Map the nodes to the new values. axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) { + +std::cout << index << ":B srcConnView=" << srcConnView[index] + << ", newNodeId=" << (coordOffset + srcConnView[index]) + << " -> " << (connOffset + index) << std::endl; + connView[connOffset + index] = coordOffset + srcConnView[index]; }); } diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 46a90ee0b7..5c217139e5 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -526,9 +526,11 @@ struct test_matset_slice bputils::make_array_view(deviceMatset["indices"])); // Slice it. - bputils::MatsetSlicer slicer; + bputils::MatsetSlicer slicer(matsetView); conduit::Node newDeviceMatset; - slicer.execute(matsetView, selectedZones.view(), deviceMatset, newDeviceMatset); + bputils::SliceData slice; + slice.m_indicesView = selectedZones.view(); + slicer.execute(slice, deviceMatset, newDeviceMatset); // device->host conduit::Node newHostMatset; diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index a16a81200f..1a77f43cd5 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -33,13 +33,11 @@ class StructuredTopologyView * * \return The number of dimensions. */ - AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } /** * \brief Constructor */ - AXOM_HOST_DEVICE StructuredTopologyView() : m_indexing() { } /** @@ -47,7 +45,6 @@ class StructuredTopologyView * * \param indexing The indexing policy for the topology (num zones in each dimension). */ - AXOM_HOST_DEVICE StructuredTopologyView(const IndexingPolicy &indexing) : m_indexing(indexing) { } @@ -56,7 +53,6 @@ class StructuredTopologyView * * \return The number of zones. */ - AXOM_HOST_DEVICE IndexType size() const { return m_indexing.size(); } /** @@ -64,14 +60,26 @@ class StructuredTopologyView * * \return The number of zones. */ - AXOM_HOST_DEVICE IndexType numberOfZones() const { return size(); } + IndexType numberOfZones() const { return size(); } + + /** + * \brief Return the size of the connectivity. + * + * \return The size of the connectivity. + */ + IndexType connectivitySize() const + { + IndexType nodesPerElem = 1; + for(int d = 0; d < dimension(); d++) + nodesPerElem *= 2; + return numberOfZones() * nodesPerElem; + } /** * \brief Return the mesh logical dimensions. * * \return The mesh logical dimensions. */ - AXOM_HOST_DEVICE const LogicalIndex &logicalDimensions() const { return m_indexing.logicalDimensions(); @@ -82,7 +90,6 @@ class StructuredTopologyView * * \return The indexing object. */ - AXOM_HOST_DEVICE IndexingPolicy &indexing() { return m_indexing; } /** @@ -90,7 +97,6 @@ class StructuredTopologyView * * \return The indexing object. */ - AXOM_HOST_DEVICE const IndexingPolicy &indexing() const { return m_indexing; } /** @@ -102,7 +108,7 @@ class StructuredTopologyView * \param func The function/lambda that will be executed for each zone in the mesh. */ template - AXOM_HOST void for_all_zones(FuncType &&func) const + void for_all_zones(FuncType &&func) const { const auto nzones = numberOfZones(); diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index b37c1de4ae..c2bd07a2ba 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -17,6 +17,9 @@ namespace views { /** * \brief Given a shape value, we can get the Shape::id() that is used internally. + * + * \note If the view was to renumber the shapes array to use the Shape::id() values + * then operator[] could return its input value and skip bsearch. */ template class ShapeMap @@ -128,6 +131,16 @@ class UnstructuredTopologyMixedShapeView */ IndexType numberOfZones() const { return m_sizes.size(); } + /** + * \brief Return the size of the connectivity. + * + * \return The size of the connectivity. + */ + IndexType connectivitySize() const + { + return m_connectivity.size(); + } + /** * \brief Execute a function for each zone in the mesh. * @@ -159,7 +172,11 @@ class UnstructuredTopologyMixedShapeView connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; - // TODO: SLIC_ASSERT(shapeID > 0); +#if defined(AXOM_DEVICE_CODE) + assert(shapeID > 0); +#else + SLIC_ASSERT(shapeID > 0); +#endif const ShapeType shape(shapeID, shapeData); func(zoneIndex, shape); }); @@ -201,7 +218,11 @@ class UnstructuredTopologyMixedShapeView connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; - // TODO: SLIC_ASSERT(shapeID > 0); +#if defined(AXOM_DEVICE_CODE) + assert(shapeID > 0); +#else + SLIC_ASSERT(shapeID > 0); +#endif const ShapeType shape(shapeID, shapeData); func(selectIndex, zoneIndex, shape); }); diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 36fa71771b..00658238f1 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -24,8 +24,12 @@ class UnstructuredTopologyPolyhedralView using ConnectivityType = ConnType; using ConnectivityView = axom::ArrayView; + /** + * \brief This struct contains views that hold polyhedral connectivity. + */ struct PolyhedronData { + /// Constructor AXOM_HOST_DEVICE PolyhedronData(const ConnectivityView &subelement_conn, const ConnectivityView &subelement_sizes, @@ -41,6 +45,7 @@ class UnstructuredTopologyPolyhedralView , m_element_offsets(element_offsets) { } + /// Constructor AXOM_HOST_DEVICE PolyhedronData(const PolyhedronData &obj) : m_subelement_conn(obj.m_subelement_conn) @@ -59,7 +64,9 @@ class UnstructuredTopologyPolyhedralView ConnectivityView m_element_offsets; }; - // Can we provide a way to provide data about Zone i's shape? + /** + * \brief This struct provides data about Zone i's shape. + */ struct PolyhedronShape { constexpr static IndexType MaximumNumberOfIds = 20 * 3; @@ -67,6 +74,7 @@ class UnstructuredTopologyPolyhedralView AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return true; } AXOM_HOST_DEVICE constexpr static int id() { return Polyhedron_ShapeID; } + /// Constructor. AXOM_HOST_DEVICE PolyhedronShape(const PolyhedronData &obj, axom::IndexType zi) : m_data(obj) , m_zoneIndex(zi) @@ -177,6 +185,9 @@ class UnstructuredTopologyPolyhedralView using ShapeType = PolyhedronShape; + /** + * \brief Constructor. + */ UnstructuredTopologyPolyhedralView(const ConnectivityView &subelement_conn, const ConnectivityView &subelement_sizes, const ConnectivityView &subelement_offsets, @@ -191,8 +202,23 @@ class UnstructuredTopologyPolyhedralView element_offsets) { } + /** + * \brief Return the number of zones in the mesh. + * + * \return The number of zones. + */ IndexType numberOfZones() const { return m_data.m_element_sizes.size(); } + /** + * \brief Return the size of the connectivity. + * + * \return The size of the connectivity. + */ + IndexType connectivitySize() const + { + return m_data.element_conn.size(); + } + /** * \brief Return the dimension of the shape. * @@ -200,6 +226,14 @@ class UnstructuredTopologyPolyhedralView */ static constexpr int dimension() { return 3; } + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param func The function/lambda that will be executed for each zone in the mesh. + */ template void for_all_zones(FuncType &&func) const { @@ -215,6 +249,16 @@ class UnstructuredTopologyPolyhedralView }); } + /** + * \brief Execute a function for each zone in the mesh. + * + * \tparam ExecSpace The execution space for the function body. + * \tparam ViewType A view type that contains zone indices. + * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. + * + * \param selectedIdsView A view that contains a list of zones to operate on. + * \param func The function/lambda that will be executed for each zone in the mesh. + */ template void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const { diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 0934fd2c68..ec5e5b0f5a 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -75,6 +75,16 @@ class UnstructuredTopologySingleShapeView : (m_connectivityView.size() / ShapeType::numberOfNodes()); } + /** + * \brief Return the size of the connectivity. + * + * \return The size of the connectivity. + */ + IndexType connectivitySize() const + { + return m_connectivityView.size(); + } + /** * \brief Execute a function for each zone in the mesh. * From c1f6fba03014451b1c07d34d83468b4b9273c854 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 18 Sep 2024 18:38:46 -0700 Subject: [PATCH 220/290] Merge is working for coordsets/topo. --- src/axom/mir/ClipField.hpp | 16 +- src/axom/mir/EquiZAlgorithm.hpp | 469 +++++++++++++++++--------------- src/axom/mir/MergeMeshes.hpp | 13 +- 3 files changed, 261 insertions(+), 237 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 529d811c3c..2aa1f40b2f 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -628,9 +628,6 @@ class ClipField uIndices); builder.setUniqueNames(uNames.view(), uIndices.view()); -// NOTE TO SELF: This is new code that I'm trying out to remove single-node -// blend groups from the selected unique blend groups. It's not -// quite working yet. #if defined(AXOM_REDUCE_BLEND_GROUPS) // Filter the unique names/indices to remove single node blend groups. builder.filterUnique(newUniqueNames, newUniqueIndices); @@ -1957,6 +1954,9 @@ class ClipField const auto origSize = blend.m_originalIdsView.size(); const auto blendSize = blend.m_selectedIndicesView.size(); const auto outputSize = origSize + blendSize; + using Precision = int; + constexpr Precision one = 1; + constexpr Precision zero = 0; if(n_newFields.has_child(newNodes)) { @@ -1966,12 +1966,12 @@ class ClipField conduit::Node &n_new_nodes = n_newFields.fetch_existing(newNodes); conduit::Node &n_new_nodes_values = n_new_nodes["values"]; - auto valuesView = bputils::make_array_view(n_new_nodes_values); + auto valuesView = bputils::make_array_view(n_new_nodes_values); // Update values for the blend groups only. axom::for_all(blendSize, AXOM_LAMBDA(auto bgid) { - valuesView[origSize + bgid] = 1.f; + valuesView[origSize + bgid] = one; }); } else @@ -1984,14 +1984,14 @@ class ClipField n_new_nodes["association"] = "vertex"; conduit::Node &n_new_nodes_values = n_new_nodes["values"]; n_new_nodes_values.set_allocator(c2a.getConduitAllocatorID()); - n_new_nodes_values.set(conduit::DataType::float32(outputSize)); - auto valuesView = bputils::make_array_view(n_new_nodes_values); + n_new_nodes_values.set(conduit::DataType(bputils::cpp2conduit::id, outputSize)); + auto valuesView = bputils::make_array_view(n_new_nodes_values); // Fill in values. Everything below origSize is an original node. // Everything above is a blended node. axom::for_all(outputSize, AXOM_LAMBDA(auto index) { - valuesView[index] = (index < origSize) ? 0.f : 1.f; + valuesView[index] = (index < origSize) ? zero : one; }); } } diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index c56b7b84d8..d25df3e3cf 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -28,15 +28,18 @@ #include #endif +// This macro makes the EquiZ algorithm split processing into clean/mixed stages. +#define AXOM_EQUIZ_SPLIT_PROCESSING + // Uncomment to save inputs and outputs. -#define AXOM_DEBUG_EQUIZ +#define AXOM_EQUIZ_DEBUG // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It // could be faster but it might not be as robust. // #define AXOM_EQUIZ_SKIP_FIRST_ITERATION -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) #include #endif @@ -292,7 +295,10 @@ class MaterialIntersector template class EquiZAlgorithm : public axom::mir::MIRAlgorithm { + using reduce_policy = typename axom::execution_space::reduce_policy; public: + using ConnectivityType = typename TopologyView::ConnectivityType; + /** * \brief Constructor * @@ -303,7 +309,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm EquiZAlgorithm(const TopologyView &topoView, const CoordsetView &coordsetView, const MatsetView &matsetView) - : m_topologyView(topoView) + : axom::mir::MIRAlgorithm() + , m_topologyView(topoView) , m_coordsetView(coordsetView) , m_matsetView(matsetView) { } @@ -312,7 +319,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm virtual ~EquiZAlgorithm() = default; protected: -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) void printNode(const conduit::Node &n) const { conduit::Node options; @@ -355,12 +362,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::copy(n_options_copy, n_options); n_options_copy["topology"] = n_topo.name(); -#if defined(AXOM_DEBUG_EQUIZ) - //-------------------------------------------------------------------------- - // +#if defined(AXOM_EQUIZ_DEBUG) // Save the MIR input. - // - //-------------------------------------------------------------------------- conduit::Node n_tmpInput; n_tmpInput[n_topo.path()].set_external(n_topo); n_tmpInput[n_coordset.path()].set_external(n_coordset); @@ -371,7 +374,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm "hdf5"); #endif -#if 1 +#if defined(AXOM_EQUIZ_SPLIT_PROCESSING) // Come up with lists of clean/mixed zones. axom::Array cleanZones, mixedZones; bputils::ZoneListBuilder zlb(m_topologyView, m_matsetView); @@ -385,40 +388,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm zlb.execute(m_coordsetView.numberOfNodes(), cleanZones, mixedZones); } SLIC_ASSERT((cleanZones.size() + mixedZones.size()) == m_topologyView.numberOfZones()); -std::cout << "cleanZones.size: " << cleanZones.size() << std::endl; -std::cout << "mixedZones.size: " << mixedZones.size() << std::endl; + SLIC_INFO(axom::fmt::format("cleanZones: {}, mixedZones: {}", cleanZones.size(), mixedZones.size())); if(cleanZones.size() > 0 && mixedZones.size() > 0) { - // Bundle the inputs so we can make the clean mesh - conduit::Node n_ezInput; - n_ezInput[n_topo.path()].set_external(n_topo); - n_ezInput[n_coordset.path()].set_external(n_coordset); - n_ezInput[n_fields.path()].set_external(n_fields); - n_ezInput[n_matset.path()].set_external(n_matset); - -std::cout << "Making clean mesh" << std::endl; - // Make the clean mesh. - bputils::ExtractZonesAndMatset ez(m_topologyView, m_coordsetView, m_matsetView); - conduit::Node n_ezopts, n_cleanOutput; - n_ezopts["topology"] = n_topo.name(); - n_ezopts["compact"] = 0; - ez.execute(cleanZones, n_ezInput, n_ezopts, n_cleanOutput); - - // Get the number of nodes in the clean mesh. - const conduit::Node &n_cleanCoordset = n_cleanOutput[n_coordset.path()]; - axom::IndexType numCleanNodes = n_cleanCoordset["values/x"].dtype().number_of_elements(); -std::cout << "numCleanNodes: " << numCleanNodes << std::endl; -#if 1 -std::cout << "Saving clean mesh" << std::endl; - bputils::ConduitAllocateThroughAxom c2a; - - AXOM_ANNOTATE_BEGIN("saveClean"); - conduit::relay::io::blueprint::save_mesh(n_cleanOutput, "clean", "hdf5"); - AXOM_ANNOTATE_END("saveClean"); - - // Add a field to the original nodes. - AXOM_ANNOTATE_BEGIN("origNodes"); // Gather the inputs into a single root but replace the fields with // a new node to which we can add additional fields. conduit::Node n_root; @@ -428,213 +401,257 @@ std::cout << "Saving clean mesh" << std::endl; conduit::Node &n_root_coordset = n_root[n_coordset.path()]; conduit::Node &n_root_topo = n_root[n_topo.path()]; conduit::Node &n_root_matset = n_root[n_matset.path()]; - // Link in original fields. conduit::Node &n_root_fields = n_root["fields"]; for(conduit::index_t i = 0; i < n_fields.number_of_children(); i++) { n_root_fields[n_fields[i].name()].set_external(n_fields[i]); } - // Add a new field. - conduit::Node &n_orig_nodes = n_root_fields["__equiz_original_node"]; - n_orig_nodes["topology"] = n_topo.name(); - n_orig_nodes["association"] = "vertex"; - n_orig_nodes["values"].set_allocator(c2a.getConduitAllocatorID()); - n_orig_nodes["values"].set(conduit::DataType::float32(m_coordsetView.numberOfNodes())); - auto origNodesView = bputils::make_array_view(n_orig_nodes["values"]); - axom::for_all(m_coordsetView.numberOfNodes(), AXOM_LAMBDA(auto index) - { - origNodesView[index] = static_cast(index); - }); -//printNode(n_root); - // If there are fields in the options, make sure the new field is handled too. - if(n_options_copy.has_child("fields")) - n_options_copy["fields/__equiz_original_node"] = "__equiz_original_node"; - AXOM_ANNOTATE_END("origNodes"); -//std::cout << "Making mixed mesh" << std::endl; -// // Make a mixed mesh. -// conduit::Node n_mixedOutput; -// n_ezopts["topology"] = n_topo.name(); -// ez.execute(mixedZones, n_ezInput, n_ezopts, n_mixedOutput); -//std::cout << "Saving mixed mesh" << std::endl; -// conduit::relay::io::blueprint::save_mesh(n_mixedOutput, "mixed", "hdf5"); - -#endif + // Make the clean mesh. + conduit::Node n_cleanOutput; + makeCleanOutput(n_root, n_topo.name(), cleanZones.view(), n_cleanOutput); -std::cout << "Process mixed zones..." << std::endl; + // Add a original nodes field. + addOriginal(n_root_fields[originalNodesFieldName()], n_topo.name(), "vertex", m_coordsetView.numberOfNodes()); + // If there are fields in the options, make sure the new field is handled too. + if(n_options_copy.has_child("fields")) + n_options_copy["fields/" + originalNodesFieldName()] = originalNodesFieldName(); // Process the mixed part of the mesh. We select just the mixed zones. n_options_copy["selectedZones"].set_external(mixedZones.data(), mixedZones.size()); - n_options_copy["newNodesField"] = "__equiz_new_nodes"; -printNode(n_options_copy); + n_options_copy["newNodesField"] = newNodesFieldName(); processMixedZones(n_root_topo, n_root_coordset, n_root_fields, n_root_matset, n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); -#if 1 - // Look over the nodes in the output and find the new ones. - SLIC_ASSERT(n_newFields.has_child("__equiz_original_node")); - AXOM_ANNOTATE_BEGIN("identifyOriginalNodes"); - const conduit::Node &n_output_orig_nodes = n_newFields["__equiz_original_node/values"]; - auto numOutputNodes = n_output_orig_nodes.dtype().number_of_elements(); -std::cout << "numOutputNodes: " << numOutputNodes << std::endl; - auto outputOrigNodesView = bputils::make_array_view(n_output_orig_nodes); - - const conduit::Node &n_new_nodes_values = n_newFields["__equiz_new_nodes/values"]; - auto newNodesView = bputils::make_array_view(n_new_nodes_values); - - // Make mask/offset for which nodes we need to keep from the mixed output. - const int allocatorID = axom::execution_space::allocatorID(); - axom::Array mask(numOutputNodes, numOutputNodes, allocatorID); - axom::Array maskOffset(numOutputNodes, numOutputNodes, allocatorID); - auto maskView = mask.view(); - auto maskOffsetsView = maskOffset.view(); - using reduce_policy = typename axom::execution_space::reduce_policy; - RAJA::ReduceSum mask_reduce(0); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - const int ival = (newNodesView[index] > 0.f) ? 1 : 0; - maskView[index] = ival; -if(ival > 0) -{ - std::cout << "maskView[" << index << "] = " << maskView[index] << std::endl; -} - mask_reduce += ival; - }); - - axom::exclusive_scan(maskView, maskOffsetsView); - const auto numNewNodes = mask_reduce.get(); -std::cout << "numNewNodes: " << numNewNodes << std::endl; - // Make a list of indices that we need to slice out of the node arrays. - axom::Array nodeSlice(numNewNodes, numNewNodes, allocatorID); - auto nodeSliceView = nodeSlice.view(); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - nodeSliceView[maskOffsetsView[index]] = index; -std::cout << "nodeSliceView[" << maskOffsetsView[index] << "] = " << index << std::endl; - } - }); - - // Make a node map for mapping mixed connectivity into combined node numbering. - axom::Array nodeMap(numOutputNodes, numOutputNodes, allocatorID); - auto nodeMapView = nodeMap.view(); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - if(maskView[index] == 0) - { - nodeMapView[index] = static_cast(outputOrigNodesView[index]); -std::cout << "nodeMapView[" < fmask(mask.size()); - for(int i = 0; i < mask.size(); i++) - fmask[i] = mask[i]; - n_newFields["mask/values"].set(fmask); - #else - n_newFields["mask/values"].set_external(mask.data(), mask.size()); - #endif - - n_newFields["maskOffset/topology"] = "mesh"; - n_newFields["maskOffset/association"] = "vertex"; - n_newFields["maskOffset/values"].set_external(maskOffsetsView.data(), maskOffsetsView.size()); - - n_newFields["nodeMap/topology"] = "mesh"; - n_newFields["nodeMap/association"] = "vertex"; - n_newFields["nodeMap/values"].set_external(nodeMapView.data(), nodeMapView.size()); -#endif -std::cout << "Nodes that are unique to mixed output: " << numNewNodes << std::endl; - AXOM_ANNOTATE_END("identifyOriginalNodes"); - - //-------------------------------------------------------------------------- - // - // Save the MIR output with the mask. - // - //-------------------------------------------------------------------------- - conduit::Node n_tmpMask; - n_tmpMask[n_topo.path()].set_external(n_newTopo); - n_tmpMask[n_coordset.path()].set_external(n_newCoordset); - n_tmpMask[n_fields.path()].set_external(n_newFields); - n_tmpMask[n_matset.path()].set_external(n_newMatset); -// printNode(n_tmpMask); - conduit::relay::io::blueprint::save_mesh(n_tmpMask, - "debug_equiz_mask", + // Make node map and slice info for merging. + axom::Array nodeMap, nodeSlice; + createNodeMapAndSlice(n_newFields, nodeMap, nodeSlice); + + // Gather the MIR output into a single node. + conduit::Node n_mirOutput; + n_mirOutput[n_topo.path()].set_external(n_newTopo); + n_mirOutput[n_coordset.path()].set_external(n_newCoordset); + n_mirOutput[n_fields.path()].set_external(n_newFields); + n_mirOutput[n_matset.path()].set_external(n_newMatset); +#if defined(AXOM_EQUIZ_DEBUG) + printNode(n_mirOutput); + conduit::relay::io::blueprint::save_mesh(n_mirOutput, + "debug_equiz_mir", "hdf5"); +#endif -// n_newFields.remove("__equiz_original_node"); -// n_newFields.remove("__equiz_mask"); - -#if 1 - conduit::Node n_merged; - { - AXOM_ANNOTATE_SCOPE("merge"); - // Merge the clean and mixed. - std::vector inputs(2); - inputs[0].m_input = &n_cleanOutput; - -std::cout << "--- clean ---\n"; -printNode(n_cleanOutput); -std::cout << "--- mixed ---\n"; -printNode(n_tmpMask); - - inputs[1].m_input = &n_tmpMask; - inputs[1].m_nodeMapView = nodeMapView; - inputs[1].m_nodeSliceView = nodeSliceView; - - conduit::Node mmOpts; - mmOpts["topology"] = n_topo.name(); - bputils::MergeMeshes mm; - mm.execute(inputs, mmOpts, n_merged); - } - -std::cout << "--- merged ---\n"; -printNode(n_merged); - conduit::relay::io::blueprint::save_mesh(n_merged, + // Merge clean and MIR output. + std::vector inputs(2); + inputs[0].m_input = &n_cleanOutput; + + inputs[1].m_input = &n_mirOutput; + inputs[1].m_nodeMapView = nodeMap.view(); + inputs[1].m_nodeSliceView = nodeSlice.view(); + + conduit::Node mmOpts, n_merged; + mmOpts["topology"] = n_topo.name(); + bputils::MergeMeshes mm; + mm.execute(inputs, mmOpts, n_merged); + +#if defined(AXOM_EQUIZ_DEBUG) + std::cout << "--- clean ---\n"; + printNode(n_cleanOutput); + std::cout << "--- MIR ---\n"; + printNode(n_mirOutput); + std::cout << "--- merged ---\n"; + + // Save merged output. + printNode(n_merged); + conduit::relay::io::blueprint::save_mesh(n_merged, "debug_equiz_merged", - "hdf5"); + "hdf5"); #endif - -#endif - // Merge the 2 parts together. + // Move the merged output into the output variables. + n_newCoordset.move(n_merged[n_coordset.path()]); + n_newTopo.move(n_merged[n_topo.path()]); + n_newFields.move(n_merged[n_fields.path()]); + n_newMatset.move(n_merged[n_matset.path()]); } else if(cleanZones.size() == 0 && mixedZones.size() > 0) { -std::cout << "All zones are mixed..." << std::endl; - // Only mixed zones. + // Only mixed zones. processMixedZones(n_topo, n_coordset, n_fields, n_matset, n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); } else if(cleanZones.size() > 0 && mixedZones.size() == 0) { -std::cout << "All zones are clean..." << std::endl; // There were no mixed zones. We can copy the input to the output. + { + AXOM_ANNOTATE_SCOPE("copy"); + bputils::copy(n_newCoordset, n_coordset); + bputils::copy(n_newTopo, n_topo); + bputils::copy(n_newFields, n_fields); + bputils::copy(n_newMatset, n_matset); + } - // Add an originalZones array. + // Add an originalElements array. + addOriginal(n_newFields["originalElements"], n_topo.name(), "element", m_topologyView.numberOfZones()); } #else -std::cout << "Normal handling" << std::endl; - // Handle all zones. + // Handle all zones via MIR. processMixedZones(n_topo, n_coordset, n_fields, n_matset, n_options_copy, n_newTopo, n_newCoordset, n_newFields, n_newMatset); #endif } +#if defined(AXOM_EQUIZ_SPLIT_PROCESSING) + /** + * \brief Adds original ids field to supplied fields node. + * + * \param n_field The new field node. + * \param topoName The topology name for the field. + * \param association The field association. + * \param nvalues The number of nodes in the field. + * + * \note This field is added to the mesh before feeding it through MIR so we will have an idea + * of which nodes are original nodes in the output. Blended nodes may not have good values + * but there is a mask field that can identify those nodes. + */ + void addOriginal(conduit::Node &n_field, const std::string &topoName, const std::string &association, axom::IndexType nvalues) const + { + AXOM_ANNOTATE_BEGIN("addOriginal"); + namespace bputils = axom::mir::utilities::blueprint; + bputils::ConduitAllocateThroughAxom c2a; + + // Add a new field for the original ids. + n_field["topology"] = topoName; + n_field["association"] = association; + n_field["values"].set_allocator(c2a.getConduitAllocatorID()); + n_field["values"].set(conduit::DataType(bputils::cpp2conduit::id, nvalues)); + auto view = bputils::make_array_view(n_field["values"]); + axom::for_all(nvalues, AXOM_LAMBDA(auto index) + { + view[index] = static_cast(index); + }); + } + + /** + * \brief Take the mesh in n_root and extract the zones identified by the + * \a cleanZones array and store the results into the \a n_cleanOutput + * node. + * + * \param n_root The input mesh from which zones are being extracted. + * \param topoName The name of the topology. + * \param cleanZones An array of clean zone ids. + * \param[out] n_cleanOutput The node that will contain the clean mesh output. + * + * \return The number of nodes in the clean mesh output. + */ + void makeCleanOutput(const conduit::Node &n_root, const std::string &topoName, + const axom::ArrayView &cleanZones, conduit::Node &n_cleanOutput) const + { + AXOM_ANNOTATE_SCOPE("makeCleanOutput"); + namespace bputils = axom::mir::utilities::blueprint; + + // Make the clean mesh. Set compact=0 so it does not change the number of nodes. + bputils::ExtractZonesAndMatset ez(m_topologyView, m_coordsetView, m_matsetView); + conduit::Node n_ezopts; + n_ezopts["topology"] = topoName; + n_ezopts["compact"] = 0; + ez.execute(cleanZones, n_root, n_ezopts, n_cleanOutput); +#if defined(AXOM_EQUIZ_DEBUG) + AXOM_ANNOTATE_BEGIN("saveClean"); + conduit::relay::io::blueprint::save_mesh(n_cleanOutput, "clean", "hdf5"); + AXOM_ANNOTATE_END("saveClean"); +#endif + } + + /** + * \brief Create node map and node slice arrays for the MIR output that help + * merge it back with the clean output. + * + * \param n_newFields The fields from the MIR output. + * \param[out] nodeMap An array used to map node ids from the MIR output to their node ids in the merged mesh. + * \param[out] nodeSlice An array that identifies new blended node ids in the MIR output so they can be appended into coordsets and fields during merge. + */ + void createNodeMapAndSlice(conduit::Node &n_newFields, axom::Array &nodeMap, axom::Array &nodeSlice) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("createNodeMapAndSlice"); + SLIC_ASSERT(n_newFields.has_child(originalNodesFieldName())); + SLIC_ASSERT(n_newFields.has_child(newNodesFieldName())); + + const axom::IndexType numCleanNodes = m_coordsetView.numberOfNodes(); + + // These are the original node ids. + const conduit::Node &n_output_orig_nodes = n_newFields[originalNodesFieldName() + "/values"]; + auto numOutputNodes = n_output_orig_nodes.dtype().number_of_elements(); + auto outputOrigNodesView = bputils::make_array_view(n_output_orig_nodes); + + // __equiz_new_nodes is the int mask field that identifies new nodes created from blending. + const conduit::Node &n_new_nodes_values = n_newFields[newNodesFieldName() + "/values"]; + const auto maskView = bputils::make_array_view(n_new_nodes_values); + + // Count new nodes created from blending. + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array maskOffset(numOutputNodes, numOutputNodes, allocatorID); + auto maskOffsetsView = maskOffset.view(); + RAJA::ReduceSum mask_reduce(0); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + mask_reduce += maskView[index]; + }); + const auto numNewNodes = mask_reduce.get(); + + // Make offsets. + axom::exclusive_scan(maskView, maskOffsetsView); + + // Make a list of indices that we need to slice out of the node arrays. + nodeSlice = axom::Array(numNewNodes, numNewNodes, allocatorID); + auto nodeSliceView = nodeSlice.view(); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + if(maskView[index] > 0) + { + nodeSliceView[maskOffsetsView[index]] = index; + } + }); + + // Make a node map for mapping mixed connectivity into combined node numbering. + nodeMap = axom::Array(numOutputNodes, numOutputNodes, allocatorID); + auto nodeMapView = nodeMap.view(); + axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) + { + if(maskView[index] == 0) + { + nodeMapView[index] = static_cast(outputOrigNodesView[index]); + } + else + { + nodeMapView[index] = numCleanNodes + maskOffsetsView[index]; + } + }); + + // Remove fields that are no longer needed. + n_newFields.remove(originalNodesFieldName()); + n_newFields.remove(newNodesFieldName()); + } +#endif + + /** + * \brief Perform material interface reconstruction on a single domain. + * + * \param[in] n_topo The Conduit node containing the topology that will be used for MIR. + * \param[in] n_coordset The Conduit node containing the coordset. + * \param[in] n_fields The Conduit node containing the fields. + * \param[in] n_matset The Conduit node containing the matset. + * \param[in] n_options The Conduit node containing the options that help govern MIR execution. + * + * \param[out] n_newTopo A node that will contain the new clipped topology. + * \param[out] n_newCoordset A node that will contain the new coordset for the clipped topology. + * \param[out] n_newFields A node that will contain the new fields for the clipped topology. + * \param[out] n_newMatset A Conduit node that will contain the new matset. + * + */ void processMixedZones(const conduit::Node &n_topo, const conduit::Node &n_coordset, const conduit::Node &n_fields, @@ -645,6 +662,8 @@ std::cout << "Normal handling" << std::endl; conduit::Node &n_newFields, conduit::Node &n_newMatset) const { + AXOM_ANNOTATE_SCOPE("processMixedZones"); + // Make some nodes that will contain the inputs to subsequent iterations. // Store them under a single node so the nodes will have names. conduit::Node n_Input; @@ -772,7 +791,7 @@ std::cout << "Normal handling" << std::endl; { n_newFields.remove(nodalMatName); } -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) const std::string zonalMatName(zonalFieldName(mat.number)); if(n_newFields.has_child(zonalMatName)) { @@ -783,7 +802,7 @@ std::cout << "Normal handling" << std::endl; n_newFields.remove(zonalMaterialIDName()); } -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) //-------------------------------------------------------------------------- // // Save the MIR output. @@ -855,6 +874,24 @@ std::cout << "Normal handling" << std::endl; */ std::string zonalMaterialIDName() const { return "__equiz_zonalMaterialID"; } + /** + * \brief Return the name of the original nodes field. + * \return The name of the original nodes field. + */ + std::string originalNodesFieldName() const + { + return "__equiz_original_node"; + } + + /** + * \brief Return the name of the new nodes field that identifies blended nodes in the MIR output. + * \return The name of the new nodes field. + */ + std::string newNodesFieldName() const + { + return "__equiz_new_nodes"; + } + /** * \brief Makes node-cenetered volume fractions for the materials in the matset * and attaches them as fields. @@ -888,8 +925,6 @@ std::cout << "Normal handling" << std::endl; // Make nodal VFs for each mixed material. const auto nzones = m_topologyView.numberOfZones(); const auto nnodes = m_coordsetView.numberOfNodes(); -std::cout << "makeNodeCenteredVFs: nzones=" << nzones << std::endl; -std::cout << "makeNodeCenteredVFs: nnodes=" << nnodes << std::endl; { AXOM_ANNOTATE_SCOPE("zonal"); for(const auto &mat : mixedMats) @@ -936,7 +971,7 @@ std::cout << "makeNodeCenteredVFs: nnodes=" << nnodes << std::endl; bputils::RecenterField z2n; z2n.execute(n_zonalField, relation, n_nodalField); -#if !defined(AXOM_DEBUG_EQUIZ) +#if !defined(AXOM_EQUIZ_DEBUG) // Remove the zonal field that we don't normally need (unless we're debugging). n_fields.remove(zonalName); #endif @@ -1081,7 +1116,7 @@ std::cout << "makeNodeCenteredVFs: nnodes=" << nnodes << std::endl; const std::string colorField("__equiz__colors"); -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) //-------------------------------------------------------------------------- // // Save the iteration inputs. @@ -1234,7 +1269,7 @@ std::cout << "makeNodeCenteredVFs: nnodes=" << nnodes << std::endl; }); } -#if defined(AXOM_DEBUG_EQUIZ) +#if defined(AXOM_EQUIZ_DEBUG) //-------------------------------------------------------------------------- // // Save the clip results. diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index ce857b0aa4..ae86e3ff9b 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -276,7 +276,6 @@ class MergeMeshes for(size_t i = 0; i < inputs.size(); i++) { const auto nnodes = countNodes(inputs, i); -std::cout << "countNodes " << i << ": nnodes=" << nnodes << std::endl; nodeTotal += nnodes; } return nodeTotal; @@ -301,7 +300,6 @@ std::cout << "countNodes " << i << ": nnodes=" << nnodes << std::endl; const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); const auto nzones = n_size.dtype().number_of_elements(); totalZones += nzones; -std::cout << "countZones " << i << ": connLength=" << connLength << ", totalZones=" << totalZones << std::endl; } } @@ -398,7 +396,7 @@ std::cout << "countZones " << i << ": connLength=" << connLength << ", totalZone using ConnType = typename decltype(srcConnView)::value_type; conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); auto connView = bputils::make_array_view(n_newConn); -std::cout << "-------------- input " << i << " ----------------" << std::endl; + if(inputs[i].m_nodeMapView.size() > 0) { // Copy all zones from the input but map the nodes to new values. @@ -409,23 +407,14 @@ std::cout << "-------------- input " << i << " ----------------" << std::endl; { const auto nodeId = srcConnView[index]; const auto newNodeId = nodeMapView[nodeId]; - -std::cout << index << ":A nodeId=" << nodeId << ", newNodeId=" << newNodeId << " -> " << (connOffset + index) << std::endl; - connView[connOffset + index] = newNodeId; }); -std::cout << "coordOffset=" << coordOffset << std::endl; } else { // Copy all zones from the input. Map the nodes to the new values. axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) { - -std::cout << index << ":B srcConnView=" << srcConnView[index] - << ", newNodeId=" << (coordOffset + srcConnView[index]) - << " -> " << (connOffset + index) << std::endl; - connView[connOffset + index] = coordOffset + srcConnView[index]; }); } From 954df19072c8ee9e3220e34f44d4d103f55a681a Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 19 Sep 2024 15:29:11 -0700 Subject: [PATCH 221/290] Fixed bug in NodeArrayView. --- src/axom/mir/views/NodeArrayView.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 346af37f45..e38308176a 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -251,7 +251,7 @@ std::enable_if_t Node_to_ArrayView_single_uint8(conduit::Node &n, FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint8_ptr(), size); + axom::ArrayView view(n.as_uint8_ptr(), size); func(view); } @@ -288,7 +288,7 @@ std::enable_if_t Node_to_ArrayView_single_uint16(conduit::Node &n FuncType &&func) { const auto size = n.dtype().number_of_elements(); - axom::ArrayView view(n.as_uint16_ptr(), size); + axom::ArrayView view(n.as_uint16_ptr(), size); func(view); } From 3b91266684a3aca58ea32e0230e625d51ebb8be6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 19 Sep 2024 15:46:41 -0700 Subject: [PATCH 222/290] Added a test --- src/axom/mir/tests/mir_views.cpp | 85 ++++++++++++++++++++++++++++++ src/axom/mir/views/view_traits.hpp | 46 ++++++++++++++++ 2 files changed, 131 insertions(+) diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index f8133f3437..4b4bcea6f1 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -52,6 +52,91 @@ TEST(mir_views, shape2conduitName) EXPECT_EQ(axom::mir::views::HexShape::name(), "hex"); } +//------------------------------------------------------------------------------ +template +struct test_node_to_arrayview +{ + static int constexpr sum(int n) + { + int s = 0; + for(int i = 0; i < n; i++) + s += i; + return s; + } + + static void test() + { + using reduce_policy = typename axom::execution_space::reduce_policy; + + std::vector dtypes{conduit::DataType::INT8_ID, + conduit::DataType::INT16_ID, + conduit::DataType::INT32_ID, + conduit::DataType::INT64_ID, + conduit::DataType::UINT8_ID, + conduit::DataType::UINT16_ID, + conduit::DataType::UINT32_ID, + conduit::DataType::UINT64_ID, + conduit::DataType::FLOAT32_ID, + conduit::DataType::FLOAT64_ID}; + constexpr int n = 16; + axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a; + for(int dtype : dtypes) + { + // Make a node and fill it with data. + conduit::Node n_data; + n_data.set_allocator(c2a.getConduitAllocatorID()); + n_data.set(conduit::DataType(dtype, n)); + + int sumValues = 0; + axom::mir::views::Node_to_ArrayView(n_data, [&](auto dataView) + { + std::cout << axom::mir::views::array_view_traits::name() << std::endl; + using value_type = typename decltype(dataView)::value_type; + + // Make sure we can store values in dataView + axom::for_all(n, AXOM_LAMBDA(auto index) + { + dataView[index] = static_cast(index); + }); + + // Read the values and sum them. + RAJA::ReduceSum sumValues_reduce(0); + axom::for_all(n, AXOM_LAMBDA(auto index) + { + sumValues_reduce += dataView[index]; + }); + sumValues = static_cast(sumValues_reduce.get()); + }); + + EXPECT_EQ(sumValues, sum(n)); + } + } +}; + +TEST(mir_views, node_to_arrayview_seq) +{ + test_node_to_arrayview::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_views, node_to_arrayview_omp) +{ + test_node_to_arrayview::test(); +} +#endif +#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +TEST(mir_views, node_to_arrayview_cuda) +{ + test_node_to_arrayview::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_views, node_to_arrayview_hip) +{ + test_node_to_arrayview::test(); +} +#endif + +//------------------------------------------------------------------------------ TEST(mir_views, explicit_coordsetview) { axom::Array x {{0., 1., 2., 3., 4., 5.}}; diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index a07fff4e33..b3e63f0a3d 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -71,6 +71,52 @@ struct view_traits>> static constexpr int selected_shapes() { return shapes_for_dimension(1); } }; +/** + * \brief Base template for some ArrayView traits. + */ +template +struct array_view_traits +{ +}; + +/** + * \brief This macro defines some template specializations that help us access + * ArrayView<> names. This can be helpful when the ArrayView comes into + * a lambda as an auto argument. + */ +#define AXOM_MAKE_TRAIT(TYPE)\ +template <>\ +struct array_view_traits>\ +{\ + using value_type = TYPE;\ + static constexpr const char * name()\ + {\ + return "axom::ArrayView<" #TYPE ">";\ + }\ +};\ +template <>\ +struct array_view_traits>\ +{\ + using value_type = const TYPE;\ + static constexpr const char * name()\ + {\ + return "axom::ArrayView";\ + }\ +}; + +AXOM_MAKE_TRAIT(signed char) +AXOM_MAKE_TRAIT(char) +AXOM_MAKE_TRAIT(short) +AXOM_MAKE_TRAIT(long) +AXOM_MAKE_TRAIT(int) +AXOM_MAKE_TRAIT(unsigned char) +AXOM_MAKE_TRAIT(unsigned short) +AXOM_MAKE_TRAIT(unsigned long) +AXOM_MAKE_TRAIT(unsigned int) +AXOM_MAKE_TRAIT(float) +AXOM_MAKE_TRAIT(double) +#undef AXOM_MAKE_TRAIT + } // end namespace views } // end namespace mir } // end namespace axom From fb7ff8b2272734f2b0adbfd627f49c93be71b1fd Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 19 Sep 2024 15:52:06 -0700 Subject: [PATCH 223/290] Initial stab at merging fields. --- src/axom/mir/MergeMeshes.hpp | 204 ++++++++++++++++++++++++++++++++++- 1 file changed, 203 insertions(+), 1 deletion(-) diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index ae86e3ff9b..683e278644 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -73,6 +73,17 @@ class MergeMeshes } } private: + /** + * \brief This struct contains information used when merging fields. + */ + struct FieldInformation + { + std::string topology; + std::string association; + int dtype; + std::vector components; + }; + /** * \brief Check that the mesh inputs are valid and meet constraints. There must * be 1 coordset/topology/matset. The coordset must be explicit and the @@ -163,7 +174,7 @@ class MergeMeshes { mergeCoordset(inputs, output); mergeTopology(inputs, output); - //mergeFields(inputs, output); + mergeFields(inputs, output); //mergeMatset(inputs, output); } @@ -281,6 +292,16 @@ class MergeMeshes return nodeTotal; } + axom::IndexType countZones(const std::vector &inputs, size_t index) const + { + const conduit::Node &n_topologies = inputs[index].m_input->fetch_existing("topologies"); + const conduit::Node &n_topo = n_topologies[0]; + + const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); + axom::IndexType nzones = n_size.dtype().number_of_elements(); + return nzones; + } + void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const { totalConnLength = 0; @@ -487,6 +508,187 @@ class MergeMeshes axom::exclusive_scan(sizesView, offsetsView); }); } + + void mergeFields(const std::vector &inputs, conduit::Node &output) const + { + namespace bputils = axom::mir::utilities::blueprint; + AXOM_ANNOTATE_SCOPE("mergeFields"); + axom::IndexType totalNodes = countNodes(inputs); + axom::IndexType totalConnLen = 0, totalZones = 0; + countZones(inputs, totalConnLen, totalZones); + const axom::IndexType n = static_cast(inputs.size()); + + // Determine whether any inputs have fields. + bool hasFields = false; + for(axom::IndexType i = 0; i < n; i++) + { + hasFields |= inputs[i].m_input->has_child("fields"); + } + + if(hasFields) + { + // Make field information in case some inputs do not have the field. + std::map fieldInfo; + for(axom::IndexType i = 0; i < n; i++) + { + if(inputs[i].m_input->has_child("fields")) + { + const conduit::Node &n_fields = inputs[i].m_input->fetch_existing("fields"); + for(conduit::index_t c = 0; c < n_fields.number_of_children(); c++) + { + const conduit::Node &n_field = n_fields[c]; + const conduit::Node &n_values = n_field.fetch_existing("values"); + FieldInformation fi; + fi.topology = n_field.fetch_existing("topology").as_string(); + fi.association = n_field.fetch_existing("association").as_string(); + if(n_values.number_of_children() > 0) + { + for(conduit::index_t comp = 0; comp < n_values.number_of_children(); comp++) + { + fi.components.push_back(n_values[comp].name()); + fi.dtype = n_values[comp].dtype().id(); + } + } + else + { + fi.dtype = n_values.dtype().id(); + } + fieldInfo[n_field.name()] = fi; + } + } + } + + // Make new fields + bputils::ConduitAllocateThroughAxom c2a; + conduit::Node &n_newFields = output["fields"]; + for(auto it = fieldInfo.begin(); it != fieldInfo.end(); it++) + { + conduit::Node &n_newField = n_newFields[it->first]; + n_newField["association"] = it->second.association; + n_newField["topology"] = it->second.topology; + conduit::Node &n_values = n_newField["values"]; + if(it->second.components.empty()) + { + // Scalar + conduit::Node &n_values = n_newField["values"]; + n_values.set_allocator(c2a.getConduitAllocatorID()); + const std::string srcPath("fields/" + it->first + "/values"); + if(it->second.association == "element") + { + n_values.set(conduit::DataType(it->second.dtype, totalZones)); + copyZonal(inputs, n_values, srcPath); + } + else if(it->second.association == "vertex") + { + n_values.set(conduit::DataType(it->second.dtype, totalNodes)); + copyNodal(inputs, n_values, srcPath); + } + } + else + { + // Vector + for(size_t ci = 0; ci < it->second.components.size(); ci++) + { + conduit::Node &n_comp = n_values[it->second.components[ci]]; + n_comp.set_allocator(c2a.getConduitAllocatorID()); + const std::string srcPath("fields/" + it->first + "/values/" + it->second.components[ci]); + if(it->second.association == "element") + { + n_comp.set(conduit::DataType(it->second.dtype, totalZones)); + copyZonal(inputs, n_comp, srcPath); + } + else if(it->second.association == "vertex") + { + n_comp.set(conduit::DataType(it->second.dtype, totalNodes)); + copyNodal(inputs, n_comp, srcPath); + } + } + } + } + } + } + + void copyZonal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const + { + axom::IndexType offset = 0; + for(size_t i = 0; i < inputs.size(); i++) + { + const auto nzones = countZones(inputs, i); + + if(inputs[i].m_input->has_path(srcPath)) + { + const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); + axom::mir::views::Node_to_ArrayView(n_src_values, [&](auto srcView) + { + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) + { + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + destView[offset + index] = srcView[index]; + }); + }); + }); + } + else + { + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) + { + axom::for_all(nzones, AXOM_LAMBDA(auto index) + { + destView[offset + index] = 0; + }); + }); + } + offset += nzones; + } + } + + void copyNodal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const + { + axom::IndexType offset = 0; + for(size_t i = 0; i < inputs.size(); i++) + { + const auto nnodes = countNodes(inputs, i); + + if(inputs[i].m_input->has_path(srcPath)) + { + const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); + axom::mir::views::Node_to_ArrayView(n_src_values, [&](auto srcView) + { + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) + { + if(inputs[i].m_nodeSliceView.empty()) + { + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + destView[offset + index] = srcView[index]; + }); + } + else + { + auto nodeSliceView(inputs[i].m_nodeSliceView); + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + const auto nodeId = nodeSliceView[index]; + destView[offset + index] = srcView[nodeId]; + }); + } + }); + }); + } + else + { + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) + { + axom::for_all(nnodes, AXOM_LAMBDA(auto index) + { + destView[offset + index] = 0; + }); + }); + } + offset += nnodes; + } + } }; } // end namespace blueprint From 0cae1d2edeb70717e619edb6bda9610002479ff1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 19 Sep 2024 18:46:03 -0700 Subject: [PATCH 224/290] Added material merging in MergeMeshes. --- src/axom/mir/EquiZAlgorithm.hpp | 2 +- src/axom/mir/MergeMeshes.hpp | 290 ++++++++++++++++++++++- src/axom/mir/views/MaterialView.hpp | 49 +--- src/axom/mir/views/dispatch_material.hpp | 12 +- 4 files changed, 290 insertions(+), 63 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index d25df3e3cf..0586ad67ef 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -32,7 +32,7 @@ #define AXOM_EQUIZ_SPLIT_PROCESSING // Uncomment to save inputs and outputs. -#define AXOM_EQUIZ_DEBUG +// #define AXOM_EQUIZ_DEBUG // This enables a tweak to the algorithm that tries to skip the first iteration // by incorporating the first material's ids into the zonalMaterialID field. It diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index 683e278644..177985ca59 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -10,6 +10,8 @@ #include "axom/mir.hpp" #include "axom/slic.hpp" +#include "axom/mir/views/dispatch_material.hpp" + #include #include @@ -175,14 +177,14 @@ class MergeMeshes mergeCoordset(inputs, output); mergeTopology(inputs, output); mergeFields(inputs, output); - //mergeMatset(inputs, output); + mergeMatset(inputs, output); } /** * \brief Merge multiple coordsets into a single coordset. No node merging takes place. * * \param inputs A vector of inputs to be merged. - * \param[out] output The node that will contain the merged coordset. + * \param[out] output The node that will contain the output mesh. */ void mergeCoordset(const std::vector &inputs, conduit::Node &output) const { @@ -324,6 +326,12 @@ class MergeMeshes } } + /** + * \brief Merge multiple topologies into a single topology. + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the output mesh. + */ void mergeTopology(const std::vector &inputs, conduit::Node &output) const { namespace bputils = axom::mir::utilities::blueprint; @@ -509,6 +517,13 @@ class MergeMeshes }); } + /** + * \brief Merge fields that exist on the various mesh inputs. Zero-fill values + * where a field does not exist in an input. + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the output mesh. + */ void mergeFields(const std::vector &inputs, conduit::Node &output) const { namespace bputils = axom::mir::utilities::blueprint; @@ -608,6 +623,13 @@ class MergeMeshes } } + /** + * \brief Copy zonal field data into a Conduit node. + * + * \param inputs A vector of inputs to be merged. + * \param[out] n_values The node will be populated with data values from the field inputs. + * \param srcPath The path to the source data in each input node. + */ void copyZonal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const { axom::IndexType offset = 0; @@ -618,15 +640,12 @@ class MergeMeshes if(inputs[i].m_input->has_path(srcPath)) { const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_src_values, [&](auto srcView) + axom::mir::views::Node_to_ArrayView(n_src_values, n_values, [&](auto srcView, auto destView) { - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) - { axom::for_all(nzones, AXOM_LAMBDA(auto index) { destView[offset + index] = srcView[index]; }); - }); }); } else @@ -643,6 +662,13 @@ class MergeMeshes } } + /** + * \brief Copy nodal field data into a Conduit node. + * + * \param inputs A vector of inputs to be merged. + * \param[out] n_values The node will be populated with data values from the field inputs. + * \param srcPath The path to the source data in each input node. + */ void copyNodal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const { axom::IndexType offset = 0; @@ -653,10 +679,8 @@ class MergeMeshes if(inputs[i].m_input->has_path(srcPath)) { const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_src_values, [&](auto srcView) + axom::mir::views::Node_to_ArrayView(n_src_values, n_values, [&](auto srcView, auto destView) { - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) - { if(inputs[i].m_nodeSliceView.empty()) { axom::for_all(nnodes, AXOM_LAMBDA(auto index) @@ -673,7 +697,6 @@ class MergeMeshes destView[offset + index] = srcView[nodeId]; }); } - }); }); } else @@ -689,6 +712,253 @@ class MergeMeshes offset += nnodes; } } + + /** + * \brief Merge matsets that exist on the various mesh inputs. + * + * \param inputs A vector of inputs to be merged. + * \param[out] output The node that will contain the output mesh. + */ + void mergeMatset(const std::vector &inputs, conduit::Node &output) const + { + AXOM_ANNOTATE_SCOPE("mergeMatset"); + namespace bputils = axom::mir::utilities::blueprint; + using reduce_policy = typename axom::execution_space::reduce_policy; + bputils::ConduitAllocateThroughAxom c2a; + + // Make a pass through the inputs and make a list of the material names. + bool hasMatsets = false, defaultMaterial = false; + int nmats = 0; + std::map allMats; + std::string matsetName, topoName; + for(size_t i = 0; i < inputs.size(); i++) + { + if(inputs[i].m_input->has_path("matsets")) + { + conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + matsetName = n_matset.name(); + topoName = n_matset.fetch_existing("topology").as_string(); + auto matInfo = axom::mir::views::materials(n_matset); + for(const auto &info : matInfo) + { + if(allMats.find(info.name) == allMats.end()) + { + allMats[info.name] = nmats++; + } + } + hasMatsets = true; + } + else + { + defaultMaterial = true; + } + } + + if(hasMatsets) + { + // One or more inputs did not have a matset. + if(defaultMaterial) + allMats["default"] = nmats++; + + // Make a pass through the matsets to determine the overall storage. + axom::IndexType totalZones = 0, totalMatCount = 0; + int itype, ftype; + { + AXOM_ANNOTATE_SCOPE("sizes"); + for(size_t i = 0; i < inputs.size(); i++) + { + const auto nzones = countZones(inputs, i); + totalZones += nzones; + + if(inputs[i].m_input->has_path("matsets")) + { + conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + axom::IndexType matCount = 0; + axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) + { + // Figure out the types to use for storing the data. + using IType = typename decltype(matsetView)::IndexType; + using FType = typename decltype(matsetView)::FloatType; + itype = bputils::cpp2conduit::id; + ftype = bputils::cpp2conduit::id; + + RAJA::ReduceSum matCount_reduce(0); + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + const auto nmats = matsetView.numberOfMaterials(zoneIndex); + matCount_reduce += nmats; + }); + matCount = matCount_reduce.get(); + }); + totalMatCount += matCount; + } + else + { + totalMatCount += nzones; + } + } + } + + // Allocate + AXOM_ANNOTATE_BEGIN("allocate"); + conduit::Node &n_newMatset = output["matsets/" + matsetName]; + n_newMatset["topology"] = topoName; + conduit::Node &n_volume_fractions = n_newMatset["volume_fractions"]; + n_volume_fractions.set_allocator(c2a.getConduitAllocatorID()); + n_volume_fractions.set(conduit::DataType(ftype, totalMatCount)); + + conduit::Node &n_material_ids = n_newMatset["material_ids"]; + n_material_ids.set_allocator(c2a.getConduitAllocatorID()); + n_material_ids.set(conduit::DataType(itype, totalMatCount)); + + conduit::Node &n_sizes = n_newMatset["sizes"]; + n_sizes.set_allocator(c2a.getConduitAllocatorID()); + n_sizes.set(conduit::DataType(itype, totalZones)); + + conduit::Node &n_offsets = n_newMatset["offsets"]; + n_offsets.set_allocator(c2a.getConduitAllocatorID()); + n_offsets.set(conduit::DataType(itype, totalZones)); + + conduit::Node &n_indices = n_newMatset["indices"]; + n_indices.set_allocator(c2a.getConduitAllocatorID()); + n_indices.set(conduit::DataType(itype, totalMatCount)); + AXOM_ANNOTATE_END("allocate"); + + { + AXOM_ANNOTATE_SCOPE("populate"); + + // Make material_map. + conduit::Node &n_material_map = n_newMatset["material_map"]; + for(auto it = allMats.begin(); it != allMats.end(); it++) + n_material_map[it->first] = it->second; + + // Populate + axom::mir::views::IndexNode_to_ArrayView_same(n_material_ids, n_sizes, n_offsets, n_indices, + [&](auto materialIdsView, auto sizesView, auto offsetsView, auto indicesView) + { + axom::mir::views::FloatNode_to_ArrayView(n_volume_fractions, + [&](auto volumeFractionsView) + { + // Fill in sizes array. + axom::IndexType zOffset = 0; + for(size_t i = 0; i < inputs.size(); i++) + { + const auto nzones = countZones(inputs, i); + + if(inputs[i].m_input->has_child("matsets")) + { + conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + + axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) + { + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + sizesView[zOffset + zoneIndex] = matsetView.numberOfMaterials(zoneIndex); + }); + }); + } + else + { + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + sizesView[zOffset + zoneIndex] = 1; + }); + } + zOffset += nzones; + } + // Make offsets. + axom::exclusive_scan(sizesView, offsetsView); + + // Make indices. + axom::for_all(totalMatCount, AXOM_LAMBDA(auto index) + { + indicesView[index] = index; + }); + + // Fill in material info. + zOffset = 0; + for(size_t i = 0; i < inputs.size(); i++) + { + const auto nzones = countZones(inputs, i); + + if(inputs[i].m_input->has_child("matsets")) + { + conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + + axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) + { + using IDList = typename decltype(matsetView)::IDList; + using VFList = typename decltype(matsetView)::VFList; + using MatID = typename decltype(matsetView)::IndexType; + + // Make some maps for renumbering material numbers. + const auto localMaterialMap = axom::mir::views::materials(n_matset); + std::map localToAll; + for(const auto &info : localMaterialMap) + { + MatID matno = allMats[info.name]; + localToAll[info.number] = matno; + } + std::vector localVec, allVec; + for(auto it = localToAll.begin(); it != localToAll.end(); it++) + { + localVec.push_back(it->first); + allVec.push_back(it->second); + } + // Put maps on device. + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array local(localVec.size(), localVec.size(), allocatorID); + axom::Array all(allVec.size(), allVec.size(), allocatorID); + axom::copy(local.data(), localVec.data(), sizeof(MatID) * local.size()); + axom::copy(all.data(), allVec.data(), sizeof(MatID) * all.size()); + const auto localView = local.view(); + const auto allView = all.view(); + + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + // Get this zone's materials. + IDList ids; + VFList vfs; + matsetView.zoneMaterials(zoneIndex, ids, vfs); + + // Store the materials in the new material. + const auto zoneStart = offsetsView[zOffset + zoneIndex]; + for(axom::IndexType mi = 0; mi < ids.size(); mi++) + { + const auto destIndex = zoneStart + mi; + volumeFractionsView[destIndex] = vfs[mi]; + + // Get the index of the material number in the local map. + const auto mapIndex = axom::mir::utilities::bsearch(ids[mi], localView); + assert(mapIndex != -1); + // We'll store the all materials number. + const auto allMatno = allView[mapIndex]; + materialIdsView[destIndex] = allMatno; + } + }); + }); + } + else + { + const int dmat = allMats["default"]; + axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + { + const auto zoneStart = offsetsView[zOffset + zoneIndex]; + volumeFractionsView[zoneStart] = 1; + materialIdsView[zoneStart] = dmat; + }); + } + zOffset += nzones; + } + }); + }); + } + } // if hasMatsets + } }; } // end namespace blueprint diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index c9641c91b1..7ee74c45d6 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -101,14 +101,14 @@ class UnibufferMaterialView AXOM_HOST_DEVICE inline axom::IndexType numberOfMaterials(ZoneIndex zi) const { - assert(zi < numberOfZones()); + assert(zi < static_cast(numberOfZones())); return m_sizes[zi]; } AXOM_HOST_DEVICE void zoneMaterials(ZoneIndex zi, IDList &ids, VFList &vfs) const { - assert(zi < numberOfZones()); + assert(zi < static_cast(numberOfZones())); ids.clear(); vfs.clear(); @@ -127,7 +127,7 @@ class UnibufferMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat) const { - assert(zi < numberOfZones()); + assert(zi < static_cast(numberOfZones())); const auto sz = numberOfMaterials(zi); const auto offset = m_offsets[zi]; for(axom::IndexType i = 0; i < sz; i++) @@ -142,7 +142,7 @@ class UnibufferMaterialView AXOM_HOST_DEVICE bool zoneContainsMaterial(ZoneIndex zi, MaterialIndex mat, FloatType &vf) const { - assert(zi < numberOfZones()); + assert(zi < static_cast(numberOfZones())); const auto sz = numberOfMaterials(zi); const auto offset = m_offsets[zi]; for(axom::IndexType i = 0; i < sz; i++) @@ -183,9 +183,6 @@ class UnibufferMaterialView a: 0 b: 1 */ - -// NOTE: I'm not sure I 100% get this one. - template class MultiBufferMaterialView { @@ -433,16 +430,6 @@ class MaterialDominantMaterialView const auto sz = m_element_ids[mi].size(); for(axom::IndexType i = 0; i < sz; i++) m_nzones = axom::utilities::max(m_nzones, m_element_ids[mi][i]); -#if 0 - // host-only - // Eh, do this. - RAJA::ReduceMax rm(0); - axom::forall(0, sz, AXOM_LAMBDA(int i) - { - rm.max(m_element_ids[mi][i]); - } - m_nzones = axom::utilties::max(m_nzones, rm.get()); -#endif } } return m_nzones; @@ -455,7 +442,6 @@ class MaterialDominantMaterialView for(axom::IndexType mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); -#if 1 for(axom::IndexType i = 0; i < sz; i++) { if(m_element_ids[mi][i] == zi) @@ -464,15 +450,6 @@ class MaterialDominantMaterialView break; } } -#else - // host-only - RAJA::ReduceMax rm(0); - axom::forall(0, sz, AXOM_LAMBDA(int i) - { - rm.max((m_element_ids[mi][i] == zi) ? 1 : 0); - } - m_nzones += rm.get(); -#endif } return nmats; } @@ -486,7 +463,6 @@ class MaterialDominantMaterialView for(axom::IndexType mi = 0; mi < m_size; mi++) { const auto sz = m_element_ids[mi].size(); -#if 1 for(axom::IndexType i = 0; i < sz; i++) { if(m_element_ids[mi][i] == zi) @@ -496,19 +472,6 @@ class MaterialDominantMaterialView break; } } -#else - RAJA::ReduceMax rm(-1); - axom::forall(0, sz, AXOM_LAMBDA(int i) - { - rm.max((m_element_ids[mi][i] == zi) ? i : -1); - } - const auto index = rm.get(); - if(index != -1) - { - ids.push_back(mi); - vfs.push_back(m_volume_fractions[mi][index]); - } -#endif } } @@ -518,7 +481,6 @@ class MaterialDominantMaterialView assert(mat < m_element_ids.size()); bool found = false; -#if 1 const auto element_ids = m_element_ids[mat]; for(axom::IndexType i = 0; i < element_ids.size(); i++) { @@ -528,7 +490,6 @@ class MaterialDominantMaterialView break; } } -#endif return found; } @@ -538,7 +499,6 @@ class MaterialDominantMaterialView assert(mat < m_element_ids.size()); bool found = false; -#if 1 const auto element_ids = m_element_ids[mat]; for(axom::IndexType i = 0; i < element_ids.size(); i++) { @@ -549,7 +509,6 @@ class MaterialDominantMaterialView break; } } -#endif return found; } diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp index 36d7bbbc1a..867e46a54e 100644 --- a/src/axom/mir/views/dispatch_material.hpp +++ b/src/axom/mir/views/dispatch_material.hpp @@ -25,11 +25,9 @@ namespace views * \param matset The node that contains the matset. * \param func The function/lambda that will operate on the matset view. */ -template +template void dispatch_material(const conduit::Node &matset, FuncType &&func) { - constexpr static size_t MaxMaterials = 20; - if(conduit::blueprint::mesh::matset::is_uni_buffer(matset)) { IndexNode_to_ArrayView_same( @@ -42,7 +40,7 @@ void dispatch_material(const conduit::Node &matset, FuncType &&func) using IndexType = typename decltype(material_ids)::value_type; using FloatType = typename decltype(volume_fractions)::value_type; - UnibufferMaterialView matsetView; + UnibufferMaterialView matsetView; matsetView.set(material_ids, volume_fractions, sizes, offsets, indices); func(matsetView); }); @@ -63,7 +61,7 @@ void dispatch_material(const conduit::Node &matset, FuncType &&func) using FloatView = decltype(firstValues); using FloatElement = typename FloatView::value_type; - MultiBufferMaterialView matsetView; + MultiBufferMaterialView matsetView; for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) { @@ -91,7 +89,7 @@ void dispatch_material(const conduit::Node &matset, FuncType &&func) using FloatView = decltype(firstValues); using FloatElement = typename FloatView::value_type; - ElementDominantMaterialView matsetView; + ElementDominantMaterialView matsetView; for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) { @@ -120,7 +118,7 @@ void dispatch_material(const conduit::Node &matset, FuncType &&func) using FloatView = decltype(firstValues); using FloatElement = typename FloatView::value_type; - MaterialDominantMaterialView matsetView; + MaterialDominantMaterialView matsetView; for(conduit::index_t i = 0; i < volume_fractions.number_of_children(); i++) { From 2225bf5a4c80abfc631f52dfa55f104f846422b0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 19 Sep 2024 19:08:19 -0700 Subject: [PATCH 225/290] Fix timers --- src/axom/mir/EquiZAlgorithm.hpp | 2 +- src/axom/mir/ExtractZones.hpp | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 0586ad67ef..702a2f03e9 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -518,7 +518,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm */ void addOriginal(conduit::Node &n_field, const std::string &topoName, const std::string &association, axom::IndexType nvalues) const { - AXOM_ANNOTATE_BEGIN("addOriginal"); + AXOM_ANNOTATE_SCOPE("addOriginal"); namespace bputils = axom::mir::utilities::blueprint; bputils::ConduitAllocateThroughAxom c2a; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 39d1bd5a82..3ea204d694 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -81,8 +81,6 @@ class ExtractZones AXOM_ANNOTATE_SCOPE("ExtractZones"); namespace bputils = axom::mir::utilities::blueprint; - AXOM_ANNOTATE_BEGIN("nodeMap"); - // Determine the dataSizes and map/slice information for nodes. axom::Array old2new; axom::Array nodeSlice; From debe458f4bccec28c166decd1365bd8bcc42eb48 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 10:28:27 -0700 Subject: [PATCH 226/290] make style --- src/axom/mir/ClipField.hpp | 24 +- src/axom/mir/EquiZAlgorithm.hpp | 184 ++++-- src/axom/mir/ExtractZones.hpp | 93 +-- src/axom/mir/MatsetSlicer.hpp | 6 +- src/axom/mir/MergeMeshes.hpp | 585 ++++++++++-------- .../mir/tests/mir_blueprint_utilities.cpp | 3 +- src/axom/mir/tests/mir_clipfield.cpp | 103 +-- src/axom/mir/tests/mir_views.cpp | 50 +- src/axom/mir/views/StructuredTopologyView.hpp | 3 +- .../UnstructuredTopologyMixedShapeView.hpp | 5 +- .../UnstructuredTopologyPolyhedralView.hpp | 5 +- .../UnstructuredTopologySingleShapeView.hpp | 5 +- src/axom/mir/views/view_traits.hpp | 41 +- 13 files changed, 596 insertions(+), 511 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 2aa1f40b2f..47dfe55a76 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1945,7 +1945,10 @@ class ClipField * \param topoName The name of the output topology. * \param[inout] n_newFields The fields node for the output mesh. */ - void markNewNodes(const BlendData &blend, const std::string &newNodes, const std::string &topoName, conduit::Node &n_newFields) const + void markNewNodes(const BlendData &blend, + const std::string &newNodes, + const std::string &topoName, + conduit::Node &n_newFields) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("markNewNodes"); @@ -1969,10 +1972,9 @@ class ClipField auto valuesView = bputils::make_array_view(n_new_nodes_values); // Update values for the blend groups only. - axom::for_all(blendSize, AXOM_LAMBDA(auto bgid) - { - valuesView[origSize + bgid] = one; - }); + axom::for_all( + blendSize, + AXOM_LAMBDA(auto bgid) { valuesView[origSize + bgid] = one; }); } else { @@ -1984,15 +1986,17 @@ class ClipField n_new_nodes["association"] = "vertex"; conduit::Node &n_new_nodes_values = n_new_nodes["values"]; n_new_nodes_values.set_allocator(c2a.getConduitAllocatorID()); - n_new_nodes_values.set(conduit::DataType(bputils::cpp2conduit::id, outputSize)); + n_new_nodes_values.set( + conduit::DataType(bputils::cpp2conduit::id, outputSize)); auto valuesView = bputils::make_array_view(n_new_nodes_values); // Fill in values. Everything below origSize is an original node. // Everything above is a blended node. - axom::for_all(outputSize, AXOM_LAMBDA(auto index) - { - valuesView[index] = (index < origSize) ? zero : one; - }); + axom::for_all( + outputSize, + AXOM_LAMBDA(auto index) { + valuesView[index] = (index < origSize) ? zero : one; + }); } } } diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 702a2f03e9..7d4b123b8e 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -296,6 +296,7 @@ template ::reduce_policy; + public: using ConnectivityType = typename TopologyView::ConnectivityType; @@ -377,18 +378,27 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #if defined(AXOM_EQUIZ_SPLIT_PROCESSING) // Come up with lists of clean/mixed zones. axom::Array cleanZones, mixedZones; - bputils::ZoneListBuilder zlb(m_topologyView, m_matsetView); + bputils::ZoneListBuilder zlb( + m_topologyView, + m_matsetView); if(n_options.has_child("selectedZones")) { - auto selectedZonesView = bputils::make_array_view(n_options.fetch_existing("selectedZones")); - zlb.execute(m_coordsetView.numberOfNodes(), selectedZonesView, cleanZones, mixedZones); + auto selectedZonesView = bputils::make_array_view( + n_options.fetch_existing("selectedZones")); + zlb.execute(m_coordsetView.numberOfNodes(), + selectedZonesView, + cleanZones, + mixedZones); } else { zlb.execute(m_coordsetView.numberOfNodes(), cleanZones, mixedZones); } - SLIC_ASSERT((cleanZones.size() + mixedZones.size()) == m_topologyView.numberOfZones()); - SLIC_INFO(axom::fmt::format("cleanZones: {}, mixedZones: {}", cleanZones.size(), mixedZones.size())); + SLIC_ASSERT((cleanZones.size() + mixedZones.size()) == + m_topologyView.numberOfZones()); + SLIC_INFO(axom::fmt::format("cleanZones: {}, mixedZones: {}", + cleanZones.size(), + mixedZones.size())); if(cleanZones.size() > 0 && mixedZones.size() > 0) { @@ -412,17 +422,28 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm makeCleanOutput(n_root, n_topo.name(), cleanZones.view(), n_cleanOutput); // Add a original nodes field. - addOriginal(n_root_fields[originalNodesFieldName()], n_topo.name(), "vertex", m_coordsetView.numberOfNodes()); + addOriginal(n_root_fields[originalNodesFieldName()], + n_topo.name(), + "vertex", + m_coordsetView.numberOfNodes()); // If there are fields in the options, make sure the new field is handled too. if(n_options_copy.has_child("fields")) - n_options_copy["fields/" + originalNodesFieldName()] = originalNodesFieldName(); + n_options_copy["fields/" + originalNodesFieldName()] = + originalNodesFieldName(); // Process the mixed part of the mesh. We select just the mixed zones. - n_options_copy["selectedZones"].set_external(mixedZones.data(), mixedZones.size()); + n_options_copy["selectedZones"].set_external(mixedZones.data(), + mixedZones.size()); n_options_copy["newNodesField"] = newNodesFieldName(); - processMixedZones(n_root_topo, n_root_coordset, n_root_fields, n_root_matset, + processMixedZones(n_root_topo, + n_root_coordset, + n_root_fields, + n_root_matset, n_options_copy, - n_newTopo, n_newCoordset, n_newFields, n_newMatset); + n_newTopo, + n_newCoordset, + n_newFields, + n_newMatset); // Make node map and slice info for merging. axom::Array nodeMap, nodeSlice; @@ -434,12 +455,12 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_mirOutput[n_coordset.path()].set_external(n_newCoordset); n_mirOutput[n_fields.path()].set_external(n_newFields); n_mirOutput[n_matset.path()].set_external(n_newMatset); -#if defined(AXOM_EQUIZ_DEBUG) + #if defined(AXOM_EQUIZ_DEBUG) printNode(n_mirOutput); conduit::relay::io::blueprint::save_mesh(n_mirOutput, "debug_equiz_mir", "hdf5"); -#endif + #endif // Merge clean and MIR output. std::vector inputs(2); @@ -454,7 +475,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm bputils::MergeMeshes mm; mm.execute(inputs, mmOpts, n_merged); -#if defined(AXOM_EQUIZ_DEBUG) + #if defined(AXOM_EQUIZ_DEBUG) std::cout << "--- clean ---\n"; printNode(n_cleanOutput); std::cout << "--- MIR ---\n"; @@ -465,8 +486,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm printNode(n_merged); conduit::relay::io::blueprint::save_mesh(n_merged, "debug_equiz_merged", - "hdf5"); -#endif + "hdf5"); + #endif // Move the merged output into the output variables. n_newCoordset.move(n_merged[n_coordset.path()]); @@ -477,9 +498,15 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm else if(cleanZones.size() == 0 && mixedZones.size() > 0) { // Only mixed zones. - processMixedZones(n_topo, n_coordset, n_fields, n_matset, + processMixedZones(n_topo, + n_coordset, + n_fields, + n_matset, n_options_copy, - n_newTopo, n_newCoordset, n_newFields, n_newMatset); + n_newTopo, + n_newCoordset, + n_newFields, + n_newMatset); } else if(cleanZones.size() > 0 && mixedZones.size() == 0) { @@ -493,13 +520,22 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } // Add an originalElements array. - addOriginal(n_newFields["originalElements"], n_topo.name(), "element", m_topologyView.numberOfZones()); + addOriginal(n_newFields["originalElements"], + n_topo.name(), + "element", + m_topologyView.numberOfZones()); } #else // Handle all zones via MIR. - processMixedZones(n_topo, n_coordset, n_fields, n_matset, + processMixedZones(n_topo, + n_coordset, + n_fields, + n_matset, n_options_copy, - n_newTopo, n_newCoordset, n_newFields, n_newMatset); + n_newTopo, + n_newCoordset, + n_newFields, + n_newMatset); #endif } @@ -516,7 +552,10 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * of which nodes are original nodes in the output. Blended nodes may not have good values * but there is a mask field that can identify those nodes. */ - void addOriginal(conduit::Node &n_field, const std::string &topoName, const std::string &association, axom::IndexType nvalues) const + void addOriginal(conduit::Node &n_field, + const std::string &topoName, + const std::string &association, + axom::IndexType nvalues) const { AXOM_ANNOTATE_SCOPE("addOriginal"); namespace bputils = axom::mir::utilities::blueprint; @@ -526,12 +565,14 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_field["topology"] = topoName; n_field["association"] = association; n_field["values"].set_allocator(c2a.getConduitAllocatorID()); - n_field["values"].set(conduit::DataType(bputils::cpp2conduit::id, nvalues)); + n_field["values"].set( + conduit::DataType(bputils::cpp2conduit::id, nvalues)); auto view = bputils::make_array_view(n_field["values"]); - axom::for_all(nvalues, AXOM_LAMBDA(auto index) - { - view[index] = static_cast(index); - }); + axom::for_all( + nvalues, + AXOM_LAMBDA(auto index) { + view[index] = static_cast(index); + }); } /** @@ -546,23 +587,26 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * * \return The number of nodes in the clean mesh output. */ - void makeCleanOutput(const conduit::Node &n_root, const std::string &topoName, - const axom::ArrayView &cleanZones, conduit::Node &n_cleanOutput) const + void makeCleanOutput(const conduit::Node &n_root, + const std::string &topoName, + const axom::ArrayView &cleanZones, + conduit::Node &n_cleanOutput) const { AXOM_ANNOTATE_SCOPE("makeCleanOutput"); namespace bputils = axom::mir::utilities::blueprint; // Make the clean mesh. Set compact=0 so it does not change the number of nodes. - bputils::ExtractZonesAndMatset ez(m_topologyView, m_coordsetView, m_matsetView); + bputils::ExtractZonesAndMatset + ez(m_topologyView, m_coordsetView, m_matsetView); conduit::Node n_ezopts; n_ezopts["topology"] = topoName; n_ezopts["compact"] = 0; ez.execute(cleanZones, n_root, n_ezopts, n_cleanOutput); -#if defined(AXOM_EQUIZ_DEBUG) + #if defined(AXOM_EQUIZ_DEBUG) AXOM_ANNOTATE_BEGIN("saveClean"); conduit::relay::io::blueprint::save_mesh(n_cleanOutput, "clean", "hdf5"); AXOM_ANNOTATE_END("saveClean"); -#endif + #endif } /** @@ -573,7 +617,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \param[out] nodeMap An array used to map node ids from the MIR output to their node ids in the merged mesh. * \param[out] nodeSlice An array that identifies new blended node ids in the MIR output so they can be appended into coordsets and fields during merge. */ - void createNodeMapAndSlice(conduit::Node &n_newFields, axom::Array &nodeMap, axom::Array &nodeSlice) const + void createNodeMapAndSlice(conduit::Node &n_newFields, + axom::Array &nodeMap, + axom::Array &nodeSlice) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("createNodeMapAndSlice"); @@ -583,12 +629,15 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm const axom::IndexType numCleanNodes = m_coordsetView.numberOfNodes(); // These are the original node ids. - const conduit::Node &n_output_orig_nodes = n_newFields[originalNodesFieldName() + "/values"]; + const conduit::Node &n_output_orig_nodes = + n_newFields[originalNodesFieldName() + "/values"]; auto numOutputNodes = n_output_orig_nodes.dtype().number_of_elements(); - auto outputOrigNodesView = bputils::make_array_view(n_output_orig_nodes); + auto outputOrigNodesView = + bputils::make_array_view(n_output_orig_nodes); // __equiz_new_nodes is the int mask field that identifies new nodes created from blending. - const conduit::Node &n_new_nodes_values = n_newFields[newNodesFieldName() + "/values"]; + const conduit::Node &n_new_nodes_values = + n_newFields[newNodesFieldName() + "/values"]; const auto maskView = bputils::make_array_view(n_new_nodes_values); // Count new nodes created from blending. @@ -596,40 +645,44 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm axom::Array maskOffset(numOutputNodes, numOutputNodes, allocatorID); auto maskOffsetsView = maskOffset.view(); RAJA::ReduceSum mask_reduce(0); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - mask_reduce += maskView[index]; - }); + axom::for_all( + numOutputNodes, + AXOM_LAMBDA(auto index) { mask_reduce += maskView[index]; }); const auto numNewNodes = mask_reduce.get(); // Make offsets. axom::exclusive_scan(maskView, maskOffsetsView); // Make a list of indices that we need to slice out of the node arrays. - nodeSlice = axom::Array(numNewNodes, numNewNodes, allocatorID); + nodeSlice = + axom::Array(numNewNodes, numNewNodes, allocatorID); auto nodeSliceView = nodeSlice.view(); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - if(maskView[index] > 0) - { - nodeSliceView[maskOffsetsView[index]] = index; - } - }); + axom::for_all( + numOutputNodes, + AXOM_LAMBDA(auto index) { + if(maskView[index] > 0) + { + nodeSliceView[maskOffsetsView[index]] = index; + } + }); // Make a node map for mapping mixed connectivity into combined node numbering. - nodeMap = axom::Array(numOutputNodes, numOutputNodes, allocatorID); + nodeMap = + axom::Array(numOutputNodes, numOutputNodes, allocatorID); auto nodeMapView = nodeMap.view(); - axom::for_all(numOutputNodes, AXOM_LAMBDA(auto index) - { - if(maskView[index] == 0) - { - nodeMapView[index] = static_cast(outputOrigNodesView[index]); - } - else - { - nodeMapView[index] = numCleanNodes + maskOffsetsView[index]; - } - }); + axom::for_all( + numOutputNodes, + AXOM_LAMBDA(auto index) { + if(maskView[index] == 0) + { + nodeMapView[index] = + static_cast(outputOrigNodesView[index]); + } + else + { + nodeMapView[index] = numCleanNodes + maskOffsetsView[index]; + } + }); // Remove fields that are no longer needed. n_newFields.remove(originalNodesFieldName()); @@ -878,19 +931,13 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \brief Return the name of the original nodes field. * \return The name of the original nodes field. */ - std::string originalNodesFieldName() const - { - return "__equiz_original_node"; - } + std::string originalNodesFieldName() const { return "__equiz_original_node"; } /** * \brief Return the name of the new nodes field that identifies blended nodes in the MIR output. * \return The name of the new nodes field. */ - std::string newNodesFieldName() const - { - return "__equiz_new_nodes"; - } + std::string newNodesFieldName() const { return "__equiz_new_nodes"; } /** * \brief Makes node-cenetered volume fractions for the materials in the matset @@ -1218,7 +1265,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm if(n_options.has_child("newNodesField")) { // Pass along newNodesField, if present. - options["newNodesField"] = n_options.fetch_existing("newNodesField").as_string(); + options["newNodesField"] = + n_options.fetch_existing("newNodesField").as_string(); } options["topology"] = n_options["topology"]; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 3ea204d694..06c5870c4b 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -100,7 +100,13 @@ class ExtractZones const std::string topoName = topologyName(n_input, n_options); const conduit::Node &n_topo = n_topologies.fetch_existing(topoName); conduit::Node &n_newTopo = n_output["topologies/" + topoName]; - makeTopology(selectedZonesView, dataSizes, extra, old2new.view(), n_topo, n_options, n_newTopo); + makeTopology(selectedZonesView, + dataSizes, + extra, + old2new.view(), + n_topo, + n_options, + n_newTopo); // Make a new coordset. SliceData nSlice; @@ -133,8 +139,11 @@ class ExtractZones n_origZones["topology"] = topoName; n_origZones["association"] = "element"; n_origZones["values"].set_allocator(c2a.getConduitAllocatorID()); - n_origZones["values"].set(conduit::DataType(cpp2conduit::id, selectedZonesView.size())); - axom::copy(n_origZones["values"].data_ptr(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); + n_origZones["values"].set(conduit::DataType(cpp2conduit::id, + selectedZonesView.size())); + axom::copy(n_origZones["values"].data_ptr(), + selectedZonesView.data(), + sizeof(axom::IndexType) * selectedZonesView.size()); } } @@ -144,9 +153,9 @@ class ExtractZones */ struct Sizes { - axom::IndexType nodes{0}; - axom::IndexType zones{0}; - axom::IndexType connectivity{0}; + axom::IndexType nodes {0}; + axom::IndexType zones {0}; + axom::IndexType connectivity {0}; }; /** @@ -156,8 +165,10 @@ class ExtractZones * \param extra A Sizes object containing any extra size that needs to be allocated. * * \return An array view containing the zone slice. - */ - axom::ArrayView zoneSliceView(const SelectedZonesView &selectedZonesView, const Sizes &extra) + */ + axom::ArrayView zoneSliceView( + const SelectedZonesView &selectedZonesView, + const Sizes &extra) { axom::ArrayView view; if(extra.zones > 0) @@ -169,13 +180,13 @@ class ExtractZones const auto n = selectedZonesView.size() + extra.zones; m_zoneSlice = axom::Array(n, n, allocatorID); view = m_zoneSlice.view(); - axom::copy(view.data(), selectedZonesView.data(), sizeof(axom::IndexType) * selectedZonesView.size()); - axom::for_all(selectedZonesView.size(), - n, - AXOM_LAMBDA(auto index) - { - view[index] = 0; - }); + axom::copy(view.data(), + selectedZonesView.data(), + sizeof(axom::IndexType) * selectedZonesView.size()); + axom::for_all( + selectedZonesView.size(), + n, + AXOM_LAMBDA(auto index) { view[index] = 0; }); } view = m_zoneSlice.view(); } @@ -242,9 +253,7 @@ class ExtractZones selectedZonesView, AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { - connsize_reduce += zone.numberOfNodes(); - }); + const auto &zone) { connsize_reduce += zone.numberOfNodes(); }); const auto newConnSize = connsize_reduce.get(); Sizes sizes {}; @@ -252,13 +261,15 @@ class ExtractZones sizes.zones = selectedZonesView.size(); sizes.connectivity = newConnSize; - nodeSlice = axom::Array(sizes.nodes + extra.nodes, sizes.nodes + extra.nodes, allocatorID); + nodeSlice = axom::Array(sizes.nodes + extra.nodes, + sizes.nodes + extra.nodes, + allocatorID); auto nodeSliceView = nodeSlice.view(); axom::for_all( sizes.nodes + extra.nodes, - AXOM_LAMBDA(auto index) { - nodeSliceView[index] = (index < sizes.nodes) ? index : 0; - }); + AXOM_LAMBDA(auto index) { + nodeSliceView[index] = (index < sizes.nodes) ? index : 0; + }); return sizes; } @@ -321,7 +332,9 @@ class ExtractZones // Make an array of original node ids that we can use to "slice" the nodal data. old2new = axom::Array(nnodes, nnodes, allocatorID); - nodeSlice = axom::Array(newNumNodes + extra.nodes, newNumNodes + extra.nodes, allocatorID); + nodeSlice = axom::Array(newNumNodes + extra.nodes, + newNumNodes + extra.nodes, + allocatorID); auto old2newView = old2new.view(); auto nodeSliceView = nodeSlice.view(); axom::for_all( @@ -338,9 +351,7 @@ class ExtractZones axom::for_all( nnodes, nnodes + extra.nodes, - AXOM_LAMBDA(auto index) { - nodeSliceView[index] = 0; - }); + AXOM_LAMBDA(auto index) { nodeSliceView[index] = 0; }); } Sizes sizes {}; @@ -363,7 +374,7 @@ class ExtractZones */ void makeTopology(const SelectedZonesView &selectedZonesView, const Sizes &dataSizes, - const Sizes &extra, + const Sizes &extra, const axom::ArrayView &old2newView, const conduit::Node &n_topo, const conduit::Node &n_options, @@ -389,8 +400,8 @@ class ExtractZones conduit::Node &n_conn = n_newTopo["elements/connectivity"]; n_conn.set_allocator(c2a.getConduitAllocatorID()); - n_conn.set( - conduit::DataType(cpp2conduit::id, dataSizes.connectivity + extra.connectivity)); + n_conn.set(conduit::DataType(cpp2conduit::id, + dataSizes.connectivity + extra.connectivity)); auto connView = bputils::make_array_view(n_conn); conduit::Node &n_sizes = n_newTopo["elements/sizes"]; @@ -415,10 +426,10 @@ class ExtractZones }); if(extra.zones > 0) { - axom::for_all(dataSizes.zones, dataSizes.zones + extra.zones, AXOM_LAMBDA(auto index) - { - sizesView[index] = 0; - }); + axom::for_all( + dataSizes.zones, + dataSizes.zones + extra.zones, + AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); } axom::exclusive_scan(sizesView, offsetsView); @@ -459,10 +470,10 @@ class ExtractZones } if(extra.connectivity > 0) { - axom::for_all(dataSizes.connectivity, dataSizes.connectivity + extra.connectivity, AXOM_LAMBDA(auto index) - { - connView[index] = 0; - }); + axom::for_all( + dataSizes.connectivity, + dataSizes.connectivity + extra.connectivity, + AXOM_LAMBDA(auto index) { connView[index] = 0; }); } // Handle shapes, if present. @@ -487,10 +498,10 @@ class ExtractZones }); if(extra.zones > 0) { - axom::for_all(dataSizes.zones, dataSizes.zones + extra.zones, AXOM_LAMBDA(auto index) - { - newShapesView[index] = 0; - }); + axom::for_all( + dataSizes.zones, + dataSizes.zones + extra.zones, + AXOM_LAMBDA(auto index) { newShapesView[index] = 0; }); } } } diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index a608c25cfa..84a62bee4a 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -36,8 +36,7 @@ class MatsetSlicer /** * \brief Constructor. */ - MatsetSlicer(const MatsetView &matsetView) : m_matsetView(matsetView) - { } + MatsetSlicer(const MatsetView &matsetView) : m_matsetView(matsetView) { } /** * \brief Slice the input matset and output a new matset. @@ -54,7 +53,8 @@ class MatsetSlicer using MatsetIndex = typename MatsetView::IndexType; using MatsetFloat = typename MatsetView::FloatType; namespace bputils = axom::mir::utilities::blueprint; - const axom::ArrayView &selectedZonesView = slice.m_indicesView; + const axom::ArrayView &selectedZonesView = + slice.m_indicesView; SLIC_ASSERT(selectedZonesView.size() > 0); // Copy the material_map if it exists. diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index 177985ca59..e440909a0d 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -28,15 +28,16 @@ namespace utilities { namespace blueprint { - /** * \brief A mesh input containing a Blueprint mesh and some mapping array views. */ struct MeshInput { - conduit::Node *m_input{nullptr}; //!< Pointer to Blueprint mesh. - axom::ArrayView m_nodeMapView{}; //!< Map for mesh nodeIds to nodeIds in final mesh. - axom::ArrayView m_nodeSliceView{};//!< Node ids to be extracted and added to final mesh. + conduit::Node *m_input {nullptr}; //!< Pointer to Blueprint mesh. + axom::ArrayView + m_nodeMapView {}; //!< Map for mesh nodeIds to nodeIds in final mesh. + axom::ArrayView + m_nodeSliceView {}; //!< Node ids to be extracted and added to final mesh. }; /** @@ -55,7 +56,9 @@ class MergeMeshes * \param options A Node containing algorithm options. * \param[out] output The node that will contain the merged mesh. */ - void execute(const std::vector &inputs, const conduit::Node &options, conduit::Node &output) const + void execute(const std::vector &inputs, + const conduit::Node &options, + conduit::Node &output) const { AXOM_ANNOTATE_SCOPE("MergeMeshes"); bool ok = validInputs(inputs, options); @@ -74,6 +77,7 @@ class MergeMeshes mergeInputs(inputs, output); } } + private: /** * \brief This struct contains information used when merging fields. @@ -82,7 +86,7 @@ class MergeMeshes { std::string topology; std::string association; - int dtype; + int dtype; std::vector components; }; @@ -95,7 +99,8 @@ class MergeMeshes * * \return True if the inputs appear to be valid; False otherwise. */ - bool validInputs(const std::vector &inputs, const conduit::Node &options) const + bool validInputs(const std::vector &inputs, + const conduit::Node &options) const { std::string topoName; if(options.has_child("topology")) @@ -103,8 +108,7 @@ class MergeMeshes for(size_t i = 0; i < inputs.size(); i++) { - if(inputs[i].m_input == nullptr) - return false; + if(inputs[i].m_input == nullptr) return false; // If we did not specify which topology, make sure that there is only 1. const char *keys[] = {"coordsets", "topologies", "matsets"}; @@ -115,20 +119,21 @@ class MergeMeshes if(inputs[i].m_input->has_path(keys[k])) { const conduit::Node &n = inputs[i].m_input->fetch_existing(keys[k]); - if(n.number_of_children() > 1) - return false; + if(n.number_of_children() > 1) return false; } } } - const conduit::Node &n_coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &n_coordsets = + inputs[i].m_input->fetch_existing("coordsets"); const conduit::Node &n_coordset = n_coordsets[0]; if(n_coordset["type"].as_string() != "explicit") { - return false; + return false; } - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topologies = + inputs[i].m_input->fetch_existing("topologies"); const conduit::Node *n_topo = nullptr; if(topoName.empty()) n_topo = n_topologies.child_ptr(0); @@ -136,19 +141,21 @@ class MergeMeshes n_topo = n_topologies.fetch_ptr(topoName); if(n_topo->operator[]("type").as_string() != "unstructured") { - return false; + return false; } // For now if(n_topo->operator[]("elements/shape").as_string() == "polyhedral") { - return false; + return false; } // Require no nodeMap/nodeSlice or that they both be present. - if(!((inputs[i].m_nodeMapView.size() == 0 && inputs[i].m_nodeSliceView.size() == 0) || - (inputs[i].m_nodeMapView.size() > 0 && inputs[i].m_nodeSliceView.size() > 0))) + if(!((inputs[i].m_nodeMapView.size() == 0 && + inputs[i].m_nodeSliceView.size() == 0) || + (inputs[i].m_nodeMapView.size() > 0 && + inputs[i].m_nodeSliceView.size() > 0))) { - return false; + return false; } } return true; @@ -186,7 +193,8 @@ class MergeMeshes * \param inputs A vector of inputs to be merged. * \param[out] output The node that will contain the output mesh. */ - void mergeCoordset(const std::vector &inputs, conduit::Node &output) const + void mergeCoordset(const std::vector &inputs, + conduit::Node &output) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("mergeCoordset"); @@ -199,7 +207,8 @@ class MergeMeshes const axom::IndexType n = static_cast(inputs.size()); for(axom::IndexType i = 0; i < n; i++) { - const conduit::Node &coordsets = inputs[i].m_input->fetch_existing("coordsets"); + const conduit::Node &coordsets = + inputs[i].m_input->fetch_existing("coordsets"); const conduit::Node &n_srcCoordset = coordsets[0]; const conduit::Node &n_srcValues = n_srcCoordset.fetch_existing("values"); @@ -227,8 +236,7 @@ class MergeMeshes } // Copy this input's coordinates into the new coordset. - axom::mir::views::FloatNode_to_ArrayView(n_srcValues[0], [&](auto comp0) - { + axom::mir::views::FloatNode_to_ArrayView(n_srcValues[0], [&](auto comp0) { using FloatType = typename decltype(comp0)::value_type; for(int c = 0; c < nComps; c++) { @@ -243,20 +251,22 @@ class MergeMeshes if(nodeSliceView.size() > 0) { // Pull out specific nodes from the input. - axom::for_all(nodeSliceView.size(), AXOM_LAMBDA(auto index) - { - const auto sliceIndex = nodeSliceView[index]; - compView[offset + index] = srcCompView[sliceIndex]; - }); + axom::for_all( + nodeSliceView.size(), + AXOM_LAMBDA(auto index) { + const auto sliceIndex = nodeSliceView[index]; + compView[offset + index] = srcCompView[sliceIndex]; + }); size = nodeSliceView.size(); } else { // Pull out all nodes from the input. - axom::for_all(srcCompView.size(), AXOM_LAMBDA(auto index) - { - compView[offset + index] = srcCompView[index]; - }); + axom::for_all( + srcCompView.size(), + AXOM_LAMBDA(auto index) { + compView[offset + index] = srcCompView[index]; + }); size = srcCompView.size(); } @@ -266,11 +276,13 @@ class MergeMeshes } } - axom::IndexType countNodes(const std::vector &inputs, size_t index) const + axom::IndexType countNodes(const std::vector &inputs, + size_t index) const { SLIC_ASSERT(index < inputs.size()); - const conduit::Node &coordsets = inputs[index].m_input->fetch_existing("coordsets"); + const conduit::Node &coordsets = + inputs[index].m_input->fetch_existing("coordsets"); const conduit::Node &coordset = coordsets[0]; const auto type = coordset.fetch_existing("type").as_string(); @@ -294,9 +306,11 @@ class MergeMeshes return nodeTotal; } - axom::IndexType countZones(const std::vector &inputs, size_t index) const + axom::IndexType countZones(const std::vector &inputs, + size_t index) const { - const conduit::Node &n_topologies = inputs[index].m_input->fetch_existing("topologies"); + const conduit::Node &n_topologies = + inputs[index].m_input->fetch_existing("topologies"); const conduit::Node &n_topo = n_topologies[0]; const conduit::Node &n_size = n_topo.fetch_existing("elements/sizes"); @@ -304,19 +318,23 @@ class MergeMeshes return nzones; } - void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const + void countZones(const std::vector &inputs, + axom::IndexType &totalConnLength, + axom::IndexType &totalZones) const { totalConnLength = 0; totalZones = 0; axom::IndexType n = static_cast(inputs.size()); for(axom::IndexType i = 0; i < n; i++) { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topologies = + inputs[i].m_input->fetch_existing("topologies"); const conduit::Node &n_topo = n_topologies[0]; const auto type = n_topo.fetch_existing("type").as_string(); SLIC_ASSERT(type == "unstructured"); - const conduit::Node &n_conn = n_topo.fetch_existing("elements/connectivity"); + const conduit::Node &n_conn = + n_topo.fetch_existing("elements/connectivity"); const auto connLength = n_conn.dtype().number_of_elements(); totalConnLength += connLength; @@ -332,12 +350,13 @@ class MergeMeshes * \param inputs A vector of inputs to be merged. * \param[out] output The node that will contain the output mesh. */ - void mergeTopology(const std::vector &inputs, conduit::Node &output) const + void mergeTopology(const std::vector &inputs, + conduit::Node &output) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("mergeTopology"); axom::IndexType totalConnLen = 0, totalZones = 0; - countZones(inputs, totalConnLen, totalZones); + countZones(inputs, totalConnLen, totalZones); conduit::Node &n_newTopologies = output["topologies"]; // Check whether there are mixed shapes. @@ -345,14 +364,16 @@ class MergeMeshes const axom::IndexType n = static_cast(inputs.size()); for(axom::IndexType i = 0; i < n; i++) { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topologies = + inputs[i].m_input->fetch_existing("topologies"); const conduit::Node &n_srcTopo = n_topologies[0]; const auto type = n_srcTopo.fetch_existing("type").as_string(); const auto shape = n_srcTopo.fetch_existing("elements/shape").as_string(); SLIC_ASSERT(type == "unstructured"); if(shape == "mixed") { - const conduit::Node &n_shape_map = n_srcTopo.fetch_existing("elements/shape_map"); + const conduit::Node &n_shape_map = + n_srcTopo.fetch_existing("elements/shape_map"); for(int s = 0; s < n_shape_map.number_of_children(); s++) { const std::string sname = n_shape_map[s].name(); @@ -368,15 +389,20 @@ class MergeMeshes } conduit::Node *n_newTopoPtr = nullptr; - axom::IndexType connOffset = 0, sizesOffset = 0, shapesOffset = 0, coordOffset = 0; + axom::IndexType connOffset = 0, sizesOffset = 0, shapesOffset = 0, + coordOffset = 0; for(axom::IndexType i = 0; i < n; i++) { - const conduit::Node &n_topologies = inputs[i].m_input->fetch_existing("topologies"); + const conduit::Node &n_topologies = + inputs[i].m_input->fetch_existing("topologies"); const conduit::Node &n_srcTopo = n_topologies[0]; - const std::string srcShape = n_srcTopo.fetch_existing("elements/shape").as_string(); - const conduit::Node &n_srcConn = n_srcTopo.fetch_existing("elements/connectivity"); - const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); + const std::string srcShape = + n_srcTopo.fetch_existing("elements/shape").as_string(); + const conduit::Node &n_srcConn = + n_srcTopo.fetch_existing("elements/connectivity"); + const conduit::Node &n_srcSizes = + n_srcTopo.fetch_existing("elements/sizes"); // Make all of the elements the first time. if(i == 0) @@ -420,10 +446,10 @@ class MergeMeshes } // Copy this input's connectivity into the new topology. - axom::mir::views::IndexNode_to_ArrayView(n_srcConn, [&](auto srcConnView) - { + axom::mir::views::IndexNode_to_ArrayView(n_srcConn, [&](auto srcConnView) { using ConnType = typename decltype(srcConnView)::value_type; - conduit::Node &n_newConn = n_newTopoPtr->fetch_existing("elements/connectivity"); + conduit::Node &n_newConn = + n_newTopoPtr->fetch_existing("elements/connectivity"); auto connView = bputils::make_array_view(n_newConn); if(inputs[i].m_nodeMapView.size() > 0) @@ -432,37 +458,40 @@ class MergeMeshes // The supplied nodeMap is assumed to be a mapping from the current // node connectivity to the merged node connectivity. const auto nodeMapView = inputs[i].m_nodeMapView; - axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) - { - const auto nodeId = srcConnView[index]; - const auto newNodeId = nodeMapView[nodeId]; - connView[connOffset + index] = newNodeId; - }); + axom::for_all( + srcConnView.size(), + AXOM_LAMBDA(auto index) { + const auto nodeId = srcConnView[index]; + const auto newNodeId = nodeMapView[nodeId]; + connView[connOffset + index] = newNodeId; + }); } else { // Copy all zones from the input. Map the nodes to the new values. - axom::for_all(srcConnView.size(), AXOM_LAMBDA(auto index) - { - connView[connOffset + index] = coordOffset + srcConnView[index]; - }); + axom::for_all( + srcConnView.size(), + AXOM_LAMBDA(auto index) { + connView[connOffset + index] = coordOffset + srcConnView[index]; + }); } connOffset += srcConnView.size(); coordOffset += countNodes(inputs, static_cast(i)); }); // Copy this input's sizes into the new topology. - axom::mir::views::IndexNode_to_ArrayView(n_srcSizes, [&](auto srcSizesView) - { + axom::mir::views::IndexNode_to_ArrayView(n_srcSizes, [&](auto srcSizesView) { using ConnType = typename decltype(srcSizesView)::value_type; - conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); + conduit::Node &n_newSizes = + n_newTopoPtr->fetch_existing("elements/sizes"); auto sizesView = bputils::make_array_view(n_newSizes); // Copy all sizes from the input. - axom::for_all(srcSizesView.size(), AXOM_LAMBDA(auto index) - { - sizesView[sizesOffset + index] = srcSizesView[index]; - }); + axom::for_all( + srcSizesView.size(), + AXOM_LAMBDA(auto index) { + sizesView[sizesOffset + index] = srcSizesView[index]; + }); sizesOffset += srcSizesView.size(); }); @@ -470,37 +499,43 @@ class MergeMeshes // Copy shape information if it exists. if(n_srcTopo.has_path("elements/shapes")) { - const conduit::Node &n_srcShapes = n_srcTopo.fetch_existing("elements/shapes"); - - axom::mir::views::IndexNode_to_ArrayView(n_srcShapes, [&](auto srcShapesView) - { - using ConnType = typename decltype(srcShapesView)::value_type; - conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/shapes"); - auto shapesView = bputils::make_array_view(n_newShapes); + const conduit::Node &n_srcShapes = + n_srcTopo.fetch_existing("elements/shapes"); + + axom::mir::views::IndexNode_to_ArrayView( + n_srcShapes, + [&](auto srcShapesView) { + using ConnType = typename decltype(srcShapesView)::value_type; + conduit::Node &n_newShapes = + n_newTopoPtr->fetch_existing("elements/shapes"); + auto shapesView = bputils::make_array_view(n_newShapes); + + // Copy all sizes from the input. + axom::for_all( + srcShapesView.size(), + AXOM_LAMBDA(auto index) { + shapesView[shapesOffset + index] = srcShapesView[index]; + }); - // Copy all sizes from the input. - axom::for_all(srcShapesView.size(), AXOM_LAMBDA(auto index) - { - shapesView[shapesOffset + index] = srcShapesView[index]; + shapesOffset += srcShapesView.size(); }); - - shapesOffset += srcShapesView.size(); - }); } else { // Fill in shape information. There is no source shape information. Use // sizes to get the number of zones. - const conduit::Node &n_srcSizes = n_srcTopo.fetch_existing("elements/sizes"); + const conduit::Node &n_srcSizes = + n_srcTopo.fetch_existing("elements/sizes"); axom::IndexType nz = n_srcSizes.dtype().number_of_elements(); - conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/shapes"); - axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) - { + conduit::Node &n_newShapes = + n_newTopoPtr->fetch_existing("elements/shapes"); + axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) { const int shapeId = axom::mir::views::shapeNameToID(srcShape); - axom::for_all(nz, AXOM_LAMBDA(auto index) - { - shapesView[shapesOffset + index] = shapeId; - }); + axom::for_all( + nz, + AXOM_LAMBDA(auto index) { + shapesView[shapesOffset + index] = shapeId; + }); shapesOffset += nz; }); } @@ -508,10 +543,10 @@ class MergeMeshes // Make new offsets from the sizes. conduit::Node &n_newSizes = n_newTopoPtr->fetch_existing("elements/sizes"); - axom::mir::views::IndexNode_to_ArrayView(n_newSizes, [&](auto sizesView) - { + axom::mir::views::IndexNode_to_ArrayView(n_newSizes, [&](auto sizesView) { using ConnType = typename decltype(sizesView)::value_type; - conduit::Node &n_newOffsets = n_newTopoPtr->fetch_existing("elements/offsets"); + conduit::Node &n_newOffsets = + n_newTopoPtr->fetch_existing("elements/offsets"); auto offsetsView = bputils::make_array_view(n_newOffsets); axom::exclusive_scan(sizesView, offsetsView); }); @@ -548,7 +583,8 @@ class MergeMeshes { if(inputs[i].m_input->has_child("fields")) { - const conduit::Node &n_fields = inputs[i].m_input->fetch_existing("fields"); + const conduit::Node &n_fields = + inputs[i].m_input->fetch_existing("fields"); for(conduit::index_t c = 0; c < n_fields.number_of_children(); c++) { const conduit::Node &n_field = n_fields[c]; @@ -558,7 +594,8 @@ class MergeMeshes fi.association = n_field.fetch_existing("association").as_string(); if(n_values.number_of_children() > 0) { - for(conduit::index_t comp = 0; comp < n_values.number_of_children(); comp++) + for(conduit::index_t comp = 0; comp < n_values.number_of_children(); + comp++) { fi.components.push_back(n_values[comp].name()); fi.dtype = n_values[comp].dtype().id(); @@ -606,7 +643,8 @@ class MergeMeshes { conduit::Node &n_comp = n_values[it->second.components[ci]]; n_comp.set_allocator(c2a.getConduitAllocatorID()); - const std::string srcPath("fields/" + it->first + "/values/" + it->second.components[ci]); + const std::string srcPath("fields/" + it->first + "/values/" + + it->second.components[ci]); if(it->second.association == "element") { n_comp.set(conduit::DataType(it->second.dtype, totalZones)); @@ -630,7 +668,9 @@ class MergeMeshes * \param[out] n_values The node will be populated with data values from the field inputs. * \param srcPath The path to the source data in each input node. */ - void copyZonal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const + void copyZonal(const std::vector &inputs, + conduit::Node &n_values, + const std::string &srcPath) const { axom::IndexType offset = 0; for(size_t i = 0; i < inputs.size(); i++) @@ -639,23 +679,25 @@ class MergeMeshes if(inputs[i].m_input->has_path(srcPath)) { - const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_src_values, n_values, [&](auto srcView, auto destView) - { - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - destView[offset + index] = srcView[index]; - }); - }); + const conduit::Node &n_src_values = + inputs[i].m_input->fetch_existing(srcPath); + axom::mir::views::Node_to_ArrayView(n_src_values, + n_values, + [&](auto srcView, auto destView) { + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { + destView[offset + index] = + srcView[index]; + }); + }); } else { - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) - { - axom::for_all(nzones, AXOM_LAMBDA(auto index) - { - destView[offset + index] = 0; - }); + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { + axom::for_all( + nzones, + AXOM_LAMBDA(auto index) { destView[offset + index] = 0; }); }); } offset += nzones; @@ -669,7 +711,9 @@ class MergeMeshes * \param[out] n_values The node will be populated with data values from the field inputs. * \param srcPath The path to the source data in each input node. */ - void copyNodal(const std::vector &inputs, conduit::Node &n_values, const std::string &srcPath) const + void copyNodal(const std::vector &inputs, + conduit::Node &n_values, + const std::string &srcPath) const { axom::IndexType offset = 0; for(size_t i = 0; i < inputs.size(); i++) @@ -678,35 +722,38 @@ class MergeMeshes if(inputs[i].m_input->has_path(srcPath)) { - const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_src_values, n_values, [&](auto srcView, auto destView) - { + const conduit::Node &n_src_values = + inputs[i].m_input->fetch_existing(srcPath); + axom::mir::views::Node_to_ArrayView( + n_src_values, + n_values, + [&](auto srcView, auto destView) { if(inputs[i].m_nodeSliceView.empty()) { - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - destView[offset + index] = srcView[index]; - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { + destView[offset + index] = srcView[index]; + }); } else { auto nodeSliceView(inputs[i].m_nodeSliceView); - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - const auto nodeId = nodeSliceView[index]; - destView[offset + index] = srcView[nodeId]; - }); + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { + const auto nodeId = nodeSliceView[index]; + destView[offset + index] = srcView[nodeId]; + }); } - }); + }); } else { - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) - { - axom::for_all(nnodes, AXOM_LAMBDA(auto index) - { - destView[offset + index] = 0; - }); + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { + axom::for_all( + nnodes, + AXOM_LAMBDA(auto index) { destView[offset + index] = 0; }); }); } offset += nnodes; @@ -723,7 +770,8 @@ class MergeMeshes { AXOM_ANNOTATE_SCOPE("mergeMatset"); namespace bputils = axom::mir::utilities::blueprint; - using reduce_policy = typename axom::execution_space::reduce_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; bputils::ConduitAllocateThroughAxom c2a; // Make a pass through the inputs and make a list of the material names. @@ -758,8 +806,7 @@ class MergeMeshes if(hasMatsets) { // One or more inputs did not have a matset. - if(defaultMaterial) - allMats["default"] = nmats++; + if(defaultMaterial) allMats["default"] = nmats++; // Make a pass through the matsets to determine the overall storage. axom::IndexType totalZones = 0, totalMatCount = 0; @@ -773,11 +820,11 @@ class MergeMeshes if(inputs[i].m_input->has_path("matsets")) { - conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matsets = + inputs[i].m_input->fetch_existing("matsets"); conduit::Node &n_matset = n_matsets[0]; axom::IndexType matCount = 0; - axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) - { + axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) { // Figure out the types to use for storing the data. using IType = typename decltype(matsetView)::IndexType; using FType = typename decltype(matsetView)::FloatType; @@ -785,11 +832,12 @@ class MergeMeshes ftype = bputils::cpp2conduit::id; RAJA::ReduceSum matCount_reduce(0); - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - const auto nmats = matsetView.numberOfMaterials(zoneIndex); - matCount_reduce += nmats; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const auto nmats = matsetView.numberOfMaterials(zoneIndex); + matCount_reduce += nmats; + }); matCount = matCount_reduce.get(); }); totalMatCount += matCount; @@ -835,129 +883,156 @@ class MergeMeshes n_material_map[it->first] = it->second; // Populate - axom::mir::views::IndexNode_to_ArrayView_same(n_material_ids, n_sizes, n_offsets, n_indices, - [&](auto materialIdsView, auto sizesView, auto offsetsView, auto indicesView) - { - axom::mir::views::FloatNode_to_ArrayView(n_volume_fractions, - [&](auto volumeFractionsView) - { - // Fill in sizes array. - axom::IndexType zOffset = 0; - for(size_t i = 0; i < inputs.size(); i++) - { - const auto nzones = countZones(inputs, i); - - if(inputs[i].m_input->has_child("matsets")) - { - conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); - conduit::Node &n_matset = n_matsets[0]; - - axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) + axom::mir::views::IndexNode_to_ArrayView_same( + n_material_ids, + n_sizes, + n_offsets, + n_indices, + [&](auto materialIdsView, + auto sizesView, + auto offsetsView, + auto indicesView) { + axom::mir::views::FloatNode_to_ArrayView( + n_volume_fractions, + [&](auto volumeFractionsView) { + // Fill in sizes array. + axom::IndexType zOffset = 0; + for(size_t i = 0; i < inputs.size(); i++) { - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + const auto nzones = countZones(inputs, i); + + if(inputs[i].m_input->has_child("matsets")) { - sizesView[zOffset + zoneIndex] = matsetView.numberOfMaterials(zoneIndex); - }); - }); - } - else - { - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) + conduit::Node &n_matsets = + inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + + axom::mir::views::dispatch_material( + n_matset, + [&](auto matsetView) { + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + sizesView[zOffset + zoneIndex] = + matsetView.numberOfMaterials(zoneIndex); + }); + }); + } + else + { + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + sizesView[zOffset + zoneIndex] = 1; + }); + } + zOffset += nzones; + } + // Make offsets. + axom::exclusive_scan(sizesView, offsetsView); + + // Make indices. + axom::for_all( + totalMatCount, + AXOM_LAMBDA(auto index) { indicesView[index] = index; }); + + // Fill in material info. + zOffset = 0; + for(size_t i = 0; i < inputs.size(); i++) { - sizesView[zOffset + zoneIndex] = 1; - }); - } - zOffset += nzones; - } - // Make offsets. - axom::exclusive_scan(sizesView, offsetsView); - - // Make indices. - axom::for_all(totalMatCount, AXOM_LAMBDA(auto index) - { - indicesView[index] = index; - }); - - // Fill in material info. - zOffset = 0; - for(size_t i = 0; i < inputs.size(); i++) - { - const auto nzones = countZones(inputs, i); - - if(inputs[i].m_input->has_child("matsets")) - { - conduit::Node &n_matsets = inputs[i].m_input->fetch_existing("matsets"); - conduit::Node &n_matset = n_matsets[0]; + const auto nzones = countZones(inputs, i); - axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) - { - using IDList = typename decltype(matsetView)::IDList; - using VFList = typename decltype(matsetView)::VFList; - using MatID = typename decltype(matsetView)::IndexType; - - // Make some maps for renumbering material numbers. - const auto localMaterialMap = axom::mir::views::materials(n_matset); - std::map localToAll; - for(const auto &info : localMaterialMap) + if(inputs[i].m_input->has_child("matsets")) { - MatID matno = allMats[info.name]; - localToAll[info.number] = matno; + conduit::Node &n_matsets = + inputs[i].m_input->fetch_existing("matsets"); + conduit::Node &n_matset = n_matsets[0]; + + axom::mir::views::dispatch_material( + n_matset, + [&](auto matsetView) { + using IDList = typename decltype(matsetView)::IDList; + using VFList = typename decltype(matsetView)::VFList; + using MatID = typename decltype(matsetView)::IndexType; + + // Make some maps for renumbering material numbers. + const auto localMaterialMap = + axom::mir::views::materials(n_matset); + std::map localToAll; + for(const auto &info : localMaterialMap) + { + MatID matno = allMats[info.name]; + localToAll[info.number] = matno; + } + std::vector localVec, allVec; + for(auto it = localToAll.begin(); it != localToAll.end(); + it++) + { + localVec.push_back(it->first); + allVec.push_back(it->second); + } + // Put maps on device. + const int allocatorID = + axom::execution_space::allocatorID(); + axom::Array local(localVec.size(), + localVec.size(), + allocatorID); + axom::Array all(allVec.size(), + allVec.size(), + allocatorID); + axom::copy(local.data(), + localVec.data(), + sizeof(MatID) * local.size()); + axom::copy(all.data(), + allVec.data(), + sizeof(MatID) * all.size()); + const auto localView = local.view(); + const auto allView = all.view(); + + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + // Get this zone's materials. + IDList ids; + VFList vfs; + matsetView.zoneMaterials(zoneIndex, ids, vfs); + + // Store the materials in the new material. + const auto zoneStart = + offsetsView[zOffset + zoneIndex]; + for(axom::IndexType mi = 0; mi < ids.size(); mi++) + { + const auto destIndex = zoneStart + mi; + volumeFractionsView[destIndex] = vfs[mi]; + + // Get the index of the material number in the local map. + const auto mapIndex = + axom::mir::utilities::bsearch(ids[mi], localView); + assert(mapIndex != -1); + // We'll store the all materials number. + const auto allMatno = allView[mapIndex]; + materialIdsView[destIndex] = allMatno; + } + }); + }); } - std::vector localVec, allVec; - for(auto it = localToAll.begin(); it != localToAll.end(); it++) + else { - localVec.push_back(it->first); - allVec.push_back(it->second); + const int dmat = allMats["default"]; + axom::for_all( + nzones, + AXOM_LAMBDA(auto zoneIndex) { + const auto zoneStart = offsetsView[zOffset + zoneIndex]; + volumeFractionsView[zoneStart] = 1; + materialIdsView[zoneStart] = dmat; + }); } - // Put maps on device. - const int allocatorID = axom::execution_space::allocatorID(); - axom::Array local(localVec.size(), localVec.size(), allocatorID); - axom::Array all(allVec.size(), allVec.size(), allocatorID); - axom::copy(local.data(), localVec.data(), sizeof(MatID) * local.size()); - axom::copy(all.data(), allVec.data(), sizeof(MatID) * all.size()); - const auto localView = local.view(); - const auto allView = all.view(); - - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - // Get this zone's materials. - IDList ids; - VFList vfs; - matsetView.zoneMaterials(zoneIndex, ids, vfs); - - // Store the materials in the new material. - const auto zoneStart = offsetsView[zOffset + zoneIndex]; - for(axom::IndexType mi = 0; mi < ids.size(); mi++) - { - const auto destIndex = zoneStart + mi; - volumeFractionsView[destIndex] = vfs[mi]; - - // Get the index of the material number in the local map. - const auto mapIndex = axom::mir::utilities::bsearch(ids[mi], localView); - assert(mapIndex != -1); - // We'll store the all materials number. - const auto allMatno = allView[mapIndex]; - materialIdsView[destIndex] = allMatno; - } - }); - }); - } - else - { - const int dmat = allMats["default"]; - axom::for_all(nzones, AXOM_LAMBDA(auto zoneIndex) - { - const auto zoneStart = offsetsView[zOffset + zoneIndex]; - volumeFractionsView[zoneStart] = 1; - materialIdsView[zoneStart] = dmat; - }); - } - zOffset += nzones; - } + zOffset += nzones; + } + }); }); - }); } - } // if hasMatsets + } // if hasMatsets } }; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 5c217139e5..1b2121c4b1 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -1235,7 +1235,7 @@ struct test_mergemeshes inputs[1].m_input = deviceMesh.fetch_ptr("domain0001"); // The node names for input 1 in the final merged mesh. - const axom::IndexType nodeMap[] = {1,2,5,6,9,10,13,14,16,17}; + const axom::IndexType nodeMap[] = {1, 2, 5, 6, 9, 10, 13, 14, 16, 17}; // The 2 nodes in input 1 that do not appear in input 0 const axom::IndexType nodeSlice[] = {8, 9}; const int allocatorID = axom::execution_space::allocatorID(); @@ -1356,7 +1356,6 @@ struct test_mergemeshes )xx"; mesh.parse(yaml); } - }; TEST(mir_blueprint_utilities, mergemeshes_seq) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 43dfc1d8b2..02749afb7f 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -337,12 +337,16 @@ struct test_unique // Make unique ids. axom::Array uIds, uIndices; - axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); + axom::mir::utilities::Unique::execute(ids.view(), + uIds, + uIndices); // device->host axom::Array hostuIds(uIds.size()), hostuIndices(uIndices.size()); axom::copy(hostuIds.data(), uIds.data(), sizeof(int) * uIds.size()); - axom::copy(hostuIndices.data(), uIndices.data(), sizeof(int) * uIndices.size()); + axom::copy(hostuIndices.data(), + uIndices.data(), + sizeof(int) * uIndices.size()); // compare results EXPECT_EQ(hostuIds.size(), 12); @@ -355,27 +359,15 @@ struct test_unique } }; -TEST(mir_clipfield, unique_seq) -{ - test_unique::test(); -} +TEST(mir_clipfield, unique_seq) { test_unique::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_clipfield, unique_omp) -{ - test_unique::test(); -} +TEST(mir_clipfield, unique_omp) { test_unique::test(); } #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) -TEST(mir_clipfield, unique_cuda) -{ - test_unique::test(); -} +TEST(mir_clipfield, unique_cuda) { test_unique::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_clipfield, unique_hip) -{ - test_unique::test(); -} +TEST(mir_clipfield, unique_hip) { test_unique::test(); } #endif //------------------------------------------------------------------------------ @@ -638,8 +630,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Copy device->host conduit::Node hostClipMixedMesh; - bputils::copy(hostClipMixedMesh, - deviceClipMixedMesh); + bputils::copy(hostClipMixedMesh, deviceClipMixedMesh); #if defined(DEBUGGING_TEST_CASES) std::cout << "---------------------------------- clipped mixed " "----------------------------------" @@ -812,8 +803,7 @@ void strided_structured_clip_test(const std::string &name, clipper.execute(deviceMesh, deviceOptions, deviceClipMesh); // device->host - bputils::copy(hostClipMesh, - deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); }); // Handle baseline comparison. @@ -1036,27 +1026,15 @@ void braid3d_mixed_clip_test(const std::string &name) #endif } -TEST(mir_clipfield, mixed_seq) -{ - braid3d_mixed_clip_test("mixed"); -} +TEST(mir_clipfield, mixed_seq) { braid3d_mixed_clip_test("mixed"); } #if defined(AXOM_USE_OPENMP) -TEST(mir_clipfield, mixed_omp) -{ - braid3d_mixed_clip_test("mixed"); -} +TEST(mir_clipfield, mixed_omp) { braid3d_mixed_clip_test("mixed"); } #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) -TEST(mir_clipfield, mixed_cuda) -{ - braid3d_mixed_clip_test("mixed"); -} +TEST(mir_clipfield, mixed_cuda) { braid3d_mixed_clip_test("mixed"); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_clipfield, mixed_hip) -{ - braid3d_mixed_clip_test("mixed"); -} +TEST(mir_clipfield, mixed_hip) { braid3d_mixed_clip_test("mixed"); } #endif //------------------------------------------------------------------------------ @@ -1085,7 +1063,8 @@ struct point_merge_test hostMesh["topologies/mesh/elements/shape"] = "quad"; hostMesh["topologies/mesh/elements/connectivity"].set( std::vector {{0, 1, 4, 3, 1, 2, 5, 4, 3, 4, 7, 6, 4, 5, 8, 7}}); - hostMesh["topologies/mesh/elements/sizes"].set(std::vector {{4, 4, 4, 4}}); + hostMesh["topologies/mesh/elements/sizes"].set( + std::vector {{4, 4, 4, 4}}); hostMesh["topologies/mesh/elements/offsets"].set( std::vector {{0, 4, 8, 12}}); hostMesh["fields/clip/topology"] = "mesh"; @@ -1125,8 +1104,7 @@ struct point_merge_test clip.execute(deviceMesh, options, deviceClipMesh); conduit::Node hostClipMesh; - bputils::copy(hostClipMesh, - deviceClipMesh); + bputils::copy(hostClipMesh, deviceClipMesh); //printNode(hostClipMesh); // Check that the points were merged when making the new mesh. @@ -1160,27 +1138,15 @@ struct point_merge_test } }; -TEST(mir_clipfield, pointmerging_seq) -{ - point_merge_test::test(); -} +TEST(mir_clipfield, pointmerging_seq) { point_merge_test::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_clipfield, pointmerging_omp) -{ - point_merge_test::test(); -} +TEST(mir_clipfield, pointmerging_omp) { point_merge_test::test(); } #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) -TEST(mir_clipfield, pointmerging_cuda) -{ - point_merge_test::test(); -} +TEST(mir_clipfield, pointmerging_cuda) { point_merge_test::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_clipfield, pointmerging_hip) -{ - point_merge_test::test(); -} +TEST(mir_clipfield, pointmerging_hip) { point_merge_test::test(); } #endif //------------------------------------------------------------------------------ @@ -1208,7 +1174,8 @@ struct test_selectedzones using TopologyView = decltype(topologyView); conduit::Node hostOptions; - hostOptions["selectedZones"].set(std::vector{{1,3,4,5,7}}); + hostOptions["selectedZones"].set( + std::vector {{1, 3, 4, 5, 7}}); hostOptions["inside"] = 1; hostOptions["outside"] = 1; hostOptions["clipField"] = "zero"; @@ -1216,7 +1183,9 @@ struct test_selectedzones conduit::Node deviceOptions, deviceResult; bputils::copy(deviceOptions, hostOptions); - axom::mir::clipping::ClipField clip(topologyView, coordsetView); + axom::mir::clipping::ClipField clip( + topologyView, + coordsetView); clip.execute(deviceMesh, deviceOptions, deviceResult); // device->host @@ -1252,7 +1221,6 @@ struct test_selectedzones #else EXPECT_TRUE(compareBaseline(paths, baselineName, hostResult)); #endif - } static void create(conduit::Node &mesh) @@ -1292,15 +1260,9 @@ struct test_selectedzones } }; -TEST(mir_clipfield, selectedzones_seq) -{ - test_selectedzones::test(); -} +TEST(mir_clipfield, selectedzones_seq) { test_selectedzones::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_clipfield, selectedzones_omp) -{ - test_selectedzones::test(); -} +TEST(mir_clipfield, selectedzones_omp) { test_selectedzones::test(); } #endif #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) TEST(mir_clipfield, selectedzones_cuda) @@ -1309,10 +1271,7 @@ TEST(mir_clipfield, selectedzones_cuda) } #endif #if defined(AXOM_USE_HIP) -TEST(mir_clipfield, selectedzones_hip) -{ - test_selectedzones::test(); -} +TEST(mir_clipfield, selectedzones_hip) { test_selectedzones::test(); } #endif //------------------------------------------------------------------------------ diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 4b4bcea6f1..321fc1e67a 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -59,25 +59,25 @@ struct test_node_to_arrayview static int constexpr sum(int n) { int s = 0; - for(int i = 0; i < n; i++) - s += i; + for(int i = 0; i < n; i++) s += i; return s; } static void test() { - using reduce_policy = typename axom::execution_space::reduce_policy; - - std::vector dtypes{conduit::DataType::INT8_ID, - conduit::DataType::INT16_ID, - conduit::DataType::INT32_ID, - conduit::DataType::INT64_ID, - conduit::DataType::UINT8_ID, - conduit::DataType::UINT16_ID, - conduit::DataType::UINT32_ID, - conduit::DataType::UINT64_ID, - conduit::DataType::FLOAT32_ID, - conduit::DataType::FLOAT64_ID}; + using reduce_policy = + typename axom::execution_space::reduce_policy; + + std::vector dtypes {conduit::DataType::INT8_ID, + conduit::DataType::INT16_ID, + conduit::DataType::INT32_ID, + conduit::DataType::INT64_ID, + conduit::DataType::UINT8_ID, + conduit::DataType::UINT16_ID, + conduit::DataType::UINT32_ID, + conduit::DataType::UINT64_ID, + conduit::DataType::FLOAT32_ID, + conduit::DataType::FLOAT64_ID}; constexpr int n = 16; axom::mir::utilities::blueprint::ConduitAllocateThroughAxom c2a; for(int dtype : dtypes) @@ -88,23 +88,23 @@ struct test_node_to_arrayview n_data.set(conduit::DataType(dtype, n)); int sumValues = 0; - axom::mir::views::Node_to_ArrayView(n_data, [&](auto dataView) - { - std::cout << axom::mir::views::array_view_traits::name() << std::endl; + axom::mir::views::Node_to_ArrayView(n_data, [&](auto dataView) { + std::cout << axom::mir::views::array_view_traits::name() + << std::endl; using value_type = typename decltype(dataView)::value_type; // Make sure we can store values in dataView - axom::for_all(n, AXOM_LAMBDA(auto index) - { - dataView[index] = static_cast(index); - }); + axom::for_all( + n, + AXOM_LAMBDA(auto index) { + dataView[index] = static_cast(index); + }); // Read the values and sum them. RAJA::ReduceSum sumValues_reduce(0); - axom::for_all(n, AXOM_LAMBDA(auto index) - { - sumValues_reduce += dataView[index]; - }); + axom::for_all( + n, + AXOM_LAMBDA(auto index) { sumValues_reduce += dataView[index]; }); sumValues = static_cast(sumValues_reduce.get()); }); diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 1a77f43cd5..c4e2e44f5f 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -70,8 +70,7 @@ class StructuredTopologyView IndexType connectivitySize() const { IndexType nodesPerElem = 1; - for(int d = 0; d < dimension(); d++) - nodesPerElem *= 2; + for(int d = 0; d < dimension(); d++) nodesPerElem *= 2; return numberOfZones() * nodesPerElem; } diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index c2bd07a2ba..479c3615f4 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -136,10 +136,7 @@ class UnstructuredTopologyMixedShapeView * * \return The size of the connectivity. */ - IndexType connectivitySize() const - { - return m_connectivity.size(); - } + IndexType connectivitySize() const { return m_connectivity.size(); } /** * \brief Execute a function for each zone in the mesh. diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 00658238f1..25b594aa97 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -214,10 +214,7 @@ class UnstructuredTopologyPolyhedralView * * \return The size of the connectivity. */ - IndexType connectivitySize() const - { - return m_data.element_conn.size(); - } + IndexType connectivitySize() const { return m_data.element_conn.size(); } /** * \brief Return the dimension of the shape. diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index ec5e5b0f5a..9c632575ad 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -80,10 +80,7 @@ class UnstructuredTopologySingleShapeView * * \return The size of the connectivity. */ - IndexType connectivitySize() const - { - return m_connectivityView.size(); - } + IndexType connectivitySize() const { return m_connectivityView.size(); } /** * \brief Execute a function for each zone in the mesh. diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index b3e63f0a3d..417b2e66fe 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -76,33 +76,32 @@ struct view_traits>> */ template struct array_view_traits -{ -}; +{ }; /** * \brief This macro defines some template specializations that help us access * ArrayView<> names. This can be helpful when the ArrayView comes into * a lambda as an auto argument. */ -#define AXOM_MAKE_TRAIT(TYPE)\ -template <>\ -struct array_view_traits>\ -{\ - using value_type = TYPE;\ - static constexpr const char * name()\ - {\ - return "axom::ArrayView<" #TYPE ">";\ - }\ -};\ -template <>\ -struct array_view_traits>\ -{\ - using value_type = const TYPE;\ - static constexpr const char * name()\ - {\ - return "axom::ArrayView";\ - }\ -}; +#define AXOM_MAKE_TRAIT(TYPE) \ + template <> \ + struct array_view_traits> \ + { \ + using value_type = TYPE; \ + static constexpr const char* name() \ + { \ + return "axom::ArrayView<" #TYPE ">"; \ + } \ + }; \ + template <> \ + struct array_view_traits> \ + { \ + using value_type = const TYPE; \ + static constexpr const char* name() \ + { \ + return "axom::ArrayView"; \ + } \ + }; AXOM_MAKE_TRAIT(signed char) AXOM_MAKE_TRAIT(char) From e52d1bdb8aaacdd370c7e97b67477102aae4b00d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 13:52:06 -0700 Subject: [PATCH 227/290] More timings --- src/axom/mir/MIRAlgorithm.hpp | 5 +++-- src/axom/mir/ZoneListBuilder.hpp | 22 +++++++++++++++++++ .../mir/examples/mir_concentric_circles.cpp | 1 + 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index d265d3f658..87485ea26f 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -37,6 +37,7 @@ class MIRAlgorithm domains that contain a topology and matset to be used for MIR. \param[in] n_options A node that contains options that help govern MIR execution. +\code{.yaml} options: topology: main matset: matset @@ -46,9 +47,9 @@ class MIRAlgorithm fields: - temperature - pressure - zones: [0,1,6,9] + selectedZones: [0,1,6,9] mapping: 0 - +\endcode The "topology" option specifies which topology we'll reconstruct. It must have an associated matset. "new_topology" is the name of the topology that will be created in the output node. "new_coordset" is the name of the new coordset that will be created in the output node. If it is not provided then the name of the topology's coordset will be used. diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index bc8a9dd9a1..414b195ebe 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -68,6 +68,7 @@ class ZoneListBuilder AXOM_ANNOTATE_SCOPE("ZoneListBuilder"); const int allocatorID = axom::execution_space::allocatorID(); + AXOM_ANNOTATE_BEGIN("nMatsPerNode"); axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); nMatsPerNode.fill(0); auto nMatsPerNodeView = nMatsPerNode.view(); @@ -86,8 +87,10 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); + AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all zones that have 1 mat per node as clean. + AXOM_ANNOTATE_BEGIN("mask"); const auto nzones = m_topologyView.numberOfZones(); axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); @@ -106,15 +109,19 @@ class ZoneListBuilder maskView[zoneIndex] = ival; mask_reduce += ival; }); + AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); if(nClean > 0) { + AXOM_ANNOTATE_BEGIN("offsets"); axom::Array maskOffsets(nzones, nzones, allocatorID); auto maskOffsetsView = maskOffsets.view(); axom::exclusive_scan(maskView, maskOffsetsView); + AXOM_ANNOTATE_END("offsets"); // Make the output cleanIndices array. + AXOM_ANNOTATE_BEGIN("cleanIndices"); cleanIndices = axom::Array(nClean, nClean, allocatorID); auto cleanIndicesView = cleanIndices.view(); axom::for_all( @@ -125,8 +132,10 @@ class ZoneListBuilder cleanIndicesView[maskOffsetsView[index]] = index; } }); + AXOM_ANNOTATE_END("cleanIndices"); // Make the mixedIndices array. + AXOM_ANNOTATE_BEGIN("mixedIndices"); axom::for_all( nzones, AXOM_LAMBDA(auto index) { @@ -144,9 +153,11 @@ class ZoneListBuilder mixedIndicesView[maskOffsetsView[index]] = index; } }); + AXOM_ANNOTATE_END("mixedIndices"); } else { + AXOM_ANNOTATE_SCOPE("mixedIndices"); cleanIndices = axom::Array(); // There were no clean, so it must all be mixed. @@ -187,6 +198,7 @@ class ZoneListBuilder const int allocatorID = axom::execution_space::allocatorID(); + AXOM_ANNOTATE_BEGIN("nMatsPerNode"); axom::Array nMatsPerNode(nnodes, nnodes, allocatorID); nMatsPerNode.fill(0); auto nMatsPerNodeView = nMatsPerNode.view(); @@ -206,8 +218,10 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); + AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all selected zones that have 1 mat per node as clean. + AXOM_ANNOTATE_BEGIN("mask"); const auto nzones = selectedZonesView.size(); axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); @@ -227,15 +241,19 @@ class ZoneListBuilder maskView[szIndex] = ival; mask_reduce += ival; }); + AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); if(nClean > 0) { + AXOM_ANNOTATE_BEGIN("offsets"); axom::Array maskOffsets(nzones, nzones, allocatorID); auto maskOffsetsView = maskOffsets.view(); axom::exclusive_scan(maskView, maskOffsetsView); + AXOM_ANNOTATE_END("offsets"); // Make the output cleanIndices array. + AXOM_ANNOTATE_BEGIN("cleanIndices"); cleanIndices = axom::Array(nClean, nClean, allocatorID); auto cleanIndicesView = cleanIndices.view(); axom::for_all( @@ -246,8 +264,10 @@ class ZoneListBuilder cleanIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; } }); + AXOM_ANNOTATE_END("cleanIndices"); // Make the mixedIndices array. + AXOM_ANNOTATE_BEGIN("mixedIndices"); axom::for_all( nzones, AXOM_LAMBDA(auto index) { @@ -265,9 +285,11 @@ class ZoneListBuilder mixedIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; } }); + AXOM_ANNOTATE_END("mixedIndices"); } else { + AXOM_ANNOTATE_SCOPE("mixedIndices"); cleanIndices = axom::Array(); // There were no clean, so it must all be mixed. diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index c4ea482221..1c50a6392a 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -3,6 +3,7 @@ // // SPDX-License-Identifier: (BSD-3-Clause) +#include "axom/config.hpp" #include "axom/core.hpp" // for axom macros #include "axom/slic.hpp" #include "axom/mir.hpp" // for Mir classes & functions From e25cf830d7ee0e1295c05e76a64c4de95b3b2c5c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 15:18:47 -0700 Subject: [PATCH 228/290] remove warning --- src/axom/core/utilities/BitUtilities.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/axom/core/utilities/BitUtilities.hpp b/src/axom/core/utilities/BitUtilities.hpp index 034b372ab4..a22768dd11 100644 --- a/src/axom/core/utilities/BitUtilities.hpp +++ b/src/axom/core/utilities/BitUtilities.hpp @@ -245,7 +245,7 @@ template AXOM_HOST_DEVICE constexpr bool bitIsSet(FlagType flags, BitType bit) { - assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); + assert(static_cast(bit) < BitTraits::BITS_PER_WORD << 3); return (flags & (1 << bit)) > 0; } @@ -264,7 +264,7 @@ template AXOM_HOST_DEVICE constexpr void setBit(FlagType &flags, BitType bit, bool value = true) { - assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); + assert(static_cast(bit) < BitTraits::BITS_PER_WORD << 3); const auto mask = 1 << bit; flags = value ? (flags | mask) : (flags & ~mask); } @@ -283,7 +283,7 @@ template AXOM_HOST_DEVICE constexpr void setBitOn(FlagType &flags, BitType bit) { - assert(bit >= 0 && (static_cast(bit) < BitTraits::BITS_PER_WORD << 3)); + assert(static_cast(bit) < BitTraits::BITS_PER_WORD << 3); flags |= (1 << bit); } From 99596ffb2a48a22d2d8567b29595483afe87f842 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 15:19:06 -0700 Subject: [PATCH 229/290] Fix build --- src/axom/mir/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index d6c6b051f4..3c90c8278d 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -129,9 +129,3 @@ if (AXOM_ENABLE_EXAMPLES) endif() -#------------------------------------------------------------------------------ -# Add code checks -#------------------------------------------------------------------------------ -axom_add_code_checks( - PREFIX mir ) - From d6ea69105d2c29ddffea609223dd87510146b0c3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 18:28:36 -0700 Subject: [PATCH 230/290] Some progress on adjusting for CUDA --- src/axom/core/utilities/Sorting.hpp | 9 ++- src/axom/mir/BlendGroupBuilder.hpp | 6 +- src/axom/mir/ClipField.hpp | 58 +++++++++-------- src/axom/mir/ExtractZones.hpp | 63 +++++++++++-------- src/axom/mir/NodeToZoneRelationBuilder.hpp | 15 ++--- src/axom/mir/SelectedZones.hpp | 6 +- src/axom/mir/ZoneListBuilder.hpp | 25 ++++---- src/axom/mir/blueprint_utilities.hpp | 6 +- src/axom/mir/clipping/ClipTableManager.hpp | 3 +- src/axom/mir/examples/CMakeLists.txt | 2 +- .../mir/examples/mir_concentric_circles.cpp | 5 ++ src/axom/mir/tests/CMakeLists.txt | 10 +-- src/axom/mir/tests/mir_views.cpp | 8 +-- src/axom/mir/utilities.hpp | 13 +--- src/axom/mir/views/StructuredTopologyView.hpp | 49 ++++++++------- .../UnstructuredTopologyMixedShapeView.hpp | 6 +- .../UnstructuredTopologyPolyhedralView.hpp | 6 +- .../UnstructuredTopologySingleShapeView.hpp | 10 +-- 18 files changed, 158 insertions(+), 142 deletions(-) diff --git a/src/axom/core/utilities/Sorting.hpp b/src/axom/core/utilities/Sorting.hpp index 5fee2d4a32..4e2cd4b846 100644 --- a/src/axom/core/utilities/Sorting.hpp +++ b/src/axom/core/utilities/Sorting.hpp @@ -6,7 +6,7 @@ #ifndef AXOM_CORE_UTILITIES_SORTING_HPP #define AXOM_CORE_UTILITIES_SORTING_HPP #include -#include +#include namespace axom { @@ -22,10 +22,9 @@ namespace utilities * increases, the algorithm switches to qsort. Also, this is designed as * a template class so it can be specialized. */ -template ::max()> -class Sorting +template ::max()> +struct Sorting { -public: /** * \brief Sort an array of values in place. * @@ -41,11 +40,11 @@ class Sorting qsort(values, n); } -private: /** * \brief Computes stack size for qsort. * \return A number of elements for an array-based stack. */ + AXOM_HOST_DEVICE constexpr static int stack_size() { int v = N; diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index 6faa746fb9..5147f814c4 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -101,7 +101,7 @@ class BlendGroupBuilder const auto localBlendGroupsLenView = m_state.m_blendGroupsLenView; axom::for_all( m_state.m_nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { blendGroups_sum += localBlendGroupsView[zoneIndex]; blendGroupLen_sum += localBlendGroupsLenView[zoneIndex]; }); @@ -464,7 +464,7 @@ class BlendGroupBuilder State deviceState(m_state); axom::for_all( nIndices, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto uniqueIndex = deviceState.m_blendUniqueIndicesView[index]; const int m = (deviceState.m_blendGroupSizesView[uniqueIndex] > 1) ? 1 : 0; @@ -491,7 +491,7 @@ class BlendGroupBuilder auto newUniqueIndicesView = newUniqueIndices.view(); axom::for_all( nIndices, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { const auto offset = offsetView[index]; diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 47dfe55a76..9811852065 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -291,7 +291,7 @@ class FieldIntersector views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { axom::for_all( n, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { clipFieldView[index] = static_cast(clipFieldViewSrc[index]); }); @@ -359,6 +359,7 @@ class ClipField using BlendGroupBuilderType = BlendGroupBuilder; using ClipFieldType = float; + using ZoneType = typename TopologyView::ShapeType; /** * \brief Constructor @@ -709,7 +710,7 @@ class ClipField const auto selectedZonesView = selectedZones.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto zoneIndex = selectedZonesView[index]; const auto start = fragmentData.m_fragmentOffsetsView[index]; for(int i = 0; i < fragmentData.m_fragmentsView[index]; i++) @@ -735,7 +736,11 @@ class ClipField markNewNodes(blend, newNodes, opts.topologyName(n_topo.name()), n_newFields); } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /** * \brief Contains data that describes the number and size of zone fragments in the output. */ @@ -839,12 +844,12 @@ class ClipField // Initialize nodeUsed data for nodes. axom::for_all( nodeData.m_nodeUsedView.size(), - AXOM_LAMBDA(auto index) { nodeData.m_nodeUsedView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { nodeData.m_nodeUsedView[index] = 0; }); const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { // Get the clip case for the current zone. const auto clipcase = deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); @@ -996,7 +1001,7 @@ class ClipField const auto fragmentsView = fragmentData.m_fragmentsView; axom::for_all( nzones, - AXOM_LAMBDA(auto szIndex) { fragment_sum += fragmentsView[szIndex]; }); + AXOM_LAMBDA(axom::IndexType szIndex) { fragment_sum += fragmentsView[szIndex]; }); fragmentData.m_finalNumZones = fragment_sum.get(); // Sum the fragment connectivity sizes. @@ -1004,7 +1009,7 @@ class ClipField const auto fragmentsSizeView = fragmentData.m_fragmentsSizeView; axom::for_all( nzones, - AXOM_LAMBDA(auto szIndex) { + AXOM_LAMBDA(axom::IndexType szIndex) { fragment_nids_sum += fragmentsSizeView[szIndex]; }); fragmentData.m_finalConnSize = fragment_nids_sum.get(); @@ -1052,7 +1057,7 @@ class ClipField const auto nodeUsedView = nodeData.m_nodeUsedView; axom::for_all( nodeUsedView.size(), - AXOM_LAMBDA(auto index) { nUsed_reducer += nodeUsedView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { nUsed_reducer += nodeUsedView[index]; }); return nUsed_reducer.get(); } @@ -1075,7 +1080,7 @@ class ClipField // Make the compact node list and oldToNew map. axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { IndexType newId = 0; if(nodeData.m_nodeUsedView[index] > 0) { @@ -1122,7 +1127,7 @@ class ClipField const auto deviceIntersector = m_intersector.view(); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -1308,13 +1313,13 @@ class ClipField // Fill in connectivity values in case we leave empty slots later. axom::for_all( connView.size(), - AXOM_LAMBDA(auto index) { connView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { connView[index] = 0; }); #if defined(AXOM_DEBUG_CLIP_FIELD) // Initialize the values beforehand. For debugging. axom::for_all( shapesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { shapesView[index] = -2; sizesView[index] = -3; offsetsView[index] = -4; @@ -1339,9 +1344,9 @@ class ClipField const auto origSize = nodeData.m_originalIdsView.size(); m_topologyView.template for_selected_zones( selectedZones.view(), - AXOM_LAMBDA(auto szIndex, - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; @@ -1538,7 +1543,7 @@ class ClipField RAJA::ReduceSum mask_reduce(0); axom::for_all( nz, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const int ival = (sizesView[index] > 0) ? 1 : 0; maskView[index] = ival; mask_reduce += ival; @@ -1560,7 +1565,7 @@ class ClipField const auto nValues = maskView.size(); axom::for_all( nValues, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { const auto destIndex = maskOffsetsView[index]; @@ -1591,7 +1596,7 @@ class ClipField RAJA::ReduceBitOr shapesUsed_reduce(0); axom::for_all( shapesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { BitSet shapeBit {}; axom::utilities::setBitOn(shapeBit, shapesView[index]); shapesUsed_reduce |= shapeBit; @@ -1623,7 +1628,6 @@ class ClipField n_newFields.remove(opts.colorField()); } -#if 1 // Handle some quad->tri degeneracies if constexpr(TopologyView::dimension() == 2) { @@ -1634,7 +1638,7 @@ class ClipField RAJA::ReduceBitOr shapesUsed_reduce(0); axom::for_all( numOutputZones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(shapesView[index] == views::Quad_ShapeID) { const auto offset = offsetsView[index]; @@ -1655,8 +1659,8 @@ class ClipField connView[offset] = pts[0]; connView[offset + 1] = pts[1]; connView[offset + 2] = pts[2]; - connView[offset + 3] = - pts[2]; // Repeat the last point (it won't be used though). + // Repeat the last point (it won't be used though). + connView[offset + 3] = pts[2]; } } @@ -1668,7 +1672,6 @@ class ClipField shapesUsed = shapesUsed_reduce.get(); } } -#endif // Add shape information to the connectivity. SLIC_ASSERT_MSG(shapesUsed != 0, "No shapes were produced!"); @@ -1864,7 +1867,7 @@ class ClipField auto valuesView = bputils::make_array_view(n_values); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; const int nFragments = fragmentData.m_fragmentsView[index]; const auto zoneIndex = selectedZonesView[index]; @@ -1885,7 +1888,7 @@ class ClipField auto valuesView = bputils::make_array_view(n_values); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; const int nFragments = fragmentData.m_fragmentsView[index]; const auto zoneIndex = selectedZonesView[index]; @@ -1974,7 +1977,7 @@ class ClipField // Update values for the blend groups only. axom::for_all( blendSize, - AXOM_LAMBDA(auto bgid) { valuesView[origSize + bgid] = one; }); + AXOM_LAMBDA(axom::IndexType bgid) { valuesView[origSize + bgid] = one; }); } else { @@ -1994,14 +1997,17 @@ class ClipField // Everything above is a blended node. axom::for_all( outputSize, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { valuesView[index] = (index < origSize) ? zero : one; }); } } } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif TopologyView m_topologyView {}; CoordsetView m_coordsetView {}; Intersector m_intersector {}; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 06c5870c4b..635467a5a2 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -32,6 +32,7 @@ class ExtractZones public: using SelectedZonesView = axom::ArrayView; + using ZoneType = typename TopologyView::ShapeType; /** * \brief Constructor @@ -147,7 +148,10 @@ class ExtractZones } } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) protected: +#endif /** * \brief This struct contains extra amounts of storage that we might want to overallocate. */ @@ -251,9 +255,9 @@ class ExtractZones RAJA::ReduceSum connsize_reduce(0); m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { connsize_reduce += zone.numberOfNodes(); }); + AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { connsize_reduce += zone.numberOfNodes(); }); const auto newConnSize = connsize_reduce.get(); Sizes sizes {}; @@ -267,7 +271,7 @@ class ExtractZones auto nodeSliceView = nodeSlice.view(); axom::for_all( sizes.nodes + extra.nodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { nodeSliceView[index] = (index < sizes.nodes) ? index : 0; }); @@ -305,11 +309,11 @@ class ExtractZones RAJA::ReduceSum connsize_reduce(0); m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { - const int nids = zone.numberOfNodes(); - for(int i = 0; i < nids; i++) + AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { + const axom::IndexType nids = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nids; i++) { const auto nodeId = zone.getId(i); maskView[nodeId] = 1; @@ -322,7 +326,7 @@ class ExtractZones RAJA::ReduceSum mask_reduce(0); axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { mask_reduce += maskView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { mask_reduce += maskView[index]; }); const int newNumNodes = mask_reduce.get(); // Make a compact list of nodes. @@ -339,7 +343,7 @@ class ExtractZones auto nodeSliceView = nodeSlice.view(); axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { nodeSliceView[maskOffsetsView[index]] = index; @@ -351,7 +355,7 @@ class ExtractZones axom::for_all( nnodes, nnodes + extra.nodes, - AXOM_LAMBDA(auto index) { nodeSliceView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { nodeSliceView[index] = 0; }); } Sizes sizes {}; @@ -419,9 +423,9 @@ class ExtractZones // Fill sizes, offsets m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto szIndex, - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { sizesView[szIndex] = zone.numberOfNodes(); }); if(extra.zones > 0) @@ -429,7 +433,7 @@ class ExtractZones axom::for_all( dataSizes.zones, dataSizes.zones + extra.zones, - AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { sizesView[index] = 0; }); } axom::exclusive_scan(sizesView, offsetsView); @@ -439,9 +443,9 @@ class ExtractZones const axom::ArrayView deviceOld2NewView(old2newView); m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto szIndex, - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { const int size = static_cast(sizesView[szIndex]); const auto offset = offsetsView[szIndex]; for(int i = 0; i < size; i++) @@ -457,9 +461,9 @@ class ExtractZones { m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto szIndex, - auto AXOM_UNUSED_PARAM(zoneIndex), - const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, + axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), + const ZoneType &zone) { const int size = static_cast(sizesView[szIndex]); const auto offset = offsetsView[szIndex]; for(int i = 0; i < size; i++) @@ -473,7 +477,7 @@ class ExtractZones axom::for_all( dataSizes.connectivity, dataSizes.connectivity + extra.connectivity, - AXOM_LAMBDA(auto index) { connView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { connView[index] = 0; }); } // Handle shapes, if present. @@ -493,7 +497,7 @@ class ExtractZones const SelectedZonesView deviceSelectedZonesView(selectedZonesView); axom::for_all( dataSizes.zones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { newShapesView[index] = shapesView[deviceSelectedZonesView[index]]; }); if(extra.zones > 0) @@ -501,7 +505,7 @@ class ExtractZones axom::for_all( dataSizes.zones, dataSizes.zones + extra.zones, - AXOM_LAMBDA(auto index) { newShapesView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { newShapesView[index] = 0; }); } } } @@ -629,7 +633,10 @@ class ExtractZones return shape; } -private: +// The following members are protected (unless using CUDA) +#if !defined(__CUDACC__) +protected: +#endif TopologyView m_topologyView; CoordsetView m_coordsetView; axom::Array m_zoneSlice; @@ -701,7 +708,11 @@ class ExtractZonesAndMatset } } +// The following members are protected (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /** * \brief Return the matset for the named topology. * diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 7b9f0f006c..a8d90271af 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -99,7 +99,7 @@ struct BuildRelation const value_type totalSize = nodesView.size(); axom::for_all( offsetsView.size(), - AXOM_LAMBDA(auto i) { + AXOM_LAMBDA(axom::IndexType i) { sizesView[i] = (i < offsetsView.size() - 1) ? (offsetsView[i + 1] - offsetsView[i]) : (totalSize - offsetsView[i]); @@ -126,10 +126,10 @@ struct BuildRelation const auto nnodes = offsetsView.size(); axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { sizesView[index] = 0; }); axom::for_all( nodesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { sizesView[nodesView[index]]++; // Works only because ExecSpace=SEQ_EXEC. }); // Make offsets @@ -137,7 +137,7 @@ struct BuildRelation axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { sizesView[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { sizesView[index] = 0; }); axom::Array zcopy(zonesView.size(), zonesView.size(), allocatorID); axom::copy(zcopy.data(), @@ -148,7 +148,7 @@ struct BuildRelation // Fill in zonesView, sizesView with each node's zones. axom::for_all( nodesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto ni = nodesView[index]; const auto destOffset = offsetsView[ni] + sizesView[ni]; zonesView[destOffset] = zcopyView[index]; @@ -218,9 +218,10 @@ class NodeToZoneRelationBuilder auto sizes_view = sizes.view(); // Run through the topology once to do a count of each zone's unique node ids. + using ZoneType = typename decltype(topoView)::ShapeType; RAJA::ReduceSum count(0); topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { const auto uniqueIds = zone.getUniqueIds(); sizes_view[zoneIndex] = uniqueIds.size(); count += uniqueIds.size(); @@ -253,7 +254,7 @@ class NodeToZoneRelationBuilder auto offsetsView) { // Run through the data one more time to build the nodes and zones arrays. topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { const auto uniqueIds = zone.getUniqueIds(); auto destIdx = offsets_view[zoneIndex]; for(axom::IndexType i = 0; i < uniqueIds.size(); diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index b670b1d325..5833894241 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -76,13 +76,13 @@ class SelectedZones auto szView = m_selectedZonesView = m_selectedZones.view(); axom::for_all( szView.size(), - AXOM_LAMBDA(auto index) { szView[index] = zonesView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { szView[index] = zonesView[index]; }); // Check that the selected zone values are in range. RAJA::ReduceSum errReduce(0); axom::for_all( szView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const int err = (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; errReduce += err; @@ -105,7 +105,7 @@ class SelectedZones auto szView = m_selectedZonesView = m_selectedZones.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { szView[zoneIndex] = zoneIndex; }); + AXOM_LAMBDA(axom::IndexType zoneIndex) { szView[zoneIndex] = zoneIndex; }); } } diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index 414b195ebe..6b6838b1fa 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -31,6 +31,7 @@ class ZoneListBuilder { public: using SelectedZonesView = axom::ArrayView; + using ZoneType = typename TopologyView::ShapeType; /** * \brief Constructor @@ -76,7 +77,7 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); m_topologyView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); const auto nnodesThisZone = zone.numberOfNodes(); int *nodeData = nMatsPerNodeView.data(); @@ -96,7 +97,7 @@ class ZoneListBuilder auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); m_topologyView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { bool clean = true; const axom::IndexType nnodesThisZone = zone.numberOfNodes(); for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) @@ -126,7 +127,7 @@ class ZoneListBuilder auto cleanIndicesView = cleanIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { cleanIndicesView[maskOffsetsView[index]] = index; @@ -138,7 +139,7 @@ class ZoneListBuilder AXOM_ANNOTATE_BEGIN("mixedIndices"); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { maskView[index] = (maskView[index] == 1) ? 0 : 1; }); axom::exclusive_scan(maskView, maskOffsetsView); @@ -147,7 +148,7 @@ class ZoneListBuilder auto mixedIndicesView = mixedIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { mixedIndicesView[maskOffsetsView[index]] = index; @@ -165,7 +166,7 @@ class ZoneListBuilder auto mixedIndicesView = mixedIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { mixedIndicesView[index] = index; }); + AXOM_LAMBDA(axom::IndexType index) { mixedIndicesView[index] = index; }); } } @@ -207,7 +208,7 @@ class ZoneListBuilder MatsetView deviceMatsetView(m_matsetView); m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto AXOM_UNUSED_PARAM(szIndex), auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), axom::IndexType zoneIndex, const ZoneType &zone) { const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); const auto nnodesThisZone = zone.numberOfNodes(); int *nodeData = nMatsPerNodeView.data(); @@ -228,7 +229,7 @@ class ZoneListBuilder RAJA::ReduceSum mask_reduce(0); m_topologyView.template for_selected_zones( selectedZonesView, - AXOM_LAMBDA(auto szIndex, auto AXOM_UNUSED_PARAM(zoneIndex), const auto &zone) { + AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ZoneType &zone) { bool clean = true; const axom::IndexType nnodesThisZone = zone.numberOfNodes(); for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) @@ -258,7 +259,7 @@ class ZoneListBuilder auto cleanIndicesView = cleanIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { cleanIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; @@ -270,7 +271,7 @@ class ZoneListBuilder AXOM_ANNOTATE_BEGIN("mixedIndices"); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { maskView[index] = (maskView[index] == 1) ? 0 : 1; }); axom::exclusive_scan(maskView, maskOffsetsView); @@ -279,7 +280,7 @@ class ZoneListBuilder auto mixedIndicesView = mixedIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { mixedIndicesView[maskOffsetsView[index]] = selectedZonesView[index]; @@ -297,7 +298,7 @@ class ZoneListBuilder auto mixedIndicesView = mixedIndices.view(); axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { mixedIndicesView[index] = selectedZonesView[index]; }); } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 361eb36908..ee6cde8f9b 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -9,6 +9,7 @@ #include "axom/core/execution/execution_space.hpp" #include "axom/core/Array.hpp" #include "axom/core/ArrayView.hpp" +#include "axom/core/NumericLimits.hpp" #include "axom/core/memory_management.hpp" #include "axom/mir/views/dispatch_structured_topology.hpp" #include "axom/mir/views/NodeArrayView.hpp" @@ -20,7 +21,6 @@ #include #endif -#include #include #include @@ -482,9 +482,9 @@ std::pair minmax(const conduit::Node &n) typename axom::execution_space::reduce_policy; RAJA::ReduceMin vmin( - std::numeric_limits::max()); + axom::numeric_limits::max()); RAJA::ReduceMax vmax( - std::numeric_limits::min()); + axom::numeric_limits::min()); axom::for_all( nview.size(), diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 569aa866f5..5ee2cca948 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -405,9 +405,8 @@ class ClipTableManager */ Table &operator[](size_t shape) { - const auto index = shapeToIndex(shape); + const size_t index = shapeToIndex(shape); assert(shape < ST_MAX); - assert(index >= 0); loadShape(shape); return m_tables[index]; } diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 24878a8dde..6e20afe1ac 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -18,7 +18,7 @@ foreach( example ${mir_examples} ) get_filename_component( example_name ${example} NAME_WE ) - blt_add_executable( + axom_add_executable( NAME ${example_name}_ex SOURCES ${example} OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 1c50a6392a..ef3d833658 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -128,20 +128,24 @@ int runMIR(RuntimePolicy policy, options["matset"] = "mat"; int retval = 0; +std::cout << axom::fmt::format("policy={}", policy) << std::endl; if(policy == RuntimePolicy::seq) { +#pragma message "SEQ supported" retval = runMIR(mesh, options, resultMesh); } #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) #if defined(AXOM_USE_OPENMP) else if(policy == RuntimePolicy::omp) { +#pragma message "OMP supported" retval = runMIR(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_CUDA) else if(policy == RuntimePolicy::cuda) { +#pragma message "CUDA supported" constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; retval = runMIR(mesh, options, resultMesh); @@ -150,6 +154,7 @@ int runMIR(RuntimePolicy policy, #if defined(AXOM_USE_HIP) else if(policy == RuntimePolicy::hip) { +#pragma message "HIP supported" constexpr int HIP_BLOCK_SIZE = 64; using hip_exec = axom::HIP_EXEC; retval = runMIR(mesh, options, resultMesh); diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 123d96c45e..08f3bfd18b 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -33,11 +33,11 @@ set(mir_tests_depends_on #------------------------------------------------------------------------------ foreach(test ${gtest_mir_tests}) get_filename_component( test_name ${test} NAME_WE ) - blt_add_executable( NAME ${test_name}_test - SOURCES ${test} - OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} - DEPENDS_ON ${mir_tests_depends_on} - FOLDER axom/mir/tests ) + axom_add_executable( NAME ${test_name}_test + SOURCES ${test} + OUTPUT_DIR ${TEST_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_tests_depends_on} + FOLDER axom/mir/tests ) blt_add_test( NAME ${test_name} COMMAND ${test_name}_test ) diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 321fc1e67a..872f0971c3 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -96,7 +96,7 @@ struct test_node_to_arrayview // Make sure we can store values in dataView axom::for_all( n, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { dataView[index] = static_cast(index); }); @@ -104,7 +104,7 @@ struct test_node_to_arrayview RAJA::ReduceSum sumValues_reduce(0); axom::for_all( n, - AXOM_LAMBDA(auto index) { sumValues_reduce += dataView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { sumValues_reduce += dataView[index]; }); sumValues = static_cast(sumValues_reduce.get()); }); @@ -190,7 +190,7 @@ TEST(mir_views, strided_structured) [&](const std::string &AXOM_UNUSED_PARAM(shape), auto topoView) { // Traverse the zones in the mesh and check the zone ids. topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const auto &zone) { // Check zone ids. const auto ids = zone.getIds(); for(axom::IndexType i = 0; i < ids.size(); i++) @@ -256,7 +256,7 @@ void test_matsetview(MatsetView matsetView, int allocatorID) constexpr int nResultsPerZone = nResults / nZones; axom::for_all( 3, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { resultsView[nResultsPerZone * index + 0] = matsetView.zoneContainsMaterial(zoneidsView[index], MATA) ? 1 : 0; resultsView[nResultsPerZone * index + 1] = diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 4f3a320ccf..c76241808a 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -7,9 +7,6 @@ #define AXOM_MIR_UTILITIES_HPP_ #include "axom/core.hpp" -//#include "axom/core/Array.hpp" -//#include "axom/core/ArrayView.hpp" -//#include "axom/core/memory_management.hpp" #include #include @@ -46,10 +43,6 @@ inline void exclusive_scan(const ContiguousMemoryContainer &input, } // end namespace axom -/// This header is for device utility functions for MIR. - -// Q: does this belong in core with a better name? - namespace axom { namespace mir @@ -265,8 +258,8 @@ class HashNaming assert(static_cast(p0) <= Max31Bit && static_cast(p1) <= Max31Bit); // Store p0 and p1 both in the 64-bit key as 31-bit integers - KeyType k0 = (static_cast(std::min(p0, p1)) & Max31Bit); - KeyType k1 = (static_cast(std::max(p0, p1)) & Max31Bit); + KeyType k0 = (static_cast(axom::utilities::min(p0, p1)) & Max31Bit); + KeyType k1 = (static_cast(axom::utilities::max(p0, p1)) & Max31Bit); return KeyIDPair | (k0 << 31) | k1; } @@ -365,7 +358,7 @@ class HashNaming return retval; } - KeyType m_maxId {std::numeric_limits::max()}; + KeyType m_maxId {axom::numeric_limits::max()}; }; // Host-callable methods diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index c4e2e44f5f..d47cb56dec 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -27,13 +27,14 @@ class StructuredTopologyView using IndexType = typename IndexingPolicy::IndexType; using LogicalIndex = typename IndexingPolicy::LogicalIndex; using ConnectivityType = IndexType; + using ShapeType = typename std::conditional, typename std::conditional, LineShape>::type>::type; /** * \brief Return the number of dimensions. * * \return The number of dimensions. */ - constexpr static int dimension() { return IndexingPolicy::dimension(); } + AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } /** * \brief Constructor @@ -122,13 +123,13 @@ class StructuredTopologyView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { - using ShapeType = HexShape; + AXOM_LAMBDA(axom::IndexType zoneIndex) { + //using ShapeType = HexShape; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); - IndexType data[8]; + ConnectivityType data[8]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; @@ -139,7 +140,7 @@ class StructuredTopologyView data[6] = data[2] + kp; data[7] = data[3] + kp; - const ShapeType shape(axom::ArrayView(data, 8)); + const ShapeType shape(axom::ArrayView(data, 8)); func(zoneIndex, shape); }); } @@ -152,18 +153,18 @@ class StructuredTopologyView 0, nzones, AXOM_LAMBDA(auto zoneIndex) { - using ShapeType = QuadShape; + //using ShapeType = QuadShape; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); - IndexType data[4]; + ConnectivityType data[4]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; - const ShapeType shape(axom::ArrayView(data, 4)); + const ShapeType shape(axom::ArrayView(data, 4)); func(zoneIndex, shape); }); } @@ -175,16 +176,16 @@ class StructuredTopologyView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { - using ShapeType = LineShape; + AXOM_LAMBDA(axom::IndexType zoneIndex) { + //using ShapeType = LineShape; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - IndexType data[2]; + ConnectivityType data[2]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; - const ShapeType shape(axom::ArrayView(data, 2)); + const ShapeType shape(axom::ArrayView(data, 2)); func(zoneIndex, shape); }); } @@ -217,14 +218,14 @@ class StructuredTopologyView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { - using ShapeType = HexShape; + AXOM_LAMBDA(axom::IndexType selectIndex) { + //using ShapeType = HexShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); - IndexType data[8]; + ConnectivityType data[8]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; @@ -235,7 +236,7 @@ class StructuredTopologyView data[6] = data[2] + kp; data[7] = data[3] + kp; - const ShapeType shape(axom::ArrayView(data, 8)); + const ShapeType shape(axom::ArrayView(data, 8)); func(selectIndex, zoneIndex, shape); }); } @@ -247,20 +248,20 @@ class StructuredTopologyView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { - using ShapeType = QuadShape; + AXOM_LAMBDA(axom::IndexType selectIndex) { + //using ShapeType = QuadShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); - IndexType data[4]; + ConnectivityType data[4]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; - const ShapeType shape(axom::ArrayView(data, 4)); + const ShapeType shape(axom::ArrayView(data, 4)); func(selectIndex, zoneIndex, shape); }); } @@ -272,17 +273,17 @@ class StructuredTopologyView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { - using ShapeType = LineShape; + AXOM_LAMBDA(axom::IndexType selectIndex) { + //using ShapeType = LineShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - IndexType data[2]; + ConnectivityType data[2]; data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; - const ShapeType shape(axom::ArrayView(data, 2)); + const ShapeType shape(axom::ArrayView(data, 2)); func(selectIndex, zoneIndex, shape); }); } diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 479c3615f4..4d628322fd 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -122,7 +122,7 @@ class UnstructuredTopologyMixedShapeView * * \return -1 for unknown dimension. We'd have to look at the shapes. */ - static constexpr int dimension() { return -1; } + AXOM_HOST_DEVICE static constexpr int dimension() { return -1; } /** * \brief Return the number of zones. @@ -164,7 +164,7 @@ class UnstructuredTopologyMixedShapeView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { const ConnectivityView shapeData( connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); @@ -209,7 +209,7 @@ class UnstructuredTopologyMixedShapeView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { + AXOM_LAMBDA(axom::IndexType selectIndex) { const auto zoneIndex = deviceSelectedIdsView[selectIndex]; const ConnectivityView shapeData( connectivityView.data() + offsets[zoneIndex], diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 25b594aa97..c4e8a4697d 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -221,7 +221,7 @@ class UnstructuredTopologyPolyhedralView * * \return The dimension of the shape. */ - static constexpr int dimension() { return 3; } + AXOM_HOST_DEVICE static constexpr int dimension() { return 3; } /** * \brief Execute a function for each zone in the mesh. @@ -240,7 +240,7 @@ class UnstructuredTopologyPolyhedralView axom::for_all( 0, nzones, - AXOM_LAMBDA(int zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { const PolyhedronShape shape(sd, zoneIndex); func(zoneIndex, shape); }); @@ -266,7 +266,7 @@ class UnstructuredTopologyPolyhedralView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { + AXOM_LAMBDA(axom::IndexType selectIndex) { const auto zoneIndex = idsView[selectIndex]; const PolyhedronShape shape(sd, zoneIndex); func(selectIndex, zoneIndex, shape); diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 9c632575ad..ef8f1d5d6e 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -61,7 +61,7 @@ class UnstructuredTopologySingleShapeView * * \return The dimension of the shape. */ - static constexpr int dimension() { return ShapeT::dimension(); } + AXOM_HOST_DEVICE static constexpr int dimension() { return ShapeT::dimension(); } /** * \brief Return the number of zones. @@ -103,7 +103,7 @@ class UnstructuredTopologySingleShapeView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { const ConnectivityView shapeIdsView( connectivityView.data() + offsetsView[zoneIndex], sizesView[zoneIndex]); @@ -118,7 +118,7 @@ class UnstructuredTopologySingleShapeView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { ConnectivityView shapeIdsView {}; if(sizesView.empty()) { @@ -161,7 +161,7 @@ class UnstructuredTopologySingleShapeView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { + AXOM_LAMBDA(axom::IndexType selectIndex) { const auto zoneIndex = localSelectedIdsView[selectIndex]; const ConnectivityView shapeIdsView( connectivityView.data() + offsetsView[zoneIndex], @@ -177,7 +177,7 @@ class UnstructuredTopologySingleShapeView axom::for_all( 0, nSelectedZones, - AXOM_LAMBDA(auto selectIndex) { + AXOM_LAMBDA(axom::IndexType selectIndex) { const auto zoneIndex = localSelectedIdsView[selectIndex]; ConnectivityView shapeIdsView {}; if(sizesView.empty()) From 0a1d705a087cf0927d7ebf2eeb7c4ec2871cd25c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 18:52:10 -0700 Subject: [PATCH 231/290] Changed comment format --- src/axom/mir/BlendGroupBuilder.hpp | 62 +++++++++---------- src/axom/mir/ClipField.hpp | 62 +++++++++---------- src/axom/mir/CoordsetBlender.hpp | 4 +- src/axom/mir/CoordsetSlicer.hpp | 4 +- src/axom/mir/EquiZAlgorithm.hpp | 50 +++++++-------- src/axom/mir/ExtractZones.hpp | 38 ++++++------ src/axom/mir/FieldBlender.hpp | 14 ++--- src/axom/mir/FieldSlicer.hpp | 10 +-- src/axom/mir/MIRAlgorithm.hpp | 10 +-- src/axom/mir/MatsetSlicer.hpp | 6 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 26 ++++---- src/axom/mir/Options.hpp | 12 ++-- src/axom/mir/RecenterField.hpp | 6 +- src/axom/mir/SelectedZones.hpp | 8 +-- src/axom/mir/ZoneListBuilder.hpp | 8 +-- src/axom/mir/clipping/ClipTableManager.hpp | 50 +++++++-------- src/axom/mir/utilities.hpp | 24 +++---- src/axom/mir/views/ExplicitCoordsetView.hpp | 22 +++---- src/axom/mir/views/MaterialView.hpp | 16 ++--- .../mir/views/RectilinearCoordsetView.hpp | 28 ++++----- src/axom/mir/views/Shapes.hpp | 30 ++++----- .../mir/views/StridedStructuredIndexing.hpp | 38 ++++++------ src/axom/mir/views/StructuredIndexing.hpp | 36 +++++------ src/axom/mir/views/StructuredTopologyView.hpp | 24 +++---- src/axom/mir/views/UniformCoordsetView.hpp | 12 ++-- .../UnstructuredTopologyMixedShapeView.hpp | 28 ++++----- .../UnstructuredTopologyPolyhedralView.hpp | 18 +++--- .../UnstructuredTopologySingleShapeView.hpp | 16 ++--- src/axom/mir/views/dispatch_coordset.hpp | 38 ++++++------ src/axom/mir/views/dispatch_material.hpp | 2 +- .../views/dispatch_rectilinear_topology.hpp | 20 +++--- .../views/dispatch_structured_topology.hpp | 46 +++++++------- src/axom/mir/views/dispatch_topology.hpp | 2 +- .../mir/views/dispatch_uniform_topology.hpp | 22 +++---- .../views/dispatch_unstructured_topology.hpp | 6 +- src/axom/mir/views/view_traits.hpp | 4 +- 36 files changed, 400 insertions(+), 402 deletions(-) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index 5147f814c4..c4215301b6 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -14,7 +14,7 @@ namespace mir { namespace clipping { -/** +/*! * \brief This class encapsulates the logic for building blend groups. * * \tparam ExecSpace The execution space where the algorithm will run. @@ -28,7 +28,7 @@ class BlendGroupBuilder public: using KeyType = typename NamingPolicyView::KeyType; - /** + /*! * \brief This struct holds the views that represent data for blend groups. */ struct State @@ -53,14 +53,14 @@ class BlendGroupBuilder // clang-format on }; - /** + /*! * \brief Access the state views. * \return A reference to the state. */ State &state() { return m_state; } const State &state() const { return m_state; } - /** + /*! * \brief Provide a hint to the naming policy view so it can do narrowing. * * \param nnodes The number of nodes in the input mesh. @@ -70,7 +70,7 @@ class BlendGroupBuilder m_state.m_namingView = view; } - /** + /*! * \brief Set the number of zones. * * \param blendGroupsView The view that holds the number of blend groups for each zone. @@ -84,7 +84,7 @@ class BlendGroupBuilder m_state.m_blendGroupsLenView = blendGroupsLenView; } - /** + /*! * \brief Compute the total sizes of blend group storage. * * \param[out] bgSum The total number of blend groups for all zones. @@ -109,7 +109,7 @@ class BlendGroupBuilder bgLenSum = blendGroupLen_sum.get(); } - /** + /*! * \brief Set the views for the blend group offsets and then fill them using a scan. * * \param blendOffsetView The offsets to each blend group for views sized: view[blendGroupSum]. @@ -122,7 +122,7 @@ class BlendGroupBuilder m_state.m_blendGroupOffsetsView = blendGroupOffsetsView; } - /** + /*! * brief Compute the blend group offsets that make it easier to store data. */ void computeBlendGroupOffsets() @@ -134,7 +134,7 @@ class BlendGroupBuilder m_state.m_blendGroupOffsetsView); } - /** + /*! * \brief Set the views that we'll use for blend groups. */ void setBlendViews(const axom::ArrayView &blendNames, @@ -150,7 +150,7 @@ class BlendGroupBuilder m_state.m_blendCoeffView = blendCoeff; } - /** + /*! * \brief Set the unique names and ids views. These are used in mapping blend groups to unique blend groups. * * \param uniqueNames A view containing unique, sorted blend group names. @@ -163,7 +163,7 @@ class BlendGroupBuilder m_state.m_blendUniqueIndicesView = uniqueIndices; } - /** + /*! * \brief Get the blend names view. * \return The blend names view. */ @@ -172,13 +172,13 @@ class BlendGroupBuilder return m_state.m_blendNamesView; } - /** + /*! * \brief This class helps us manage blend group creation and usage for blend groups within a single zone. */ class zone_blend_groups { public: - /** + /*! * \brief Return the number of blend groups for this zone. * \return The number of blend groups for this zone. */ @@ -188,7 +188,7 @@ class BlendGroupBuilder return m_state->m_blendGroupsView[m_zoneIndex]; } - /** + /*! * \brief Set the number of blend groups and total size of the blend groups for a zone. * * \param zoneIndex The index of the zone we're initializing. @@ -202,13 +202,13 @@ class BlendGroupBuilder m_state->m_blendGroupsLenView[m_zoneIndex] = blendGroupsSize; } - /** + /*! * \brief Start creating a new blend group within the allocated space for this zone. */ AXOM_HOST_DEVICE inline void beginGroup() { m_currentDataOffset = m_startOffset; } - /** + /*! * \brief Add a new blend point in the current blend group. * * \param id The node id that will be used for blending. @@ -222,7 +222,7 @@ class BlendGroupBuilder m_currentDataOffset++; } - /** + /*! * \brief End the current blend group, storing its name, size, etc. */ AXOM_HOST_DEVICE @@ -255,7 +255,7 @@ class BlendGroupBuilder m_startOffset = m_currentDataOffset; } - /** + /*! * \brief Return the sum of the weights for the current blend group. * \note The blendGroup must have finished construction. * \return The sum of weights, which should be about 1. @@ -271,7 +271,7 @@ class BlendGroupBuilder return w; } - /** + /*! * \brief Return the size for the current blend group. * \note The blendGroup must have finished construction. * \return The size of the current blend group. @@ -282,7 +282,7 @@ class BlendGroupBuilder return m_state->m_blendGroupSizesView[m_blendGroupId]; } - /** + /*! * \brief Return the index'th weight in the blend group. * \note The blendGroup must have finished construction. * \return The index'th weight. @@ -294,7 +294,7 @@ class BlendGroupBuilder return m_state->m_blendCoeffView[start + index]; } - /** + /*! * \brief Return the index'th weight in the blend group. * \note The blendGroup must have finished construction. * \return The index'th weight. @@ -306,7 +306,7 @@ class BlendGroupBuilder return m_state->m_blendIdsView[start + index]; } - /** + /*! * \brief Return the name of the current blend group. * \return The name of the current blend group. */ @@ -316,7 +316,7 @@ class BlendGroupBuilder return m_state->m_blendNamesView[m_blendGroupId]; } - /** + /*! * \brief Return index of the current blend group in the unique blend groups. * \return The unique index of the current blend group. */ @@ -327,20 +327,20 @@ class BlendGroupBuilder m_state->m_blendUniqueNamesView); } - /** + /*! * \brief Advance to the next blend group. */ AXOM_HOST_DEVICE inline void operator++() { m_blendGroupId++; } - /** + /*! * \brief Advance to the next blend group. */ AXOM_HOST_DEVICE inline void operator++(int) { m_blendGroupId++; } #if !defined(AXOM_DEVICE_CODE) - /** + /*! * \brief Print the current blend group to a stream. * \param os The stream to which the blend group will print. */ @@ -376,7 +376,7 @@ class BlendGroupBuilder } #endif - /** + /*! * \brief Return the current blend group's ids. * \return The current blend group's ids. * \note This method should not be used if blend groups are still being constructed. @@ -389,7 +389,7 @@ class BlendGroupBuilder n); } - /** + /*! * \brief Return the current blend group's ids. * \return The current blend group's ids. * \note This method should not be used if blend groups are still being constructed. @@ -412,7 +412,7 @@ class BlendGroupBuilder State *m_state; // Pointer to the main state. }; - /** + /*! * \brief Return a zone_blend_groups object for the current zone so we can add blend groups. * * \param zoneIndex The zone whose blend groups we want to edit. @@ -438,7 +438,7 @@ class BlendGroupBuilder return groups; } - /** + /*! * \brief Filter out single node blend groups from the unique. * * \param blend The BlendData that describes the blend groups. @@ -509,7 +509,7 @@ class BlendGroupBuilder } } - /** + /*! * \brief Make a BlendData object from the views in this object. * * \return A BlendData object suitable for making new fields and coordsets. diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 9811852065..13fb08d8e8 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -47,7 +47,7 @@ namespace clipping { namespace details { -/** +/*! * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate ShapeID value. * * \param st_index The value we want to translate into a ShapeID value. @@ -85,7 +85,7 @@ inline AXOM_HOST_DEVICE IntegerType ST_Index_to_ShapeID(IntegerType st_index) return shapeID; } -/** +/*! * \brief Returns a clip table index for the input shapeId. * \param shapeId A shapeID (e.g. Tet_ShapeID) * \return The clip table index for the shape. @@ -192,7 +192,7 @@ void printHost(const std::string &name, const ViewType &deviceView) } // end namespace details //------------------------------------------------------------------------------ -/** +/*! * \brief This class helps ClipField determine intersection cases and weights * using a field designated by the options. */ @@ -203,12 +203,12 @@ class FieldIntersector using ClipFieldType = float; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief This is a view class for FieldIntersector that can be used in device code. */ struct View { - /** + /*! * \brief Given a zone index and the node ids that comprise the zone, return * the appropriate clip case, taking into account the clip field and * clip value. @@ -230,7 +230,7 @@ class FieldIntersector return clipcase; } - /** + /*! * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. * * \param id0 The mesh node at the start of the edge. @@ -256,7 +256,7 @@ class FieldIntersector ClipFieldType m_clipValue {}; }; - /** + /*! * \brief Initialize the object from options. * \param n_options The node that contains the options. * \param n_fields The node that contains fields. @@ -299,7 +299,7 @@ class FieldIntersector } } - /** + /*! * \brief Determine the name of the topology on which to operate. * \param n_input The input mesh node. * \param n_options The clipping options. @@ -315,7 +315,7 @@ class FieldIntersector return n_clipField["topology"].as_string(); } - /** + /*! * \brief Return a new instance of the view. * \return A new instance of the view. */ @@ -327,7 +327,7 @@ class FieldIntersector }; //------------------------------------------------------------------------------ -/** +/*! * \accelerated * \brief This class clips a topology using a field and puts the new topology into a new Conduit node. * @@ -361,7 +361,7 @@ class ClipField using ClipFieldType = float; using ZoneType = typename TopologyView::ShapeType; - /** + /*! * \brief Constructor * * \param topoView A topology view suitable for the supplied topology. @@ -378,14 +378,14 @@ class ClipField , m_naming() { } - /** + /*! * \brief Allow the user to pass in a NamingPolicy to use when making blend group names. * * \param naming A new naming policy object. */ void setNamingPolicy(NamingPolicy &naming) { m_naming = naming; } - /** + /*! * \brief Execute the clipping operation using the data stored in the specified \a clipField. * * \param[in] n_input The Conduit node that contains the topology, coordsets, and fields. @@ -418,7 +418,7 @@ class ClipField n_output["fields"]); } - /** + /*! * \brief Execute the clipping operation using the data stored in the specified \a clipField. * * \param[in] n_topo The node that contains the input mesh topology. @@ -741,7 +741,7 @@ class ClipField private: #endif - /** + /*! * \brief Contains data that describes the number and size of zone fragments in the output. */ struct FragmentData @@ -754,7 +754,7 @@ class ClipField axom::ArrayView m_fragmentSizeOffsetsView {}; }; - /** + /*! * \brief Contains some per-zone data that we want to hold onto between methods. */ struct ZoneData @@ -763,7 +763,7 @@ class ClipField axom::ArrayView m_pointsUsedView {}; }; - /** + /*! * \brief Contains some per-node data that we want to hold onto between methods. */ struct NodeData @@ -773,7 +773,7 @@ class ClipField axom::ArrayView m_originalIdsView {}; }; - /** + /*! * \brief Make a bitset that indicates the parts of the selection that are selected. */ int getSelection(const ClipOptions &opts) const @@ -785,7 +785,7 @@ class ClipField return selection; } - /** + /*! * \brief Create views for the clip tables of various shapes. * * \param[out] views The views array that will contain the table views. @@ -814,7 +814,7 @@ class ClipField } } - /** + /*! * \brief Iterate over zones and their respective fragments to determine sizes * for fragments and blend groups. * @@ -985,7 +985,7 @@ class ClipField #endif } - /** + /*! * \brief Compute the total number of fragments and their size. * * \param[inout] fragmentData The object that contains data about the zone fragments. @@ -1015,7 +1015,7 @@ class ClipField fragmentData.m_finalConnSize = fragment_nids_sum.get(); } - /** + /*! * \brief Compute fragment offsets. * * \param[inout] fragmentData The object that contains data about the zone fragments. @@ -1043,7 +1043,7 @@ class ClipField } #if defined(AXOM_REDUCE_BLEND_GROUPS) - /** + /*! * \brief Counts the number of original nodes used by the selected fragments. * * \param nodeData The node data (passed by value on purpose) @@ -1061,7 +1061,7 @@ class ClipField return nUsed_reducer.get(); } - /** + /*! * \brief Creates the node lists/maps. * * \param nodeData The node data that contains views where the node data is stored. @@ -1105,7 +1105,7 @@ class ClipField } #endif - /** + /*! * \brief Fill in the data for the blend group views. * * \param[in] clipTableViews An object that holds views of the clipping table data. @@ -1236,7 +1236,7 @@ class ClipField }); } - /** + /*! * \brief Make the clipped mesh topology. * * \param[in] clipTableViews An object that holds views of the clipping table data. @@ -1691,7 +1691,7 @@ class ClipField } } - /** + /*! * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * * \param blend The BlendData that we need to construct the new coordset. @@ -1710,7 +1710,7 @@ class ClipField cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } - /** + /*! * \brief Make new fields for the output topology. * * \param blend The BlendData that we need to construct the new vertex fields. @@ -1820,7 +1820,7 @@ class ClipField } } - /** + /*! * \brief Make an originalElements field so we can know each output zone's original zone number in the input mesh. * * \param[in] fragmentData This object holds views to per-fragment data. @@ -1898,7 +1898,7 @@ class ClipField } } - /** + /*! * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. * * \param shapes This is a bitwise-or of various (1 << ShapeID) values. @@ -1939,7 +1939,7 @@ class ClipField return sm; } - /** + /*! * \brief If we're making a field that marks the new nodes that were created as * a result of clipping, update those nodes now. * diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 01e2a073e5..22bfd2651e 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -23,7 +23,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \accelerated * \class FieldBlender * @@ -40,7 +40,7 @@ class CoordsetBlender public: using CoordsetViewType = CSVType; - /** + /*! * \brief Create a new blended field from the \a n_input field and place it in \a n_output. * * \param blend The BlendData that will be used to make the new coordset. diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/CoordsetSlicer.hpp index 86a28facdc..089e2d958a 100644 --- a/src/axom/mir/CoordsetSlicer.hpp +++ b/src/axom/mir/CoordsetSlicer.hpp @@ -18,7 +18,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \accelerated * \class CoordsetSlicer * @@ -36,7 +36,7 @@ class CoordsetSlicer : m_coordsetView(coordsetView) { } - /** + /*! * \brief Execute the slice on the \a n_input coordset and store the new sliced coordset in \a n_output. * * \param slice The slice data that indicates how the coordset will be sliced. diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 7d4b123b8e..a2244a4eb7 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -59,7 +59,7 @@ constexpr static MaterialVF NULL_MATERIAL_VF = -1.f; namespace detail { -/** +/*! * \brief This class is an intersection policy compatible with ClipField. It * helps determine clip cases and weights using material-aware logic. * @@ -75,14 +75,14 @@ class MaterialIntersector using ConnectivityType = ConnectivityT; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief This is a view class for MatsetIntersector that can be used in device code. */ struct View { static constexpr int INVALID_INDEX = -1; - /** + /*! * \brief Determine the clipping case, taking into account the zone's material * and the current material being added. * @@ -124,7 +124,7 @@ class MaterialIntersector return clipcase; } - /** + /*! * \brief Compute the weight of a clip value along an edge (id0, id1) using the clip field and value. * * \param id0 The mesh node at the start of the edge. @@ -178,7 +178,7 @@ class MaterialIntersector return t; } - /** + /*! * \brief Return the volume fraction array index in m_matIndicesView for the * given material number \a matNumber. * @@ -230,7 +230,7 @@ class MaterialIntersector int m_currentMaterialIndex {}; //!< The current material's index in the m_matvfViews. }; - /** + /*! * \brief Initialize the object from options. * \param n_options The node that contains the options. * \param n_fields The node that contains fields. @@ -239,7 +239,7 @@ class MaterialIntersector const conduit::Node &AXOM_UNUSED_PARAM(n_fields)) { } - /** + /*! * \brief Determine the name of the topology on which to operate. * \param n_input The input mesh node. * \param n_options The clipping options. @@ -275,7 +275,7 @@ class MaterialIntersector m_view.setCurrentMaterial(matNumber, matNumberIndex); } - /** + /*! * \brief Return a new instance of the view. * \return A new instance of the view. * \note Call this after all values are set. @@ -288,7 +288,7 @@ class MaterialIntersector } // end namespace detail -/** +/*! * \accelerated * \brief Implements Meredith's Equi-Z algorithm on the GPU using Blueprint inputs/outputs. */ @@ -300,7 +300,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm public: using ConnectivityType = typename TopologyView::ConnectivityType; - /** + /*! * \brief Constructor * * \param topoView The topology view to use for the input data. @@ -330,7 +330,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } #endif - /** + /*! * \brief Perform material interface reconstruction on a single domain. * * \param[in] n_topo The Conduit node containing the topology that will be used for MIR. @@ -540,7 +540,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } #if defined(AXOM_EQUIZ_SPLIT_PROCESSING) - /** + /*! * \brief Adds original ids field to supplied fields node. * * \param n_field The new field node. @@ -575,7 +575,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm }); } - /** + /*! * \brief Take the mesh in n_root and extract the zones identified by the * \a cleanZones array and store the results into the \a n_cleanOutput * node. @@ -609,7 +609,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif } - /** + /*! * \brief Create node map and node slice arrays for the MIR output that help * merge it back with the clean output. * @@ -690,7 +690,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } #endif - /** + /*! * \brief Perform material interface reconstruction on a single domain. * * \param[in] n_topo The Conduit node containing the topology that will be used for MIR. @@ -873,7 +873,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif } - /** + /*! * \brief Examine the materials and determine which are clean/mixed. * * \param n_matset A Conduit node containing the matset. @@ -899,7 +899,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mixedMats = allMats; } - /** + /*! * \brief Return the name of the zonal material field for a given matId. * \return The name of the zonal material field. */ @@ -910,7 +910,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm return ss.str(); } - /** + /*! * \brief Return the name of the nodal material field for a given matId. * \return The name of the nodal material field. */ @@ -921,25 +921,25 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm return ss.str(); } - /** + /*! * \brief Return the name of the zonal material id field. * \return The name of the zonal material id field. */ std::string zonalMaterialIDName() const { return "__equiz_zonalMaterialID"; } - /** + /*! * \brief Return the name of the original nodes field. * \return The name of the original nodes field. */ std::string originalNodesFieldName() const { return "__equiz_original_node"; } - /** + /*! * \brief Return the name of the new nodes field that identifies blended nodes in the MIR output. * \return The name of the new nodes field. */ std::string newNodesFieldName() const { return "__equiz_new_nodes"; } - /** + /*! * \brief Makes node-cenetered volume fractions for the materials in the matset * and attaches them as fields. * @@ -1026,7 +1026,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } } - /** + /*! * \brief Set up the "working fields", mainly a zonalMaterialID that includes * the contributions from the clean materials and the first mixed material. * @@ -1117,7 +1117,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif } - /** + /*! * \brief Perform one iteration of material clipping. * * \tparam ITopologyView The topology view type for the intermediate topology. @@ -1345,7 +1345,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_newFields.remove(colorField); } - /** + /*! * \brief Build a new matset with only clean zones, representing the MIR output. * * \param n_matset n_matset The Conduit node that contains the input matset. diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 635467a5a2..0bcec0bbbb 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -18,7 +18,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief Make a new topology and coordset by extracting certain zones from the input mesh. * * \tparam ExecSpace The execution space where the algorithm will run. @@ -34,7 +34,7 @@ class ExtractZones using SelectedZonesView = axom::ArrayView; using ZoneType = typename TopologyView::ShapeType; - /** + /*! * \brief Constructor * * \param topoView The input topology view. @@ -47,7 +47,7 @@ class ExtractZones , m_zoneSlice() { } - /** + /*! * \brief Select zones from the input mesh by id and output them in the output mesh. * * \param selectedZonesView A view that contains the selected zone ids. @@ -152,7 +152,7 @@ class ExtractZones #if !defined(__CUDACC__) protected: #endif - /** + /*! * \brief This struct contains extra amounts of storage that we might want to overallocate. */ struct Sizes @@ -162,7 +162,7 @@ class ExtractZones axom::IndexType connectivity {0}; }; - /** + /*! * \brief Create a zone slice view, building m_zoneSlice if needed. * * \param selectedZonesView A view that contains the selected zone ids. @@ -201,7 +201,7 @@ class ExtractZones return view; } - /** + /*! * \brief Return a Sizes object initialized from the options. * * \param n_options The options node that contains extra sizes. @@ -226,7 +226,7 @@ class ExtractZones return extra; } - /** + /*! * \brief Make node map and node slice information for the selected zones but * do not limit the selection to only the used nodes. * @@ -278,7 +278,7 @@ class ExtractZones return sizes; } - /** + /*! * \brief Make node map and node slice information for the selected zones * selecting only the used nodes. * @@ -366,7 +366,7 @@ class ExtractZones return sizes; } - /** + /*! * \brief Make the output topology for just the selected zones. * * \param selectedZonesView A view that contains the ids of the zones to extract. @@ -511,7 +511,7 @@ class ExtractZones } } - /** + /*! * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * * \param nodeSlice Node slice information. @@ -529,7 +529,7 @@ class ExtractZones cs.execute(nodeSlice, n_coordset, n_newCoordset); } - /** + /*! * \brief Make fields for the output mesh, as needed. * * \param nodeSlice Node slice information. @@ -560,7 +560,7 @@ class ExtractZones } } - /** + /*! * \brief Get the topology name. * * \param n_input The input mesh. @@ -584,7 +584,7 @@ class ExtractZones return name; } - /** + /*! * \brief Return whether coordset/vertex compaction is desired. * * \param n_options The options node that may contain a "topology" string. @@ -601,7 +601,7 @@ class ExtractZones return retval; } - /** + /*! * \brief Return the name of the output shape type. * * \param n_topo The input topology node. @@ -642,7 +642,7 @@ class ExtractZones axom::Array m_zoneSlice; }; -/** +/*! * \brief Make a new topology and coordset by extracting certain zones from the input mesh. * * \tparam ExecSpace The execution space where the algorithm will run. @@ -656,7 +656,7 @@ class ExtractZonesAndMatset public: using SelectedZonesView = axom::ArrayView; - /** + /*! * \brief Constructor * * \param topoView The input topology view. @@ -670,7 +670,7 @@ class ExtractZonesAndMatset , m_matsetView(matsetView) { } - /** + /*! * \brief Select zones from the input mesh by id and output them in the output mesh. * * \param selectedZonesView A view that contains the selected zone ids. @@ -713,7 +713,7 @@ class ExtractZonesAndMatset private: #endif - /** + /*! * \brief Return the matset for the named topology. * * \param n_input The input mesh. @@ -741,7 +741,7 @@ class ExtractZonesAndMatset return matset; } - /** + /*! * \brief Make a new matset that covers the selected zones. * * \param selectedZonesView A view that contains the selected zones. diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 148b9924fa..8370213d9a 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -20,7 +20,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief This class contains views of blend data. Blend data lets is make new * nodal fields and coordsets. The field data are sampled using m_originalIdsView * which is a compact list of the original node ids that we want to preserve @@ -45,7 +45,7 @@ struct BlendData axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. }; -/** +/*! * \brief This policy can be used with FieldBlender to select all blend groups. */ struct SelectAllPolicy @@ -64,7 +64,7 @@ struct SelectAllPolicy } }; -/** +/*! * \brief This policy can be used with FieldBlender to select a subset of blend groups, according to m_selectedIndicesView. */ struct SelectSubsetPolicy @@ -82,7 +82,7 @@ struct SelectSubsetPolicy } }; -/** +/*! * \accelerated * \class FieldBlender * @@ -99,13 +99,13 @@ class FieldBlender /// Constructor FieldBlender() : m_indexing() { } - /** + /*! * \brief Constructor * \param indexing An object used to transform node indices. */ FieldBlender(const IndexingPolicy &indexing) : m_indexing(indexing) { } - /** + /*! * \brief Create a new blended field from the \a n_input field and place it in \a n_output. * * \param blend The BlendData that will be used to make the new field. @@ -138,7 +138,7 @@ class FieldBlender } private: - /** + /*! * \brief Blend data for a single field component. * * \param blend The BlendData that will be used to make the new field. diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 2b2d42887b..9fcbd830ec 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -19,7 +19,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief Contains the indices to be sliced out of a Blueprint field. */ struct SliceData @@ -27,7 +27,7 @@ struct SliceData axom::ArrayView m_indicesView; }; -/** +/*! * \accelerated * \class FieldSlicer * @@ -44,13 +44,13 @@ class FieldSlicer /// Constructor FieldSlicer() : m_indexing() { } - /** + /*! * \brief Constructor * \param indexing An object used to transform node indices. */ FieldSlicer(const IndexingPolicy &indexing) : m_indexing(indexing) { } - /** + /*! * \brief Execute the slice on the \a n_input field and store the new sliced field in \a n_output. * * \param slice The slice data that indicates how the field will be sliced. @@ -85,7 +85,7 @@ class FieldSlicer } private: - /** + /*! * \brief Slice data for a single field component. * * \param slice The SliceData that will be used to make the new field. diff --git a/src/axom/mir/MIRAlgorithm.hpp b/src/axom/mir/MIRAlgorithm.hpp index 87485ea26f..334d3e865e 100644 --- a/src/axom/mir/MIRAlgorithm.hpp +++ b/src/axom/mir/MIRAlgorithm.hpp @@ -19,7 +19,7 @@ namespace axom { namespace mir { -/** +/*! \brief Base class for Material Interface Reconstruction (MIR) algorithms. */ class MIRAlgorithm @@ -28,7 +28,7 @@ class MIRAlgorithm MIRAlgorithm() = default; virtual ~MIRAlgorithm() = default; - /** + /*! \brief Perform material interface reconstruction on the meshes supplied in the root node. Root can either be a mesh domain or a node that contains multiple domains. @@ -66,7 +66,7 @@ class MIRAlgorithm conduit::Node &n_output); protected: - /** + /*! * \brief Set up the new domain from the old one and invoke executeDomain. * * \param n_domain The input domain. @@ -77,7 +77,7 @@ class MIRAlgorithm const conduit::Node &n_options, conduit::Node &n_newDomain); - /** + /*! * \brief Perform material interface reconstruction on a single domain. Derived classes * must implement this method and any device-specific coding gets handled under it. * @@ -103,7 +103,7 @@ class MIRAlgorithm conduit::Node &n_newFields, conduit::Node &n_newMatset) = 0; - /** + /*! * \brief Copy state from the src domain to the destination domain. * \param srcState The node that contains the state in the source domain. * \param destState The node that contains the state in the destination domain. diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 84a62bee4a..92f8cbf969 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -19,7 +19,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief Slices the input matset view and outputs a new matset (unibuffer flavor). * * \tparam ExecSpace The execution space where the algorithm will run. @@ -33,12 +33,12 @@ class MatsetSlicer public: using SelectedZonesView = axom::ArrayView; - /** + /*! * \brief Constructor. */ MatsetSlicer(const MatsetView &matsetView) : m_matsetView(matsetView) { } - /** + /*! * \brief Slice the input matset and output a new matset. * * \param matsetView A view that wraps the input matset. diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index a8d90271af..444339110b 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -30,17 +30,17 @@ namespace blueprint { namespace details { -/** - * \brief Given views that contain the nodes and zones, sort the zones using the - * node numbers to produce a list of zones for each node and an offsets array - * that points to the start of each list of zones. - * - * \param[in] nodesView A view that contains the set of all of the nodes in the topology (the connectivity) - * \param[inout[ zonesView A view (same size as \a nodesView) that contains the zone number of each node. - * \param[out] offsetsView A view that we fill with offsets so offsetsView[i] points to the start of the i'th list in \a zonesView. - * - * \note RAJA::sort_pairs can be slow if there are a lot of nodes (depends on ExecSpace too). - */ +/*! + * \brief Given views that contain the nodes and zones, sort the zones using the + * node numbers to produce a list of zones for each node and an offsets array + * that points to the start of each list of zones. + * + * \param[in] nodesView A view that contains the set of all of the nodes in the topology (the connectivity) + * \param[inout[ zonesView A view (same size as \a nodesView) that contains the zone number of each node. + * \param[out] offsetsView A view that we fill with offsets so offsetsView[i] points to the start of the i'th list in \a zonesView. + * + * \note RAJA::sort_pairs can be slow if there are a lot of nodes (depends on ExecSpace too). + */ template struct BuildRelation { @@ -159,7 +159,7 @@ struct BuildRelation } // end namespace details -/** +/*! * \brief Build an o2m relation that lets us look up the zones for a node. * * \note The zone list for each point is not sorted. @@ -168,7 +168,7 @@ template class NodeToZoneRelationBuilder { public: - /** + /*! * \brief Build a node to zone relation and store the resulting O2M relation in the \a relation conduit node. * * \param topo The topology for which we're building the O2M relation. diff --git a/src/axom/mir/Options.hpp b/src/axom/mir/Options.hpp index 764fbf5d73..c4a14497c9 100644 --- a/src/axom/mir/Options.hpp +++ b/src/axom/mir/Options.hpp @@ -14,16 +14,14 @@ namespace axom { namespace mir { -// IDEA: maybe use inlet for this stuff. - -/** +/*! * \brief This class provides a kind of schema over options, as well * as default values, and some utilities functions. */ class Options { public: - /** + /*! * \brief Constructor * * \param nzones The total number of zones in the associated topology. @@ -31,7 +29,7 @@ class Options */ Options(const conduit::Node &options) : m_options(options) { } - /** + /*! * \brief Return the name of the topology to make in the output. * \param default_value The name to use if the option is not defined. * \return The name of the topology to make in the output. @@ -44,7 +42,7 @@ class Options return name; } - /** + /*! * \brief Return the name of the coordset to make in the output. * \param default_value The name to use if the option is not defined. * \return The name of the coordset to make in the output. @@ -57,7 +55,7 @@ class Options return name; } - /** + /*! * \brief Extract the names of the fields to process (and their output names) from the * options or \a n_fields if the options do not contain fields. * diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index a04d3baee2..d8f01dcdc3 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -21,7 +21,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief Convert a field with one association type to a field of another association type using an o2mrelation. * * \tparam ExecSpace The execution space where the algorithm runs. @@ -30,7 +30,7 @@ template class RecenterField { public: - /** + /*! * \brief Convert the input field to a different association type using the o2mrelation and store the new field in the output field. * * \param field The input field. @@ -42,7 +42,7 @@ class RecenterField conduit::Node &outField) const; private: - /** + /*! * \brief Recenter a single field component. * * \param relation The node that contains an o2mrelation with nodes to zones. diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index 5833894241..b64738da0f 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -13,7 +13,7 @@ namespace axom { namespace mir { -/** +/*! * \brief This class provides a kind of schema over options, as well * as default values, and some utilities functions. */ @@ -21,7 +21,7 @@ template class SelectedZones { public: - /** + /*! * \brief Constructor * * \param nzones The total number of zones in the associated topology. @@ -34,7 +34,7 @@ class SelectedZones buildSelectedZones(nzones, options); } - /** + /*! * \brief Return a view that contains the list of selected zone ids for the mesh. * \return A view that contains the list of selected zone ids for the mesh. */ @@ -44,7 +44,7 @@ class SelectedZones } protected: - /** + /*! * \brief The options may contain a "selectedZones" member that is a list of zones * that will be operated on. If such an array is present, copy and sort it. * If the zone list is not present, make an array that selects every zone. diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index 6b6838b1fa..278652d497 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -19,7 +19,7 @@ namespace utilities { namespace blueprint { -/** +/*! * \brief This struct builds lists of clean and mixed zones using the input topology and matset views. * * \tparam ExecSpace The execution space where the algorithm will run. @@ -33,7 +33,7 @@ class ZoneListBuilder using SelectedZonesView = axom::ArrayView; using ZoneType = typename TopologyView::ShapeType; - /** + /*! * \brief Constructor * * \param topoView The topology view to use for creating the zone lists. @@ -44,7 +44,7 @@ class ZoneListBuilder , m_matsetView(matsetView) { } - /** + /*! * \brief Build the list of clean and mixed zones using the number of materials * per zone, maxed to the nodes. * @@ -170,7 +170,7 @@ class ZoneListBuilder } } - /** + /*! * \brief Build the list of clean and mixed zones using the number of materials * per zone, maxed to the nodes. Limit the number of zones. * diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index 5ee2cca948..cb4d5662e3 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -15,7 +15,7 @@ namespace mir { namespace clipping { -/** +/*! * \accelerated * \brief This class contains a view of table data and it provides an * iterator for traversing shapes in a case. @@ -28,27 +28,27 @@ class TableView using IndexView = axom::ArrayView; using TableDataView = axom::ArrayView; - /** + /*! * \brief An iterator for shapes within a table case. */ class iterator { public: - /** + /*! * \brief Return the index of the iterator's current shape. * \return The index of the iterator's current shape. */ AXOM_HOST_DEVICE inline int index() const { return m_currentShape; } - /** + /*! * \brief Return the number of shapes in the iterator's case. * \return The number of shapes in the iterator's case. */ AXOM_HOST_DEVICE inline int size() const { return m_numShapes; } - /** + /*! * \brief Increment the iterator, moving it to the next shape. */ AXOM_HOST_DEVICE @@ -62,7 +62,7 @@ class TableView } } - /** + /*! * \brief Increment the iterator, moving it to the next shape. */ AXOM_HOST_DEVICE @@ -76,7 +76,7 @@ class TableView } } - /** + /*! * \brief Compare 2 iterators for equality. * \param it The iterator to be compared to this. * \return true if the iterators are equal; false otherwise. @@ -89,7 +89,7 @@ class TableView m_currentShape == it.m_currentShape && m_numShapes == it.m_numShapes; } - /** + /*! * \brief Compare 2 iterators to see if not equal. * \param it The iterator to be compared to this. * \return true if the iterators are different; false otherwise. @@ -102,7 +102,7 @@ class TableView m_currentShape != it.m_currentShape || m_numShapes != it.m_numShapes; } - /** + /*! * \brief Dereference operator that wraps the current shape data in an array * view so the caller can use the shape data. */ @@ -208,7 +208,7 @@ class TableView private: friend class TableView; - /** + /*! * \brief Given the input shape, return how many values to advance to get to the next shape. * * \param shape The shape type. @@ -253,13 +253,13 @@ class TableView int m_numShapes {0}; }; - /** + /*! * \brief Constructor */ AXOM_HOST_DEVICE TableView() : m_shapes(), m_offsets(), m_table() { } - /** + /*! * \brief Constructor * * \param shapes The number of shapes in each table case. @@ -275,7 +275,7 @@ class TableView , m_table(table) { } - /** + /*! * \brief Return the number of cases for the clipping table. * * \return The number of cases for the clipping table. @@ -283,7 +283,7 @@ class TableView AXOM_HOST_DEVICE size_t size() const { return m_shapes.size(); } - /** + /*! * \brief Return the iterator for the beginning of a case. * * \param caseId The case whose begin iterator we want. @@ -301,7 +301,7 @@ class TableView return it; } - /** + /*! * \brief Return the iterator for the end of a case. * * \param caseId The case whose end iterator we want. @@ -325,7 +325,7 @@ class TableView TableDataView m_table; // The table data that contains the shapes. }; -/** +/*! * \brief This class manages data table arrays and can produce a view for the data. */ template @@ -337,13 +337,13 @@ class Table using IndexDataArray = axom::Array; using TableDataArray = axom::Array; - /** + /*! * \brief Returns whether the table data have been loaded. * \return True if the data have been loaded; false otherwise. */ bool isLoaded() const { return m_shapes.size() > 0; } - /** + /*! * \brief Load table data into the arrays, moving data as needed. * * \param n The number of cases in the clip table. @@ -371,7 +371,7 @@ class Table axom::copy(m_table.data(), table, tableLen * sizeof(unsigned char)); } - /** + /*! * \brief Create a view to access the table data. * * \return A view of the table data. @@ -387,7 +387,7 @@ class Table TableDataArray m_table; }; -/** +/*! * \brief Manage several clipping tables. */ template @@ -396,7 +396,7 @@ class ClipTableManager public: static constexpr int NumberOfTables = ST_MAX - ST_MIN; - /** + /*! * \brief Return a reference to the clipping table, which is loaded on demand. * * \param shape The shape type to be retrieved. @@ -411,7 +411,7 @@ class ClipTableManager return m_tables[index]; } - /** + /*! * \brief Load tables based on dimension. * \param dim The dimension of shapes to load. */ @@ -420,7 +420,7 @@ class ClipTableManager for(const auto shape : shapes(dim)) loadShape(shape); } - /** + /*! * \brief Return a vector of clipping shape ids for the given dimension. * * \param The spatial dimension. @@ -444,7 +444,7 @@ class ClipTableManager } private: - /** + /*! * \brief Turn a shape into an table index. * * \param shape The shape type ST_XXX. @@ -453,7 +453,7 @@ class ClipTableManager */ constexpr static size_t shapeToIndex(size_t shape) { return shape - ST_MIN; } - /** + /*! * \brief Load the clipping table for a shape. * * \param shape The shape whose table will be loaded. diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index c76241808a..cc7780e8f2 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -50,7 +50,7 @@ namespace mir namespace utilities { //------------------------------------------------------------------------------ -/** +/*! * \brief This class and its specializations provide a type trait that lets us * determine the type that should be used to accumulate values when we * do floating point math. @@ -82,7 +82,7 @@ struct accumulation_traits }; //------------------------------------------------------------------------------ -/** +/*! * \brief Use binary search to find the index of the \a value in the supplied * sorted view. * @@ -115,7 +115,7 @@ AXOM_HOST_DEVICE std::int32_t bsearch(T value, const axom::ArrayView &view) } //------------------------------------------------------------------------------ -/** +/*! * \brief Hash a stream of bytes into a uint64_t hash value. * * \param[in] data The bytes to be hashed. @@ -168,7 +168,7 @@ inline std::uint64_t hash_bytes(const std::uint8_t *data, std::uint32_t length) } //------------------------------------------------------------------------------ -/** +/*! * \brief This class implements a naming policy that uses some hashing functions * to produce a "name" for an array of ids. */ @@ -194,7 +194,7 @@ class HashNaming constexpr static KeyType Max31Bit = (KeyType(1) << 31) - 1; constexpr static KeyType Max32Bit = (KeyType(1) << 32) - 1; - /** + /*! * \brief A view for making names, suitable for use in device code. */ class View @@ -202,7 +202,7 @@ class HashNaming public: using KeyType = HashNaming::KeyType; - /** + /*! * \brief Make a name from an array of ids. * * \param p The array of ids. @@ -232,7 +232,7 @@ class HashNaming void setMaxId(IndexType m) { m_maxId = static_cast(m); } private: - /** + /*! * \brief Encode a single id as a name. * \param p0 The id to encode. * \return A name that encodes the id. @@ -246,7 +246,7 @@ class HashNaming return KeyIDSingle | k0; } - /** + /*! * \brief Encode 2 ids as a name. * \param p0 The first id to encode. * \param p1 The second id to encode. @@ -263,7 +263,7 @@ class HashNaming return KeyIDPair | (k0 << 31) | k1; } - /** + /*! * \brief Encode multiple ids as a name. * \param p The ids to encode. * \param n The number of ids. @@ -369,7 +369,7 @@ class HashNaming return m_view.makeName(p, n); } - /** + /*! * \brief Set the max number of nodes, which can help with id packing/narrowing. * \param n The number of nodes. */ @@ -378,7 +378,7 @@ class HashNaming /// Return a view that can be used on device. View view() { return m_view; } - /** + /*! * \brief Turn name into a string. * \param key The name. * \return A string that represents the name. @@ -430,7 +430,7 @@ class HashNaming }; //------------------------------------------------------------------------------ -/** +/*! * \brief This function makes a unique array of values from an input list of keys. * * \tparam ExecSpace The execution space. diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 4612d5f533..5b1f81b262 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -16,14 +16,14 @@ namespace mir { namespace views { -/** +/*! * \brief This class provides a view for Conduit/Blueprint explicit coordsets. */ template class ExplicitCoordsetView { }; -/** +/*! * \brief This class provides a view for Conduit/Blueprint 2d explicit coordsets. */ template @@ -36,7 +36,7 @@ class ExplicitCoordsetView constexpr static int dimension() { return 2; } - /** + /*! * \brief Constructor * * \param x The first coordinate component. @@ -50,7 +50,7 @@ class ExplicitCoordsetView SLIC_ASSERT_MSG(x.size() == y.size(), "Coordinate size mismatch."); } - /** + /*! * \brief Return the number of nodes in the coordset. * * \return The number of nodes in the coordset. @@ -63,7 +63,7 @@ class ExplicitCoordsetView IndexType numberOfNodes() const { return m_coordinates[0].size(); } /// @} - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -85,7 +85,7 @@ class ExplicitCoordsetView return PointType(X); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -102,7 +102,7 @@ class ExplicitCoordsetView axom::ArrayView m_coordinates[2]; }; -/** +/*! * \brief This class provides a view for Conduit/Blueprint 3d explicit coordsets. */ template @@ -115,7 +115,7 @@ class ExplicitCoordsetView constexpr static int dimension() { return 3; } - /** + /*! * \brief Constructor * * \param x The first coordinate component. @@ -132,7 +132,7 @@ class ExplicitCoordsetView "Coordinate size mismatch."); } - /** + /*! * \brief Return the number of nodes in the coordset. * * \return The number of nodes in the coordset. @@ -145,7 +145,7 @@ class ExplicitCoordsetView IndexType numberOfNodes() const { return m_coordinates[0].size(); } /// @} - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -168,7 +168,7 @@ class ExplicitCoordsetView return PointType(X); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index 7ee74c45d6..b854d2ad9b 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -21,7 +21,7 @@ namespace mir { namespace views { -/** +/*! * \brief This object contains information about the materials as provided by a Conduit node. * * \note This would only be used on the host. @@ -34,7 +34,7 @@ struct Material using MaterialInformation = std::vector; -/** +/*! * \brief Return a vector of Material from a matset (this is the material_map) * * \param matset The Conduit node that contains the matset. @@ -50,7 +50,7 @@ MaterialInformation materials(const conduit::Node &matset); // device kernels. //--------------------------------------------------------------------------- -/** +/*! matsets: matset: @@ -167,7 +167,7 @@ class UnibufferMaterialView axom::ArrayView m_indices; }; -/** +/*! matsets: matset: @@ -278,7 +278,7 @@ class MultiBufferMaterialView axom::IndexType m_size {0}; }; -/** +/*! matsets: matset: topology: topology @@ -378,7 +378,7 @@ class ElementDominantMaterialView axom::StaticArray, MAXMATERIALS> m_volume_fractions {}; }; -/** +/*! matsets: matset: topology: topology @@ -524,7 +524,7 @@ class MaterialDominantMaterialView // Some host-algorithms on material views. //--------------------------------------------------------------------------- -/** +/*! */ template axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, axom::IndexType nzones) @@ -609,7 +609,7 @@ axom::Array selectMixedZones(const MaterialDominantMaterialView &view) template axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex mat, Predicate &&pred) const { -/** +/*! NOTE: I really do not like the code below because it forces the main Axom algorithm to use RAJA directly. In the case of the reducer, I'd prefer to do this: diff --git a/src/axom/mir/views/RectilinearCoordsetView.hpp b/src/axom/mir/views/RectilinearCoordsetView.hpp index a759e306e9..da88b4b3b5 100644 --- a/src/axom/mir/views/RectilinearCoordsetView.hpp +++ b/src/axom/mir/views/RectilinearCoordsetView.hpp @@ -20,7 +20,7 @@ namespace views /// NOTE: The rectilinear coordset views could be combined into a single RectilinearCoordset /// view that is templated on NDIMS but the resulting SFINAE would just overcomplicate it. -/** +/*! * \class This class provides a view for Conduit/Blueprint 2D rectilinear coordsets. */ template @@ -34,7 +34,7 @@ class RectilinearCoordsetView2 constexpr static int dimension() { return 2; } - /** + /*! * \brief Constructor * * \param x The first coordinate component. @@ -47,7 +47,7 @@ class RectilinearCoordsetView2 , m_indexing(LogicalIndex {{x.size(), y.size()}}) { } - /** + /*! * \brief Return the number of points in the coordset. * * \return The number of points in the coordset. @@ -60,7 +60,7 @@ class RectilinearCoordsetView2 IndexType numberOfNodes() const { return m_indexing.size(); } /// @} - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -75,7 +75,7 @@ class RectilinearCoordsetView2 return PointType(X); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -88,7 +88,7 @@ class RectilinearCoordsetView2 return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -101,7 +101,7 @@ class RectilinearCoordsetView2 return getPoint(vertex_index); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -119,7 +119,7 @@ class RectilinearCoordsetView2 StructuredIndexing m_indexing; }; -/** +/*! * \class This class provides a view for Conduit/Blueprint 3D rectilinear coordsets. */ template @@ -133,7 +133,7 @@ class RectilinearCoordsetView3 constexpr static int dimension() { return 3; } - /** + /*! * \brief Constructor * * \param x The first coordinate component. @@ -148,7 +148,7 @@ class RectilinearCoordsetView3 , m_indexing(LogicalIndex {{x.size(), y.size(), z.size()}}) { } - /** + /*! * \brief Return the number of nodes in the coordset. * * \return The number of nodes in the coordset. @@ -162,7 +162,7 @@ class RectilinearCoordsetView3 IndexType numberOfNodes() const { return m_indexing.size(); } /// @} - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -178,7 +178,7 @@ class RectilinearCoordsetView3 return PointType(X); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. @@ -191,7 +191,7 @@ class RectilinearCoordsetView3 return getPoint(m_indexing.IndexToLogicalIndex(vertex_index)); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -204,7 +204,7 @@ class RectilinearCoordsetView3 return getPoint(vertex_index); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 6ed0c1818b..150f5b4169 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -429,20 +429,20 @@ struct PolygonShape : public PolygonTraits using ConnectivityType = ConnType; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief Construct a shape. */ AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) : m_idsView(ids) { } - /** + /*! * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. */ AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } - /** + /*! * \brief Get the ids for the requested face. * * \param faceIndex The index of the desired face. @@ -465,7 +465,7 @@ struct PolygonShape : public PolygonTraits ConnectivityView m_idsView; }; -/** +/*! * \brief This class extends the ShapeTraits with object state so it can represent a zone. */ template @@ -474,7 +474,7 @@ struct Shape : public ShapeTraits using ConnectivityType = ConnType; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief Construct a shape. */ AXOM_HOST_DEVICE Shape(const ConnectivityView &ids) @@ -484,7 +484,7 @@ struct Shape : public ShapeTraits assert(m_idsView.size() == ShapeTraits::numberOfNodes()); } - /** + /*! * \brief Get a specific id that makes up this shape. * * \return The i'th id that makes up this shape. @@ -495,21 +495,21 @@ struct Shape : public ShapeTraits return m_idsView[index]; } - /** + /*! * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. */ AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } - /** + /*! * \brief Get the unique ids that make up this shape. For basic shapes, assume they are unique. * * \return A view containing the ids that make up this shape. */ AXOM_HOST_DEVICE ConnectivityView getUniqueIds() const { return m_idsView; } - /** + /*! * \brief Get the ids for the requested face. * * \param faceIndex The index of the desired face. @@ -557,7 +557,7 @@ using WedgeShape = Shape; template using HexShape = Shape; -/** +/*! * \brief This is a shape that can act as any of the other shapes. * * \note This is a substitute for polymorphism so we can run on device. @@ -568,7 +568,7 @@ struct VariableShape using ConnectivityType = ConnType; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief Constructor * * \param shapeId The shape id that describes the points. @@ -582,7 +582,7 @@ struct VariableShape assert(shapeId >= Point_ShapeID && shapeId <= Hex_ShapeID); } - /** + /*! * \brief Returns the shape id of the actual shape represented by the variable shape. * \return The actual shape represented. */ @@ -795,7 +795,7 @@ struct VariableShape return edge; } - /** + /*! * \brief Get a specific id that makes up this shape. * * \return The i'th id that makes up this shape. @@ -805,7 +805,7 @@ struct VariableShape return m_idsView[index]; } - /** + /*! * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. @@ -819,7 +819,7 @@ struct VariableShape ConnectivityView m_idsView; }; -/** +/*! * \brief Given a shape name (matches Blueprint shape name), return the Shape id() value. * * \param name The shape name. diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index 6b9707167b..e2f1be39db 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -15,7 +15,7 @@ namespace mir { namespace views { -/** +/*! * \accelerated * \class StridedStructuredIndexing * @@ -51,7 +51,7 @@ struct StridedStructuredIndexing AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } - /** + /*! * \brief Return whether the view supports strided structured indexing. * \return true */ @@ -60,7 +60,7 @@ struct StridedStructuredIndexing return true; } - /** + /*! * \brief constructor */ AXOM_HOST_DEVICE @@ -74,7 +74,7 @@ struct StridedStructuredIndexing } } - /** + /*! * \brief Constructor * * \param dims The number of zones in each logical dimension. @@ -90,7 +90,7 @@ struct StridedStructuredIndexing , m_strides(strides) { } - /** + /*! * \brief Return the number of values in the index space. * * \return The number of values in the index space. @@ -103,7 +103,7 @@ struct StridedStructuredIndexing return sz; } - /** + /*! * \brief Return the logical dimensions. * * \return The logical dimensions. @@ -111,7 +111,7 @@ struct StridedStructuredIndexing AXOM_HOST_DEVICE const LogicalIndex &logicalDimensions() const { return m_dimensions; } - /** + /*! * \brief Return the j stride. * * \return The j stride to move up a row. @@ -122,7 +122,7 @@ struct StridedStructuredIndexing return m_strides[1]; } - /** + /*! * \brief Return the k stride. * * \return The k stride to move forward a "page". @@ -133,7 +133,7 @@ struct StridedStructuredIndexing return m_strides[2]; } - /** + /*! * \brief Turn a global logical index into an index. * \param global The global logical index to convert. * \return The global index. @@ -149,7 +149,7 @@ struct StridedStructuredIndexing return gl; } - /** + /*! * \brief Turn a global index into a global logical index. * * \param global The index to convert. @@ -188,7 +188,7 @@ struct StridedStructuredIndexing } /// @} - /** + /*! * \brief Convert global logical index to a local one. * \param local The local logical index. * \return local logical index. @@ -204,7 +204,7 @@ struct StridedStructuredIndexing return local; } - /** + /*! * \brief Turn a global index into a local index. * * \param global The index to convert. @@ -216,7 +216,7 @@ struct StridedStructuredIndexing return LogicalIndexToIndex(GlobalToLocal(GlobalToGlobal(global))); } - /** + /*! * \brief Convert local logical index to a global one. * \param local The local logical index. * \return global logical index. @@ -232,7 +232,7 @@ struct StridedStructuredIndexing return global; } - /** + /*! * \brief Convert local logical index to a global one. * \param local The local logical index. * \return local logical index. @@ -243,7 +243,7 @@ struct StridedStructuredIndexing return GlobalToGlobal(LocalToGlobal(IndexToLogicalIndex(local))); } - /** + /*! * \brief Turn a local index into a local logical index. * * \param index The index to convert. @@ -286,7 +286,7 @@ struct StridedStructuredIndexing } /// @} - /** + /*! * \brief Turn a local logical index into a local flat index. * * \param logical The logical indexto convert to a flat index. @@ -306,7 +306,7 @@ struct StridedStructuredIndexing return index; } - /** + /*! * \brief Determines whether the indexing contains the supplied logical index. * * \param logical The logical index being tested. @@ -324,7 +324,7 @@ struct StridedStructuredIndexing return retval; } - /** + /*! * \brief Determines whether the indexing contains the supplied index. * * \param index The index being tested. @@ -337,7 +337,7 @@ struct StridedStructuredIndexing return contains(IndexToLogicalIndex(index)); } - /** + /*! * \brief Expand the current StridedStructuredIndexing by one in each dimension. * * \return An expanded StridedStructuredIndexing. diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index 95d0782916..b94e6827e8 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -16,7 +16,7 @@ namespace mir { namespace views { -/** +/*! * \brief This class encapsulates a structured mesh size and contains methods to * help with indexing into it. * @@ -31,7 +31,7 @@ class StructuredIndexing AXOM_HOST_DEVICE constexpr static int dimension() { return NDIMS; } - /** + /*! * \brief Return whether the view supports strided structured indexing. * \return false */ @@ -40,7 +40,7 @@ class StructuredIndexing return false; } - /** + /*! * \brief constructor * * \param dims The dimensions we're indexing. @@ -51,7 +51,7 @@ class StructuredIndexing AXOM_HOST_DEVICE StructuredIndexing(const LogicalIndex &dims) : m_dimensions(dims) { } - /** + /*! * \brief Return the number of points in the coordset. * * \return The number of points in the coordset. @@ -64,7 +64,7 @@ class StructuredIndexing return sz; } - /** + /*! * \brief Return the logical dimensions. * * \return The logical dimensions. @@ -72,7 +72,7 @@ class StructuredIndexing AXOM_HOST_DEVICE const LogicalIndex &logicalDimensions() const { return m_dimensions; } - /** + /*! * \brief Return the j stride. * * \return The j stride to move up a row. @@ -83,7 +83,7 @@ class StructuredIndexing return m_dimensions[0]; } - /** + /*! * \brief Return the k stride. * * \return The k stride to move forward a "page". @@ -94,7 +94,7 @@ class StructuredIndexing return m_dimensions[0] * m_dimensions[1]; } - /** + /*! * \brief Turn a global logical index into an index. * \param global The global logical index to convert. * \return The global index. @@ -105,7 +105,7 @@ class StructuredIndexing return LogicalIndexToIndex(global); } - /** + /*! * \brief Turn a global index into a global logical index. * \param global The global index to convert. * \return The global logical index. @@ -116,7 +116,7 @@ class StructuredIndexing return IndexToLogicalIndex(global); } - /** + /*! * \brief Turn global logical index to local logical index. no-op. * \param index The index to convert. * \return Same as the input in this case. @@ -127,7 +127,7 @@ class StructuredIndexing return index; } - /** + /*! * \brief Turn global index to local index. no-op. * \param index The index to convert. * \return Same as the input in this case. @@ -135,7 +135,7 @@ class StructuredIndexing AXOM_HOST_DEVICE inline IndexType GlobalToLocal(IndexType index) const { return index; } - /** + /*! * \brief Turn local logical index to global logical index. no-op. * \param index The index to convert. * \return Same as the input in this case. @@ -146,7 +146,7 @@ class StructuredIndexing return index; } - /** + /*! * \brief Turn local index to global index. no-op. * \param index The index to convert. * \return Same as the input in this case. @@ -154,7 +154,7 @@ class StructuredIndexing AXOM_HOST_DEVICE inline IndexType LocalToGlobal(IndexType index) const { return index; } - /** + /*! * \brief Turn an index into a logical index. * * \param index The index to convert. @@ -198,7 +198,7 @@ class StructuredIndexing /// @} - /** + /*! * \brief Turn a logical index into a flat index. * * \param logical The logical indexto convert to a flat index. @@ -230,7 +230,7 @@ class StructuredIndexing /// @} - /** + /*! * \brief Determines whether the indexing contains the supplied logical index. * * \param logical The logical index being tested. @@ -248,7 +248,7 @@ class StructuredIndexing return retval; } - /** + /*! * \brief Determines whether the indexing contains the supplied index. * * \param index The index being tested. @@ -261,7 +261,7 @@ class StructuredIndexing return contains(IndexToLogicalIndex(index)); } - /** + /*! * \brief Expand the current StructuredIndexing by one in each dimension. * * \return An expanded StructuredIndexing. diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index d47cb56dec..cb5928330d 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -14,7 +14,7 @@ namespace mir { namespace views { -/** +/*! * \brief This class provides a view for Conduit/Blueprint structured grid types. * * \tparam IndexPolicy The policy for making/using indices. @@ -29,19 +29,19 @@ class StructuredTopologyView using ConnectivityType = IndexType; using ShapeType = typename std::conditional, typename std::conditional, LineShape>::type>::type; - /** + /*! * \brief Return the number of dimensions. * * \return The number of dimensions. */ AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } - /** + /*! * \brief Constructor */ StructuredTopologyView() : m_indexing() { } - /** + /*! * \brief Constructor * * \param indexing The indexing policy for the topology (num zones in each dimension). @@ -49,21 +49,21 @@ class StructuredTopologyView StructuredTopologyView(const IndexingPolicy &indexing) : m_indexing(indexing) { } - /** + /*! * \brief Return the number of zones. * * \return The number of zones. */ IndexType size() const { return m_indexing.size(); } - /** + /*! * \brief Return the number of zones. * * \return The number of zones. */ IndexType numberOfZones() const { return size(); } - /** + /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. @@ -75,7 +75,7 @@ class StructuredTopologyView return numberOfZones() * nodesPerElem; } - /** + /*! * \brief Return the mesh logical dimensions. * * \return The mesh logical dimensions. @@ -85,21 +85,21 @@ class StructuredTopologyView return m_indexing.logicalDimensions(); } - /** + /*! * \brief Return indexing object. * * \return The indexing object. */ IndexingPolicy &indexing() { return m_indexing; } - /** + /*! * \brief Return indexing object. * * \return The indexing object. */ const IndexingPolicy &indexing() const { return m_indexing; } - /** + /*! * \brief Execute a function for each zone in the mesh using axom::for_all. * * \tparam ExecSpace The execution space for the function body. @@ -191,7 +191,7 @@ class StructuredTopologyView } } - /** + /*! * \brief Execute a function for each zone in the mesh using axom::for_all. * * \tparam ExecSpace The execution space for the function body. diff --git a/src/axom/mir/views/UniformCoordsetView.hpp b/src/axom/mir/views/UniformCoordsetView.hpp index 67f9916e58..56fb829faf 100644 --- a/src/axom/mir/views/UniformCoordsetView.hpp +++ b/src/axom/mir/views/UniformCoordsetView.hpp @@ -17,7 +17,7 @@ namespace mir { namespace views { -/** +/*! * \class This class provides a view for Conduit/Blueprint uniform coordsets. * * \tparam DataType The underlying type used for the coordinates. @@ -37,7 +37,7 @@ class UniformCoordsetView constexpr static int dimension() { return NDIMS; } - /** + /*! * \brief Constructor * * \param dims The logical dimensions of the coordset. @@ -53,7 +53,7 @@ class UniformCoordsetView , m_spacing(spacing) { } - /** + /*! * \brief Return the number of points in the coordset. * * \return The number of points in the coordset. @@ -66,7 +66,7 @@ class UniformCoordsetView IndexType numberOfNodes() const { return m_indexing.size(); } /// @} - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -82,7 +82,7 @@ class UniformCoordsetView return pt; } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The logical index of the point to return. @@ -95,7 +95,7 @@ class UniformCoordsetView return getPoint(vertex_index); } - /** + /*! * \brief Return the requested point from the coordset. * * \param vertex_index The index of the point to return. diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 4d628322fd..2bffdc8e35 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -15,7 +15,7 @@ namespace mir { namespace views { -/** +/*! * \brief Given a shape value, we can get the Shape::id() that is used internally. * * \note If the view was to renumber the shapes array to use the Shape::id() values @@ -27,12 +27,12 @@ class ShapeMap public: using IndexType = IndexT; - /** + /*! * \brief Constructor */ AXOM_HOST_DEVICE ShapeMap() : m_shape_values(), m_shape_ids() { } - /** + /*! * \brief Constructor * * \param shape_values A view of sorted values used in the Conduit data. @@ -44,19 +44,19 @@ class ShapeMap , m_shape_ids(shape_ids) { } - /** + /*! * \brief Return the size of the shape map. * \return The number of entries in the shape map. */ AXOM_HOST_DEVICE IndexType size() const { return m_shape_values.size(); } - /** + /*! * \brief Return whether the shape map is empty. * \return True if the map is empty; False otherwise. */ AXOM_HOST_DEVICE bool empty() const { return m_shape_values.empty(); } - /** + /*! * \brief Given a shape value (as in the Conduit shapes array), return the shape id. * * \param value A value from the shapes array that we want to map to a shape id. @@ -74,7 +74,7 @@ class ShapeMap axom::ArrayView m_shape_ids; }; -/** +/*! * \brief This class provides a view for Conduit/Blueprint mixed shape unstructured grids. * * \tparam IndexT The index type that will be used for connectivity, etc. @@ -90,7 +90,7 @@ class UnstructuredTopologyMixedShapeView using ConnectivityView = axom::ArrayView; using ShapeType = VariableShape; - /** + /*! * \brief Constructor * * \param topo A reference to the topology. @@ -117,28 +117,28 @@ class UnstructuredTopologyMixedShapeView m_offsets.size() == m_shapes.size()); } - /** + /*! * \brief Return the dimension of the shape. * * \return -1 for unknown dimension. We'd have to look at the shapes. */ AXOM_HOST_DEVICE static constexpr int dimension() { return -1; } - /** + /*! * \brief Return the number of zones. * * \return The number of zones. */ IndexType numberOfZones() const { return m_sizes.size(); } - /** + /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. */ IndexType connectivitySize() const { return m_connectivity.size(); } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. @@ -179,7 +179,7 @@ class UnstructuredTopologyMixedShapeView }); } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. @@ -226,7 +226,7 @@ class UnstructuredTopologyMixedShapeView } private: - /** + /*! * \brief Populate the shape map values/ids arrays using data in the topology's shape_map. * * \param[out] values The sorted values used for shapes in the topology. diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index c4e8a4697d..4be9c6546d 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -14,7 +14,7 @@ namespace mir { namespace views { -/** +/*! * \brief This class implements a view for Blueprint polyhedral topologies. */ template @@ -24,7 +24,7 @@ class UnstructuredTopologyPolyhedralView using ConnectivityType = ConnType; using ConnectivityView = axom::ArrayView; - /** + /*! * \brief This struct contains views that hold polyhedral connectivity. */ struct PolyhedronData @@ -64,7 +64,7 @@ class UnstructuredTopologyPolyhedralView ConnectivityView m_element_offsets; }; - /** + /*! * \brief This struct provides data about Zone i's shape. */ struct PolyhedronShape @@ -185,7 +185,7 @@ class UnstructuredTopologyPolyhedralView using ShapeType = PolyhedronShape; - /** + /*! * \brief Constructor. */ UnstructuredTopologyPolyhedralView(const ConnectivityView &subelement_conn, @@ -202,28 +202,28 @@ class UnstructuredTopologyPolyhedralView element_offsets) { } - /** + /*! * \brief Return the number of zones in the mesh. * * \return The number of zones. */ IndexType numberOfZones() const { return m_data.m_element_sizes.size(); } - /** + /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. */ IndexType connectivitySize() const { return m_data.element_conn.size(); } - /** + /*! * \brief Return the dimension of the shape. * * \return The dimension of the shape. */ AXOM_HOST_DEVICE static constexpr int dimension() { return 3; } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. @@ -246,7 +246,7 @@ class UnstructuredTopologyPolyhedralView }); } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index ef8f1d5d6e..c58e0b5051 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -14,7 +14,7 @@ namespace mir { namespace views { -/** +/*! * \brief This class provides a view for Conduit/Blueprint single shape unstructured grids. * * \tparam IndexT The index type that will be used for connectivity, etc. @@ -28,7 +28,7 @@ class UnstructuredTopologySingleShapeView using ConnectivityType = typename ShapeType::ConnectivityType; using ConnectivityView = typename ShapeType::ConnectivityView; - /** + /*! * \brief Constructor * * \param conn The mesh connectivity. @@ -39,7 +39,7 @@ class UnstructuredTopologySingleShapeView , m_offsetsView() { } - /** + /*! * \brief Constructor * * \param conn The mesh connectivity. @@ -56,14 +56,14 @@ class UnstructuredTopologySingleShapeView SLIC_ASSERT(m_offsetsView.size() == m_sizesView.size()); } - /** + /*! * \brief Return the dimension of the shape. * * \return The dimension of the shape. */ AXOM_HOST_DEVICE static constexpr int dimension() { return ShapeT::dimension(); } - /** + /*! * \brief Return the number of zones. * * \return The number of zones. @@ -75,14 +75,14 @@ class UnstructuredTopologySingleShapeView : (m_connectivityView.size() / ShapeType::numberOfNodes()); } - /** + /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. */ IndexType connectivitySize() const { return m_connectivityView.size(); } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. @@ -138,7 +138,7 @@ class UnstructuredTopologySingleShapeView } } - /** + /*! * \brief Execute a function for each zone in the mesh. * * \tparam ExecSpace The execution space for the function body. diff --git a/src/axom/mir/views/dispatch_coordset.hpp b/src/axom/mir/views/dispatch_coordset.hpp index 6d0c33c9f1..b5bca61007 100644 --- a/src/axom/mir/views/dispatch_coordset.hpp +++ b/src/axom/mir/views/dispatch_coordset.hpp @@ -17,14 +17,14 @@ namespace mir { namespace views { -/** +/*! * \brief Base template for creating a rectilinear coordset view. */ template struct make_rectilinear_coordset { }; -/** +/*! * \brief Partial specialization for creating 3D rectilinear coordset view. */ template @@ -32,7 +32,7 @@ struct make_rectilinear_coordset { using CoordsetView = axom::mir::views::RectilinearCoordsetView3; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -48,7 +48,7 @@ struct make_rectilinear_coordset } }; -/** +/*! * \brief Partial specialization for creating 2D rectilinear coordset view. */ template @@ -56,7 +56,7 @@ struct make_rectilinear_coordset { using CoordsetView = axom::mir::views::RectilinearCoordsetView2; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -71,14 +71,14 @@ struct make_rectilinear_coordset } }; -/** +/*! * \brief Base template for creating a rectilinear coordset view. */ template struct make_uniform_coordset { }; -/** +/*! * \brief Partial specialization for creating 3D uniform coordset view. */ template <> @@ -86,7 +86,7 @@ struct make_uniform_coordset<3> { using CoordsetView = axom::mir::views::UniformCoordsetView; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -109,7 +109,7 @@ struct make_uniform_coordset<3> } }; -/** +/*! * \brief Partial specialization for creating 2D rectilinear coordset view. */ template <> @@ -117,7 +117,7 @@ struct make_uniform_coordset<2> { using CoordsetView = axom::mir::views::UniformCoordsetView; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -140,7 +140,7 @@ struct make_uniform_coordset<2> } }; -/** +/*! * \brief Dispatch an uniform coordset to a function. * * \tparam FuncType The type of the function / lambda to invoke. It is expected @@ -166,7 +166,7 @@ void dispatch_uniform_coordset(const conduit::Node &coordset, FuncType &&func) } } -/** +/*! * \brief Dispatch a rectilinear coordset to a function. * * \tparam FuncType The type of the function / lambda to invoke. It is expected @@ -207,14 +207,14 @@ void dispatch_rectilinear_coordset(const conduit::Node &coordset, FuncType &&fun } } -/** +/*! * \brief Base template for creating a explicit coordset view. */ template struct make_explicit_coordset { }; -/** +/*! * \brief Partial specialization for creating 3D explicit coordset view. */ template @@ -222,7 +222,7 @@ struct make_explicit_coordset { using CoordsetView = axom::mir::views::ExplicitCoordsetView; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -238,7 +238,7 @@ struct make_explicit_coordset } }; -/** +/*! * \brief Partial specialization for creating 2D explicit coordset view. */ template @@ -246,7 +246,7 @@ struct make_explicit_coordset { using CoordsetView = axom::mir::views::ExplicitCoordsetView; - /** + /*! * \brief Create the coordset view and initialize it from the coordset. * \param topo The node containing the coordset. * \return The coordset view. @@ -261,7 +261,7 @@ struct make_explicit_coordset } }; -/** +/*! * \brief Dispatch an explicit coordset to a function. * * \tparam FuncType The type of the function / lambda to invoke. It is expected @@ -302,7 +302,7 @@ void dispatch_explicit_coordset(const conduit::Node &coordset, FuncType &&func) } } -/** +/*! * \brief Given a Conduit/Blueprint coordset, create an appropriate view and * call the supplied function, passing the coordset view to it. * diff --git a/src/axom/mir/views/dispatch_material.hpp b/src/axom/mir/views/dispatch_material.hpp index 867e46a54e..284178b2ec 100644 --- a/src/axom/mir/views/dispatch_material.hpp +++ b/src/axom/mir/views/dispatch_material.hpp @@ -17,7 +17,7 @@ namespace mir { namespace views { -/** +/*! * \brief Dispatch a Conduit node containing a matset to a function as the appropriate type of matset view. * * \tparam FuncType The function/lambda type that will take the matset. diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index deab009c31..f318ca5ca0 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -22,7 +22,7 @@ template struct make_rectilinear { }; -/** +/*! * \brief Create a 3D structured topology view with normal structured indexing. */ template <> @@ -32,7 +32,7 @@ struct make_rectilinear<3> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topo. * \param topo The node containing the topology. * \return The indexing. @@ -51,7 +51,7 @@ struct make_rectilinear<3> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topo. * \param topo The node containing the topology. * \return The topology view. @@ -62,7 +62,7 @@ struct make_rectilinear<3> } }; -/** +/*! * \brief Create a 2D structured topology view with normal structured indexing. */ template <> @@ -72,7 +72,7 @@ struct make_rectilinear<2> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -90,7 +90,7 @@ struct make_rectilinear<2> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -101,7 +101,7 @@ struct make_rectilinear<2> } }; -/** +/*! * \brief Create a 1D structured topology view with normal structured indexing. */ template <> @@ -111,7 +111,7 @@ struct make_rectilinear<1> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -128,7 +128,7 @@ struct make_rectilinear<1> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -139,7 +139,7 @@ struct make_rectilinear<1> } }; -/** +/*! * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index fb1a025712..d77322a3cd 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -22,7 +22,7 @@ namespace mir namespace views { //------------------------------------------------------------------------------ -/** +/*! * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. * * \tparam ArrayType The array type to use. @@ -48,14 +48,14 @@ bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr return found; } -/** +/*! * \brief Base template for strided structured topology creation */ template struct make_strided_structured { }; -/** +/*! * \brief Create a 3D structured topology view with strided structured indexing. */ template <> @@ -65,7 +65,7 @@ struct make_strided_structured<3> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing @@ -97,7 +97,7 @@ struct make_strided_structured<3> return Indexing(zoneDims, offsets, strides); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -108,7 +108,7 @@ struct make_strided_structured<3> } }; -/** +/*! * \brief Create a 2D structured topology view with strided structured indexing. */ template <> @@ -118,7 +118,7 @@ struct make_strided_structured<2> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -146,7 +146,7 @@ struct make_strided_structured<2> return Indexing(zoneDims, offsets, strides); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -157,7 +157,7 @@ struct make_strided_structured<2> } }; -/** +/*! * \brief Create a 1D structured topology view with strided structured indexing. */ template <> @@ -167,7 +167,7 @@ struct make_strided_structured<1> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -187,7 +187,7 @@ struct make_strided_structured<1> return Indexing(zoneDims, offsets, strides); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -198,14 +198,14 @@ struct make_strided_structured<1> } }; -/** +/*! * \brief Base template for structured topology creation */ template struct make_structured { }; -/** +/*! * \brief Create a 3D structured topology view with normal structured indexing. */ template <> @@ -215,7 +215,7 @@ struct make_structured<3> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -230,7 +230,7 @@ struct make_structured<3> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -241,7 +241,7 @@ struct make_structured<3> } }; -/** +/*! * \brief Create a 2D structured topology view with normal structured indexing. */ template <> @@ -251,7 +251,7 @@ struct make_structured<2> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -264,7 +264,7 @@ struct make_structured<2> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -275,7 +275,7 @@ struct make_structured<2> } }; -/** +/*! * \brief Create a 1D structured topology view with normal structured indexing. */ template <> @@ -285,7 +285,7 @@ struct make_structured<1> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -298,7 +298,7 @@ struct make_structured<1> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -309,7 +309,7 @@ struct make_structured<1> } }; -/** +/*! * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. @@ -379,7 +379,7 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) } } -/** +/*! * \brief Creates a topology view compatible with various logically "structured" topologies (uniform, rectilinear, structured) and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. diff --git a/src/axom/mir/views/dispatch_topology.hpp b/src/axom/mir/views/dispatch_topology.hpp index ac149c4f7a..767a065951 100644 --- a/src/axom/mir/views/dispatch_topology.hpp +++ b/src/axom/mir/views/dispatch_topology.hpp @@ -20,7 +20,7 @@ namespace mir { namespace views { -/** +/*! * \brief Creates a topology view and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 28dfe25396..36f5efa99d 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -18,14 +18,14 @@ namespace mir { namespace views { -/** +/*! * \brief Base template for uniform topology creation */ template struct make_uniform { }; -/** +/*! * \brief Create a 3D structured topology view with normal structured indexing. */ template <> @@ -35,7 +35,7 @@ struct make_uniform<3> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -53,7 +53,7 @@ struct make_uniform<3> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -64,7 +64,7 @@ struct make_uniform<3> } }; -/** +/*! * \brief Create a 2D structured topology view with normal structured indexing. */ template <> @@ -74,7 +74,7 @@ struct make_uniform<2> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -91,7 +91,7 @@ struct make_uniform<2> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -102,7 +102,7 @@ struct make_uniform<2> } }; -/** +/*! * \brief Create a 1D structured topology view with normal structured indexing. */ template <> @@ -112,7 +112,7 @@ struct make_uniform<1> using LogicalIndex = typename Indexing::LogicalIndex; using TopoView = views::StructuredTopologyView; - /** + /*! * \brief Create the indexing and initialize it from the topology. * \param topo The node containing the topology. * \return The indexing. @@ -128,7 +128,7 @@ struct make_uniform<1> return Indexing(zoneDims); } - /** + /*! * \brief Create the topology view and initialize it from the topology. * \param topo The node containing the topology. * \return The topology view. @@ -139,7 +139,7 @@ struct make_uniform<1> } }; -/** +/*! * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. * * \tparam FuncType The function/lambda type to invoke on the view. diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 53d3e7fd24..57a25549d8 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -25,7 +25,7 @@ namespace views // Turn on all bits so all shapes will be enabled. constexpr int AnyShape = -1; -/** +/*! * \brief This function dispatches a Conduit polyhedral unstructured topology. * * \tparam FuncType The function/lambda type that will be invoked on the view. @@ -95,7 +95,7 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, } } -/** +/*! * \brief This function dispatches a Conduit mixed unstructured topology. * * \tparam FuncType The function/lambda type that will be invoked on the view. @@ -169,7 +169,7 @@ constexpr int select_shapes(Args... args) return encode_types((1 << args)...); } -/** +/*! * \brief This function dispatches a Conduit topology to the right view type * and passes that view to the supplied function/lambda. * diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index 417b2e66fe..7f63505da8 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -71,14 +71,14 @@ struct view_traits>> static constexpr int selected_shapes() { return shapes_for_dimension(1); } }; -/** +/*! * \brief Base template for some ArrayView traits. */ template struct array_view_traits { }; -/** +/*! * \brief This macro defines some template specializations that help us access * ArrayView<> names. This can be helpful when the ArrayView comes into * a lambda as an auto argument. From ef1fb762e4684232796833261fd5d1fffc2f4379 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 20 Sep 2024 19:02:22 -0700 Subject: [PATCH 232/290] possible CUDA solution --- src/axom/mir/MergeMeshes.hpp | 192 +++++++++++++++++++++++++++++------ 1 file changed, 161 insertions(+), 31 deletions(-) diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index e440909a0d..7f51b0e51b 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -78,7 +78,11 @@ class MergeMeshes } } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /** * \brief This struct contains information used when merging fields. */ @@ -253,7 +257,7 @@ class MergeMeshes // Pull out specific nodes from the input. axom::for_all( nodeSliceView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto sliceIndex = nodeSliceView[index]; compView[offset + index] = srcCompView[sliceIndex]; }); @@ -264,7 +268,7 @@ class MergeMeshes // Pull out all nodes from the input. axom::for_all( srcCompView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { compView[offset + index] = srcCompView[index]; }); size = srcCompView.size(); @@ -460,7 +464,7 @@ class MergeMeshes const auto nodeMapView = inputs[i].m_nodeMapView; axom::for_all( srcConnView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto nodeId = srcConnView[index]; const auto newNodeId = nodeMapView[nodeId]; connView[connOffset + index] = newNodeId; @@ -471,7 +475,7 @@ class MergeMeshes // Copy all zones from the input. Map the nodes to the new values. axom::for_all( srcConnView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { connView[connOffset + index] = coordOffset + srcConnView[index]; }); } @@ -489,7 +493,7 @@ class MergeMeshes // Copy all sizes from the input. axom::for_all( srcSizesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { sizesView[sizesOffset + index] = srcSizesView[index]; }); @@ -513,7 +517,7 @@ class MergeMeshes // Copy all sizes from the input. axom::for_all( srcShapesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { shapesView[shapesOffset + index] = srcShapesView[index]; }); @@ -533,7 +537,7 @@ class MergeMeshes const int shapeId = axom::mir::views::shapeNameToID(srcShape); axom::for_all( nz, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { shapesView[shapesOffset + index] = shapeId; }); shapesOffset += nz; @@ -686,7 +690,7 @@ class MergeMeshes [&](auto srcView, auto destView) { axom::for_all( nzones, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = srcView[index]; }); @@ -697,13 +701,117 @@ class MergeMeshes axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { axom::for_all( nzones, - AXOM_LAMBDA(auto index) { destView[offset + index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); }); } offset += nzones; } } +#if 0 +#define AXOM_NODE_TO_ARRAYVIEW1(node1, view1, CODE) \ +axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); +#else +#define AXOM_NODE_TO_ARRAYVIEW1(node1, view1, CODE) \ + switch(node1.dtype().id()) \ + { \ + case conduit::DataType::INT8_ID: \ + { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT16_ID: { \ + axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT32_ID: \ + { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT64_ID: { \ + axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT8_ID: \ + { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT16_ID: { \ + axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT32_ID: \ + { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT64_ID: { \ + axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::FLOAT32_ID: \ + { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::FLOAT64_ID: { \ + axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ + CODE \ + } break; \ + } + +#define AXOM_NODE_TO_ARRAYVIEW_SAME2(node1, node2, view1, view2, CODE) \ + switch(node1.dtype().id()) \ + { \ + case conduit::DataType::INT8_ID: {\ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT16_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT32_ID: {\ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::INT64_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT8_ID: {\ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT16_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT32_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::UINT64_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::FLOAT32_ID: {\ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + case conduit::DataType::FLOAT64_ID: { \ + axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ + axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ + CODE \ + } break; \ + } +#endif /** * \brief Copy nodal field data into a Conduit node. * @@ -724,40 +832,62 @@ class MergeMeshes { const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); + axom::mir::views::Node_to_ArrayView( n_src_values, n_values, [&](auto srcView, auto destView) { - if(inputs[i].m_nodeSliceView.empty()) + + copyNodal_copy(inputs[i].m_nodeSliceView, srcView, destView, nnodes, offset); + + }); + } + else + { +#if 1 + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { + copyNodal_fill(destView, nnodes, offset); + }); +#else + axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { + axom::for_all( + nnodes, + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); + }); +#endif + } + offset += nnodes; + } + } + + template + void copyNodal_copy(axom::ArrayView nodeSliceView, SrcViewType srcView, DestViewType destView, axom::IndexType nnodes, axom::IndexType offset) const + { + if(nodeSliceView.empty()) { axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = srcView[index]; }); } else { - auto nodeSliceView(inputs[i].m_nodeSliceView); axom::for_all( nnodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto nodeId = nodeSliceView[index]; destView[offset + index] = srcView[nodeId]; }); } - }); - } - else - { - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { - axom::for_all( - nnodes, - AXOM_LAMBDA(auto index) { destView[offset + index] = 0; }); - }); - } - offset += nnodes; - } + } + + template + void copyNodal_fill(DestViewType destView, axom::IndexType nnodes, axom::IndexType offset) const + { + axom::for_all( + nnodes, + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); } /** @@ -834,7 +964,7 @@ class MergeMeshes RAJA::ReduceSum matCount_reduce(0); axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto nmats = matsetView.numberOfMaterials(zoneIndex); matCount_reduce += nmats; }); @@ -912,7 +1042,7 @@ class MergeMeshes [&](auto matsetView) { axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { sizesView[zOffset + zoneIndex] = matsetView.numberOfMaterials(zoneIndex); }); @@ -922,7 +1052,7 @@ class MergeMeshes { axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { sizesView[zOffset + zoneIndex] = 1; }); } @@ -934,7 +1064,7 @@ class MergeMeshes // Make indices. axom::for_all( totalMatCount, - AXOM_LAMBDA(auto index) { indicesView[index] = index; }); + AXOM_LAMBDA(axom::IndexType index) { indicesView[index] = index; }); // Fill in material info. zOffset = 0; @@ -991,7 +1121,7 @@ class MergeMeshes axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { // Get this zone's materials. IDList ids; VFList vfs; @@ -1021,7 +1151,7 @@ class MergeMeshes const int dmat = allMats["default"]; axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto zoneStart = offsetsView[zOffset + zoneIndex]; volumeFractionsView[zoneStart] = 1; materialIdsView[zoneStart] = dmat; From 1a6c6df55cf875585d513a1711aeeed9e30b052f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 23 Sep 2024 17:15:26 -0700 Subject: [PATCH 233/290] Progress on nvcc compatibility. --- src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/CoordsetBlender.hpp | 4 +- src/axom/mir/CoordsetSlicer.hpp | 2 +- src/axom/mir/FieldBlender.hpp | 120 +++-- src/axom/mir/FieldSlicer.hpp | 42 +- src/axom/mir/MakeUnstructured.hpp | 137 +++++ src/axom/mir/MergeMeshes.hpp | 504 +++++++++++++----- src/axom/mir/NodeToZoneRelationBuilder.hpp | 248 ++++++--- src/axom/mir/SelectedZones.hpp | 82 ++- src/axom/mir/blueprint_utilities.hpp | 80 --- src/axom/mir/clipping/ClipTableManager.hpp | 2 +- src/axom/mir/tests/mir_views.cpp | 132 +++-- src/axom/mir/views/NodeArrayView.hpp | 23 +- .../views/dispatch_unstructured_topology.hpp | 20 +- src/axom/mir/views/dispatch_utilities.hpp | 19 + 15 files changed, 961 insertions(+), 455 deletions(-) create mode 100644 src/axom/mir/MakeUnstructured.hpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 3c90c8278d..bd1e2fa730 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -39,6 +39,7 @@ set(mir_headers ExtractZones.hpp FieldBlender.hpp FieldSlicer.hpp + MakeUnstructured.hpp MatsetSlicer.hpp MergeMeshes.hpp MIRAlgorithm.hpp diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/CoordsetBlender.hpp index 22bfd2651e..233fef8ff8 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/CoordsetBlender.hpp @@ -109,7 +109,7 @@ class CoordsetBlender // Copy over some original values to the start of the array. axom::for_all( origSize, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto srcIndex = deviceBlend.m_originalIdsView[index]; const auto pt = deviceView[srcIndex]; @@ -123,7 +123,7 @@ class CoordsetBlender // Append blended values to the end of the array. axom::for_all( blendSize, - AXOM_LAMBDA(auto bgid) { + AXOM_LAMBDA(axom::IndexType bgid) { // Get the blend group index we want. const auto selectedIndex = SelectionPolicy::selectedIndex(deviceBlend, bgid); diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/CoordsetSlicer.hpp index 089e2d958a..c8a3cfd0cd 100644 --- a/src/axom/mir/CoordsetSlicer.hpp +++ b/src/axom/mir/CoordsetSlicer.hpp @@ -98,7 +98,7 @@ class CoordsetSlicer const auto deviceIndicesView = slice.m_indicesView; axom::for_all( outputSize, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto srcIndex = deviceIndicesView[index]; const auto pt = deviceView[srcIndex]; diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index 8370213d9a..d054bd9fde 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -137,7 +137,11 @@ class FieldBlender } } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /*! * \brief Blend data for a single field component. * @@ -164,55 +168,81 @@ class FieldBlender n_values, n_output_values, [&](auto compView, auto outView) { - using value_type = typename decltype(compView)::value_type; - using accum_type = - typename axom::mir::utilities::accumulation_traits::value_type; - - const IndexingPolicy deviceIndexing(m_indexing); - const BlendData deviceBlend(blend); - - // Copy over some original values to the start of the array. - axom::for_all( - origSize, - AXOM_LAMBDA(auto index) { - const auto srcIndex = deviceBlend.m_originalIdsView[index]; - outView[index] = compView[srcIndex]; - }); - - // Append blended values to the end of the array. - axom::for_all( - blendSize, - AXOM_LAMBDA(auto bgid) { - // Get the blend group index we want. - const auto selectedIndex = - SelectionPolicy::selectedIndex(deviceBlend, bgid); - const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; - const auto nValues = deviceBlend.m_blendGroupSizesView[selectedIndex]; - const auto destIndex = origSize + bgid; - if(nValues == 1) - { - const auto index = deviceBlend.m_blendIdsView[start]; - const auto srcIndex = deviceIndexing[index]; - outView[destIndex] = compView[srcIndex]; - } - else - { - const auto end = start + nValues; - accum_type blended = 0; - for(IndexType i = start; i < end; i++) - { - const auto index = deviceBlend.m_blendIdsView[i]; - const auto weight = deviceBlend.m_blendCoeffView[i]; - const auto srcIndex = deviceIndexing[index]; - blended += static_cast(compView[srcIndex]) * weight; - } - outView[destIndex] = static_cast(blended); - } - }); + blendSingleComponentImpl(blend, compView, outView); + }); + } + + /*! + * \brief Slice the source view and copy values into the output view. + * + * \param valuesView The source values view. + * \param outputView The output values view. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void blendSingleComponentImpl(const BlendData &blend, SrcView compView, OutputView outView) const + { + using value_type = typename decltype(compView)::value_type; + using accum_type = + typename axom::mir::utilities::accumulation_traits::value_type; + + // We're allowing selectedIndicesView to be used to select specific blend + // groups. If the user did not provide that, use all blend groups. + const auto origSize = blend.m_originalIdsView.size(); + const auto blendSize = SelectionPolicy::size(blend); +// const auto outputSize = origSize + blendSize; + + const IndexingPolicy deviceIndexing(m_indexing); + const BlendData deviceBlend(blend); + + // Copy over some original values to the start of the array. + axom::for_all( + origSize, + AXOM_LAMBDA(axom::IndexType index) { + const auto srcIndex = deviceBlend.m_originalIdsView[index]; + outView[index] = compView[srcIndex]; + }); + + // Append blended values to the end of the array. + axom::for_all( + blendSize, + AXOM_LAMBDA(axom::IndexType bgid) { + // Get the blend group index we want. + const auto selectedIndex = + SelectionPolicy::selectedIndex(deviceBlend, bgid); + const auto start = deviceBlend.m_blendGroupStartView[selectedIndex]; + const auto nValues = deviceBlend.m_blendGroupSizesView[selectedIndex]; + const auto destIndex = origSize + bgid; + if(nValues == 1) + { + const auto index = deviceBlend.m_blendIdsView[start]; + const auto srcIndex = deviceIndexing[index]; + outView[destIndex] = compView[srcIndex]; + } + else + { + const auto end = start + nValues; + accum_type blended = 0; + for(IndexType i = start; i < end; i++) + { + const auto index = deviceBlend.m_blendIdsView[i]; + const auto weight = deviceBlend.m_blendCoeffView[i]; + const auto srcIndex = deviceIndexing[index]; + blended += static_cast(compView[srcIndex]) * weight; + } + outView[destIndex] = static_cast(blended); + } }); } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + IndexingPolicy m_indexing {}; }; diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 9fcbd830ec..10d305e6aa 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -84,7 +84,11 @@ class FieldSlicer } } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /*! * \brief Slice data for a single field component. * @@ -107,19 +111,39 @@ class FieldSlicer n_values, n_output_values, [&](auto valuesView, auto outputView) { - IndexingPolicy deviceIndexing(m_indexing); - SliceData deviceSlice(slice); - axom::for_all( - outputSize, - AXOM_LAMBDA(auto index) { - const auto zoneIndex = deviceSlice.m_indicesView[index]; - const auto transformedIndex = deviceIndexing[zoneIndex]; - outputView[index] = valuesView[transformedIndex]; - }); + sliceSingleComponentImpl(slice, valuesView, outputView); + }); + } + + /*! + * \brief Slice the source view and copy values into the output view. + * + * \param valuesView The source values view. + * \param outputView The output values view. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void sliceSingleComponentImpl(const SliceData &slice, ValuesView valuesView, OutputView outputView) const + { + IndexingPolicy deviceIndexing(m_indexing); + SliceData deviceSlice(slice); + axom::for_all( + outputView.size(), + AXOM_LAMBDA(auto index) { + const auto zoneIndex = deviceSlice.m_indicesView[index]; + const auto transformedIndex = deviceIndexing[zoneIndex]; + outputView[index] = valuesView[transformedIndex]; }); } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + IndexingPolicy m_indexing {}; }; diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp new file mode 100644 index 0000000000..3ed4bab638 --- /dev/null +++ b/src/axom/mir/MakeUnstructured.hpp @@ -0,0 +1,137 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_MAKE_UNSTRUCTURED_HPP_ +#define AXOM_MIR_MAKE_UNSTRUCTURED_HPP_ + +#include "axom/core.hpp" +#include "axom/mir/views/NodeArrayView.hpp" +#include "axom/mir/utilities.hpp" +#include "axom/mir/blueprint_utilities.hpp" + +#include + +namespace axom +{ +namespace mir +{ +namespace utilities +{ +namespace blueprint +{ + +/** + * \accelerated + * \brief Make an unstructured representation of a structured topology. + */ +template +class MakeUnstructured +{ +public: + /** + * \brief Make an unstructured representation of a structured topology. + * + * \tparam ExecSpace The execution space where the work will be done. + * + * \param topo The input topology to be turned into unstructured. + * \param coordset The topology's coordset. It will be referenced as an external node in the output \a mesh. + * \param topoName The name of the new topology to create. + * \param mesh The node that will contain the new topology and coordset. + * + * \note There are blueprint methods for this sort of thing but this one runs on device. + */ + static void execute(const conduit::Node &topo, + const conduit::Node &coordset, + const std::string &topoName, + conduit::Node &mesh) + { + const std::string type = topo.fetch_existing("type").as_string(); + ConduitAllocateThroughAxom c2a; + + mesh["coordsets"][coordset.name()].set_external(coordset); + conduit::Node &n_newtopo = mesh["topologies"][topoName]; + n_newtopo["coordset"] = coordset.name(); + + if(type == "unstructured") + { + n_newtopo.set_external(topo); + } + else + { + n_newtopo["type"] = "unstructured"; + conduit::Node &n_newconn = n_newtopo["elements/connectivity"]; + conduit::Node &n_newsizes = n_newtopo["elements/sizes"]; + conduit::Node &n_newoffsets = n_newtopo["elements/offsets"]; + n_newconn.set_allocator(c2a.getConduitAllocatorID()); + n_newsizes.set_allocator(c2a.getConduitAllocatorID()); + n_newoffsets.set_allocator(c2a.getConduitAllocatorID()); + + axom::mir::views::dispatch_structured_topologies( + topo, + [&](const std::string &shape, auto &topoView) { + + n_newtopo["elements/shape"] = shape; + makeUnstructured(shape, topoView, n_newconn, n_newsizes, n_newoffsets); + + }); + } + } + +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) +private: +#endif + + /*! + * \brief Iterate over the input topology's zones and store their connectivity in + * unstructured connectivity nodes. + * + * \param topoView The input topology view. + * \param n_newconn The node that will contain the new connectivity. + * \param n_newconn The node that will contain the new connectivity. + * \param n_newconn The node that will contain the new connectivity. + */ + template + static void makeUnstructured(const std::string &shape, const TopologyView &topoView, conduit::Node &n_newconn, conduit::Node &n_newsizes, conduit::Node &n_newoffsets) + { + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + // Allocate new mesh data. + const auto nzones = topoView.numberOfZones(); + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + n_newsizes.set(conduit::DataType::index_t(nzones)); + n_newoffsets.set(conduit::DataType::index_t(nzones)); + + // Make views for the mesh data. + auto connView = make_array_view(n_newconn); + auto sizesView = make_array_view(n_newsizes); + auto offsetsView = make_array_view(n_newoffsets); + + // Fill in the new connectivity. + using ZoneType = typename TopologyView::ShapeType; + topoView.template for_all_zones( + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < ptsPerZone; i++) + { + connView[start + i] = + static_cast(zone.getId(i)); + } + sizesView[zoneIndex] = ptsPerZone; + offsetsView[zoneIndex] = start; + }); + } +}; + +} // end namespace blueprint +} // end namespace utilities +} // end namespace mir +} // end namespace axom + +#endif diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index 7f51b0e51b..ddeabb7671 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -244,35 +244,12 @@ class MergeMeshes using FloatType = typename decltype(comp0)::value_type; for(int c = 0; c < nComps; c++) { - axom::IndexType size = 0, offset = offsets[c]; - const conduit::Node &n_srcComp = n_srcValues[c]; conduit::Node &n_comp = n_newValuesPtr->child(c); auto srcCompView = bputils::make_array_view(n_srcComp); auto compView = bputils::make_array_view(n_comp); - const auto nodeSliceView = inputs[i].m_nodeSliceView; - if(nodeSliceView.size() > 0) - { - // Pull out specific nodes from the input. - axom::for_all( - nodeSliceView.size(), - AXOM_LAMBDA(axom::IndexType index) { - const auto sliceIndex = nodeSliceView[index]; - compView[offset + index] = srcCompView[sliceIndex]; - }); - size = nodeSliceView.size(); - } - else - { - // Pull out all nodes from the input. - axom::for_all( - srcCompView.size(), - AXOM_LAMBDA(axom::IndexType index) { - compView[offset + index] = srcCompView[index]; - }); - size = srcCompView.size(); - } + axom::IndexType size = mergeCoordset_copy(inputs[i].m_nodeSliceView, offsets[c], compView, srcCompView); offsets[c] += size; } @@ -280,6 +257,56 @@ class MergeMeshes } } + /*! + * \brief Assist setting merging coordset data. + * + * \param nodeSliceView The view that contains a node slice for the current input mesh. + * \param offset The current write offset in the new coordset. + * \param compView The view that exposes the current output coordinate component. + * \param srcCompView The view that exposes the current output source coordinate component. + * + * \return The size of the data copied. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + axom::IndexType mergeCoordset_copy(const axom::ArrayView nodeSliceView, axom::IndexType offset, DataArrayView compView, DataArrayView srcCompView) const + { + axom::IndexType size = 0; + if(nodeSliceView.size() > 0) + { + // Pull out specific nodes from the input. + axom::for_all( + nodeSliceView.size(), + AXOM_LAMBDA(axom::IndexType index) { + const auto sliceIndex = nodeSliceView[index]; + compView[offset + index] = srcCompView[sliceIndex]; + }); + size = nodeSliceView.size(); + } + else + { + // Pull out all nodes from the input. + axom::for_all( + srcCompView.size(), + AXOM_LAMBDA(axom::IndexType index) { + compView[offset + index] = srcCompView[index]; + }); + size = srcCompView.size(); + } + return size; + } + + /*! + * \brief Count the number of nodes in the \a index'th input mesh. + * + * \param inputs The vector of input meshes. + * \param index The index of the mesh to count. + * + * \return The number of nodes in the \a index input mesh. + */ axom::IndexType countNodes(const std::vector &inputs, size_t index) const { @@ -299,6 +326,13 @@ class MergeMeshes return nnodes; } + /*! + * \brief Count the number of nodes in all input meshes. + * + * \param inputs The vector of input meshes. + * + * \return The total number of nodes in the input meshes. + */ axom::IndexType countNodes(const std::vector &inputs) const { axom::IndexType nodeTotal = 0; @@ -310,6 +344,14 @@ class MergeMeshes return nodeTotal; } + /*! + * \brief Count the number of zones in the \a index'th input mesh. + * + * \param inputs The vector of input meshes. + * \param index The index of the mesh to count. + * + * \return The number of zones in the \a index input mesh. + */ axom::IndexType countZones(const std::vector &inputs, size_t index) const { @@ -322,6 +364,13 @@ class MergeMeshes return nzones; } + /*! + * \brief Count the number of nodes in all input meshes. + * + * \param inputs The vector of input meshes. + * \param[out] totalConnLength The total connectivity length for all meshes. + * \param[out] totalZones The total zones for all meshes. + */ void countZones(const std::vector &inputs, axom::IndexType &totalConnLength, axom::IndexType &totalZones) const @@ -456,29 +505,9 @@ class MergeMeshes n_newTopoPtr->fetch_existing("elements/connectivity"); auto connView = bputils::make_array_view(n_newConn); - if(inputs[i].m_nodeMapView.size() > 0) - { - // Copy all zones from the input but map the nodes to new values. - // The supplied nodeMap is assumed to be a mapping from the current - // node connectivity to the merged node connectivity. - const auto nodeMapView = inputs[i].m_nodeMapView; - axom::for_all( - srcConnView.size(), - AXOM_LAMBDA(axom::IndexType index) { - const auto nodeId = srcConnView[index]; - const auto newNodeId = nodeMapView[nodeId]; - connView[connOffset + index] = newNodeId; - }); - } - else - { - // Copy all zones from the input. Map the nodes to the new values. - axom::for_all( - srcConnView.size(), - AXOM_LAMBDA(axom::IndexType index) { - connView[connOffset + index] = coordOffset + srcConnView[index]; - }); - } + // Copy all sizes from the input. + mergeTopology_copy(inputs[i].m_nodeMapView, connOffset, coordOffset, connView, srcConnView); + connOffset += srcConnView.size(); coordOffset += countNodes(inputs, static_cast(i)); }); @@ -490,12 +519,7 @@ class MergeMeshes n_newTopoPtr->fetch_existing("elements/sizes"); auto sizesView = bputils::make_array_view(n_newSizes); - // Copy all sizes from the input. - axom::for_all( - srcSizesView.size(), - AXOM_LAMBDA(axom::IndexType index) { - sizesView[sizesOffset + index] = srcSizesView[index]; - }); + mergeTopology_copy_sizes(sizesOffset, sizesView, srcSizesView); sizesOffset += srcSizesView.size(); }); @@ -556,6 +580,67 @@ class MergeMeshes }); } + /*! + * \brief Assist copying topology connectivity to the merged topology. + * + * \param nodeMapView The node map. + * \param connOffset The write offset in the new connectivity. + * \param coordOffset The current mesh's coordinate offset in the new coordinates. + * \param connView The view that contains the new merged connectivity. + * \param srcConnView The view that contains the source connectivity. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeTopology_copy(axom::ArrayView nodeMapView, axom::IndexType connOffset, axom::IndexType coordOffset, ConnectivityView connView, ConnectivityView srcConnView) const + { + if(nodeMapView.size() > 0) + { + // Copy all zones from the input but map the nodes to new values. + // The supplied nodeMap is assumed to be a mapping from the current + // node connectivity to the merged node connectivity. + axom::for_all( + srcConnView.size(), + AXOM_LAMBDA(axom::IndexType index) { + const auto nodeId = srcConnView[index]; + const auto newNodeId = nodeMapView[nodeId]; + connView[connOffset + index] = newNodeId; + }); + } + else + { + // Copy all zones from the input. Map the nodes to the new values. + axom::for_all( + srcConnView.size(), + AXOM_LAMBDA(axom::IndexType index) { + connView[connOffset + index] = coordOffset + srcConnView[index]; + }); + } + } + + /*! + * \brief Assist copying topology sizes to the merged topology. + * + * \param sizesOffset The write offset for sizes in the new connectivity. + * \param sizesView The view that contains sizes for the new connectivity. + * \param srcSizesView The view that contains sizes for the input mesh. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeTopology_copy_sizes(axom::IndexType sizesOffset, IntegerView sizesView, IntegerView srcSizesView) const + { + axom::for_all( + srcSizesView.size(), + AXOM_LAMBDA(axom::IndexType index) { + sizesView[sizesOffset + index] = srcSizesView[index]; + }); + } + /** * \brief Merge fields that exist on the various mesh inputs. Zero-fill values * where a field does not exist in an input. @@ -900,8 +985,6 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); { AXOM_ANNOTATE_SCOPE("mergeMatset"); namespace bputils = axom::mir::utilities::blueprint; - using reduce_policy = - typename axom::execution_space::reduce_policy; bputils::ConduitAllocateThroughAxom c2a; // Make a pass through the inputs and make a list of the material names. @@ -955,20 +1038,14 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); conduit::Node &n_matset = n_matsets[0]; axom::IndexType matCount = 0; axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) { + // Figure out the types to use for storing the data. using IType = typename decltype(matsetView)::IndexType; using FType = typename decltype(matsetView)::FloatType; itype = bputils::cpp2conduit::id; ftype = bputils::cpp2conduit::id; - RAJA::ReduceSum matCount_reduce(0); - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - const auto nmats = matsetView.numberOfMaterials(zoneIndex); - matCount_reduce += nmats; - }); - matCount = matCount_reduce.get(); + matCount = mergeMatset_count(matsetView, nzones); }); totalMatCount += matCount; } @@ -1040,21 +1117,13 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); axom::mir::views::dispatch_material( n_matset, [&](auto matsetView) { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - sizesView[zOffset + zoneIndex] = - matsetView.numberOfMaterials(zoneIndex); - }); + + mergeMatset_sizes(matsetView, sizesView, nzones, zOffset); }); } else { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - sizesView[zOffset + zoneIndex] = 1; - }); + mergeMatset_sizes1(sizesView, nzones, zOffset); } zOffset += nzones; } @@ -1062,9 +1131,7 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); axom::exclusive_scan(sizesView, offsetsView); // Make indices. - axom::for_all( - totalMatCount, - AXOM_LAMBDA(axom::IndexType index) { indicesView[index] = index; }); + mergeMatset_indices(indicesView, totalMatCount); // Fill in material info. zOffset = 0; @@ -1081,81 +1148,13 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); axom::mir::views::dispatch_material( n_matset, [&](auto matsetView) { - using IDList = typename decltype(matsetView)::IDList; - using VFList = typename decltype(matsetView)::VFList; - using MatID = typename decltype(matsetView)::IndexType; - - // Make some maps for renumbering material numbers. - const auto localMaterialMap = - axom::mir::views::materials(n_matset); - std::map localToAll; - for(const auto &info : localMaterialMap) - { - MatID matno = allMats[info.name]; - localToAll[info.number] = matno; - } - std::vector localVec, allVec; - for(auto it = localToAll.begin(); it != localToAll.end(); - it++) - { - localVec.push_back(it->first); - allVec.push_back(it->second); - } - // Put maps on device. - const int allocatorID = - axom::execution_space::allocatorID(); - axom::Array local(localVec.size(), - localVec.size(), - allocatorID); - axom::Array all(allVec.size(), - allVec.size(), - allocatorID); - axom::copy(local.data(), - localVec.data(), - sizeof(MatID) * local.size()); - axom::copy(all.data(), - allVec.data(), - sizeof(MatID) * all.size()); - const auto localView = local.view(); - const auto allView = all.view(); - - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - // Get this zone's materials. - IDList ids; - VFList vfs; - matsetView.zoneMaterials(zoneIndex, ids, vfs); - - // Store the materials in the new material. - const auto zoneStart = - offsetsView[zOffset + zoneIndex]; - for(axom::IndexType mi = 0; mi < ids.size(); mi++) - { - const auto destIndex = zoneStart + mi; - volumeFractionsView[destIndex] = vfs[mi]; - - // Get the index of the material number in the local map. - const auto mapIndex = - axom::mir::utilities::bsearch(ids[mi], localView); - assert(mapIndex != -1); - // We'll store the all materials number. - const auto allMatno = allView[mapIndex]; - materialIdsView[destIndex] = allMatno; - } - }); + mergeMatset_copy(n_matset, allMats, materialIdsView, offsetsView, volumeFractionsView, matsetView, nzones, zOffset); }); } else { const int dmat = allMats["default"]; - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - const auto zoneStart = offsetsView[zOffset + zoneIndex]; - volumeFractionsView[zoneStart] = 1; - materialIdsView[zoneStart] = dmat; - }); + mergeMatset_default(materialIdsView, offsetsView, volumeFractionsView, dmat, nzones, zOffset); } zOffset += nzones; } @@ -1164,6 +1163,217 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); } } // if hasMatsets } + + /*! + * \brief Assist in counting the total material elements needed for the input matset. + * + * \param matsetView The view that wraps the material data. + * \param nzones The number of zones. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + axom::IndexType mergeMatset_count(MatsetView matsetView, axom::IndexType nzones) const + { + using reduce_policy = + typename axom::execution_space::reduce_policy; + RAJA::ReduceSum matCount_reduce(0); + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto nmats = matsetView.numberOfMaterials(zoneIndex); + matCount_reduce += nmats; + }); + return matCount_reduce.get(); + } + + /*! + * \brief Assist in setting sizes for the new matset. + * + * \param matsetView The view that wraps the material data. + * \param sizesView The view that exposes the new matset sizes. + * \param nzones The number of zones in the current input. + * \param zOffset The current offset in the merged output. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeMatset_sizes(const MatsetView matsetView, IntegerArrayView sizesView, axom::IndexType nzones, axom::IndexType zOffset) const + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + sizesView[zOffset + zoneIndex] = + matsetView.numberOfMaterials(zoneIndex); + }); + } + + /*! + * \brief Assist in setting sizes for the new matset. + * + * \param sizesView The view that exposes the new matset sizes. + * \param nzones The number of zones in the current input. + * \param zOffset The current offset in the merged output. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeMatset_sizes1(IntegerArrayView sizesView, axom::IndexType nzones, axom::IndexType zOffset) const + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + sizesView[zOffset + zoneIndex] = 1; + }); + } + + /*! + * \brief Assist in setting indices for the new matset. + * + * \param indicesView The view that exposes the new matset indices. + * \param totalMatCount The total number of elements in the material_ids or volume_fractions array. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeMatset_indices(IntegerArrayView indicesView, axom::IndexType totalMatCount) const + { + axom::for_all( + totalMatCount, + AXOM_LAMBDA(axom::IndexType index) { indicesView[index] = index; }); + } + + /*! + * \brief Assist in copying matset data into the new merged matset. + * + * \param n_matset A Node that contains the matset. + * \param allMats A map of material numbers to merged material numbers. + * \param materialIdsView The view that exposes the merged material ids. + * \param offsetsView The view that exposes the merged material offsets. + * \param volumeFractionsView The view that exposes the merged volume fractions. + * \param matsetView The matset view that contains the source matset data. + * \param nzones The number of zones in the current input. + * \param zOffset The current offset in the merged output. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeMatset_copy(const conduit::Node &n_matset, + const std::map &allMats, + IntegerView materialIdsView, + IntegerView offsetsView, + FloatView volumeFractionsView, + MatsetView matsetView, + axom::IndexType nzones, + axom::IndexType zOffset) const + { + using IDList = typename decltype(matsetView)::IDList; + using VFList = typename decltype(matsetView)::VFList; + using MatID = typename decltype(matsetView)::IndexType; + + // Make some maps for renumbering material numbers. + const auto localMaterialMap = + axom::mir::views::materials(n_matset); + std::map localToAll; + for(const auto &info : localMaterialMap) + { + const auto it = allMats.find(info.name); + SLIC_ASSERT(it != allMats.end()); + MatID matno = it->second; + localToAll[info.number] = matno; + } + std::vector localVec, allVec; + for(auto it = localToAll.begin(); it != localToAll.end(); + it++) + { + localVec.push_back(it->first); + allVec.push_back(it->second); + } + // Put maps on device. + const int allocatorID = + axom::execution_space::allocatorID(); + axom::Array local(localVec.size(), + localVec.size(), + allocatorID); + axom::Array all(allVec.size(), + allVec.size(), + allocatorID); + axom::copy(local.data(), + localVec.data(), + sizeof(MatID) * local.size()); + axom::copy(all.data(), + allVec.data(), + sizeof(MatID) * all.size()); + const auto localView = local.view(); + const auto allView = all.view(); + + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + // Get this zone's materials. + IDList ids; + VFList vfs; + matsetView.zoneMaterials(zoneIndex, ids, vfs); + + // Store the materials in the new material. + const auto zoneStart = + offsetsView[zOffset + zoneIndex]; + for(axom::IndexType mi = 0; mi < ids.size(); mi++) + { + const auto destIndex = zoneStart + mi; + volumeFractionsView[destIndex] = vfs[mi]; + + // Get the index of the material number in the local map. + const auto mapIndex = + axom::mir::utilities::bsearch(ids[mi], localView); + assert(mapIndex != -1); + // We'll store the all materials number. + const auto allMatno = allView[mapIndex]; + materialIdsView[destIndex] = allMatno; + } + }); + } + + /*! + * \brief Assist setting default material data when the input mesh has no material. + * + * \param materialIdsView The view that exposes the merged material ids. + * \param offsetsView The view that exposes the merged material offsets. + * \param volumeFractionsView The view that exposes the merged volume fractions. + * \param matno The material number to use for these zones. + * \param nzones The number of zones in the current input. + * \param zOffset The current offset in the merged output. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void mergeMatset_default(IntegerView materialIdsView, + IntegerView offsetsView, + FloatView volumeFractionsView, + int matno, + axom::IndexType nzones, + axom::IndexType zOffset) const + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zoneStart = offsetsView[zOffset + zoneIndex]; + volumeFractionsView[zoneStart] = 1; + materialIdsView[zoneStart] = matno; + }); + } }; } // end namespace blueprint diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 444339110b..d57839c700 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -10,6 +10,7 @@ #include "axom/mir/utilities.hpp" #include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/views/dispatch_unstructured_topology.hpp" +#include "axom/mir/MakeUnstructured.hpp" #include #include @@ -130,7 +131,8 @@ struct BuildRelation axom::for_all( nodesView.size(), AXOM_LAMBDA(axom::IndexType index) { - sizesView[nodesView[index]]++; // Works only because ExecSpace=SEQ_EXEC. + // Works because ExecSpace=SEQ_EXEC. + sizesView[nodesView[index]]++; }); // Make offsets axom::exclusive_scan(sizesView, offsetsView); @@ -152,7 +154,8 @@ struct BuildRelation const auto ni = nodesView[index]; const auto destOffset = offsetsView[ni] + sizesView[ni]; zonesView[destOffset] = zcopyView[index]; - sizesView[ni]++; // Works only because ExecSpace=SEQ_EXEC. + // Works because ExecSpace=SEQ_EXEC. + sizesView[ni]++; }); } }; @@ -206,74 +209,11 @@ class NodeToZoneRelationBuilder if(shape.is_polyhedral()) { - using reduce_policy = - typename axom::execution_space::reduce_policy; - const auto allocatorID = axom::execution_space::allocatorID(); - views::dispatch_unstructured_polyhedral_topology( topo, [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { - const auto nzones = topoView.numberOfZones(); - axom::Array sizes(nzones, nzones, allocatorID); - auto sizes_view = sizes.view(); - - // Run through the topology once to do a count of each zone's unique node ids. - using ZoneType = typename decltype(topoView)::ShapeType; - RAJA::ReduceSum count(0); - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); - const auto connSize = count.get(); - - // Do a scan on the size array to build an offset array. - axom::Array offsets(nzones, nzones, allocatorID); - auto offsets_view = offsets.view(); - axom::exclusive_scan(sizes_view, offsets_view); - sizes.clear(); - - // Allocate Conduit arrays on the device in a data type that matches the connectivity. - conduit::Node n_conn; - n_conn.set_allocator(conduitAllocatorID); - n_conn.set(conduit::DataType(intTypeId, connSize)); - - n_zones.set(conduit::DataType(intTypeId, connSize)); - n_sizes.set(conduit::DataType(intTypeId, nnodes)); - n_offsets.set(conduit::DataType(intTypeId, nnodes)); - - views::IndexNode_to_ArrayView_same( - n_conn, - n_zones, - n_sizes, - n_offsets, - [&](auto connectivityView, - auto zonesView, - auto sizesView, - auto offsetsView) { - // Run through the data one more time to build the nodes and zones arrays. - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); - i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } - }); - - // Make the relation. - using ViewType = decltype(connectivityView); - details::BuildRelation::execute( - connectivityView, - zonesView, - sizesView, - offsetsView); - }); - }); + handlePolyhedralView(topoView, n_zones, n_sizes, n_offsets, nnodes, intTypeId); + }); } else if(shape.is_polygonal() || shapeType == "mixed") { @@ -293,14 +233,7 @@ class NodeToZoneRelationBuilder n_topo_sizes, n_topo_offsets, [&](auto zonesView, auto sizesView, auto offsetsView) { - using DataType = typename decltype(zonesView)::value_type; - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - for(DataType i = 0; i < sizesView[zoneIndex]; i++) - zonesView[offsetsView[zoneIndex] + i] = zoneIndex; - }); + fillZonesMixed(nzones, zonesView, sizesView, offsetsView); }); views::IndexNode_to_ArrayView_same( @@ -334,12 +267,8 @@ class NodeToZoneRelationBuilder n_offsets, [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { // Make zones for each node - axom::for_all( - 0, - connSize, - AXOM_LAMBDA(axom::IndexType index) { - zonesView[index] = index / nodesPerShape; - }); + fillZones(zonesView, connSize, nodesPerShape); + // Make the relation. using ViewType = decltype(connectivityView); details::BuildRelation::execute(connectivityView, @@ -354,7 +283,7 @@ class NodeToZoneRelationBuilder // These are all structured topos of some sort. Make an unstructured representation and recurse. conduit::Node mesh; - axom::mir::utilities::blueprint::to_unstructured(topo, + axom::mir::utilities::blueprint::MakeUnstructured::execute(topo, coordset, "newtopo", mesh); @@ -363,6 +292,161 @@ class NodeToZoneRelationBuilder execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); } } + +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) +private: +#endif + + /*! + * \brief Handle a polyhedral view. + * + * \param topoView A polyhedral topology view. + * \param[out] n_zones The new zones node for the relation. + * \param[out] n_sizes The new sizes node for the relation. + * \param[out] n_offsets The new offsets node for the relation. + * \param nnodes The number of nodes in the mesh's coordset. + * \param intTypeId The dtype id for the connectivity. + * \param connSize The length of the connectivity. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void handlePolyhedralView(PHView topoView, conduit::Node &n_zones, conduit::Node &n_sizes, conduit::Node &n_offsets, axom::IndexType nnodes, int intTypeId) const + { + using reduce_policy = + typename axom::execution_space::reduce_policy; + utilities::blueprint::ConduitAllocateThroughAxom c2a; + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + const auto allocatorID = axom::execution_space::allocatorID(); + + const auto nzones = topoView.numberOfZones(); + axom::Array sizes(nzones, nzones, allocatorID); + auto sizes_view = sizes.view(); + + // Run through the topology once to do a count of each zone's unique node ids. + using ZoneType = typename decltype(topoView)::ShapeType; + RAJA::ReduceSum count(0); + topoView.template for_all_zones( + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); + const auto connSize = count.get(); + + // Do a scan on the size array to build an offset array. + axom::Array offsets(nzones, nzones, allocatorID); + auto offsets_view = offsets.view(); + axom::exclusive_scan(sizes_view, offsets_view); + sizes.clear(); + + // Allocate Conduit arrays on the device in a data type that matches the connectivity. + conduit::Node n_conn; + n_conn.set_allocator(conduitAllocatorID); + n_conn.set(conduit::DataType(intTypeId, connSize)); + + n_zones.set(conduit::DataType(intTypeId, connSize)); + n_sizes.set(conduit::DataType(intTypeId, nnodes)); + n_offsets.set(conduit::DataType(intTypeId, nnodes)); + + views::IndexNode_to_ArrayView_same( + n_conn, + n_zones, + n_sizes, + n_offsets, + [&](auto connectivityView, + auto zonesView, + auto sizesView, + auto offsetsView) { + fillZonesPH(topoView, connectivityView, zonesView, offsets_view); + + // Make the relation. + using ViewType = decltype(connectivityView); + details::BuildRelation::execute( + connectivityView, + zonesView, + sizesView, + offsetsView); + }); + } + + /*! + * \brief Fill in the zone numbers for each mixed-sized zone. + * + * \param topoView The topology view for the PH mesh. + * \param connectivityView The view that contains the connectivity. + * \param zonesView The view that will contain the zone ids. + * \param offsetsView The view that contains the offsets. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void fillZonesPH(const TopologyView &topoView, IntegerView connectivityView, IntegerView zonesView, OffsetsView offsets_view) const + { + // Run through the data one more time to build the nodes and zones arrays. + using ZoneType = typename TopologyView::ShapeType; + topoView.template for_all_zones( + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); + i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); + } + + /*! + * \brief Fill in the zone numbers for each mixed-sized zone. + * + * \param nzones The number of zones. + * \param zonesView The view that will contain the zone ids. + * \param sizesView The view that contains the sizes. + * \param offsetsView The view that contains the offsets. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void fillZonesMixed(axom::IndexType nzones, IntegerView zonesView, IntegerView sizesView, IntegerView offsetsView) const + { + using DataType = typename decltype(zonesView)::value_type; + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType zoneIndex) { + for(DataType i = 0; i < sizesView[zoneIndex]; i++) + zonesView[offsetsView[zoneIndex] + i] = zoneIndex; + }); + } + + /*! + * \brief Fill in the zone numbers for each node in the connectivity. + * + * \param zonesView The view that will contain the zone ids. + * \param connSize The length of the connectivity. + * \param nodesPerShape The number of nodes per shape. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void fillZones(IntegerView zonesView, axom::IndexType connSize, axom::IndexType nodesPerShape) const + { + axom::for_all( + connSize, + AXOM_LAMBDA(axom::IndexType index) { + zonesView[index] = index / nodesPerShape; + }); + } }; } // end namespace blueprint diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index b64738da0f..725374a7f2 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -43,7 +43,11 @@ class SelectedZones return m_selectedZonesView; } +// The following members are protected (unless using CUDA) +#if !defined(__CUDACC__) protected: +#endif + /*! * \brief The options may contain a "selectedZones" member that is a list of zones * that will be operated on. If such an array is present, copy and sort it. @@ -62,36 +66,11 @@ class SelectedZones // Store the zone list in m_selectedZones. int badValueCount = 0; views::IndexNode_to_ArrayView(options["selectedZones"], [&](auto zonesView) { - using loop_policy = - typename axom::execution_space::loop_policy; - using reduce_policy = - typename axom::execution_space::reduce_policy; - // It probably does not make sense to request more zones than we have in the mesh. SLIC_ASSERT(zonesView.size() <= nzones); - m_selectedZones = axom::Array(zonesView.size(), - zonesView.size(), - allocatorID); - auto szView = m_selectedZonesView = m_selectedZones.view(); - axom::for_all( - szView.size(), - AXOM_LAMBDA(axom::IndexType index) { szView[index] = zonesView[index]; }); - - // Check that the selected zone values are in range. - RAJA::ReduceSum errReduce(0); - axom::for_all( - szView.size(), - AXOM_LAMBDA(axom::IndexType index) { - const int err = - (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; - errReduce += err; - }); - badValueCount = errReduce.get(); - - // Make sure the selectedZones are sorted. - RAJA::sort(RAJA::make_span(szView.data(), szView.size())); - }); + badValueCount = buildSelectedZones(zonesView, nzones); + }); if(badValueCount > 0) { @@ -109,7 +88,56 @@ class SelectedZones } } + /*! + * \brief Help build the selected zones, converting them to axom::IndexType and sorting them. + * + * \param zonesView The view that contains the source zone ids. + * \param nzones The number of zones in the mesh. + * + * \return The number of invalid zone ids. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + int buildSelectedZones(ZonesViewType zonesView, axom::IndexType nzones) + { + using loop_policy = + typename axom::execution_space::loop_policy; + using reduce_policy = + typename axom::execution_space::reduce_policy; + + const auto allocatorID = axom::execution_space::allocatorID(); + m_selectedZones = axom::Array(zonesView.size(), + zonesView.size(), + allocatorID); + auto szView = m_selectedZonesView = m_selectedZones.view(); + axom::for_all( + szView.size(), + AXOM_LAMBDA(axom::IndexType index) { szView[index] = zonesView[index]; }); + + // Check that the selected zone values are in range. + RAJA::ReduceSum errReduce(0); + axom::for_all( + szView.size(), + AXOM_LAMBDA(axom::IndexType index) { + const int err = + (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + errReduce += err; + }); + + // Make sure the selectedZones are sorted. + RAJA::sort(RAJA::make_span(szView.data(), szView.size())); + + return errReduce.get(); + } + +// The following members are protected (unless using CUDA) +#if !defined(__CUDACC__) protected: +#endif + axom::Array m_selectedZones; // Storage for a list of selected zone ids. axom::ArrayView m_selectedZonesView; }; diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index ee6cde8f9b..96eddd7659 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -332,86 +332,6 @@ struct SSVertexFieldIndexing Indexing m_fieldIndexing {}; }; -//------------------------------------------------------------------------------ -/** - * \accelerated - * \brief Make an unstructured representation of a structured topology. - * - * \tparam ExecSpace The execution space where the work will be done. - * - * \param topo The input topology to be turned into unstructured. - * \param coordset The topology's coordset. It will be referenced as an external node in the output \a mesh. - * \param topoName The name of the new topology to create. - * \param mesh The node that will contain the new topology and coordset. - * - * \note There are blueprint methods for this sort of thing but this one is accelerated. - */ -template -void to_unstructured(const conduit::Node &topo, - const conduit::Node &coordset, - const std::string &topoName, - conduit::Node &mesh) -{ - const std::string type = topo.fetch_existing("type").as_string(); - ConduitAllocateThroughAxom c2a; - - mesh["coordsets"][coordset.name()].set_external(coordset); - conduit::Node &newtopo = mesh["topologies"][topoName]; - newtopo["coordset"] = coordset.name(); - - if(type == "unstructured") - { - newtopo.set_external(topo); - } - else - { - newtopo["type"] = "unstructured"; - conduit::Node &n_newconn = newtopo["elements/connectivity"]; - conduit::Node &n_newsizes = newtopo["elements/sizes"]; - conduit::Node &n_newoffsets = newtopo["elements/offsets"]; - n_newconn.set_allocator(c2a.getConduitAllocatorID()); - n_newsizes.set_allocator(c2a.getConduitAllocatorID()); - n_newoffsets.set_allocator(c2a.getConduitAllocatorID()); - - axom::mir::views::dispatch_structured_topologies( - topo, - [&](const std::string &shape, auto &topoView) { - int ptsPerZone = 2; - if(shape == "quad") - ptsPerZone = 4; - else if(shape == "hex") - ptsPerZone = 8; - - newtopo["elements/shape"] = shape; - - // Allocate new mesh data. - const auto nzones = topoView.numberOfZones(); - const auto connSize = nzones * ptsPerZone; - n_newconn.set(conduit::DataType::index_t(connSize)); - n_newsizes.set(conduit::DataType::index_t(nzones)); - n_newoffsets.set(conduit::DataType::index_t(nzones)); - - // Make views for the mesh data. - auto connView = make_array_view(n_newconn); - auto sizesView = make_array_view(n_newsizes); - auto offsetsView = make_array_view(n_newoffsets); - - // Fill in the new connectivity. - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { - const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < ptsPerZone; i++) - { - connView[start + i] = - static_cast(zone.getIds()[i]); - } - sizesView[zoneIndex] = ptsPerZone; - offsetsView[zoneIndex] = start; - }); - }); - } -} - /** * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, * making sure to allocate array data in the appropriate memory space for diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index cb4d5662e3..f29c7a9176 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -113,7 +113,7 @@ class TableView const auto len = shapeLength(ptr); return TableDataView(ptr, len); } -#if 1 +#if !defined(AXOM_DEVICE_CODE) private: void printShape(std::ostream &os, TableData shape) const { diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 872f0971c3..b8980e1819 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -12,6 +12,8 @@ #include +namespace bputils = axom::mir::utilities::blueprint; + //------------------------------------------------------------------------------ // Uncomment to generate baselines @@ -164,65 +166,81 @@ TEST(mir_views, explicit_coordsetview) } } -TEST(mir_views, strided_structured) +//------------------------------------------------------------------------------ +struct test_strided_structured { - conduit::Node hostMesh; - axom::mir::testing::data::strided_structured<2>(hostMesh); - // hostMesh.print(); + static void test() + { + conduit::Node hostMesh; + axom::mir::testing::data::strided_structured<2>(hostMesh); + // hostMesh.print(); + + axom::mir::views::dispatch_explicit_coordset( + hostMesh["coordsets/coords"], + [&](auto coordsetView) { + axom::mir::views::dispatch_structured_topology< + axom::mir::views::select_dimensions(2)>( + hostMesh["topologies/mesh"], + [&](const std::string &AXOM_UNUSED_PARAM(shape), auto topoView) { + + execute(coordsetView, topoView); + }); + }); + } - // These are the expected zone ids for this strided structured mesh. - // clang-format off - const axom::Array expectedZones {{16, 17, 24, 23, - 17, 18, 25, 24, - 18, 19, 26, 25, - 23, 24, 31, 30, - 24, 25, 32, 31, - 25, 26, 33, 32}}; - // clang-format on - auto expectedZonesView = expectedZones.view(); - - axom::mir::views::dispatch_explicit_coordset( - hostMesh["coordsets/coords"], - [&](auto coordsetView) { - axom::mir::views::dispatch_structured_topology< - axom::mir::views::select_dimensions(2)>( - hostMesh["topologies/mesh"], - [&](const std::string &AXOM_UNUSED_PARAM(shape), auto topoView) { - // Traverse the zones in the mesh and check the zone ids. - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const auto &zone) { - // Check zone ids. - const auto ids = zone.getIds(); - for(axom::IndexType i = 0; i < ids.size(); i++) - { - EXPECT_EQ(expectedZonesView[zoneIndex * 4 + i], ids[i]); - } - - // Check coordinates - const auto nodeIndexing = topoView.indexing().expand(); - for(axom::IndexType i = 0; i < ids.size(); i++) - { - // Get coordinate from coordsetView. - const auto pt = coordsetView[ids[i]]; - - // Get the logical local id for the id. - const auto index = nodeIndexing.GlobalToLocal(ids[i]); - const auto logical = nodeIndexing.IndexToLogicalIndex(index); - - // Expected coordinate - double x = (3. + 1. / 3.) * static_cast(logical[0] - 1); - const double yvals[] = {-2, 2, 6}; - double y = yvals[logical[1]]; - - const double dx = pt[0] - x; - const double dy = pt[1] - y; - double d = sqrt(dx * dx + dy * dy); - - EXPECT_TRUE(d < 1.e-10); - } - }); - }); - }); + template + static void execute(CoordsetView coordsetView, TopologyView topoView) + { + // These are the expected zone ids for this strided structured mesh. + // clang-format off + const axom::Array expectedZones {{16, 17, 24, 23, + 17, 18, 25, 24, + 18, 19, 26, 25, + 23, 24, 31, 30, + 24, 25, 32, 31, + 25, 26, 33, 32}}; + // clang-format on + auto expectedZonesView = expectedZones.view(); + + // Traverse the zones in the mesh and check the zone ids. + topoView.template for_all_zones( + AXOM_LAMBDA(axom::IndexType zoneIndex, const auto &zone) { + // Check zone ids. + const auto ids = zone.getIds(); + for(axom::IndexType i = 0; i < ids.size(); i++) + { + EXPECT_EQ(expectedZonesView[zoneIndex * 4 + i], ids[i]); + } + + // Check coordinates + const auto nodeIndexing = topoView.indexing().expand(); + for(axom::IndexType i = 0; i < ids.size(); i++) + { + // Get coordinate from coordsetView. + const auto pt = coordsetView[ids[i]]; + + // Get the logical local id for the id. + const auto index = nodeIndexing.GlobalToLocal(ids[i]); + const auto logical = nodeIndexing.IndexToLogicalIndex(index); + + // Expected coordinate + double x = (3. + 1. / 3.) * static_cast(logical[0] - 1); + const double yvals[] = {-2, 2, 6}; + double y = yvals[logical[1]]; + + const double dx = pt[0] - x; + const double dy = pt[1] - y; + double d = sqrt(dx * dx + dy * dy); + + EXPECT_TRUE(d < 1.e-10); + } + }); + } +}; + +TEST(mir_views, strided_structured_seq) +{ + test_strided_structured::test(); } //------------------------------------------------------------------------------ diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index e38308176a..3f30793508 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -41,16 +41,33 @@ struct Delimiter /// Used to separate arguments. constexpr Delimiter ArgumentDelimiter; +#if __cplusplus >= 201703L +// C++17 and later. template constexpr int encode_types(Args... args) { return (... | args); } +#else +template +constexpr int encode_types_impl(T arg) { + return arg; +} + +template +constexpr int encode_types_impl(T arg, Args... args) { + return (arg | encode_types_impl(args...)); +} template -constexpr int select_types(Args... args) -{ - return encode_types((1 << args)...); +constexpr int encode_types(Args... args) { + return encode_types_impl(args...); +} +#endif + +template +constexpr int select_types(Args... args) { + return encode_types((1 << args)...); } constexpr bool type_selected(int flag, int bit) { return flag & (1 << bit); } diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 57a25549d8..854db4b9d2 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -157,16 +157,34 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, } } +#if __cplusplus >= 201703L +// C++17 and later. template constexpr int encode_shapes(Args... args) { return (... | args); } +#else +template +constexpr int encode_shapes_impl(T arg) { + return arg; +} + +template +constexpr int encode_shapes_impl(T arg, Args... args) { + return (arg | encode_shapes_impl(args...)); +} + +template +constexpr int encode_shapes(Args... args) { + return encode_shapes_impl(args...); +} +#endif template constexpr int select_shapes(Args... args) { - return encode_types((1 << args)...); + return encode_shapes((1 << args)...); } /*! diff --git a/src/axom/mir/views/dispatch_utilities.hpp b/src/axom/mir/views/dispatch_utilities.hpp index 1baeb3a6fa..62e456f706 100644 --- a/src/axom/mir/views/dispatch_utilities.hpp +++ b/src/axom/mir/views/dispatch_utilities.hpp @@ -12,11 +12,30 @@ namespace mir { namespace views { + +#if __cplusplus >= 201703L +// C++17 and later. template constexpr int encode_dimensions(Dimensions... dims) { return (... | dims); } +#else +template +constexpr int encode_dimensions_impl(T arg) { + return arg; +} + +template +constexpr int encode_dimensions_impl(T arg, Dimensions... dims) { + return (arg | encode_dimensions_impl(dims...)); +} + +template +constexpr int encode_dimensions(Dimensions... dims) { + return encode_dimensions_impl(dims...); +} +#endif template constexpr int select_dimensions(Dimensions... dims) From e5bc818f3a69ca44da3c53a1ff9a41c9091d5610 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 23 Sep 2024 18:20:31 -0700 Subject: [PATCH 234/290] nvcc compatibility --- src/axom/mir/ExtractZones.hpp | 2 +- src/axom/mir/FieldSlicer.hpp | 2 +- src/axom/mir/MatsetSlicer.hpp | 4 +- src/axom/mir/MergeMeshes.hpp | 275 +++++++----------- src/axom/mir/RecenterField.hpp | 150 +++++----- src/axom/mir/blueprint_utilities.hpp | 60 ++-- src/axom/mir/clipping/ClipTableManager.hpp | 2 +- .../mir/tests/mir_blueprint_utilities.cpp | 10 +- src/axom/mir/views/StructuredTopologyView.hpp | 11 +- 9 files changed, 240 insertions(+), 276 deletions(-) diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 0bcec0bbbb..b524825fb1 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -190,7 +190,7 @@ class ExtractZones axom::for_all( selectedZonesView.size(), n, - AXOM_LAMBDA(auto index) { view[index] = 0; }); + AXOM_LAMBDA(axom::IndexType index) { view[index] = 0; }); } view = m_zoneSlice.view(); } diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index 10d305e6aa..ff7987a856 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -132,7 +132,7 @@ class FieldSlicer SliceData deviceSlice(slice); axom::for_all( outputView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto zoneIndex = deviceSlice.m_indicesView[index]; const auto transformedIndex = deviceIndexing[zoneIndex]; outputView[index] = valuesView[transformedIndex]; diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 92f8cbf969..8462b6f9fe 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -87,7 +87,7 @@ class MatsetSlicer selectedZonesView); axom::for_all( selectedZonesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto nmats = deviceMatsetView.numberOfMaterials(deviceSelectedZonesView[index]); sizesView[index] = nmats; @@ -119,7 +119,7 @@ class MatsetSlicer // Fill in the matset data with the zones we're keeping. axom::for_all( selectedZonesView.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { const auto size = static_cast(sizesView[index]); const auto offset = offsetsView[index]; diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index ddeabb7671..f6ba4fd2ee 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -537,14 +537,8 @@ class MergeMeshes conduit::Node &n_newShapes = n_newTopoPtr->fetch_existing("elements/shapes"); auto shapesView = bputils::make_array_view(n_newShapes); - // Copy all sizes from the input. - axom::for_all( - srcShapesView.size(), - AXOM_LAMBDA(axom::IndexType index) { - shapesView[shapesOffset + index] = srcShapesView[index]; - }); - + mergeTopology_copy_shapes(shapesOffset, shapesView, srcShapesView); shapesOffset += srcShapesView.size(); }); } @@ -559,11 +553,7 @@ class MergeMeshes n_newTopoPtr->fetch_existing("elements/shapes"); axom::mir::views::IndexNode_to_ArrayView(n_newShapes, [&](auto shapesView) { const int shapeId = axom::mir::views::shapeNameToID(srcShape); - axom::for_all( - nz, - AXOM_LAMBDA(axom::IndexType index) { - shapesView[shapesOffset + index] = shapeId; - }); + mergeTopology_default_shapes(shapesOffset, shapesView, nz, shapeId); shapesOffset += nz; }); } @@ -641,7 +631,41 @@ class MergeMeshes }); } - /** + /*! + * \brief Copy shapes from the source mesh to the merged mesh. + * + * \param shapesOffset The write offset for the shapes. + * \param shapesView The view that exposes shapes for the merged mesh. + * \param srcShapesView The view that exposes shapes for the source mesh. + */ + template + void mergeTopology_copy_shapes(axom::IndexType shapesOffset, IntegerView shapesView, IntegerView srcShapesView) const + { + axom::for_all( + srcShapesView.size(), + AXOM_LAMBDA(axom::IndexType index) { + shapesView[shapesOffset + index] = srcShapesView[index]; + }); + } + + /*! + * \brief Set shapes in the merged mesh to a specific shape. + * + * \param shapesOffset The write offset for the shapes. + * \param shapesView The view that exposes shapes for the merged mesh. + * \param srcShapesView The view that exposes shapes for the source mesh. + */ + template + void mergeTopology_default_shapes(axom::IndexType shapesOffset, IntegerView shapesView, axom::IndexType nzones, int shapeId) const + { + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType index) { + shapesView[shapesOffset + index] = shapeId; + }); + } + + /*! * \brief Merge fields that exist on the various mesh inputs. Zero-fill values * where a field does not exist in an input. * @@ -770,133 +794,61 @@ class MergeMeshes { const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_src_values, - n_values, - [&](auto srcView, auto destView) { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType index) { - destView[offset + index] = - srcView[index]; - }); - }); + axom::mir::views::Node_to_ArrayView(n_values, + n_src_values, + [&](auto destView, auto srcView) { + copyZonal_copy(nzones, offset, destView, srcView); + }); } else { axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); + fillValues(nzones, offset, destView); }); } offset += nzones; } } -#if 0 -#define AXOM_NODE_TO_ARRAYVIEW1(node1, view1, CODE) \ -axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); -#else -#define AXOM_NODE_TO_ARRAYVIEW1(node1, view1, CODE) \ - switch(node1.dtype().id()) \ - { \ - case conduit::DataType::INT8_ID: \ - { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT16_ID: { \ - axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT32_ID: \ - { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT64_ID: { \ - axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT8_ID: \ - { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT16_ID: { \ - axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT32_ID: \ - { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT64_ID: { \ - axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::FLOAT32_ID: \ - { axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::FLOAT64_ID: { \ - axom::ArrayView view1(static_cast(node1.data_ptr()), node1.dtype().number_of_elements()); \ - CODE \ - } break; \ + /*! + * \brief Copy zonal data from src to dest. + * + * \param nzones The number of zones. + * \param offset The current write offset. + * \param destView The view that exposes the new merged field. + * \param srcView The view that exposes the source field. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void copyZonal_copy(axom::IndexType nzones, axom::IndexType offset, DestView destView, SrcView srcView) const + { + axom::for_all(nzones, AXOM_LAMBDA(axom::IndexType index) { + destView[offset + index] = srcView[index]; + }); } -#define AXOM_NODE_TO_ARRAYVIEW_SAME2(node1, node2, view1, view2, CODE) \ - switch(node1.dtype().id()) \ - { \ - case conduit::DataType::INT8_ID: {\ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT16_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT32_ID: {\ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::INT64_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT8_ID: {\ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT16_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT32_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::UINT64_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::FLOAT32_ID: {\ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ - case conduit::DataType::FLOAT64_ID: { \ - axom::ArrayView view1(static_cast(const_cast(node1.data_ptr())), node1.dtype().number_of_elements()); \ - axom::ArrayView view2(static_cast(const_cast(node2.data_ptr())), node2.dtype().number_of_elements()); \ - CODE \ - } break; \ + /*! + * \brief Fill data in dest. + * + * \param nvalues The number of values. + * \param offset The current write offset. + * \param destView The view that exposes the new merged field. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void fillValues(axom::IndexType nvalues, axom::IndexType offset, DestView destView) const + { + axom::for_all(nvalues, AXOM_LAMBDA(axom::IndexType index) { + destView[offset + index] = 0; + }); } -#endif + /** * \brief Copy nodal field data into a Conduit node. * @@ -923,56 +875,53 @@ axom::mir::views::Node_to_ArrayView(node1, [&](auto view1){CODE}); n_values, [&](auto srcView, auto destView) { - copyNodal_copy(inputs[i].m_nodeSliceView, srcView, destView, nnodes, offset); + copyNodal_copy(inputs[i].m_nodeSliceView, nnodes, offset, destView, srcView); }); } else { -#if 1 axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { - copyNodal_fill(destView, nnodes, offset); + fillValues(nnodes, offset, destView); }); -#else - axom::mir::views::Node_to_ArrayView(n_values, [&](auto destView) { - axom::for_all( - nnodes, - AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); - }); -#endif } offset += nnodes; } } + /*! + * \brief Copy nodal data from src to dest. + * + * \param nodeSliceView The nodes we're pulling out (if populated). + * \param nnodes The number of nodes. + * \param offset The current write offset. + * \param destView The view that exposes the new merged field. + * \param srcView The view that exposes the source field. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ template - void copyNodal_copy(axom::ArrayView nodeSliceView, SrcViewType srcView, DestViewType destView, axom::IndexType nnodes, axom::IndexType offset) const + void copyNodal_copy(axom::ArrayView nodeSliceView, axom::IndexType nnodes, axom::IndexType offset, SrcViewType destView, DestViewType srcView) const { - if(nodeSliceView.empty()) - { - axom::for_all( - nnodes, - AXOM_LAMBDA(axom::IndexType index) { - destView[offset + index] = srcView[index]; - }); - } - else - { - axom::for_all( - nnodes, - AXOM_LAMBDA(axom::IndexType index) { - const auto nodeId = nodeSliceView[index]; - destView[offset + index] = srcView[nodeId]; - }); - } - } - - template - void copyNodal_fill(DestViewType destView, axom::IndexType nnodes, axom::IndexType offset) const - { - axom::for_all( - nnodes, - AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); + if(nodeSliceView.empty()) + { + axom::for_all( + nnodes, + AXOM_LAMBDA(axom::IndexType index) { + destView[offset + index] = srcView[index]; + }); + } + else + { + axom::for_all( + nnodes, + AXOM_LAMBDA(axom::IndexType index) { + const auto nodeId = nodeSliceView[index]; + destView[offset + index] = srcView[nodeId]; + }); + } } /** diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index d8f01dcdc3..46684a4d95 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -39,9 +39,35 @@ class RecenterField */ void execute(const conduit::Node &field, const conduit::Node &relation, - conduit::Node &outField) const; + conduit::Node &outField) const + { + const std::string association = field.fetch_existing("association").as_string(); + + // Assume that we're flipping the association. + outField["association"] = (association == "element") ? "vertex" : "element"; + outField["topology"] = field["topology"]; + + // Make output values. + const conduit::Node &n_values = field["values"]; + if(n_values.number_of_children() > 0) + { + for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) + { + const conduit::Node &n_comp = n_values[c]; + recenterSingleComponent(n_comp, relation, outField["values"][n_comp.name()]); + } + } + else + { + recenterSingleComponent(n_values, relation, outField["values"]); + } + } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + /*! * \brief Recenter a single field component. * @@ -51,82 +77,66 @@ class RecenterField */ void recenterSingleComponent(const conduit::Node &n_comp, const conduit::Node &relation, - conduit::Node &n_out) const; -}; - -template -void RecenterField::execute(const conduit::Node &field, - const conduit::Node &relation, - conduit::Node &outField) const -{ - const std::string association = field.fetch_existing("association").as_string(); - - // Assume that we're flipping the association. - outField["association"] = (association == "element") ? "vertex" : "element"; - outField["topology"] = field["topology"]; - - // Make output values. - const conduit::Node &n_values = field["values"]; - if(n_values.number_of_children() > 0) + conduit::Node &n_out) const { - for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) - { - const conduit::Node &n_comp = n_values[c]; - recenterSingleComponent(n_comp, relation, outField["values"][n_comp.name()]); - } - } - else - { - recenterSingleComponent(n_values, relation, outField["values"]); - } -} + // Get the data field for the o2m relation. + const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); + + // Use the o2mrelation to average data from n_comp to the n_out. + const conduit::Node &n_relvalues = relation[data_paths[0]]; + const conduit::Node &n_sizes = relation["sizes"]; + const conduit::Node &n_offsets = relation["offsets"]; + views::IndexNode_to_ArrayView_same( + n_relvalues, + n_sizes, + n_offsets, + [&](auto relView, auto sizesView, auto offsetsView) { + // Allocate Conduit data through Axom. + const auto relSize = sizesView.size(); + utilities::blueprint::ConduitAllocateThroughAxom c2a; + n_out.set_allocator(c2a.getConduitAllocatorID()); + n_out.set(conduit::DataType(n_comp.dtype().id(), relSize)); -template -void RecenterField::recenterSingleComponent( - const conduit::Node &n_comp, - const conduit::Node &relation, - conduit::Node &n_out) const -{ - // Get the data field for the o2m relation. - const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); + views::Node_to_ArrayView_same(n_out, n_comp, [&](auto outView, auto compView) { - // Use the o2mrelation to average data from n_comp to the n_out. - const conduit::Node &n_relvalues = relation[data_paths[0]]; - const conduit::Node &n_sizes = relation["sizes"]; - const conduit::Node &n_offsets = relation["offsets"]; - views::IndexNode_to_ArrayView_same( - n_relvalues, - n_sizes, - n_offsets, - [&](auto relView, auto sizesView, auto offsetsView) { - // Allocate Conduit data through Axom. - const auto relSize = sizesView.size(); - utilities::blueprint::ConduitAllocateThroughAxom c2a; - n_out.set_allocator(c2a.getConduitAllocatorID()); - n_out.set(conduit::DataType(n_comp.dtype().id(), relSize)); + recenterSingleComponentImpl(relView, sizesView, offsetsView, outView, compView); + }); + }); + } - views::Node_to_ArrayView_same(n_comp, n_out, [&](auto compView, auto outView) { - using Precision = typename decltype(compView)::value_type; - using AccumType = - typename axom::mir::utilities::accumulation_traits::value_type; - axom::for_all( - relSize, - AXOM_LAMBDA(auto relIndex) { - const auto n = static_cast(sizesView[relIndex]); - const auto offset = offsetsView[relIndex]; + /*! + * \brief Recenter a single field component. + * + * \param relView The view that contains the ids for the relation. + * \param sizesView The view that contains the sizes for the relation. + * \param offsetsView The view that contains the offsets for the relation. + * \param outView The view that contains the out data. + * \param compView The view that contains the source data. + */ + template + void recenterSingleComponentImpl(IndexView relView, IndexView sizesView, IndexView offsetsView, DataView outView, DataView compView) const + { + using Precision = typename DataView::value_type; + using AccumType = + typename axom::mir::utilities::accumulation_traits::value_type; + const auto relSize = sizesView.size(); + axom::for_all( + relSize, + AXOM_LAMBDA(axom::IndexType relIndex) { + const auto n = static_cast(sizesView[relIndex]); + const auto offset = offsetsView[relIndex]; - AccumType sum {}; - for(axom::IndexType i = 0; i < n; i++) - { - const auto id = relView[offset + i]; - sum += static_cast(compView[id]); - } + AccumType sum {}; + for(axom::IndexType i = 0; i < n; i++) + { + const auto id = relView[offset + i]; + sum += static_cast(compView[id]); + } - outView[relIndex] = static_cast(sum / n); - }); + outView[relIndex] = static_cast(sum / n); }); - }); -} + } +}; } // end namespace blueprint } // end namespace utilities diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 96eddd7659..7ab89a267b 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -383,43 +383,57 @@ void copy(conduit::Node &dest, const conduit::Node &src) } } -/** - * \brief Get the min/max values for the data in a Conduit node. - * - * \param[in] n The Conduit node whose data we're checking. - * - * \return A pair containing the min,max values in the node. +/*! + * \brief Get the min/max values for the data in a Conduit node or ArrayView. */ template -std::pair minmax(const conduit::Node &n) +struct minmax { - SLIC_ASSERT(n.dtype().number_of_elements() > 0); - std::pair retval; + /*! + * \brief Get the min/max values for the data in a Conduit node. + * + * \param[in] n The Conduit node whose data we're checking. + * + * \return A pair containing the min,max values in the node. + */ + static std::pair execute(const conduit::Node &n) + { + SLIC_ASSERT(n.dtype().number_of_elements() > 0); + std::pair retval; + + axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { + retval = execute(nview); + }); + return retval; + } - axom::mir::views::Node_to_ArrayView(n, [&](auto nview) { - using value_type = typename decltype(nview)::value_type; + /*! + * \brief Get the min/max values for the data in an ArrayView. + * + * \param[in] n The Conduit node whose data we're checking. + * + * \return A pair containing the min,max values in the node. + */ + template + static std::pair execute(const axom::ArrayView nview) + { using reduce_policy = typename axom::execution_space::reduce_policy; - RAJA::ReduceMin vmin( - axom::numeric_limits::max()); - RAJA::ReduceMax vmax( - axom::numeric_limits::min()); + RAJA::ReduceMin vmin(axom::numeric_limits::max()); + RAJA::ReduceMax vmax(axom::numeric_limits::min()); axom::for_all( nview.size(), - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { vmin.min(nview[index]); vmax.max(nview[index]); }); - retval = - std::pair {static_cast(vmin.get()), - static_cast(vmax.get())}; - }); - - return retval; -} + return std::pair {static_cast(vmin.get()), + static_cast(vmax.get())}; + } +}; /** * \brief Save a Blueprint mesh to a legacy ASCII VTK file. diff --git a/src/axom/mir/clipping/ClipTableManager.hpp b/src/axom/mir/clipping/ClipTableManager.hpp index f29c7a9176..b311869a8c 100644 --- a/src/axom/mir/clipping/ClipTableManager.hpp +++ b/src/axom/mir/clipping/ClipTableManager.hpp @@ -161,7 +161,7 @@ class TableView { for(int i = 0; i < n; i++) { - if(ids[i] >= P0 && ids[i] <= P7) + if(/*ids[i] >= P0 &&*/ ids[i] <= P7) os << "P" << static_cast(ids[i]); else if(ids[i] >= EA && ids[i] <= EL) { diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 1b2121c4b1..ebb6fcc984 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -85,7 +85,7 @@ struct test_copy_braid conduit::Node hostMesh; create(hostMesh); - // Copy the mesh to device. + // host->device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); @@ -93,25 +93,25 @@ struct test_copy_braid constexpr double eps = 1.e-7; - auto x = axom::mir::utilities::blueprint::minmax( + auto x = axom::mir::utilities::blueprint::minmax::execute( deviceMesh["coordsets/coords/values/x"]); //std::cout << std::setw(16) << "x={" << x.first << ", " << x.second << "}\n"; EXPECT_NEAR(x.first, -10., eps); EXPECT_NEAR(x.second, 10., eps); - auto y = axom::mir::utilities::blueprint::minmax( + auto y = axom::mir::utilities::blueprint::minmax::execute( deviceMesh["coordsets/coords/values/y"]); //std::cout << std::setw(16) << "y={" << y.first << ", " << y.second << "}\n"; EXPECT_NEAR(y.first, -10., eps); EXPECT_NEAR(y.second, 10., eps); - auto c = axom::mir::utilities::blueprint::minmax( + auto c = axom::mir::utilities::blueprint::minmax::execute( deviceMesh["topologies/mesh/elements/connectivity"]); //std::cout << std::setw(16) << "conn={" << c.first << ", " << c.second << "}\n"; EXPECT_NEAR(c.first, 0., eps); EXPECT_NEAR(c.second, 999., eps); - auto r = axom::mir::utilities::blueprint::minmax( + auto r = axom::mir::utilities::blueprint::minmax::execute( deviceMesh["fields/radial/values"]); //std::cout << std::setw(16) << "radial={" << r.first << ", " << r.second << "}\n"; EXPECT_NEAR(r.first, 19.2450089729875, eps); diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index cb5928330d..05df964b67 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -124,7 +124,6 @@ class StructuredTopologyView 0, nzones, AXOM_LAMBDA(axom::IndexType zoneIndex) { - //using ShapeType = HexShape; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); @@ -152,8 +151,7 @@ class StructuredTopologyView axom::for_all( 0, nzones, - AXOM_LAMBDA(auto zoneIndex) { - //using ShapeType = QuadShape; + AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); @@ -177,7 +175,6 @@ class StructuredTopologyView 0, nzones, AXOM_LAMBDA(axom::IndexType zoneIndex) { - //using ShapeType = LineShape; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); ConnectivityType data[2]; @@ -207,9 +204,6 @@ class StructuredTopologyView const auto nSelectedZones = selectedIdsView.size(); ViewType idsView(selectedIdsView); - // Q: Should we make a for_all() that iterates over multiple ranges? - // Q: Should the logical index be passed to the lambda? - if constexpr(IndexingPolicy::dimension() == 3) { const IndexingPolicy zoneIndexing = m_indexing; @@ -219,7 +213,6 @@ class StructuredTopologyView 0, nSelectedZones, AXOM_LAMBDA(axom::IndexType selectIndex) { - //using ShapeType = HexShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); @@ -249,7 +242,6 @@ class StructuredTopologyView 0, nSelectedZones, AXOM_LAMBDA(axom::IndexType selectIndex) { - //using ShapeType = QuadShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); @@ -274,7 +266,6 @@ class StructuredTopologyView 0, nSelectedZones, AXOM_LAMBDA(axom::IndexType selectIndex) { - //using ShapeType = LineShape; const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); From 8340505e186502d5732f4169917de6e3ab6d8282 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 24 Sep 2024 12:54:54 -0700 Subject: [PATCH 235/290] nvcc progress --- src/axom/mir/ClipField.hpp | 189 ++++++++--- src/axom/mir/EquiZAlgorithm.hpp | 25 +- .../mir/tests/mir_blueprint_utilities.cpp | 28 +- src/axom/mir/tests/mir_views.cpp | 318 ++++++++++-------- 4 files changed, 347 insertions(+), 213 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 13fb08d8e8..00100a6d32 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -287,14 +287,9 @@ class FieldIntersector const IndexType n = static_cast(n_clip_field_values.dtype().number_of_elements()); m_clipFieldData = axom::Array(n, n, allocatorID); - auto clipFieldView = m_view.m_clipFieldView = m_clipFieldData.view(); + m_view.m_clipFieldView = m_clipFieldData.view(); views::Node_to_ArrayView(n_clip_field_values, [&](auto clipFieldViewSrc) { - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType index) { - clipFieldView[index] = - static_cast(clipFieldViewSrc[index]); - }); + copyValues(clipFieldViewSrc); }); } } @@ -321,11 +316,67 @@ class FieldIntersector */ View view() const { return m_view; } +// The following members are private (unless using CUDA) +#if !defined(__CUDACC__) private: +#endif + + /*! + * \brief Copy values from srcView into m_clipFieldData. + * + * \param srcView The source data view. + */ + template + void copyValues(DataView srcView) + { + auto clipFieldView = m_clipFieldData.view(); + axom::for_all( + srcView.size(), + AXOM_LAMBDA(axom::IndexType index) { + clipFieldView[index] = + static_cast(srcView[index]); + }); + } + axom::Array m_clipFieldData {}; View m_view {}; }; +#if 1 + using BitSet = std::uint32_t; + /*! + * \brief Contains data that describes the number and size of zone fragments in the output. + */ + struct FragmentData + { + IndexType m_finalNumZones {0}; + IndexType m_finalConnSize {0}; + axom::ArrayView m_fragmentsView {}; + axom::ArrayView m_fragmentsSizeView {}; + axom::ArrayView m_fragmentOffsetsView {}; + axom::ArrayView m_fragmentSizeOffsetsView {}; + }; + + /*! + * \brief Contains some per-zone data that we want to hold onto between methods. + */ + struct ZoneData + { + axom::ArrayView m_clipCasesView {}; + axom::ArrayView m_pointsUsedView {}; + }; + + /*! + * \brief Contains some per-node data that we want to hold onto between methods. + */ + struct NodeData + { + axom::ArrayView m_nodeUsedView {}; + axom::ArrayView m_oldNodeToNewNodeView {}; + axom::ArrayView m_originalIdsView {}; + }; +#endif + //------------------------------------------------------------------------------ /*! * \accelerated @@ -351,7 +402,7 @@ class ClipField using ClipTableViews = axom::StackArray; using Intersector = IntersectPolicy; - using BitSet = std::uint32_t; +// using BitSet = std::uint32_t; using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -740,7 +791,7 @@ class ClipField #if !defined(__CUDACC__) private: #endif - +#if 0 /*! * \brief Contains data that describes the number and size of zone fragments in the output. */ @@ -772,7 +823,7 @@ class ClipField axom::ArrayView m_oldNodeToNewNodeView {}; axom::ArrayView m_originalIdsView {}; }; - +#endif /*! * \brief Make a bitset that indicates the parts of the selection that are selected. */ @@ -965,7 +1016,7 @@ class ClipField // Set blend group sizes for this zone. blendGroupsView[szIndex] = thisBlendGroups; blendGroupsLenView[szIndex] = thisBlendGroupLen; - }); + }); // for_selected_zones #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -1492,7 +1543,7 @@ class ClipField // Reduce overall whether there are degenerates. degenerates_reduce |= degenerates; #endif - }); + }); // for_selected_zones #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -1553,35 +1604,11 @@ class ClipField // Make offsets axom::exclusive_scan(maskView, maskOffsetsView); - // Replace data in the input Conduit node with a denser version using the mask. - auto filter = - [&](conduit::Node &n_src, const auto srcView, axom::IndexType newSize) { - using value_type = typename decltype(srcView)::value_type; - conduit::Node n_values; - n_values.set_allocator(conduitAllocatorID); - n_values.set( - conduit::DataType(bputils::cpp2conduit::id, newSize)); - auto valuesView = bputils::make_array_view(n_values); - const auto nValues = maskView.size(); - axom::for_all( - nValues, - AXOM_LAMBDA(axom::IndexType index) { - if(maskView[index] > 0) - { - const auto destIndex = maskOffsetsView[index]; - valuesView[destIndex] = srcView[index]; - } - }); - - n_src.swap(n_values); - return bputils::make_array_view(n_src); - }; - // Filter sizes, shapes, color using the mask - sizesView = filter(n_sizes, sizesView, filteredZoneCount); - offsetsView = filter(n_offsets, offsetsView, filteredZoneCount); - shapesView = filter(n_shapes, shapesView, filteredZoneCount); - colorView = filter(n_color_values, colorView, filteredZoneCount); + sizesView = filter(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); + offsetsView = filter(n_offsets, offsetsView, filteredZoneCount, maskView, maskOffsetsView); + shapesView = filter(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); + colorView = filter(n_color_values, colorView, filteredZoneCount, maskView, maskOffsetsView); // Record the filtered size. fragmentData.m_finalNumZones = filteredZoneCount; @@ -1691,6 +1718,47 @@ class ClipField } } +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + /*! + * \brief Replace data in the input Conduit node with a denser version using the mask. + * + * \param n_src The Conduit node that contains the data. + * \param srcView A view that wraps the input Conduit data. + * \param newSize The new array size. + * \param maskView The mask for valid data elements. + * \param maskOffsetsView The offsets view to indicate where to write the new data. + */ + template + DataView filter(conduit::Node &n_src, DataView srcView, axom::IndexType newSize, + axom::ArrayView maskView, axom::ArrayView maskOffsetsView) const + { + using value_type = typename DataView::value_type; + namespace bputils = axom::mir::utilities::blueprint; + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + + conduit::Node n_values; + n_values.set_allocator(conduitAllocatorID); + n_values.set(conduit::DataType(bputils::cpp2conduit::id, newSize)); + auto valuesView = bputils::make_array_view(n_values); + const auto nValues = maskView.size(); + axom::for_all( + nValues, + AXOM_LAMBDA(axom::IndexType index) { + if(maskView[index] > 0) + { + const auto destIndex = maskOffsetsView[index]; + valuesView[destIndex] = srcView[index]; + } + }); + + n_src.swap(n_values); + return bputils::make_array_view(n_src); + } +#endif + /*! * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * @@ -1865,15 +1933,7 @@ class ClipField n_values.set(conduit::DataType(n_orig_values.dtype().id(), fragmentData.m_finalNumZones)); auto valuesView = bputils::make_array_view(n_values); - axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType index) { - const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; - const int nFragments = fragmentData.m_fragmentsView[index]; - const auto zoneIndex = selectedZonesView[index]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = origValuesView[zoneIndex]; - }); + makeOriginalElements_copy(fragmentData, selectedZones, valuesView, origValuesView); }); } else @@ -1898,6 +1958,37 @@ class ClipField } } + /*! + * \brief Assist setting original elements that already exist, based on selected zones. + * + * \param[in] fragmentData This object holds views to per-fragment data. + * \param[in] selectedZones The selected zones. + * \param[out] valuesView The destination values view. + * \param[in] origValuesView The source values view. + * + * \note This method was broken out into a template member method since nvcc + * would not instantiate the lambda for axom::for_all() from an anonymous + * lambda. + */ + template + void makeOriginalElements_copy(FragmentData fragmentData, + const SelectedZones &selectedZones, + DataView valuesView, + DataView origValuesView) const + { + const auto selectedZonesView = selectedZones.view(); + const auto nzones = selectedZonesView.size(); + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType index) { + const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; + const int nFragments = fragmentData.m_fragmentsView[index]; + const auto zoneIndex = selectedZonesView[index]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); + } + /*! * \brief Given a flag that includes bitwise-or'd shape ids, make a map that indicates which Conduit shapes are used. * diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index a2244a4eb7..39da4d019a 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -319,7 +319,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm /// Destructor virtual ~EquiZAlgorithm() = default; +// The following members are protected (unless using CUDA) +#if !defined(__CUDACC__) protected: +#endif + #if defined(AXOM_EQUIZ_DEBUG) void printNode(const conduit::Node &n) const { @@ -570,7 +574,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm auto view = bputils::make_array_view(n_field["values"]); axom::for_all( nvalues, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { view[index] = static_cast(index); }); } @@ -647,7 +651,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm RAJA::ReduceSum mask_reduce(0); axom::for_all( numOutputNodes, - AXOM_LAMBDA(auto index) { mask_reduce += maskView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { mask_reduce += maskView[index]; }); const auto numNewNodes = mask_reduce.get(); // Make offsets. @@ -659,7 +663,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm auto nodeSliceView = nodeSlice.view(); axom::for_all( numOutputNodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] > 0) { nodeSliceView[maskOffsetsView[index]] = index; @@ -672,7 +676,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm auto nodeMapView = nodeMap.view(); axom::for_all( numOutputNodes, - AXOM_LAMBDA(auto index) { + AXOM_LAMBDA(axom::IndexType index) { if(maskView[index] == 0) { nodeMapView[index] = @@ -991,7 +995,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm MatsetView deviceMatsetView(m_matsetView); axom::for_all( m_topologyView.numberOfZones(), - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { typename MatsetView::FloatType vf {}; deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf); zonalFieldView[zoneIndex] = static_cast(vf); @@ -1067,7 +1071,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Fill all zones with NULL_MATERIAL. axom::for_all( nzones, - AXOM_LAMBDA(auto nodeIndex) { + AXOM_LAMBDA(axom::IndexType nodeIndex) { zonalIDFieldView[nodeIndex] = NULL_MATERIAL; }); @@ -1079,7 +1083,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm const int matNumber = mat.number; axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { FloatType vf {}; if(deviceMatsetView.zoneContainsMaterial(zoneIndex, matNumber, vf)) { @@ -1099,8 +1103,9 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Fill in any zone that has nodes where the nodal matVF is greater than zero. // This fuzzes it out to more zones so we get better blending with the next // material we try to overlay. + using ZoneType = typename TopologyView::ShapeType; m_topologyView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto &zone) { + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { constexpr MaterialVF VOLUME_FRACTION_CUTOFF = 1.e-6; MaterialVF matvfSum {}; for(const auto nid : zone.getIds()) @@ -1308,7 +1313,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm const int currentMatNumber = currentMat.number; axom::for_all( nzonesNew, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { // Color the part we want with the current material. if(colorView[zoneIndex] == 1) { @@ -1411,7 +1416,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Fill in the new matset data arrays. axom::for_all( nzones, - AXOM_LAMBDA(auto zoneIndex) { + AXOM_LAMBDA(axom::IndexType zoneIndex) { material_ids_view[zoneIndex] = static_cast(zonalMaterialID[zoneIndex]); volume_fractions_view[zoneIndex] = 1; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index ebb6fcc984..7fcd35e7d3 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -37,7 +37,7 @@ struct test_conduit_allocate auto nview = bputils::make_array_view(n); axom::for_all( nValues, - AXOM_LAMBDA(auto index) { nview[index] = index; }); + AXOM_LAMBDA(axom::IndexType index) { nview[index] = index; }); EXPECT_EQ(n.dtype().number_of_elements(), nValues); @@ -63,7 +63,7 @@ TEST(mir_blueprint_utilities, allocate_omp) test_conduit_allocate::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, allocate_cuda) { test_conduit_allocate::test(); @@ -130,7 +130,7 @@ TEST(mir_blueprint_utilities, copy_seq) { test_copy_braid::test(); } #if defined(AXOM_USE_OPENMP) TEST(mir_blueprint_utilities, copy_omp) { test_copy_braid::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, copy_cuda) { test_copy_braid::test(); } #endif #if defined(AXOM_USE_HIP) @@ -245,7 +245,7 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_unstructured) test_node_to_zone_relation_builder(mesh); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) test_node_to_zone_relation_builder(mesh); #endif @@ -273,7 +273,7 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_rectilinear) test_node_to_zone_relation_builder(mesh); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) test_node_to_zone_relation_builder(mesh); #endif @@ -375,7 +375,7 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) test_node_to_zone_relation_builder_polyhedral(mesh); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) test_node_to_zone_relation_builder_polyhedral(mesh); #endif @@ -469,7 +469,7 @@ TEST(mir_blueprint_utilities, recenterfield_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, recenterfield_cuda) { test_recenter_field::test(); @@ -603,7 +603,7 @@ TEST(mir_blueprint_utilities, matsetslice_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, matsetslice_cuda) { test_matset_slice::test(); @@ -705,7 +705,7 @@ TEST(mir_blueprint_utilities, coordsetslicer_explicit_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, coordsetslicer_explicit_cuda) { coordsetslicer_explicit::test(); @@ -762,7 +762,7 @@ TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_cuda) { coordsetslicer_rectilinear::test(); @@ -818,7 +818,7 @@ TEST(mir_blueprint_utilities, coordsetslicer_uniform_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, coordsetslicer_uniform_cuda) { coordsetslicer_uniform::test(); @@ -1039,7 +1039,7 @@ TEST(mir_blueprint_utilities, extractzones_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, extractzones_cuda) { test_extractzones::test(); @@ -1203,7 +1203,7 @@ TEST(mir_blueprint_utilities, zonelistbuilder_omp) test_zonelistbuilder::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, zonelistbuilder_cuda) { test_zonelistbuilder::test(); @@ -1368,7 +1368,7 @@ TEST(mir_blueprint_utilities, mergemeshes_omp) test_mergemeshes::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, mergemeshes_cuda) { test_mergemeshes::test(); diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index b8980e1819..0f7a27a8fb 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -67,9 +67,6 @@ struct test_node_to_arrayview static void test() { - using reduce_policy = - typename axom::execution_space::reduce_policy; - std::vector dtypes {conduit::DataType::INT8_ID, conduit::DataType::INT16_ID, conduit::DataType::INT32_ID, @@ -91,28 +88,37 @@ struct test_node_to_arrayview int sumValues = 0; axom::mir::views::Node_to_ArrayView(n_data, [&](auto dataView) { - std::cout << axom::mir::views::array_view_traits::name() - << std::endl; - using value_type = typename decltype(dataView)::value_type; - - // Make sure we can store values in dataView - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType index) { - dataView[index] = static_cast(index); - }); - - // Read the values and sum them. - RAJA::ReduceSum sumValues_reduce(0); - axom::for_all( - n, - AXOM_LAMBDA(axom::IndexType index) { sumValues_reduce += dataView[index]; }); - sumValues = static_cast(sumValues_reduce.get()); + sumValues = testBody(dataView, n); }); EXPECT_EQ(sumValues, sum(n)); } } + + template + static int testBody(DataView dataView, int n) + { + using reduce_policy = + typename axom::execution_space::reduce_policy; + using value_type = typename DataView::value_type; + + std::cout << axom::mir::views::array_view_traits::name() + << std::endl; + + // Make sure we can store values in dataView + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType index) { + dataView[index] = static_cast(index); + }); + + // Read the values and sum them. + RAJA::ReduceSum sumValues_reduce(0); + axom::for_all( + n, + AXOM_LAMBDA(axom::IndexType index) { sumValues_reduce += dataView[index]; }); + return static_cast(sumValues_reduce.get()); + } }; TEST(mir_views, node_to_arrayview_seq) @@ -191,50 +197,75 @@ struct test_strided_structured template static void execute(CoordsetView coordsetView, TopologyView topoView) { - // These are the expected zone ids for this strided structured mesh. + using ExecSpace = seq_exec; + + // These are the expected node ids for this strided structured mesh. // clang-format off - const axom::Array expectedZones {{16, 17, 24, 23, + const axom::Array expectedNodes {{16, 17, 24, 23, 17, 18, 25, 24, 18, 19, 26, 25, 23, 24, 31, 30, 24, 25, 32, 31, 25, 26, 33, 32}}; // clang-format on - auto expectedZonesView = expectedZones.view(); + auto expectedNodesView = expectedNodes.view(); + axom::IndexType n = expectedNodesView.size() / 4; + axom::IndexType n4 = expectedNodesView.size(); + + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array actualNodes(n4, n4, allocatorID); + axom::Array logicalNodes(n4*2, n4*2, allocatorID); + auto actualNodesView = actualNodes.view(); + auto logicalNodesView = logicalNodes.view(); + + // Traverse the zones in the mesh and gather node ids + using ZoneType = typename TopologyView::ShapeType; + topoView.template for_all_zones( + AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { + const auto nodeIndexing = topoView.indexing().expand(); - // Traverse the zones in the mesh and check the zone ids. - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const auto &zone) { - // Check zone ids. + // Get node ids for zone. const auto ids = zone.getIds(); for(axom::IndexType i = 0; i < ids.size(); i++) { - EXPECT_EQ(expectedZonesView[zoneIndex * 4 + i], ids[i]); - } - - // Check coordinates - const auto nodeIndexing = topoView.indexing().expand(); - for(axom::IndexType i = 0; i < ids.size(); i++) - { - // Get coordinate from coordsetView. - const auto pt = coordsetView[ids[i]]; + actualNodesView[zoneIndex * 4 + i] = ids[i]; // Get the logical local id for the id. const auto index = nodeIndexing.GlobalToLocal(ids[i]); const auto logical = nodeIndexing.IndexToLogicalIndex(index); + logicalNodesView[(zoneIndex * 4 + i)*2 + 0] = logical[0]; + logicalNodesView[(zoneIndex * 4 + i)*2 + 1] = logical[1]; + } + }); + + for(axom::IndexType i = 0; i < n4; i++) + { + EXPECT_EQ(expectedNodesView[i], actualNodesView[i]); + } - // Expected coordinate - double x = (3. + 1. / 3.) * static_cast(logical[0] - 1); - const double yvals[] = {-2, 2, 6}; - double y = yvals[logical[1]]; + // Check coordinates + for(axom::IndexType i = 0; i < n4; i++) + { + const auto id = actualNodesView[i]; - const double dx = pt[0] - x; - const double dy = pt[1] - y; - double d = sqrt(dx * dx + dy * dy); + // Get coordinate from coordsetView. + const auto pt = coordsetView[id]; - EXPECT_TRUE(d < 1.e-10); - } - }); + // Get the logical local id for the id. + const auto logicalI = logicalNodesView[i*2 + 0]; + const auto logicalJ = logicalNodesView[i*2 + 1]; + + // Expected coordinate + double x = (3. + 1. / 3.) * static_cast(logicalI - 1); + const double yvals[] = {-2, 2, 6}; + double y = yvals[logicalJ]; + + const double dx = pt[0] - x; + const double dy = pt[1] - y; + double d = sqrt(dx * dx + dy * dy); + + EXPECT_TRUE(d < 1.e-10); + } } }; @@ -243,119 +274,126 @@ TEST(mir_views, strided_structured_seq) test_strided_structured::test(); } -//------------------------------------------------------------------------------ -// NOTE: pass by value on purpose. -template -void test_matsetview(MatsetView matsetView, int allocatorID) -{ - constexpr int MATA = 0; - constexpr int MATB = 1; - constexpr int MATC = 2; - const int zoneids[] = {0, 36, 40}; - - // clang-format off - const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*ids.size*/ 1, /*mats in zone*/ MATB, -1, -1, - /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*ids.size*/ 2, /*mats in zone*/ MATA, MATB, -1, - /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*ids.size*/ 3, /*mats in zone*/ MATA, MATB, MATC}; - // clang-format on - constexpr int nZones = sizeof(zoneids) / sizeof(int); - - // Get zoneids into zoneidsView for device. - axom::Array zoneidsArray(nZones, nZones, allocatorID); - axom::copy(zoneidsArray.data(), zoneids, sizeof(int) * nZones); - auto zoneidsView = zoneidsArray.view(); - - // Allocate results array on device. - constexpr int nResults = sizeof(results) / sizeof(int); - axom::Array resultsArrayDevice(nResults, nResults, allocatorID); - auto resultsView = resultsArrayDevice.view(); - - // Fill in resultsView on the device. - constexpr int nResultsPerZone = nResults / nZones; - axom::for_all( - 3, - AXOM_LAMBDA(axom::IndexType index) { - resultsView[nResultsPerZone * index + 0] = - matsetView.zoneContainsMaterial(zoneidsView[index], MATA) ? 1 : 0; - resultsView[nResultsPerZone * index + 1] = - matsetView.zoneContainsMaterial(zoneidsView[index], MATB) ? 1 : 0; - resultsView[nResultsPerZone * index + 2] = - matsetView.zoneContainsMaterial(zoneidsView[index], MATC) ? 1 : 0; - resultsView[nResultsPerZone * index + 3] = - matsetView.numberOfMaterials(zoneidsView[index]); - - typename MatsetView::IDList ids {}; - typename MatsetView::VFList vfs {}; - matsetView.zoneMaterials(zoneidsView[index], ids, vfs); - resultsView[nResultsPerZone * index + 4] = ids.size(); - for(axom::IndexType i = 0; i < 3; i++) - { - resultsView[nResultsPerZone * index + 5 + i] = - (i < ids.size()) ? ids[i] : -1; - } - }); - // Get containsView data to the host and compare results - std::vector resultsHost(nResults); - axom::copy(resultsHost.data(), resultsView.data(), sizeof(int) * nResults); - for(int i = 0; i < nResults; i++) - { - EXPECT_EQ(results[i], resultsHost[i]); - } -} - //------------------------------------------------------------------------------ template -void braid2d_mat_test(const std::string &type, - const std::string &mattype, - const std::string &name) +struct test_braid2d_mat { - namespace bputils = axom::mir::utilities::blueprint; - const int allocatorID = axom::execution_space::allocatorID(); + static void test(const std::string &type, + const std::string &mattype, + const std::string &name) + { + namespace bputils = axom::mir::utilities::blueprint; + const int allocatorID = axom::execution_space::allocatorID(); - axom::StackArray dims {10, 10}; - axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; + axom::StackArray dims {10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1}; - // Create the data - conduit::Node hostMesh, deviceMesh; - axom::mir::testing::data::braid(type, dims, hostMesh); - axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); #if defined(AXOM_TESTING_SAVE_VISUALIZATION) - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); #endif - if(type == "unibuffer") + if(type == "unibuffer") + { + // clang-format off + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // clang-format on + EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); + test_matsetview(matsetView, allocatorID); + } + } + + template + static void test_matsetview(MatsetView matsetView, int allocatorID) { + constexpr int MATA = 0; + constexpr int MATB = 1; + constexpr int MATC = 2; + const int zoneids[] = {0, 36, 40}; + // clang-format off - using MatsetView = axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + const int results[] = {/*contains mat*/ 0, 1, 0, /*mats in zone*/ 1, /*ids.size*/ 1, /*mats in zone*/ MATB, -1, -1, + /*contains mat*/ 1, 1, 0, /*mats in zone*/ 2, /*ids.size*/ 2, /*mats in zone*/ MATA, MATB, -1, + /*contains mat*/ 1, 1, 1, /*mats in zone*/ 3, /*ids.size*/ 3, /*mats in zone*/ MATA, MATB, MATC}; // clang-format on - EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); - test_matsetview(matsetView, allocatorID); + constexpr int nZones = sizeof(zoneids) / sizeof(int); + + // Get zoneids into zoneidsView for device. + axom::Array zoneidsArray(nZones, nZones, allocatorID); + axom::copy(zoneidsArray.data(), zoneids, sizeof(int) * nZones); + auto zoneidsView = zoneidsArray.view(); + + // Allocate results array on device. + constexpr int nResults = sizeof(results) / sizeof(int); + axom::Array resultsArrayDevice(nResults, nResults, allocatorID); + auto resultsView = resultsArrayDevice.view(); + + // Fill in resultsView on the device. + constexpr int nResultsPerZone = nResults / nZones; + axom::for_all( + 3, + AXOM_LAMBDA(axom::IndexType index) { + resultsView[nResultsPerZone * index + 0] = + matsetView.zoneContainsMaterial(zoneidsView[index], MATA) ? 1 : 0; + resultsView[nResultsPerZone * index + 1] = + matsetView.zoneContainsMaterial(zoneidsView[index], MATB) ? 1 : 0; + resultsView[nResultsPerZone * index + 2] = + matsetView.zoneContainsMaterial(zoneidsView[index], MATC) ? 1 : 0; + resultsView[nResultsPerZone * index + 3] = + matsetView.numberOfMaterials(zoneidsView[index]); + + typename MatsetView::IDList ids {}; + typename MatsetView::VFList vfs {}; + matsetView.zoneMaterials(zoneidsView[index], ids, vfs); + resultsView[nResultsPerZone * index + 4] = ids.size(); + for(axom::IndexType i = 0; i < 3; i++) + { + resultsView[nResultsPerZone * index + 5 + i] = + (i < ids.size()) ? ids[i] : -1; + } + }); + // Get containsView data to the host and compare results + std::vector resultsHost(nResults); + axom::copy(resultsHost.data(), resultsView.data(), sizeof(int) * nResults); + for(int i = 0; i < nResults; i++) + { + EXPECT_EQ(results[i], resultsHost[i]); + } } -} +}; -TEST(mir_views, matset_unibuffer) +TEST(mir_views, matset_unibuffer_seq) { - braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); - + test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); +} #if defined(AXOM_USE_OPENMP) - braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); +TEST(mir_views, matset_unibuffer_omp) +{ + test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); +} #endif - -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) - braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); +#if defined(AXOM_USE_CUDA) +TEST(mir_views, matset_unibuffer_cuda) +{ + test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); +} #endif - #if defined(AXOM_USE_HIP) - braid2d_mat_test("uniform", "unibuffer", "uniform2d_unibuffer"); -#endif +TEST(mir_views, matset_unibuffer_hip) +{ + test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); } +#endif //------------------------------------------------------------------------------ From 27ecc840391f0339b46749b9b1b76ff75cf4d543 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 24 Sep 2024 16:59:37 -0700 Subject: [PATCH 236/290] nvcc progress --- src/axom/core/CMakeLists.txt | 1 + src/axom/core/execution/scans.hpp | 126 +++++ src/axom/mir/MakeUnstructured.hpp | 51 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 44 +- .../mir/tests/mir_blueprint_utilities.cpp | 445 ++++++++++-------- src/axom/mir/tests/mir_views.cpp | 1 - src/axom/mir/utilities.hpp | 28 -- 7 files changed, 437 insertions(+), 259 deletions(-) create mode 100644 src/axom/core/execution/scans.hpp diff --git a/src/axom/core/CMakeLists.txt b/src/axom/core/CMakeLists.txt index 75de794661..d272e031bd 100644 --- a/src/axom/core/CMakeLists.txt +++ b/src/axom/core/CMakeLists.txt @@ -74,6 +74,7 @@ set(core_headers execution/for_all.hpp execution/nested_for_exec.hpp execution/runtime_policy.hpp + execution/scans.hpp execution/synchronize.hpp execution/internal/seq_exec.hpp diff --git a/src/axom/core/execution/scans.hpp b/src/axom/core/execution/scans.hpp new file mode 100644 index 0000000000..7acce62f42 --- /dev/null +++ b/src/axom/core/execution/scans.hpp @@ -0,0 +1,126 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#ifndef AXOM_CORE_EXECUTION_SCANS_HPP_ +#define AXOM_CORE_EXECUTION_SCANS_HPP_ + +#include "axom/config.hpp" +#include "axom/core/execution/execution_space.hpp" +#include "axom/core/Macros.hpp" +#include "axom/core/Types.hpp" + +// C/C++ includes +#include +#include + +namespace axom +{ +/// \name Scans +/// @{ + +/*! + * \brief Performs exclusive scan over \a input view and stores result in \a output. + * + * \param [in] input The input container to be scanned. + * \param [out] output The container that will contain the output scan data. This + * must have the same number of elements as \a input. + * + * \tparam ExecSpace the execution space where to run the supplied kernel + * \tparam ContiguousMemoryContainer The container type that holds the data + * + * \see axom::execution_space + * + * Usage Example: + * \code + * + * axom::ArrayView sizesView = sizes.view(); + * axom::ArrayView offsetsView = offsets.view(); + * + * // Compute the scan for all elements in sizesView, store scan in offsetsView. + * axom::exclusive_scan(sizesView, offsetsView); + * + * \endcode + * + */ +template +inline void exclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) +{ + assert(input.size() == output.size()); + +#ifdef AXOM_USE_RAJA + + using loop_policy = typename axom::execution_space::loop_policy; + RAJA::exclusive_scan( + RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size())); + +#else + constexpr bool is_serial = std::is_same::value; + AXOM_STATIC_ASSERT(is_serial); + + typename decltype(ContiguousMemoryContainer)::value_type total {0}; + for(IndexType i = 0; i < input.size(); ++i) + { + output[i] = total; + total += input[i]; + } +#endif +} + +/*! + * \brief Performs inclusive scan over \a input view and stores result in \a output. + * + * \param [in] input The input container to be scanned. + * \param [out] output The container that will contain the output scan data. This + * must have the same number of elements as \a input. + * + * \tparam ExecSpace the execution space where to run the supplied kernel + * \tparam ContiguousMemoryContainer The container type that holds the data + * + * \see axom::execution_space + * + * Usage Example: + * \code + * + * axom::ArrayView sizesView = sizes.view(); + * axom::ArrayView offsetsView = offsets.view(); + * + * // Compute the scan for all elements in sizesView, store scan in offsetsView. + * axom::exclusive_scan(sizesView, offsetsView); + * + * \endcode + * + */ +template +inline void inclusive_scan(const ContiguousMemoryContainer &input, + ContiguousMemoryContainer &output) +{ + assert(input.size() == output.size()); + +#ifdef AXOM_USE_RAJA + + using loop_policy = typename axom::execution_space::loop_policy; + RAJA::inclusive_scan( + RAJA::make_span(input.data(), input.size()), + RAJA::make_span(output.data(), output.size())); + +#else + constexpr bool is_serial = std::is_same::value; + AXOM_STATIC_ASSERT(is_serial); + + typename decltype(ContiguousMemoryContainer)::value_type total {0}; + for(IndexType i = 0; i < input.size(); ++i) + { + total += input[i]; + output[i] = total; + } +#endif +} +/// @} + +} // namespace axom + +#endif // AXOM_CORE_EXECUTION_FOR_ALL_HPP_ diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp index 3ed4bab638..4200065350 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/MakeUnstructured.hpp @@ -72,7 +72,26 @@ class MakeUnstructured [&](const std::string &shape, auto &topoView) { n_newtopo["elements/shape"] = shape; - makeUnstructured(shape, topoView, n_newconn, n_newsizes, n_newoffsets); + + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + // Allocate new mesh data. + const auto nzones = topoView.numberOfZones(); + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + n_newsizes.set(conduit::DataType::index_t(nzones)); + n_newoffsets.set(conduit::DataType::index_t(nzones)); + + // Make views for the mesh data. + auto connView = make_array_view(n_newconn); + auto sizesView = make_array_view(n_newsizes); + auto offsetsView = make_array_view(n_newoffsets); + + makeUnstructured(ptsPerZone, topoView, connView, sizesView, offsetsView); }); } @@ -87,32 +106,18 @@ class MakeUnstructured * \brief Iterate over the input topology's zones and store their connectivity in * unstructured connectivity nodes. * + * \param ptsPerZone The number of points per zone. * \param topoView The input topology view. - * \param n_newconn The node that will contain the new connectivity. - * \param n_newconn The node that will contain the new connectivity. - * \param n_newconn The node that will contain the new connectivity. + * \param connView The view that will contain the new connectivity. + * \param sizesView The view that will contain the new sizes. + * \param offsetsView The view that will contain the new offsets. */ template - static void makeUnstructured(const std::string &shape, const TopologyView &topoView, conduit::Node &n_newconn, conduit::Node &n_newsizes, conduit::Node &n_newoffsets) + static void makeUnstructured(int ptsPerZone, const TopologyView &topoView, + axom::ArrayView connView, + axom::ArrayView sizesView, + axom::ArrayView offsetsView) { - int ptsPerZone = 2; - if(shape == "quad") - ptsPerZone = 4; - else if(shape == "hex") - ptsPerZone = 8; - - // Allocate new mesh data. - const auto nzones = topoView.numberOfZones(); - const auto connSize = nzones * ptsPerZone; - n_newconn.set(conduit::DataType::index_t(connSize)); - n_newsizes.set(conduit::DataType::index_t(nzones)); - n_newoffsets.set(conduit::DataType::index_t(nzones)); - - // Make views for the mesh data. - auto connView = make_array_view(n_newconn); - auto sizesView = make_array_view(n_newsizes); - auto offsetsView = make_array_view(n_newoffsets); - // Fill in the new connectivity. using ZoneType = typename TopologyView::ShapeType; topoView.template for_all_zones( diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index d57839c700..a9f4b9a1c0 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -32,23 +32,27 @@ namespace blueprint namespace details { /*! - * \brief Given views that contain the nodes and zones, sort the zones using the - * node numbers to produce a list of zones for each node and an offsets array - * that points to the start of each list of zones. - * - * \param[in] nodesView A view that contains the set of all of the nodes in the topology (the connectivity) - * \param[inout[ zonesView A view (same size as \a nodesView) that contains the zone number of each node. - * \param[out] offsetsView A view that we fill with offsets so offsetsView[i] points to the start of the i'th list in \a zonesView. - * - * \note RAJA::sort_pairs can be slow if there are a lot of nodes (depends on ExecSpace too). + * \brief Build the node to zone relation. */ template struct BuildRelation { - static void execute(const ViewType &nodesView, - ViewType &zonesView, - ViewType &sizesView, - ViewType &offsetsView) + /*! + * \brief Given views that contain the nodes and zones, sort the zones using the + * node numbers to produce a list of zones for each node and an offsets array + * that points to the start of each list of zones. + * + * \param[in] nodesView A view that contains the set of all of the nodes in the topology (the connectivity) + * \param[inout] zonesView A view (same size as \a nodesView) that contains the zone number of each node. + * \param[out] sizesView A view that we fill with sizes. + * \param[out] offsetsView A view that we fill with offsets so offsetsView[i] points to the start of the i'th list in \a zonesView. + * + * \note RAJA::sort_pairs can be slow if there are a lot of nodes (depends on ExecSpace too). + */ + static void execute(ViewType nodesView, + ViewType zonesView, + ViewType sizesView, + ViewType offsetsView) { AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); assert(nodesView.size() == zonesView.size()); @@ -112,10 +116,10 @@ struct BuildRelation template struct BuildRelation { - static void execute(const ViewType &nodesView, - ViewType &zonesView, - ViewType &sizesView, - ViewType &offsetsView) + static void execute(ViewType nodesView, + ViewType zonesView, + ViewType sizesView, + ViewType offsetsView) { AXOM_ANNOTATE_SCOPE("FillZonesAndOffsets"); assert(nodesView.size() == zonesView.size()); @@ -124,9 +128,8 @@ struct BuildRelation const int allocatorID = execution_space::allocatorID(); // Count how many times a node is used. - const auto nnodes = offsetsView.size(); axom::for_all( - nnodes, + sizesView.size(), AXOM_LAMBDA(axom::IndexType index) { sizesView[index] = 0; }); axom::for_all( nodesView.size(), @@ -138,9 +141,10 @@ struct BuildRelation axom::exclusive_scan(sizesView, offsetsView); axom::for_all( - nnodes, + sizesView.size(), AXOM_LAMBDA(axom::IndexType index) { sizesView[index] = 0; }); + // Make a copy of zonesView so we can reorganize zonesView. axom::Array zcopy(zonesView.size(), zonesView.size(), allocatorID); axom::copy(zcopy.data(), zonesView.data(), diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 7fcd35e7d3..a43a284940 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -144,90 +144,88 @@ TEST(mir_blueprint_utilities, to_unstructured) } //------------------------------------------------------------------------------ - -template -void compareRelation(const conduit::Node &hostRelation, - const axom::ArrayView &zones, - const axom::ArrayView &sizes, - const axom::ArrayView &offsets) -{ - const auto zonesView = - bputils::make_array_view(hostRelation["zones"]); - const auto sizesView = - bputils::make_array_view(hostRelation["sizes"]); - const auto offsetsView = - bputils::make_array_view(hostRelation["offsets"]); - EXPECT_EQ(sizesView.size(), sizes.size()); - EXPECT_EQ(offsetsView.size(), offsets.size()); - for(axom::IndexType i = 0; i < sizesView.size(); i++) +template +struct test_node_to_zone_relation_builder +{ + static void test(const conduit::Node &hostMesh) { - EXPECT_EQ(sizes[i], sizesView[i]); - EXPECT_EQ(offsets[i], offsetsView[i]); + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); + + // Expected answers + // clang-format off + const int zones[] = { + 0, + 0, 1, + 1, 2, + 2, + 0, 3, // NOTE: these are sorted here + 0, 1, 3, 4, + 1, 2, 4, 5, + 2, 5, + 3, + 3, 4, + 4, 5, + 5 + }; + const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; + const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; + // clang-format on + + // Compare answers. + compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); } - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - // Sort the result so we can compare to the expected answer. - IndexT *begin = zonesView.data() + offsetsView[i]; - IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; - std::sort(begin, end); - for(int j = 0; j < sizesView[i]; j++) + static void compareRelation(const conduit::Node &hostRelation, + const axom::ArrayView &zones, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) + { + const auto zonesView = + bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = + bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = + bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizes.size()); + EXPECT_EQ(offsetsView.size(), offsets.size()); + for(axom::IndexType i = 0; i < sizesView.size(); i++) { - EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + EXPECT_EQ(sizes[i], sizesView[i]); + EXPECT_EQ(offsets[i], offsetsView[i]); + } + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + // Sort the result so we can compare to the expected answer. + IndexT *begin = zonesView.data() + offsetsView[i]; + IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; + std::sort(begin, end); + + for(int j = 0; j < sizesView[i]; j++) + { + EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + } } } -} - -template -void test_node_to_zone_relation_builder(const conduit::Node &hostMesh) -{ - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceCoordset, deviceRelation); - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); -#if 0 - // Print the results. - printNode(hostRelation); -#endif - // Expected answers - // clang-format off - const int zones[] = { - 0, - 0, 1, - 1, 2, - 2, - 0, 3, // NOTE: these are sorted here - 0, 1, 3, 4, - 1, 2, 4, 5, - 2, 5, - 3, - 3, 4, - 4, 5, - 5 - }; - const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; - const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; - // clang-format on - - // Compare answers. - compareRelation( - hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); -} +}; -TEST(mir_blueprint_utilities, node_to_zone_relation_builder_unstructured) +TEST(mir_blueprint_utilities, n2zrel_unstructured_seq) { /* 8---9--10--11 @@ -239,22 +237,41 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_unstructured) conduit::Node mesh; axom::StackArray dims {{4, 3}}; axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} - test_node_to_zone_relation_builder(mesh); #if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder(mesh); +TEST(mir_blueprint_utilities, n2zrel_unstructured_omp) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} #endif #if defined(AXOM_USE_CUDA) - test_node_to_zone_relation_builder(mesh); +TEST(mir_blueprint_utilities, n2zrel_unstructured_cuda) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} #endif #if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder(mesh); -#endif +TEST(mir_blueprint_utilities, n2zrel_unstructured_hip) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); } +#endif + -TEST(mir_blueprint_utilities, node_to_zone_relation_builder_rectilinear) +TEST(mir_blueprint_utilities, n2zrel_rectilinear_seq) { /* 8---9--10--11 @@ -266,123 +283,155 @@ TEST(mir_blueprint_utilities, node_to_zone_relation_builder_rectilinear) conduit::Node mesh; axom::StackArray dims {{4, 3}}; axom::mir::testing::data::braid("rectilinear", dims, mesh); - //mesh.print(); - - test_node_to_zone_relation_builder(mesh); + test_node_to_zone_relation_builder::test(mesh); +} #if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder(mesh); +TEST(mir_blueprint_utilities, n2zrel_rectilinear_omp) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} #endif - #if defined(AXOM_USE_CUDA) - test_node_to_zone_relation_builder(mesh); +TEST(mir_blueprint_utilities, n2zrel_rectilinear_cuda) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} #endif - #if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder(mesh); -#endif +TEST(mir_blueprint_utilities, n2zrel_rectilinear_hip) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); } - -template -void test_node_to_zone_relation_builder_polyhedral(const conduit::Node &hostMesh) -{ - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceCoordset, deviceRelation); - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); -#if 0 - // Print the results. - printNode(hostRelation); #endif - // Expected answers - // clang-format off - const int zones[] = { - /*node 0*/ 0, - /*node 1*/ 0, 1, - /*node 2*/ 1, - /*node 3*/ 0, 2, - /*node 4*/ 0, 1, 2, 3, - /*node 5*/ 1, 3, - /*node 6*/ 2, - /*node 7*/ 2, 3, - /*node 8*/ 3, - - /*node 9*/ 0, 4, - /*node 10*/ 0, 1, 4, 5, - /*node 11*/ 1, 5, - /*node 12*/ 0, 2, 4, 6, - /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, - /*node 14*/ 1, 3, 5, 7, - /*node 15*/ 2, 6, - /*node 16*/ 2, 3, 6, 7, - /*node 17*/ 3, 7, - - /*node 18*/ 4, - /*node 19*/ 4, 5, - /*node 20*/ 5, - /*node 21*/ 4, 6, - /*node 22*/ 4, 5, 6, 7, - /*node 23*/ 5, 7, - /*node 24*/ 6, - /*node 25*/ 6, 7, - /*node 26*/ 7 - }; - const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, - 2, 4, 2, 4, 8, 4, 2, 4, 2, - 1, 2, 1, 2, 4, 2, 1, 2, 1 - }; - const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, - 16, 18, 22, 24, 28, 36, 40, 42, 46, - 48, 49, 51, 52, 54, 58, 60, 61, 63}; - // clang-format on - - // Compare answers. - compareRelation( - hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); -} -TEST(mir_blueprint_utilities, node_to_zone_relation_builder_polyhedral) +template +struct test_node_to_zone_relation_builder_polyhedral : public test_node_to_zone_relation_builder { - conduit::Node mesh; - conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); - // Make sure all the types are the same. - conduit::blueprint::mesh::utils::convert( - mesh, - conduit::DataType::int32(), - std::vector {{"topologies/mesh/elements/connectivity", - "topologies/mesh/elements/sizes", - "topologies/mesh/elements/offsets", - "topologies/mesh/subelements/connectivity", - "topologies/mesh/subelements/sizes", - "topologies/mesh/subelements/offsets"}}); - //printNode(mesh); - - test_node_to_zone_relation_builder_polyhedral(mesh); + using SuperClass = test_node_to_zone_relation_builder; + + static void test(const conduit::Node &hostMesh) + { + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); + + // Expected answers + // clang-format off + const int zones[] = { + /*node 0*/ 0, + /*node 1*/ 0, 1, + /*node 2*/ 1, + /*node 3*/ 0, 2, + /*node 4*/ 0, 1, 2, 3, + /*node 5*/ 1, 3, + /*node 6*/ 2, + /*node 7*/ 2, 3, + /*node 8*/ 3, + + /*node 9*/ 0, 4, + /*node 10*/ 0, 1, 4, 5, + /*node 11*/ 1, 5, + /*node 12*/ 0, 2, 4, 6, + /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, + /*node 14*/ 1, 3, 5, 7, + /*node 15*/ 2, 6, + /*node 16*/ 2, 3, 6, 7, + /*node 17*/ 3, 7, + + /*node 18*/ 4, + /*node 19*/ 4, 5, + /*node 20*/ 5, + /*node 21*/ 4, 6, + /*node 22*/ 4, 5, 6, 7, + /*node 23*/ 5, 7, + /*node 24*/ 6, + /*node 25*/ 6, 7, + /*node 26*/ 7 + }; + const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, + 2, 4, 2, 4, 8, 4, 2, 4, 2, + 1, 2, 1, 2, 4, 2, 1, 2, 1 + }; + const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, + 16, 18, 22, 24, 28, 36, 40, 42, 46, + 48, 49, 51, 52, 54, 58, 60, 61, 63}; + // clang-format on + + // Compare answers. + SuperClass::compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); + } + + static void create(conduit::Node &mesh) + { + conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); + // Make sure all the types are the same. + conduit::blueprint::mesh::utils::convert( + mesh, + conduit::DataType::int32(), + std::vector {{"topologies/mesh/elements/connectivity", + "topologies/mesh/elements/sizes", + "topologies/mesh/elements/offsets", + "topologies/mesh/subelements/connectivity", + "topologies/mesh/subelements/sizes", + "topologies/mesh/subelements/offsets"}}); + + } +}; +TEST(mir_blueprint_utilities, n2zrel_polyhedral_seq) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create(mesh); + test_node_to_zone_relation_builder_polyhedral::test(mesh); +} #if defined(AXOM_USE_OPENMP) - test_node_to_zone_relation_builder_polyhedral(mesh); +TEST(mir_blueprint_utilities, n2zrel_polyhedral_omp) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create(mesh); + test_node_to_zone_relation_builder_polyhedral::test(mesh); +} #endif - #if defined(AXOM_USE_CUDA) - test_node_to_zone_relation_builder_polyhedral(mesh); +TEST(mir_blueprint_utilities, n2zrel_polyhedral_cuda) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create(mesh); + test_node_to_zone_relation_builder_polyhedral::test(mesh); +} #endif - #if defined(AXOM_USE_HIP) - test_node_to_zone_relation_builder_polyhedral(mesh); -#endif +TEST(mir_blueprint_utilities, n2zrel_polyhedral_hip) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create(mesh); + test_node_to_zone_relation_builder_polyhedral::test(mesh); } +#endif //------------------------------------------------------------------------------ template @@ -492,6 +541,25 @@ bool compare_views(const Container1 &a, const Container2 &b) { eq &= a[i] == b[i]; } + if(!eq) + { + std::cout << "a={"; + for(axom::IndexType i = 0; i < a.size(); i++) + { + if(i > 0) + std::cout << ", "; + std::cout << a[i]; + } + std::cout << "}" << std::endl; + std::cout << "b={"; + for(axom::IndexType i = 0; i < a.size(); i++) + { + if(i > 0) + std::cout << ", "; + std::cout << b[i]; + } + std::cout << "}" << std::endl; + } return eq; } @@ -544,6 +612,9 @@ struct test_matset_slice const axom::Array volume_fractions { {0.5, 0.5, 1.0, 0.8, 0.2}}; + EXPECT_EQ(conduit::DataType::INT64_ID, newHostMatset["material_ids"].dtype().id()); + EXPECT_EQ(conduit::DataType::FLOAT64_ID, newHostMatset["volume_fractions"].dtype().id()); +printNode(newHostMatset); EXPECT_TRUE(compare_views( sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); @@ -556,9 +627,9 @@ struct test_matset_slice EXPECT_TRUE(compare_views( material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); - EXPECT_TRUE(compare_views(volume_fractions.view(), - bputils::make_array_view( - newHostMatset["volume_fractions"]))); + EXPECT_TRUE(compare_views( + volume_fractions.view(), + bputils::make_array_view(newHostMatset["volume_fractions"]))); } static void create(conduit::Node &matset) diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 0f7a27a8fb..fd3920736e 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -209,7 +209,6 @@ struct test_strided_structured 25, 26, 33, 32}}; // clang-format on auto expectedNodesView = expectedNodes.view(); - axom::IndexType n = expectedNodesView.size() / 4; axom::IndexType n4 = expectedNodesView.size(); const int allocatorID = axom::execution_space::allocatorID(); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index cc7780e8f2..19728c153c 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -15,34 +15,6 @@ #include -// NOTE: Longer term, we should hide more RAJA functionality behind axom wrappers -// so we can write serial versions for when RAJA is not enabled. -namespace axom -{ -template -struct scans -{ - inline void exclusive_scan(const ContiguousMemoryContainer &input, - ContiguousMemoryContainer &output) - { - using loop_policy = typename axom::execution_space::loop_policy; - assert(input.size() == output.size()); - RAJA::exclusive_scan( - RAJA::make_span(input.data(), input.size()), - RAJA::make_span(output.data(), output.size())); - } -}; - -template -inline void exclusive_scan(const ContiguousMemoryContainer &input, - ContiguousMemoryContainer &output) -{ - scans s; - s.exclusive_scan(input, output); -} - -} // end namespace axom - namespace axom { namespace mir From 9409974b854fdc114fe83f2b1c702ef526ee0c9e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 25 Sep 2024 13:51:57 -0700 Subject: [PATCH 237/290] testing fixes --- src/axom/core/execution/scans.hpp | 6 ++--- src/axom/mir/MatsetSlicer.hpp | 8 +++++-- src/axom/mir/MergeMeshes.hpp | 2 +- .../mir/tests/mir_blueprint_utilities.cpp | 22 +++++++++---------- src/axom/mir/views/StructuredIndexing.hpp | 5 ++++- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/axom/core/execution/scans.hpp b/src/axom/core/execution/scans.hpp index 7acce62f42..ddfa3c3d56 100644 --- a/src/axom/core/execution/scans.hpp +++ b/src/axom/core/execution/scans.hpp @@ -86,10 +86,10 @@ inline void exclusive_scan(const ContiguousMemoryContainer &input, * \code * * axom::ArrayView sizesView = sizes.view(); - * axom::ArrayView offsetsView = offsets.view(); + * axom::ArrayView totalView = totals.view(); * - * // Compute the scan for all elements in sizesView, store scan in offsetsView. - * axom::exclusive_scan(sizesView, offsetsView); + * // Compute the scan for all elements in sizesView, store scan in totalView. + * axom::inclusive_scan(sizesView, totalView); * * \endcode * diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 8462b6f9fe..4918bfa555 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -82,7 +82,6 @@ class MatsetSlicer // Figure out overall size of the matset zones we're keeping. MatsetView deviceMatsetView(m_matsetView); - RAJA::ReduceSum size_reduce(0); const axom::ArrayView deviceSelectedZonesView( selectedZonesView); axom::for_all( @@ -91,7 +90,12 @@ class MatsetSlicer const auto nmats = deviceMatsetView.numberOfMaterials(deviceSelectedZonesView[index]); sizesView[index] = nmats; - size_reduce += nmats; + }); + RAJA::ReduceSum size_reduce(0); + axom::for_all( + sizesView.size(), + AXOM_LAMBDA(axom::IndexType index) { + size_reduce += sizesView[index]; }); axom::exclusive_scan(sizesView, offsetsView); diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index f6ba4fd2ee..c4bc16247f 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -916,7 +916,7 @@ class MergeMeshes else { axom::for_all( - nnodes, + nodeSliceView.size(), AXOM_LAMBDA(axom::IndexType index) { const auto nodeId = nodeSliceView[index]; destView[offset + index] = srcView[nodeId]; diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index a43a284940..4b14378209 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -602,7 +602,7 @@ struct test_matset_slice // device->host conduit::Node newHostMatset; - bputils::copy(newHostMatset, newDeviceMatset); + bputils::copy(newHostMatset, newDeviceMatset); // Expected answers. const axom::Array sizes {{2, 1, 2}}; @@ -614,7 +614,7 @@ struct test_matset_slice EXPECT_EQ(conduit::DataType::INT64_ID, newHostMatset["material_ids"].dtype().id()); EXPECT_EQ(conduit::DataType::FLOAT64_ID, newHostMatset["volume_fractions"].dtype().id()); -printNode(newHostMatset); + EXPECT_TRUE(compare_views( sizes.view(), bputils::make_array_view(newHostMatset["sizes"]))); @@ -719,7 +719,7 @@ void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) // device->host conduit::Node newHostCoordset; - bputils::copy(newHostCoordset, newDeviceCoordset); + bputils::copy(newHostCoordset, newDeviceCoordset); // We get an explicit coordset out of the slicer. const axom::Array x {{0., 1., 2., 0., 1., 2.}}; @@ -950,7 +950,7 @@ struct test_extractzones // device->host conduit::Node newHostMesh; - bputils::copy(newHostMesh, newDeviceMesh); + bputils::copy(newHostMesh, newDeviceMesh); //printNode(newHostMesh); @@ -1015,7 +1015,7 @@ struct test_extractzones // device->host newHostMesh.reset(); - bputils::copy(newHostMesh, newDeviceMesh); + bputils::copy(newHostMesh, newDeviceMesh); // Check some of the key arrays in the sliced material const axom::Array mat_sizes {{2, 1, 2}}; @@ -1174,7 +1174,7 @@ struct test_zonelistbuilder // device->host conduit::Node hostData; - bputils::copy(hostData, deviceData); + bputils::copy(hostData, deviceData); // Compare expected const axom::Array cleanResult {{0, 1, 2, 3, 4, 8, 12}}; @@ -1202,7 +1202,7 @@ struct test_zonelistbuilder deviceData["mixed"].set_external(mixed.data(), mixed.size()); // device->host - bputils::copy(hostData, deviceData); + bputils::copy(hostData, deviceData); // Compare expected const axom::Array cleanResult2 {{2, 3, 8, 12}}; @@ -1325,10 +1325,10 @@ struct test_mergemeshes // device->host conduit::Node hostResult; - bputils::copy(hostResult, deviceResult); + bputils::copy(hostResult, deviceResult); - printNode(hostResult); - conduit::relay::io::blueprint::save_mesh(hostResult, "mergemeshes", "hdf5"); + //printNode(hostResult); + //conduit::relay::io::blueprint::save_mesh(hostResult, "mergemeshes", "hdf5"); constexpr double tolerance = 1.e-7; conduit::Node expectedResult, info; @@ -1393,7 +1393,7 @@ struct test_mergemeshes nodal: topology: mesh association: vertex - values: [1,1,1,1,1,1,1,1, 2] + values: [1,1,1,1,1,1,1,1, 2,2] zonal: topology: mesh association: element diff --git a/src/axom/mir/views/StructuredIndexing.hpp b/src/axom/mir/views/StructuredIndexing.hpp index b94e6827e8..46f3bbab72 100644 --- a/src/axom/mir/views/StructuredIndexing.hpp +++ b/src/axom/mir/views/StructuredIndexing.hpp @@ -60,7 +60,10 @@ class StructuredIndexing IndexType size() const { IndexType sz = 1; - for(int i = 0; i < NDIMS; i++) sz *= m_dimensions[i]; + for(int i = 0; i < NDIMS; i++) + { + sz *= m_dimensions[i]; + } return sz; } From cef620390a620489f2c488d088250e04a1752a80 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 25 Sep 2024 16:14:03 -0700 Subject: [PATCH 238/290] Added a note in the docs. --- src/docs/sphinx/coding_guide/sec06_scope.rst | 34 +++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/docs/sphinx/coding_guide/sec06_scope.rst b/src/docs/sphinx/coding_guide/sec06_scope.rst index ec3979827c..b0111c00fc 100644 --- a/src/docs/sphinx/coding_guide/sec06_scope.rst +++ b/src/docs/sphinx/coding_guide/sec06_scope.rst @@ -121,19 +121,43 @@ or "protected" data members **must** be scrutinized by other team members. direct access to class data enforces encapsulation and facilitates design changes through refactoring. +6.10 Guard protected/private access qualifiers when hiding methods that contain +kernel launches. + + When building algorithms that are templated for execution on the GPU, + methods that instantiate kernels via `axom::for_all()` cannot be marked + as protected or private when building for certain backends. In these + situations, add conditional compilation around the access qualifiers. + + For example:: + + class Algorithm + { + #if !defined(__CUDACC__) + private: + #endif + void helperMethod() + { + axom::for_all(100, + AXOM_LAMBDA(axom::IndexType index) + { + // do something + }); + } + }; --------------------------------------------------------- Use 'friend' and 'static' rarely --------------------------------------------------------- -6.10 "Friend" declarations **should** be used rarely. When used, they +6.11 "Friend" declarations **should** be used rarely. When used, they **must** appear within the body of a class definition before any class member declarations. This helps make the friend relationship obvious. Note that placing "friend" declarations before the "public:" keyword makes them private, which preserves encapsulation. -6.11 Static class members (methods or data) **must** be used rarely. In +6.12 Static class members (methods or data) **must** be used rarely. In every case, their usage **should** be carefully reviewed by the team. When it is determined that a static member is needed, it **must** appear @@ -148,7 +172,7 @@ every case, their usage **should** be carefully reviewed by the team. Hide nested classes when possible --------------------------------------------------------- -6.12 Nested classes **should** be private unless they are part of the +6.13 Nested classes **should** be private unless they are part of the enclosing class interface. For example:: @@ -190,7 +214,7 @@ enclosing class interface. Limit scope of local variables --------------------------------------------------------- -6.13 Local variables **should** be declared in the narrowest scope possible +6.14 Local variables **should** be declared in the narrowest scope possible and as close to first use as possible. Minimizing variable scope makes source code easier to comprehend and @@ -225,7 +249,7 @@ and as close to first use as possible. f.doSomethingCool(ii); } -6.14 A local reference to any item in the global namespace (which should be +6.15 A local reference to any item in the global namespace (which should be rare if needed at all) **should** use the scope operator ("::") to make the fact that it resides in the global namespace clear. From c723789141d85a156615893b455f5b2c67096fcf Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 26 Sep 2024 11:04:35 -0700 Subject: [PATCH 239/290] good changes --- src/axom/mir/utilities.hpp | 4 +--- src/axom/mir/views/StridedStructuredIndexing.hpp | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 19728c153c..1bc43f25ce 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -458,9 +458,7 @@ struct Unique // Do a scan on the mask array to build an offset array. axom::Array offsets(n, n, allocatorID); auto offsets_view = offsets.view(); - RAJA::exclusive_scan(RAJA::make_span(mask_view.data(), n), - RAJA::make_span(offsets_view.data(), n), - RAJA::operators::plus {}); + axom::exclusive_scan(mask_view, offsets_view); // Allocate the output arrays. const axom::IndexType newsize = mask_sum.get(); diff --git a/src/axom/mir/views/StridedStructuredIndexing.hpp b/src/axom/mir/views/StridedStructuredIndexing.hpp index e2f1be39db..568c00ce92 100644 --- a/src/axom/mir/views/StridedStructuredIndexing.hpp +++ b/src/axom/mir/views/StridedStructuredIndexing.hpp @@ -211,6 +211,7 @@ struct StridedStructuredIndexing * * \return The local index that corresponds to the \a local. */ + AXOM_HOST_DEVICE IndexType GlobalToLocal(IndexType global) const { return LogicalIndexToIndex(GlobalToLocal(GlobalToGlobal(global))); From d9a3595a0e7b79e304038aea2f7a3fdba70c79ee Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 26 Sep 2024 11:04:50 -0700 Subject: [PATCH 240/290] GPU porting doc --- src/docs/sphinx/dev_guide/gpu_porting.rst | 158 +++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) diff --git a/src/docs/sphinx/dev_guide/gpu_porting.rst b/src/docs/sphinx/dev_guide/gpu_porting.rst index ac85a03488..4f38585555 100644 --- a/src/docs/sphinx/dev_guide/gpu_porting.rst +++ b/src/docs/sphinx/dev_guide/gpu_porting.rst @@ -124,7 +124,6 @@ the memory space where data in ``Dynamic`` allows you to define the location at run time, with some caveats (see :ref:`Core Containers` for more details and examples). - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Useful Links ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -161,6 +160,157 @@ GPU device, or on both a GPU device and a CPU host. For example:: When Axom is built without RAJA, ``axom::for_all`` becomes a ``for``-loop on host (CPU). +%%%%%%%%%%%%%%%% +Portability +%%%%%%%%%%%%%%%% + +Adherence to the GPU porting guidelines generally result in code that will compile and run on +multiple backends. However, backends such as CUDA require additional guidelines. + +Do not use generic lambda functions (auto parameters) with ``axom::for_all`` +or the code will not compile under nvcc. + +Do this: + + .. code-block:: cpp + + axom::for_all(n, AXOM_LAMBDA(axom::IndexType index) { /* body */}); + + +Do NOT do this: + + .. code-block:: cpp + + axom::for_all(n, AXOM_LAMBDA(auto index) { /* body */}); + + +Data in Axom is often contained in useful containers such as ``axom::Array``. +Use ``axom::ArrayView`` to access array data from within kernels. Views contain +a pointer to the data and memory shape information and can be constructed within +device code to access data arrays. Views are captured by kernels and passed to +the device code; this copies the pointer and shape information into an object that +the device code can access. If the view was passed into a method where the ``axom::for_all`` +function is calling a kernel, be sure to pass the view by value so the compiler +does not capture a host reference to the view, causing the kernel to fail. + +Do this: + + .. code-block:: cpp + + template + void doSomething(axom::ArrayView dataView) + { + axom::for_all(dataView.size(), AXOM_LAMBDA(axom::IndexType index) + { + /* body uses dataView[index] */ + }); + } + +Do NOT do this: + + .. code-block:: cpp + + template + void doSomething(axom::ArrayView &dataView) + { + axom::for_all(dataView.size(), AXOM_LAMBDA(axom::IndexType index) + { + /* body uses dataView[index] */ + /* It will crash on GPU devices because the host reference was + captured rather than the object. + */ + }); + } + +If pass by reference is used, create a new view inside the method and then use that +"device" view in the kernel so the device code uses an object captured by value. + + +Do not call ``axom::for_all`` from class methods that are marked as protected or private. +The nvcc compiler requires the method containing the kernel to be publicly accessible. + +Do this: + + .. code-block:: cpp + + class Algorithm + { + public: + // stuff + #if !defined(__CUDACC__) + private: + #endif + void helperMethod() + { + axom::for_all(n, AXOM_LAMBDA(axom::IndexType index) { /* body */}); + } + }; + +Do NOT do this: + + .. code-block:: cpp + + class Algorithm + { + public: + // stuff + private: + void helperMethod() + { + axom::for_all(n, AXOM_LAMBDA(axom::IndexType index) { /* body */}); + } + }; + +When calling a kernel via ``axom::for_all`` from a surrounding lambda function, +consider calling ``axom::for_all`` from a class member method instead. The nvcc compiler will +not compile kernel invokations inside lambda functions. This pattern comes up an intermediate +function is supplied a lambda that uses ``axom::for_all`` such as when handling many data types. + +Do this: + + .. code-block:: cpp + + template + class Algorithm + { + public: + void execute(conduit::Node &data) + { + // Handle any data type + Node_to_ArrayView(data, [&](auto dataView) + { + // Delegate operation to a template member method + handleData(dataView); + }); + } + template + void handleData(DataView dataView) + { + // Call the kernel here in the member method + axom::for_all(AXOM_LAMBDA(axom::IndexType) { /* body */ }); + } + }; + +Do NOT do this: + + .. code-block:: cpp + + template + class Algorithm + { + public: + void execute(conduit::Node &data) + { + // Handle any data type + Node_to_ArrayView(data, [&](auto dataView) + { + // nvcc will not compile this + axom::for_all(AXOM_LAMBDA(axom::IndexType) { /* body */ }); + }); + } + }; + + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RAJA::kernel ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -337,6 +487,12 @@ General, Rough Porting Tips are Axom's equivalent functions/classes if it exists, or to add your own or rewrite the code to not use standard library. + * It may not be possible to remove all such warnings on some platforms that support + both CPU/GPU backends since AXOM_LAMBDA will expand to ``__host__ __device__`` and then + the compiler will issue warnings about host functions such as RAJA::ReduceSum::~ReduceSum + for the OpenMP backend being called from ``__host__ __device__`` code. This warning + can be ignored. + * With no more decorating complaints from the compiler, write the logically correct kernel: From cf8b98798feca1dec16190dc982384d8fdc76843 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 26 Sep 2024 12:26:55 -0700 Subject: [PATCH 241/290] Added test for MakeUnstructured --- .../mir/tests/mir_blueprint_utilities.cpp | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 4b14378209..a12b6cbbbe 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -138,10 +138,76 @@ TEST(mir_blueprint_utilities, copy_hip) { test_copy_braid::test(); } #endif //------------------------------------------------------------------------------ -TEST(mir_blueprint_utilities, to_unstructured) +template +struct test_make_unstructured { - // TODO: to_unstructured + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + conduit::Node deviceResult; + bputils::MakeUnstructured uns; + uns.execute(deviceMesh["topologies/mesh"], deviceMesh["coordsets/coords"], "mesh", deviceResult); + + // device->host + conduit::Node hostResult; + bputils::copy(hostResult, deviceResult); + + // Result + conduit::Node expectedResult; + result(expectedResult); + + // Compare just the topologies + constexpr double tolerance = 1.e-7; + conduit::Node info; + bool success = compareConduit(expectedResult["topologies/mesh"], hostResult["topologies/mesh"], tolerance, info); + if(!success) + { + info.print(); + } + EXPECT_TRUE(success); + } + + static void create(conduit::Node &mesh) + { + std::vector dims{4, 4}; + axom::mir::testing::data::braid("uniform", dims, mesh); + } + + static void result(conduit::Node &mesh) + { + std::vector dims{4, 4}; + axom::mir::testing::data::braid("quads", dims, mesh); + } +}; + +TEST(mir_blueprint_utilities, make_unstructured_seq) +{ + test_make_unstructured::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_blueprint_utilities, make_unstructured_omp) +{ + test_make_unstructured::test(); } +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_blueprint_utilities, make_unstructured_cuda) +{ + test_make_unstructured::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_blueprint_utilities, make_unstructured_hip) +{ + test_make_unstructured::test(); +} +#endif //------------------------------------------------------------------------------ template @@ -467,10 +533,10 @@ struct test_recenter_field // device -> host conduit::Node hostResultMesh; axom::mir::utilities::blueprint::copy(hostResultMesh, deviceMesh); -#if 0 + // Print the results. - printNode(hostResultMesh); -#endif + //printNode(hostResultMesh); + const float n2z_result[] = {1., 2., 4., 5., 4., 5., 7., 8., 7., 8., 10., 11.}; for(size_t i = 0; i < (sizeof(n2z_result) / sizeof(float)); i++) { From fc5763ae25289814a384e96770a28ee2454088b9 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 27 Sep 2024 15:09:58 -0700 Subject: [PATCH 242/290] Adding zone function to topology views. Removing some c++17. --- .../mir/tests/mir_testing_data_helpers.hpp | 6 +- src/axom/mir/views/ExplicitCoordsetView.hpp | 25 +- src/axom/mir/views/Shapes.hpp | 164 +++++--- src/axom/mir/views/StructuredTopologyView.hpp | 188 +++++++--- .../UnstructuredTopologyMixedShapeView.hpp | 115 +++--- .../UnstructuredTopologySingleShapeView.hpp | 66 +++- .../views/dispatch_rectilinear_topology.hpp | 100 ++++- .../views/dispatch_structured_topology.hpp | 354 ++++++++++++------ .../mir/views/dispatch_uniform_topology.hpp | 102 ++++- .../views/dispatch_unstructured_topology.hpp | 344 ++++++++++++----- 10 files changed, 1070 insertions(+), 394 deletions(-) diff --git a/src/axom/mir/tests/mir_testing_data_helpers.hpp b/src/axom/mir/tests/mir_testing_data_helpers.hpp index d5b089f1e0..97338bba79 100644 --- a/src/axom/mir/tests/mir_testing_data_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_data_helpers.hpp @@ -46,7 +46,11 @@ template void braid(const std::string &type, const Dimensions &dims, conduit::Node &mesh) { int d[3] = {0, 0, 0}; - for(int i = 0; i < dims.size(); i++) d[i] = dims[i]; + auto n = dims.size(); + for(decltype(n) i = 0; i < n; i++) + { + d[i] = dims[i]; + } conduit::blueprint::mesh::examples::braid(type, d[0], d[1], d[2], mesh); add_distance(mesh); diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 5b1f81b262..378df87c07 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -47,7 +47,13 @@ class ExplicitCoordsetView const axom::ArrayView &y) : m_coordinates {x, y} { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(x.size() == y.size(), "Coordinate size mismatch."); +#else SLIC_ASSERT_MSG(x.size() == y.size(), "Coordinate size mismatch."); +#endif +#endif } /*! @@ -73,12 +79,14 @@ class ExplicitCoordsetView AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(vertex_index < size()); + assert(vertex_index < size(), "Out of range index."); #else SLIC_ASSERT_MSG( vertex_index < size(), - axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); + axom::fmt::format("Out of range index {}.", vertex_index)); +#endif #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index]}; @@ -128,8 +136,15 @@ class ExplicitCoordsetView const axom::ArrayView &z) : m_coordinates {x, y, z} { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(x.size() == y.size() && x.size() == z.size(), + "Coordinate size mismatch."); +#else SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), "Coordinate size mismatch."); +#endif +#endif } /*! @@ -155,12 +170,14 @@ class ExplicitCoordsetView AXOM_HOST_DEVICE PointType getPoint(IndexType vertex_index) const { +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(vertex_index < size()); + assert(vertex_index < size(), "Out of range index."); #else SLIC_ASSERT_MSG( vertex_index < size(), - axom::fmt::format("Vertex {} not in range [0, {}).", vertex_index, size())); + axom::fmt::format("Out of range index {}.", vertex_index)); +#endif #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index], diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 150f5b4169..5852c2e01f 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -12,6 +12,7 @@ #include #include +#include namespace axom { @@ -433,14 +434,14 @@ struct PolygonShape : public PolygonTraits * \brief Construct a shape. */ AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) - : m_idsView(ids) { } + : m_ids(ids) { } /*! * \brief Get the ids that make up this shape. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } + AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_ids; } /*! * \brief Get the ids for the requested face. @@ -451,37 +452,55 @@ struct PolygonShape : public PolygonTraits */ AXOM_HOST_DEVICE ConnectivityView getFace(int /*faceIndex*/) const { - return m_idsView; + return m_ids; } AXOM_HOST_DEVICE axom::StackArray getEdge(int edgeIndex) const { - const auto p0 = edgeIndex % m_idsView.size(); - const auto p1 = (edgeIndex + 1) % m_idsView.size(); + const auto p0 = edgeIndex % m_ids.size(); + const auto p1 = (edgeIndex + 1) % m_ids.size(); return axom::StackArray {p0, p1}; } private: - ConnectivityView m_idsView; + ConnectivityView m_ids; }; /*! * \brief This class extends the ShapeTraits with object state so it can represent a zone. + * + * \tparam ShapeTraits A shape traits class from which to inherit. + * \tparam ConnStorage A view or container that contains connectivity. + * */ -template +template struct Shape : public ShapeTraits { - using ConnectivityType = ConnType; + using ConnectivityStorage = ConnStorage; + using ConnectivityStorageRef = ConnStorage &; + using ConnectivityStorageConstRef = const ConnStorage &; + using ConnectivityType = typename ConnStorage::value_type; using ConnectivityView = axom::ArrayView; /*! * \brief Construct a shape. */ - AXOM_HOST_DEVICE Shape(const ConnectivityView &ids) - : m_idsView(ids) + AXOM_HOST_DEVICE Shape() + : m_ids() + , m_faceIds() + { + } + + /*! + * \brief Construct a shape. + * + * \param ids A reference to connectivity storage for this shape. + */ + AXOM_HOST_DEVICE Shape(ConnectivityStorageConstRef ids) + : m_ids(ids) , m_faceIds() { - assert(m_idsView.size() == ShapeTraits::numberOfNodes()); + assert(m_ids.size() == ShapeTraits::numberOfNodes()); } /*! @@ -491,82 +510,115 @@ struct Shape : public ShapeTraits */ AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const { - assert(index < static_cast(m_idsView.size())); - return m_idsView[index]; + assert(index < static_cast(m_ids.size())); + return m_ids[index]; } /*! - * \brief Get the ids that make up this shape. + * \brief Get the storage for the ids that make up this shape. + * + * \return The container for the ids that make up this shape. + */ + AXOM_HOST_DEVICE ConnectivityStorageRef getIdsStorage() { return m_ids; } + + /*! + * \brief Get the storage for the ids that make up this shape. + * + * \return The container for the ids that make up this shape. + */ + AXOM_HOST_DEVICE ConnectivityStorageConstRef getIdsStorage() const { return m_ids; } + + /*! + * \brief Get the ids that make up this shape as a view. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } + AXOM_HOST_DEVICE ConnectivityView getIds() const { return ConnectivityView(const_cast(m_ids.data()), m_ids.size()); } /*! * \brief Get the unique ids that make up this shape. For basic shapes, assume they are unique. * - * \return A view containing the ids that make up this shape. + * \return The unique ids that make up this shape. */ - AXOM_HOST_DEVICE ConnectivityView getUniqueIds() const { return m_idsView; } + AXOM_HOST_DEVICE ConnectivityStorageConstRef getUniqueIds() const { return m_ids; } /*! * \brief Get the ids for the requested face. * * \param faceIndex The index of the desired face. * - * \return An array view (wrapping m_faceIds) that contains the ids for the face. + * \return The ids that make up the face */ - AXOM_HOST_DEVICE - ConnectivityView getFace(int faceIndex) const + /// @{ + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, ConnectivityStorageConstRef>::type + getFace(axom::IndexType AXOM_UNUSED_PARAM(faceIndex)) const { - if constexpr(ShapeTraits::dimension() == 2) - return m_idsView; - else - { - const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); - for(IndexType i = 0; i < nnodes; i++) - m_faceIds[i] = m_idsView[ShapeTraits::faces[faceIndex][i]]; - return ConnectivityView(m_faceIds.m_data, nnodes); - } + return m_ids; } +// template +// AXOM_HOST_DEVICE typename std::enable_if<_ndims > 2, ConnectivityStorage>::type +// getFace(axom::IndexType faceIndex) const +// { +// const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); +// for(IndexType i = 0; i < nnodes; i++) +// m_faceIds[i] = m_ids[ShapeTraits::faces[faceIndex][i]]; +// return ConnectivityStorage(m_faceIds.m_data, nnodes); +// } + /// @} + private: - ConnectivityView m_idsView; + ConnectivityStorage m_ids; mutable axom::StackArray m_faceIds; }; -// Make some concrete shape classes based on the shape traits. -template -using LineShape = Shape; +/*! + * \brief Some concrete shape classes based on the shape traits. + * + * \tparam ConnType A type of the connectivity values or a type that "stores" + * the data either in actuality, or as a view. If an integral + * type is passed, an axom::ArrayView will be used. + */ +/// @{ +template +using LineShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using TriShape = Shape; +template +using TriShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using QuadShape = Shape; +template +using QuadShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using TetShape = Shape; +template +using TetShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using PyramidShape = Shape; +template +using PyramidShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using WedgeShape = Shape; +template +using WedgeShape = Shape::value, axom::ArrayView, ConnType>::type>; -template -using HexShape = Shape; +template +using HexShape = Shape::value, axom::ArrayView, ConnType>::type>; +/// @} /*! * \brief This is a shape that can act as any of the other shapes. * + * \tparam ConnType type of the connectivity values. + * * \note This is a substitute for polymorphism so we can run on device. */ template struct VariableShape { + using ConnectivityStorage = axom::ArrayView; + using ConnectivityStorageRef = ConnectivityStorage &; + using ConnectivityStorageConstRef = const ConnectivityStorage &; + using ConnectivityType = ConnType; - using ConnectivityView = axom::ArrayView; + using ConnectivityView = ConnectivityStorage; /*! * \brief Constructor @@ -575,9 +627,9 @@ struct VariableShape * \param ids The ids that describe the shape. */ AXOM_HOST_DEVICE - VariableShape(int shapeId, const ConnectivityView &ids) + VariableShape(int shapeId, ConnectivityStorageConstRef ids) : m_shapeId(shapeId) - , m_idsView(ids) + , m_ids(ids) { assert(shapeId >= Point_ShapeID && shapeId <= Hex_ShapeID); } @@ -606,7 +658,7 @@ struct VariableShape dim = QuadTraits::dimension(); break; case Polygon_ShapeID: - dim = 2; + dim = PolygonTraits::dimension(); break; case Tet_ShapeID: dim = TetTraits::dimension(); @@ -624,7 +676,7 @@ struct VariableShape return dim; } - AXOM_HOST_DEVICE IndexType numberOfNodes() const { return m_idsView.size(); } + AXOM_HOST_DEVICE IndexType numberOfNodes() const { return m_ids.size(); } AXOM_HOST_DEVICE IndexType numberOfNodesInFace(int faceIndex) const { @@ -641,7 +693,7 @@ struct VariableShape nnodes = QuadTraits::numberOfNodesInFace(faceIndex); break; case Polygon_ShapeID: - nnodes = (faceIndex == 0) ? m_idsView.size() : 0; + nnodes = (faceIndex == 0) ? m_ids.size() : 0; break; case Tet_ShapeID: nnodes = TetTraits::numberOfNodesInFace(faceIndex); @@ -740,7 +792,7 @@ struct VariableShape nedges = QuadTraits::numberOfEdges(); break; case Polygon_ShapeID: - nedges = m_idsView.size(); + nedges = m_ids.size(); break; case Tet_ShapeID: nedges = TetTraits::numberOfEdges(); @@ -774,7 +826,7 @@ struct VariableShape break; case Polygon_ShapeID: { - const auto n = m_idsView.size(); + const auto n = m_ids.size(); edge[0] = edgeIndex % n; edge[1] = (edgeIndex + 1) % n; break; @@ -802,7 +854,7 @@ struct VariableShape */ AXOM_HOST_DEVICE ConnectivityType getId(IndexType index) const { - return m_idsView[index]; + return m_ids[index]; } /*! @@ -810,13 +862,13 @@ struct VariableShape * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE const ConnectivityView &getIds() const { return m_idsView; } + AXOM_HOST_DEVICE ConnectivityView getIds() const { return m_ids; } AXOM_HOST_DEVICE constexpr static const char *name() { return "mixed"; } private: int m_shapeId; - ConnectivityView m_idsView; + ConnectivityStorage m_ids; }; /*! diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 05df964b67..e2b17c208a 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -6,8 +6,11 @@ #ifndef AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ #define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ +#include "axom/core.hpp" #include "axom/mir/views/Shapes.hpp" +#include + namespace axom { namespace mir @@ -27,7 +30,10 @@ class StructuredTopologyView using IndexType = typename IndexingPolicy::IndexType; using LogicalIndex = typename IndexingPolicy::LogicalIndex; using ConnectivityType = IndexType; - using ShapeType = typename std::conditional, typename std::conditional, LineShape>::type>::type; + using Shape1D = LineShape>; + using Shape2D = QuadShape>; + using Shape3D = HexShape>; + using ShapeType = typename std::conditional::type>::type; /*! * \brief Return the number of dimensions. @@ -39,39 +45,43 @@ class StructuredTopologyView /*! * \brief Constructor */ - StructuredTopologyView() : m_indexing() { } + AXOM_HOST_DEVICE StructuredTopologyView() : m_zoneIndexing(), m_nodeIndexing() { } /*! * \brief Constructor * * \param indexing The indexing policy for the topology (num zones in each dimension). */ - StructuredTopologyView(const IndexingPolicy &indexing) : m_indexing(indexing) - { } + AXOM_HOST_DEVICE StructuredTopologyView(const IndexingPolicy &indexing) : m_zoneIndexing(indexing), m_nodeIndexing(indexing.expand()) + { + } /*! * \brief Return the number of zones. * * \return The number of zones. */ - IndexType size() const { return m_indexing.size(); } + AXOM_HOST_DEVICE IndexType size() const { return m_zoneIndexing.size(); } /*! * \brief Return the number of zones. * * \return The number of zones. */ - IndexType numberOfZones() const { return size(); } + AXOM_HOST_DEVICE IndexType numberOfZones() const { return size(); } /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. */ - IndexType connectivitySize() const + AXOM_HOST_DEVICE IndexType connectivitySize() const { IndexType nodesPerElem = 1; - for(int d = 0; d < dimension(); d++) nodesPerElem *= 2; + for(int d = 0; d < dimension(); d++) + { + nodesPerElem *= 2; + } return numberOfZones() * nodesPerElem; } @@ -80,9 +90,9 @@ class StructuredTopologyView * * \return The mesh logical dimensions. */ - const LogicalIndex &logicalDimensions() const + AXOM_HOST_DEVICE const LogicalIndex &logicalDimensions() const { - return m_indexing.logicalDimensions(); + return m_zoneIndexing.logicalDimensions(); } /*! @@ -90,14 +100,103 @@ class StructuredTopologyView * * \return The indexing object. */ - IndexingPolicy &indexing() { return m_indexing; } + AXOM_HOST_DEVICE IndexingPolicy &indexing() { return m_zoneIndexing; } /*! * \brief Return indexing object. * * \return The indexing object. */ - const IndexingPolicy &indexing() const { return m_indexing; } + AXOM_HOST_DEVICE const IndexingPolicy &indexing() const { return m_zoneIndexing; } + + /*! + * \brief Return a zone. + * + * \param zoneIndex The index of the zone to return. + * + * \return The requested zone. + * + * \note 3D implementation. + */ + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, Shape3D>::type + zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#endif + const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = m_nodeIndexing.jStride(); + const auto kp = m_nodeIndexing.kStride(); + + Shape3D shape; + auto &data = shape.getIdsStorage(); + data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + data[4] = data[0] + kp; + data[5] = data[1] + kp; + data[6] = data[2] + kp; + data[7] = data[3] + kp; + + return shape; + } + + /*! + * \brief Return a zone. + * + * \param zoneIndex The index of the zone to return. + * + * \return The requested zone. + * + * \note 2D implementation. + */ + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, Shape2D>::type + zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#endif + const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); + const auto jp = m_nodeIndexing.jStride(); + + Shape2D shape; + auto &data = shape.getIdsStorage(); + data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[1] = data[0] + 1; + data[2] = data[1] + jp; + data[3] = data[2] - 1; + + return shape; + } + + /*! + * \brief Return a zone. + * + * \param index The index of the zone to return. + * + * \return The requested zone. + * + * \note 1D implementation. + */ + template + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, Shape1D>::type + zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#endif + const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); + + Shape1D shape; + auto &data = shape.getIdsStorage(); + data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[1] = data[0] + 1; + + return shape; + } /*! * \brief Execute a function for each zone in the mesh using axom::for_all. @@ -117,8 +216,8 @@ class StructuredTopologyView if constexpr(IndexingPolicy::dimension() == 3) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -128,9 +227,9 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); - ConnectivityType data[8]; - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + Shape3D shape; + auto &data = shape.getIdsStorage(); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -139,14 +238,13 @@ class StructuredTopologyView data[6] = data[2] + kp; data[7] = data[3] + kp; - const ShapeType shape(axom::ArrayView(data, 8)); func(zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 2) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -155,21 +253,21 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); - ConnectivityType data[4]; + Shape2D shape; + auto &data = shape.getIdsStorage(); data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; - const ShapeType shape(axom::ArrayView(data, 4)); func(zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 1) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -177,12 +275,12 @@ class StructuredTopologyView AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - ConnectivityType data[2]; + Shape1D shape; + auto &data = shape.getIdsStorage(); data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; - const ShapeType shape(axom::ArrayView(data, 2)); func(zoneIndex, shape); }); } @@ -206,8 +304,8 @@ class StructuredTopologyView if constexpr(IndexingPolicy::dimension() == 3) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -218,9 +316,9 @@ class StructuredTopologyView const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); const auto kp = nodeIndexing.kStride(); - ConnectivityType data[8]; - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + Shape3D shape; + auto &data = shape.getIdsStorage(); + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -229,14 +327,13 @@ class StructuredTopologyView data[6] = data[2] + kp; data[7] = data[3] + kp; - const ShapeType shape(axom::ArrayView(data, 8)); func(selectIndex, zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 2) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -246,21 +343,21 @@ class StructuredTopologyView const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = nodeIndexing.jStride(); - ConnectivityType data[4]; - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + Shape2D shape; + auto &data = shape.getIdsStorage(); + + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; - const ShapeType shape(axom::ArrayView(data, 4)); func(selectIndex, zoneIndex, shape); }); } else if constexpr(IndexingPolicy::dimension() == 1) { - const IndexingPolicy zoneIndexing = m_indexing; - const IndexingPolicy nodeIndexing = m_indexing.expand(); + const IndexingPolicy zoneIndexing = m_zoneIndexing; + const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); axom::for_all( 0, @@ -269,19 +366,20 @@ class StructuredTopologyView const auto zoneIndex = idsView[selectIndex]; const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - ConnectivityType data[2]; - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); + Shape1D shape; + auto &data = shape.getIdsStorage(); + + data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; - const ShapeType shape(axom::ArrayView(data, 2)); func(selectIndex, zoneIndex, shape); }); } } private: - IndexingPolicy m_indexing; + IndexingPolicy m_zoneIndexing; + IndexingPolicy m_nodeIndexing; }; } // end namespace views diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 2bffdc8e35..7b21e94389 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -21,12 +21,9 @@ namespace views * \note If the view was to renumber the shapes array to use the Shape::id() values * then operator[] could return its input value and skip bsearch. */ -template class ShapeMap { public: - using IndexType = IndexT; - /*! * \brief Constructor */ @@ -74,6 +71,18 @@ class ShapeMap axom::ArrayView m_shape_ids; }; +/*! + * \brief Populate the shape map values/ids arrays using data in the topology's shape_map. + * + * \param n_topo The topology that contains the shape map. + * \param[out] values The sorted values used for shapes in the topology. + * \param[out] ids The Shape ids that correspond to the shape values. + * \param allocatorID The allocator to use when creating the arrays. + */ +ShapeMap buildShapeMap(const conduit::Node &n_topo, + axom::Array &values, + axom::Array &ids, + int allocatorID); /*! * \brief This class provides a view for Conduit/Blueprint mixed shape unstructured grids. * @@ -103,18 +112,30 @@ class UnstructuredTopologyMixedShapeView const ConnectivityView &conn, const ConnectivityView &shapes, const ConnectivityView &sizes, - const ConnectivityView &offsets) + const ConnectivityView &offsets, + const ShapeMap &shapemap) : m_topo(topo) , m_connectivity(conn) , m_shapes(shapes) , m_sizes(sizes) , m_offsets(offsets) + , m_shapeMap(shapemap) { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(m_shapes.size() != 0); + assert(m_sizes.size() != 0); + assert(m_offsets.size() != 0); + assert(m_offsets.size() == m_sizes.size() && + m_offsets.size() == m_shapes.size()); +#else SLIC_ASSERT(m_shapes.size() != 0); SLIC_ASSERT(m_sizes.size() != 0); SLIC_ASSERT(m_offsets.size() != 0); SLIC_ASSERT(m_offsets.size() == m_sizes.size() && m_offsets.size() == m_shapes.size()); +#endif +#endif } /*! @@ -129,14 +150,45 @@ class UnstructuredTopologyMixedShapeView * * \return The number of zones. */ - IndexType numberOfZones() const { return m_sizes.size(); } + AXOM_HOST_DEVICE IndexType numberOfZones() const { return m_sizes.size(); } /*! * \brief Return the size of the connectivity. * * \return The size of the connectivity. */ - IndexType connectivitySize() const { return m_connectivity.size(); } + AXOM_HOST_DEVICE IndexType connectivitySize() const { return m_connectivity.size(); } + + /*! + * \brief Return a zone. + * + * \param zoneIndex The index of the zone to return. + * + * \return The requested zone. + */ + ShapeType zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif +#endif + const ConnectivityView shapeData( + m_connectivity.data() + m_offsets[zoneIndex], + m_sizes[zoneIndex]); + const auto shapeID = m_shapeMap[m_shapes[zoneIndex]]; +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(shapeID > 0); +#else + SLIC_ASSERT(shapeID > 0); +#endif +#endif + + return ShapeType(shapeID, shapeData); + } /*! * \brief Execute a function for each zone in the mesh. @@ -154,8 +206,8 @@ class UnstructuredTopologyMixedShapeView // Build a ShapeMap from the Conduit shape map. axom::Array values, ids; const auto allocatorID = axom::execution_space::allocatorID(); - buildShapeMap(values, ids, allocatorID); - const ShapeMap shapeMap(values.view(), ids.view()); + buildShapeMap(m_topo, values, ids, allocatorID); + const ShapeMap shapeMap(values.view(), ids.view()); const ConnectivityView connectivityView(m_connectivity); const ConnectivityView shapes(m_shapes); @@ -169,10 +221,12 @@ class UnstructuredTopologyMixedShapeView connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) assert(shapeID > 0); #else SLIC_ASSERT(shapeID > 0); +#endif #endif const ShapeType shape(shapeID, shapeData); func(zoneIndex, shape); @@ -197,8 +251,8 @@ class UnstructuredTopologyMixedShapeView // Build a ShapeMap from the Conduit shape map. axom::Array values, ids; const auto allocatorID = axom::execution_space::allocatorID(); - buildShapeMap(values, ids, allocatorID); - const ShapeMap shapeMap(values.view(), ids.view()); + buildShapeMap(m_topo, values, ids, allocatorID); + const ShapeMap shapeMap(values.view(), ids.view()); // Make views that can be captured. const ConnectivityView connectivityView(m_connectivity); @@ -215,10 +269,12 @@ class UnstructuredTopologyMixedShapeView connectivityView.data() + offsets[zoneIndex], sizes[zoneIndex]); const auto shapeID = shapeMap[shapes[zoneIndex]]; +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) assert(shapeID > 0); #else SLIC_ASSERT(shapeID > 0); +#endif #endif const ShapeType shape(shapeID, shapeData); func(selectIndex, zoneIndex, shape); @@ -226,50 +282,13 @@ class UnstructuredTopologyMixedShapeView } private: - /*! - * \brief Populate the shape map values/ids arrays using data in the topology's shape_map. - * - * \param[out] values The sorted values used for shapes in the topology. - * \param[out] ids The Shape ids that correspond to the shape values. - * \param allocatorID The allocator to use when creating the arrays. - */ - void buildShapeMap(axom::Array &values, - axom::Array &ids, - int allocatorID) const - { - // Make the map from the Conduit shape_map. Use std::map to sort the key values. - std::map sm; - const conduit::Node &m_shape_map = - m_topo.fetch_existing("elements/shape_map"); - for(conduit::index_t i = 0; i < m_shape_map.number_of_children(); i++) - { - const auto value = static_cast(m_shape_map[i].to_int()); - sm[value] = axom::mir::views::shapeNameToID(m_shape_map[i].name()); - } - - // Store the map in 2 vectors so data are contiguous. - const auto n = sm.size(); - std::vector valuesvec, idsvec; - valuesvec.reserve(n); - idsvec.reserve(n); - for(auto it = sm.begin(); it != sm.end(); it++) - { - valuesvec.push_back(it->first); - idsvec.push_back(it->second); - } - - // Copy the map values to the device memory. - values = axom::Array(n, n, allocatorID); - ids = axom::Array(n, n, allocatorID); - axom::copy(values.data(), valuesvec.data(), n * sizeof(IndexType)); - axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); - } const conduit::Node &m_topo; ConnectivityView m_connectivity; ConnectivityView m_shapes; ConnectivityView m_sizes; ConnectivityView m_offsets; + ShapeMap m_shapeMap; }; } // end namespace views diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index c58e0b5051..9ae8dfe015 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -26,13 +26,14 @@ class UnstructuredTopologySingleShapeView public: using ShapeType = ShapeT; using ConnectivityType = typename ShapeType::ConnectivityType; - using ConnectivityView = typename ShapeType::ConnectivityView; + using ConnectivityView = axom::ArrayView; /*! * \brief Constructor * * \param conn The mesh connectivity. */ + AXOM_HOST_DEVICE UnstructuredTopologySingleShapeView(const ConnectivityView &conn) : m_connectivityView(conn) , m_sizesView() @@ -46,6 +47,7 @@ class UnstructuredTopologySingleShapeView * \param sizes The number of nodes in each zone. * \param offsets The offset to each zone in the connectivity. */ + AXOM_HOST_DEVICE UnstructuredTopologySingleShapeView(const ConnectivityView &conn, const ConnectivityView &sizes, const ConnectivityView &offsets) @@ -53,7 +55,13 @@ class UnstructuredTopologySingleShapeView , m_sizesView(sizes) , m_offsetsView(offsets) { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(m_offsetsView.size() == m_sizesView.size()); +#else SLIC_ASSERT(m_offsetsView.size() == m_sizesView.size()); +#endif +#endif } /*! @@ -68,7 +76,7 @@ class UnstructuredTopologySingleShapeView * * \return The number of zones. */ - IndexType numberOfZones() const + AXOM_HOST_DEVICE IndexType numberOfZones() const { return (m_sizesView.size() != 0) ? m_sizesView.size() @@ -80,7 +88,59 @@ class UnstructuredTopologySingleShapeView * * \return The size of the connectivity. */ - IndexType connectivitySize() const { return m_connectivityView.size(); } + AXOM_HOST_DEVICE IndexType connectivitySize() const { return m_connectivityView.size(); } + + /*! + * \brief Return a zone. + * + * \param zoneIndex The requested zone. + * + * \return The requested zone. + */ + /// @{ + template + AXOM_HOST_DEVICE typename std::enable_if<_variable_size, ShapeType>::type + zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif +#endif + + return ShapeType(ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], + m_sizesView[zoneIndex])); + } + + template + AXOM_HOST_DEVICE typename std::enable_if::type + zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif +#endif + + ConnectivityView shapeIdsView {}; + if(m_sizesView.empty()) + { + shapeIdsView = ConnectivityView( + m_connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); + } + else + { + shapeIdsView = ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], + m_sizesView[zoneIndex]); + } + return ShapeType(shapeIdsView); + } + /// @} /*! * \brief Execute a function for each zone in the mesh. diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index f318ca5ca0..c6ee23964c 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -18,6 +18,7 @@ namespace mir { namespace views { +/// Base template template struct make_rectilinear { }; @@ -139,6 +140,84 @@ struct make_rectilinear<1> } }; +namespace internal +{ +/*! + * \brief Base template for dispatching rectilinear topology. + */ +template +struct dispatch_one_rectilinear_topology +{ + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) + { + } +}; + +/*! + * \brief Partial specialization to dispatch 3D rectilinear topology. + */ +template +struct dispatch_one_rectilinear_topology +{ + /*! + * \brief Make a proper view type for the rectilinear topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_rectilinear<3>::view(topo); + const std::string shape("hex"); + func(shape, topoView); + } +}; + +/*! + * \brief Partial specialization to dispatch 2D rectilinear topology. + */ +template +struct dispatch_one_rectilinear_topology +{ + /*! + * \brief Make a proper view type for the rectilinear topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_rectilinear<2>::view(topo); + const std::string shape("quad"); + func(shape, topoView); + } +}; + +/*! + * \brief Partial specialization to dispatch 1D rectilinear topology. + */ +template +struct dispatch_one_rectilinear_topology +{ + /*! + * \brief Make a proper view type for the rectilinear topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_rectilinear<1>::view(topo); + const std::string shape("line"); + func(shape, topoView); + } +}; + +} // end namespace internal + /*! * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. * @@ -158,28 +237,13 @@ void dispatch_rectilinear_topology(const conduit::Node &topo, FuncType &&func) switch(axes.size()) { case 3: - if constexpr(dimension_selected(SelectedDimensions, 3)) - { - auto topoView = make_rectilinear<3>::view(topo); - const std::string shape("hex"); - func(shape, topoView); - } + internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); break; case 2: - if constexpr(dimension_selected(SelectedDimensions, 2)) - { - auto topoView = make_rectilinear<2>::view(topo); - const std::string shape("quad"); - func(shape, topoView); - } + internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); break; case 1: - if constexpr(dimension_selected(SelectedDimensions, 1)) - { - auto topoView = make_rectilinear<1>::view(topo); - const std::string shape("line"); - func(shape, topoView); - } + internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); break; default: break; diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index d77322a3cd..4638b375d3 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -15,6 +15,8 @@ #include +#include + namespace axom { namespace mir @@ -23,7 +25,8 @@ namespace views { //------------------------------------------------------------------------------ /*! - * \brief Fill an array from a Conduit node, filling the destination array if the values do not exist. + * \brief Fill an array from a Conduit node, filling the destination array if + * the values do not exist. * * \tparam ArrayType The array type to use. * @@ -309,6 +312,237 @@ struct make_structured<1> } }; +//------------------------------------------------------------------------------ +namespace internal +{ +/*! + * \brief Base template for dispatching structured topology. + */ +template +struct dispatch_only_structured_topology +{ + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) + { + } +}; + +/*! + * \brief Partial specialization to dispatch 3D structured topology. + */ +template +struct dispatch_only_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); + const std::string shape("hex"); + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<3>::view(topo); + func(shape, topoView); + } + else + { + auto topoView = make_structured<3>::view(topo); + func(shape, topoView); + } + } +}; + +/*! + * \brief Partial specialization to dispatch 2D structured topology. + */ +template +struct dispatch_only_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); + const std::string shape("quad"); + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<2>::view(topo); + func(shape, topoView); + } + else + { + auto topoView = make_structured<2>::view(topo); + func(shape, topoView); + } + } +}; + +/*! + * \brief Partial specialization to dispatch 1D structured topology. + */ +template +struct dispatch_only_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("elements/dims/offsets"); + const std::string stridesKey("elements/dims/strides"); + const std::string shape("line"); + if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<1>::view(topo); + func(shape, topoView); + } + else + { + auto topoView = make_structured<1>::view(topo); + func(shape, topoView); + } + } +}; + +//------------------------------------------------------------------------------ +/*! + * \brief Base template for dispatching structured topology. + */ +template +struct dispatch_any_structured_topology +{ + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) + { + } +}; + +/*! + * \brief Partial specialization to dispatch 3D structured topologies. + */ +template +struct dispatch_any_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("offsets"), stridesKey("strides"); + const std::string type = topo.fetch_existing("type").as_string(); + const std::string shape("hex"); + + if(type == "structured" && topo.has_path(offsetsKey) && + topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<3>::view(topo); + func(shape, topoView); + } + else + { + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<3>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<3>::view(topo); + else if(type == "structured") + topoView = make_structured<3>::view(topo); + func(shape, topoView); + } + } +}; + +/*! + * \brief Partial specialization to dispatch 2D structured topologies. + */ +template +struct dispatch_any_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("offsets"), stridesKey("strides"); + const std::string type = topo.fetch_existing("type").as_string(); + const std::string shape("quad"); + if(type == "structured" && topo.has_path(offsetsKey) && + topo.has_path(stridesKey)) + { + auto topoView = make_strided_structured<2>::view(topo); + func(shape, topoView); + } + else + { + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<2>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<2>::view(topo); + else if(type == "structured") + topoView = make_structured<2>::view(topo); + func(shape, topoView); + } + } +}; + +/*! + * \brief Partial specialization to dispatch 1D structured topologies. + */ +template +struct dispatch_any_structured_topology +{ + /*! + * \brief Make a proper view type for the structured topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + const std::string offsetsKey("offsets"), stridesKey("strides"); + const std::string type = topo.fetch_existing("type").as_string(); + const std::string shape("line"); + + // Make these topology types share the same func dispatch. + StructuredTopologyView> topoView; + if(type == "uniform") + topoView = make_uniform<1>::view(topo); + else if(type == "rectilinear") + topoView = make_rectilinear<1>::view(topo); + else if(type == "structured") + topoView = make_structured<1>::view(topo); + func(shape, topoView); + } +}; + +} // end namespace internal + /*! * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. * @@ -324,58 +558,20 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) int ndims = 1; ndims += topo.has_path("elements/dims/j") ? 1 : 0; ndims += topo.has_path("elements/dims/k") ? 1 : 0; - const std::string offsetsKey("elements/dims/offsets"); - const std::string stridesKey("elements/dims/strides"); switch(ndims) { case 3: - if constexpr(dimension_selected(SelectedDimensions, 3)) - { - const std::string shape("hex"); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) - { - auto topoView = make_strided_structured<3>::view(topo); - func(shape, topoView); - } - else - { - auto topoView = make_structured<3>::view(topo); - func(shape, topoView); - } - } + internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); break; case 2: - if constexpr(dimension_selected(SelectedDimensions, 2)) - { - const std::string shape("quad"); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) - { - auto topoView = make_strided_structured<2>::view(topo); - func(shape, topoView); - } - else - { - auto topoView = make_structured<2>::view(topo); - func(shape, topoView); - } - } + internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); break; case 1: - if constexpr(dimension_selected(SelectedDimensions, 1)) - { - const std::string shape("line"); - if(topo.has_path(offsetsKey) || topo.has_path(stridesKey)) - { - auto topoView = make_strided_structured<1>::view(topo); - func(shape, topoView); - } - else - { - auto topoView = make_structured<1>::view(topo); - func(shape, topoView); - } - } + internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); + break; + default: + break; } } @@ -393,74 +589,20 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) template void dispatch_structured_topologies(const conduit::Node &topo, FuncType &&func) { - const std::string offsetsKey("offsets"), stridesKey("strides"); - const std::string type = topo.fetch_existing("type").as_string(); - auto ndims = conduit::blueprint::mesh::utils::topology::dims(topo); + const auto ndims = conduit::blueprint::mesh::utils::topology::dims(topo); switch(ndims) { case 3: - if constexpr(dimension_selected(SelectedDimensions, 3)) - { - const std::string shape("hex"); - - if(type == "structured" && topo.has_path(offsetsKey) && - topo.has_path(stridesKey)) - { - auto topoView = make_strided_structured<3>::view(topo); - func(shape, topoView); - } - else - { - // Make these topology types share the same func dispatch. - StructuredTopologyView> topoView; - if(type == "uniform") - topoView = make_uniform<3>::view(topo); - else if(type == "rectilinear") - topoView = make_rectilinear<3>::view(topo); - else if(type == "structured") - topoView = make_structured<3>::view(topo); - func(shape, topoView); - } - } + internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); break; case 2: - if constexpr(dimension_selected(SelectedDimensions, 2)) - { - const std::string shape("quad"); - if(type == "structured" && topo.has_path(offsetsKey) && - topo.has_path(stridesKey)) - { - auto topoView = make_strided_structured<2>::view(topo); - func(shape, topoView); - } - else - { - // Make these topology types share the same func dispatch. - StructuredTopologyView> topoView; - if(type == "uniform") - topoView = make_uniform<2>::view(topo); - else if(type == "rectilinear") - topoView = make_rectilinear<2>::view(topo); - else if(type == "structured") - topoView = make_structured<2>::view(topo); - func(shape, topoView); - } - } + internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); break; case 1: - if constexpr(dimension_selected(SelectedDimensions, 1)) - { - const std::string shape("line"); - // Make these topology types share the same func dispatch. - StructuredTopologyView> topoView; - if(type == "uniform") - topoView = make_uniform<1>::view(topo); - else if(type == "rectilinear") - topoView = make_rectilinear<1>::view(topo); - else if(type == "structured") - topoView = make_structured<1>::view(topo); - func(shape, topoView); - } + internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); + break; + default: + break; } } diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 36f5efa99d..7441663b0d 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -139,6 +139,84 @@ struct make_uniform<1> } }; +namespace internal +{ +/*! + * \brief Base template for dispatching uniform topology. + */ +template +struct dispatch_one_uniform_topology +{ + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) + { + } +}; + +/*! + * \brief Partial specialization to dispatch 3D uniform topology. + */ +template +struct dispatch_one_uniform_topology +{ + /*! + * \brief Make a proper view type for the uniform topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_uniform<3>::view(topo); + const std::string shape("hex"); + func(shape, topoView); + } +}; + +/*! + * \brief Partial specialization to dispatch 2D uniform topology. + */ +template +struct dispatch_one_uniform_topology +{ + /*! + * \brief Make a proper view type for the uniform topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_uniform<2>::view(topo); + const std::string shape("quad"); + func(shape, topoView); + } +}; + +/*! + * \brief Partial specialization to dispatch 1D uniform topology. + */ +template +struct dispatch_one_uniform_topology +{ + /*! + * \brief Make a proper view type for the uniform topology and pass the + * view to the supplied kernel. + * + * \param topo The node that contains the topology. + * \param func The kernel to be invoked. + */ + static void execute(const conduit::Node &topo, FuncType &&func) + { + auto topoView = make_uniform<1>::view(topo); + const std::string shape("line"); + func(shape, topoView); + } +}; + +} // end namespace internal + /*! * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. * @@ -157,30 +235,16 @@ void dispatch_uniform_topology(const conduit::Node &topo, FuncType &&func) const conduit::Node &n_dims = coordset->fetch_existing("dims"); switch(n_dims.dtype().number_of_elements()) { - default: case 3: - if constexpr(dimension_selected(SelectedDimensions, 3)) - { - auto topoView = make_uniform<3>::view(topo); - const std::string shape("hex"); - func(shape, topoView); - } + internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); break; case 2: - if constexpr(dimension_selected(SelectedDimensions, 2)) - { - auto topoView = make_uniform<2>::view(topo); - const std::string shape("quad"); - func(shape, topoView); - } + internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); break; case 1: - if constexpr(dimension_selected(SelectedDimensions, 3)) - { - auto topoView = make_uniform<1>::view(topo); - const std::string shape("line"); - func(shape, topoView); - } + internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); + break; + default: break; } } diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 854db4b9d2..3427c81bb7 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -33,6 +33,7 @@ constexpr int AnyShape = -1; * \param topo The node that contains the topology. * \param func The function/lambda to call with the topology view. */ +// @{ template void dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, FuncType &&func) @@ -94,6 +95,7 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, func(shape, ugView); } } +// @} /*! * \brief This function dispatches a Conduit mixed unstructured topology. @@ -107,6 +109,7 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, * the shape_map within the topology so we can build our own shape map * later in the for_all_zones method. */ +// @{ template void dispatch_unstructured_mixed_topology(const conduit::Node &topo, FuncType &&func) @@ -122,11 +125,20 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, [&](auto connView, auto shapesView, auto sizesView, auto offsetsView) { using ConnType = typename decltype(connView)::value_type; + // Get the allocator that allocated the connectivity. The shape map data + // need to go into the same memory space. + const int allocatorID = axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); + + // Make the shape map. + axom::Array values, ids; + auto shapeMap = buildShapeMap(topo, values, ids, allocatorID); + UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, - offsetsView); + offsetsView, + shapeMap); func(shape, ugView); }); } @@ -148,14 +160,24 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, auto offsetsView = bputils::make_array_view(topo["elements/offsets"]); + // Get the allocator that allocated the connectivity. The shape map data + // need to go into the same memory space. + const int allocatorID = axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); + + // Make the shape map. + axom::Array values, ids; + auto shapeMap = buildShapeMap(topo, values, ids, allocatorID); + UnstructuredTopologyMixedShapeView ugView(topo, connView, shapesView, sizesView, - offsetsView); + offsetsView, + shapeMap); func(shape, ugView); } } +// @} #if __cplusplus >= 201703L // C++17 and later. @@ -181,12 +203,226 @@ constexpr int encode_shapes(Args... args) { } #endif +/*! + * \brief This function turns a list of shapeID values into a bitfield that + * encodes the shapes. We use this in templating to limit which + * shapes get supported in dispatch instantiation. + * + * \param args A template parameter pack that contains ShapeID values. + * + * \return An integer that encodes the shape ids. + */ template constexpr int select_shapes(Args... args) { return encode_shapes((1 << args)...); } +//------------------------------------------------------------------------------ +namespace internal +{ +/*! + * \brief Base template for dispatching various shapes conditionally. + */ +template +struct dispatch_shape +{ + /*! + * \brief Execute method that gets generated when a shape is not enabled or supported. Do nothing. + */ + static void execute(bool &AXOM_UNUSED_PARAM(eligible), + const std::string &AXOM_UNUSED_PARAM(shape), + const axom::ArrayView &AXOM_UNUSED_PARAM(connView), + const axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), + const axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), + FuncType &&AXOM_UNUSED_PARAM(func)) + { + } + + /*! + * \brief Execute method that gets generated when a shape is not enabled or supported. Do nothing. + */ + static void execute4(bool &AXOM_UNUSED_PARAM(eligible), + const std::string &AXOM_UNUSED_PARAM(shape), + const conduit::Node &AXOM_UNUSED_PARAM(topo), + FuncType &&AXOM_UNUSED_PARAM(func)) + { + } +}; + +// Partial specializations that make views for various shape types. + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "tri") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "quad") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "tet") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "pyramid") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "wedge") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +template +struct dispatch_shape, FuncType> +{ + static void execute(bool &eligible, + const std::string &shape, + const axom::ArrayView &connView, + const axom::ArrayView &sizesView, + const axom::ArrayView &offsetsView, + FuncType &&func) + { + if(eligible && shape == "hex") + { + UnstructuredTopologySingleShapeView> ugView( + connView, + sizesView, + offsetsView); + func(shape, ugView); + eligible = false; + } + } +}; + +struct SelectMixedShape {}; + +template +struct dispatch_shape +{ + static void execute4(bool &eligible, + const std::string &shape, + const conduit::Node &topo, + FuncType &&func) + { + if(eligible && shape == "mixed") + { + typed_dispatch_unstructured_mixed_topology(topo, std::forward(func)); + eligible = false; + } + } +}; + +struct SelectPHShape {}; + +template +struct dispatch_shape +{ + static void execute4(bool &eligible, + const std::string &shape, + const conduit::Node &topo, + FuncType &&func) + { + if(eligible && shape == "polyhedral") + { + typed_dispatch_unstructured_polyhedral_topology(topo, std::forward(func)); + eligible = false; + } + } +}; + +} // end namespace internal +//------------------------------------------------------------------------------ + /*! * \brief This function dispatches a Conduit topology to the right view type * and passes that view to the supplied function/lambda. @@ -211,27 +447,10 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, bool eligible = true; // Conditionally add polyhedron support. - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Polyhedron_ShapeID)) - { - if(shape == "polyhedral") - { - typed_dispatch_unstructured_polyhedral_topology(topo, func); - eligible = false; - } - } - - // TODO: add polygon - - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Mixed_ShapeID)) - { - if(eligible && shape == "mixed") - { - typed_dispatch_unstructured_mixed_topology(topo, func); - eligible = false; - } - } + internal::dispatch_shape::execute4(eligible, shape, topo, std::forward(func)); - // TODO: points, lines + // Conditionally add mixed shape support. + internal::dispatch_shape::execute4(eligible, shape, topo, std::forward(func)); // Make sizes / offsets views if the values are present. axom::ArrayView sizesView, offsetsView; @@ -242,78 +461,15 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, offsetsView = bputils::make_array_view( topo.fetch_existing("elements/offsets")); - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tri_ShapeID)) - { - if(eligible && shape == "tri") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Quad_ShapeID)) - { - if(eligible && shape == "quad") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Tet_ShapeID)) - { - if(eligible && shape == "tet") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Pyramid_ShapeID)) - { - if(eligible && shape == "pyramid") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Wedge_ShapeID)) - { - if(eligible && shape == "wedge") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } - if constexpr(axom::utilities::bitIsSet(ShapeTypes, Hex_ShapeID)) - { - if(eligible && shape == "hex") - { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); - func(shape, ugView); - eligible = false; - } - } + // Conditionally add support for other shapes. + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + + // TODO: points, lines, polygon } } From 4f8e7511e77fed9996716593e2768b6de4be824c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 27 Sep 2024 16:18:39 -0700 Subject: [PATCH 243/290] Added getAllocatorIDForAddress --- src/axom/core/memory_management.hpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/axom/core/memory_management.hpp b/src/axom/core/memory_management.hpp index 7edbd51304..2be866114e 100644 --- a/src/axom/core/memory_management.hpp +++ b/src/axom/core/memory_management.hpp @@ -110,6 +110,34 @@ inline int getDefaultAllocatorID() #endif } +/*! + * \brief Returns the ID of the allocator that allocated the memory pointed + * to by \a ptr. + * \param ptr A pointer to memory. + * \return ID of the allocator that allocated the memory. + */ +/// &{ +inline int getAllocatorIDForAddress(void *ptr) +{ +#ifdef AXOM_USE_UMPIRE + umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); + return rm.getAllocator(ptr).getId(); +#else + return axom::getDefaultAllocatorID(); +#endif +} + +inline int getAllocatorIDForAddress(const void *ptr) +{ +#ifdef AXOM_USE_UMPIRE + umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); + return rm.getAllocator(const_cast(ptr)).getId(); +#else + return axom::getDefaultAllocatorID(); +#endif +} +/// @} + /*! * \brief Allocates a chunk of memory of type T. * From c216e1db63d10b5beffec68ace786404f3b0e724 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 27 Sep 2024 16:19:05 -0700 Subject: [PATCH 244/290] Added StackArray::data method. --- src/axom/core/StackArray.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/axom/core/StackArray.hpp b/src/axom/core/StackArray.hpp index 41136f6f2d..209d0aa939 100644 --- a/src/axom/core/StackArray.hpp +++ b/src/axom/core/StackArray.hpp @@ -65,8 +65,12 @@ struct StackArray AXOM_HOST_DEVICE constexpr operator const T*() const noexcept { return &m_data[0]; } + AXOM_HOST_DEVICE T *data() noexcept { return &m_data[0]; } + AXOM_HOST_DEVICE const T *data() const noexcept { return &m_data[0]; } + /// @} + /*! * \brief Begin/end iterators */ From 95517c9de7dd0f8addbbe11c0a3d48130cf209ac Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 27 Sep 2024 17:03:15 -0700 Subject: [PATCH 245/290] nvcc compatibility changes --- src/axom/mir/CMakeLists.txt | 1 + src/axom/mir/ClipField.hpp | 35 ++++- src/axom/mir/EquiZAlgorithm.hpp | 46 +------ src/axom/mir/ExtractZones.hpp | 73 ++++++++++- src/axom/mir/MakeUnstructured.hpp | 28 +++- src/axom/mir/MeshTester.cpp | 25 +++- src/axom/mir/NodeToZoneRelationBuilder.hpp | 29 ++++- src/axom/mir/ZoneListBuilder.hpp | 75 +++++++++++ src/axom/mir/tests/mir_clipfield.cpp | 14 +- src/axom/mir/tests/mir_equiz.cpp | 6 +- src/axom/mir/tests/mir_views.cpp | 91 ++++++++++++- src/axom/mir/views/ExplicitCoordsetView.hpp | 9 +- src/axom/mir/views/Shapes.hpp | 122 +++++++++++++++--- .../UnstructuredTopologyMixedShapeView.hpp | 6 +- .../UnstructuredTopologyPolyhedralView.hpp | 23 ++++ 15 files changed, 492 insertions(+), 91 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index bd1e2fa730..d61e3040bd 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -96,6 +96,7 @@ set(mir_sources clipping/ClipCasesTet.cpp clipping/ClipCasesTri.cpp clipping/ClipCasesWdg.cpp + views/UnstructuredTopologyMixedShapeView.cpp views/MaterialView.cpp utilities.cpp ) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 00100a6d32..bf9672827b 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -898,9 +898,18 @@ class ClipField AXOM_LAMBDA(axom::IndexType index) { nodeData.m_nodeUsedView[index] = 0; }); const auto deviceIntersector = m_intersector.view(); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + const auto selectedZonesView = selectedZones.view(); + axom::for_all(selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); +#else m_topologyView.template for_selected_zones( selectedZones.view(), AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { +#endif // Get the clip case for the current zone. const auto clipcase = deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); @@ -1176,9 +1185,18 @@ class ClipField const auto selection = getSelection(opts); const auto deviceIntersector = m_intersector.view(); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + const auto selectedZonesView = selectedZones.view(); + axom::for_all(selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); +#else m_topologyView.template for_selected_zones( selectedZones.view(), AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { +#endif // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -1393,11 +1411,20 @@ class ClipField { AXOM_ANNOTATE_SCOPE("build"); const auto origSize = nodeData.m_originalIdsView.size(); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + const auto selectedZonesView = selectedZones.view(); + axom::for_all(selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); +#else m_topologyView.template for_selected_zones( selectedZones.view(), AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ZoneType &zone) { +#endif // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; @@ -1621,11 +1648,13 @@ class ClipField { AXOM_ANNOTATE_SCOPE("shapesUsed"); RAJA::ReduceBitOr shapesUsed_reduce(0); + const axom::IndexType nShapes = shapesView.size(); axom::for_all( - shapesView.size(), + nShapes, AXOM_LAMBDA(axom::IndexType index) { - BitSet shapeBit {}; - axom::utilities::setBitOn(shapeBit, shapesView[index]); +// BitSet shapeBit {}; +// axom::utilities::setBitOn(shapeBit, shapesView[index]); + BitSet shapeBit = 1 << shapesView[index]; shapesUsed_reduce |= shapeBit; }); shapesUsed = shapesUsed_reduce.get(); diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 39da4d019a..42de23e338 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -34,11 +34,6 @@ // Uncomment to save inputs and outputs. // #define AXOM_EQUIZ_DEBUG -// This enables a tweak to the algorithm that tries to skip the first iteration -// by incorporating the first material's ids into the zonalMaterialID field. It -// could be faster but it might not be as robust. -// #define AXOM_EQUIZ_SKIP_FIRST_ITERATION - #if defined(AXOM_EQUIZ_DEBUG) #include #endif @@ -750,11 +745,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm // Iterate over mixed materials. // //-------------------------------------------------------------------------- -#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) - constexpr int first = 1; -#else constexpr int first = 0; -#endif for(size_t i = first; i < mixedMats.size(); i++) { if(i == first) @@ -1042,13 +1033,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm void makeWorkingFields(const conduit::Node &n_topo, conduit::Node &n_fields, const axom::mir::views::MaterialInformation &cleanMats, -#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) - const axom::mir::views::MaterialInformation &mixedMats -#else - const axom::mir::views::MaterialInformation - &AXOM_UNUSED_PARAM(mixedMats) -#endif - ) const + const axom::mir::views::MaterialInformation &AXOM_UNUSED_PARAM(mixedMats)) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("makeWorkingFields"); @@ -1091,35 +1076,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm } }); } -#if defined(AXOM_EQUIZ_SKIP_FIRST_ITERATION) - // Fill in the mixed zones for the first mixed material. - if(!mixedMats.empty()) - { - const int matNumber = mixedMats[0].number; - const std::string matFieldName = nodalFieldName(matNumber); - auto matVFView = bputils::make_array_view( - n_fields.fetch_existing(matFieldName + "/values")); - - // Fill in any zone that has nodes where the nodal matVF is greater than zero. - // This fuzzes it out to more zones so we get better blending with the next - // material we try to overlay. - using ZoneType = typename TopologyView::ShapeType; - m_topologyView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - constexpr MaterialVF VOLUME_FRACTION_CUTOFF = 1.e-6; - MaterialVF matvfSum {}; - for(const auto nid : zone.getIds()) - { - matvfSum += matVFView[nid]; - } - // Overwrite the existing material. - if(matvfSum > VOLUME_FRACTION_CUTOFF) - { - zonalIDFieldView[zoneIndex] = matNumber; - } - }); - } -#endif } /*! diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index b524825fb1..239a1e4449 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -253,11 +253,21 @@ class ExtractZones // Figure out the topology size based on selected zones. RAJA::ReduceSum connsize_reduce(0); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + connsize_reduce += zone.numberOfNodes(); + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ZoneType &zone) { connsize_reduce += zone.numberOfNodes(); }); +#endif const auto newConnSize = connsize_reduce.get(); Sizes sizes {}; @@ -291,7 +301,7 @@ class ExtractZones * \return A Sizes object that contains the size of the nodes,zones,connectivity * (excluding extra) for the output mesh. */ - Sizes compactNodeMap(const SelectedZonesView &selectedZonesView, + Sizes compactNodeMap(const SelectedZonesView selectedZonesView, const Sizes &extra, axom::Array &old2new, axom::Array &nodeSlice) const @@ -307,6 +317,21 @@ class ExtractZones // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. RAJA::ReduceSum connsize_reduce(0); +#if 1 + TopologyView deviceTopologyView(m_topologyView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + const axom::IndexType nids = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nids; i++) + { + const auto nodeId = zone.getId(i); + maskView[nodeId] = 1; + } + connsize_reduce += nids; + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), @@ -320,6 +345,7 @@ class ExtractZones } connsize_reduce += nids; }); +#endif const auto newConnSize = connsize_reduce.get(); // Count the used nodes. @@ -376,7 +402,7 @@ class ExtractZones * \param n_topo The input topology. * \param n_newTopo A node to contain the new topology. */ - void makeTopology(const SelectedZonesView &selectedZonesView, + void makeTopology(const SelectedZonesView selectedZonesView, const Sizes &dataSizes, const Sizes &extra, const axom::ArrayView &old2newView, @@ -421,6 +447,15 @@ class ExtractZones auto offsetsView = bputils::make_array_view(n_offsets); // Fill sizes, offsets +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + sizesView[szIndex] = zone.numberOfNodes(); + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType szIndex, @@ -428,6 +463,7 @@ class ExtractZones const ZoneType &zone) { sizesView[szIndex] = zone.numberOfNodes(); }); +#endif if(extra.zones > 0) { axom::for_all( @@ -441,6 +477,23 @@ class ExtractZones if(compact(n_options)) { const axom::ArrayView deviceOld2NewView(old2newView); +#if 1 + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + const auto oldNodeId = zone.getId(i); + // When compact, we map node ids to the compact node ids. + const auto newNodeId = deviceOld2NewView[oldNodeId]; + connView[offset + i] = newNodeId; + } + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType szIndex, @@ -456,9 +509,24 @@ class ExtractZones connView[offset + i] = newNodeId; } }); +#endif } else { +#if 1 + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + connView[offset + i] = zone.getId(i); + } + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType szIndex, @@ -471,6 +539,7 @@ class ExtractZones connView[offset + i] = zone.getId(i); } }); +#endif } if(extra.connectivity > 0) { diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp index 4200065350..fac655e1e2 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/MakeUnstructured.hpp @@ -34,10 +34,10 @@ class MakeUnstructured * * \tparam ExecSpace The execution space where the work will be done. * - * \param topo The input topology to be turned into unstructured. - * \param coordset The topology's coordset. It will be referenced as an external node in the output \a mesh. - * \param topoName The name of the new topology to create. - * \param mesh The node that will contain the new topology and coordset. + * \param topo The input topology to be turned into unstructured. + * \param coordset The topology's coordset. It will be referenced as an external node in the output \a mesh. + * \param topoName The name of the new topology to create. + * \param[out] mesh The node that will contain the new topology and coordset. * * \note There are blueprint methods for this sort of thing but this one runs on device. */ @@ -113,11 +113,28 @@ class MakeUnstructured * \param offsetsView The view that will contain the new offsets. */ template - static void makeUnstructured(int ptsPerZone, const TopologyView &topoView, + static void makeUnstructured(int ptsPerZone, + TopologyView topoView, axom::ArrayView connView, axom::ArrayView sizesView, axom::ArrayView offsetsView) { +#if 1 + // Fill in the new connectivity. + axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = topoView.zone(zoneIndex); + + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < ptsPerZone; i++) + { + connView[start + i] = + static_cast(zone.getId(i)); + } + sizesView[zoneIndex] = ptsPerZone; + offsetsView[zoneIndex] = start; + }); +#else // Fill in the new connectivity. using ZoneType = typename TopologyView::ShapeType; topoView.template for_all_zones( @@ -131,6 +148,7 @@ class MakeUnstructured sizesView[zoneIndex] = ptsPerZone; offsetsView[zoneIndex] = start; }); +#endif } }; diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 22161b7258..50c21bd449 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -654,6 +654,22 @@ static void addCircleMaterial(const TopoView& topoView, typename CoordsetView::PointType center; center[0] = circleCenter[0]; center[1] = circleCenter[1]; +#if 1 + const TopoView deviceTopologyView(topoView); + axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = deviceTopologyView.zone(zoneIndex); + auto vf = calculatePercentOverlapMonteCarlo(numSamples, + center, + circleRadius, + coordsetView[zone.getId(0)], + coordsetView[zone.getId(1)], + coordsetView[zone.getId(2)], + coordsetView[zone.getId(3)]); + greenView[zoneIndex] = vf; + blueView[zoneIndex] = 1.0 - vf; + }); +#else topoView.template for_all_zones( AXOM_LAMBDA(auto zoneIndex, const auto& zone) { auto vf = calculatePercentOverlapMonteCarlo(numSamples, @@ -666,7 +682,7 @@ static void addCircleMaterial(const TopoView& topoView, greenView[zoneIndex] = vf; blueView[zoneIndex] = 1.0 - vf; }); - +#endif // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; std::vector volume_fractions; @@ -1220,8 +1236,15 @@ void addConcentricCircleMaterial(const TopoView& topoView, // Use the uniform sampling method to generate volume fractions for each material // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation +#if 1 + const TopoView deviceTopologyView(topoView); + axom::for_all(topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType eID) { + const auto zone = deviceTopologyView.zone(eID); +#else topoView.template for_all_zones( AXOM_LAMBDA(auto eID, const auto& zone) { +#endif auto v0 = coordsetView[zone.getId(0)]; auto v1 = coordsetView[zone.getId(1)]; auto v2 = coordsetView[zone.getId(2)]; diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index a9f4b9a1c0..baa5d485e9 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -331,14 +331,25 @@ class NodeToZoneRelationBuilder auto sizes_view = sizes.view(); // Run through the topology once to do a count of each zone's unique node ids. - using ZoneType = typename decltype(topoView)::ShapeType; RAJA::ReduceSum count(0); +#if 1 + const PHView deviceTopologyView(topoView); + axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = deviceTopologyView.zone(zoneIndex); + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); +#else + using ZoneType = typename decltype(topoView)::ShapeType; topoView.template for_all_zones( AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { const auto uniqueIds = zone.getUniqueIds(); sizes_view[zoneIndex] = uniqueIds.size(); count += uniqueIds.size(); }); +#endif const auto connSize = count.get(); // Do a scan on the size array to build an offset array. @@ -393,6 +404,21 @@ class NodeToZoneRelationBuilder void fillZonesPH(const TopologyView &topoView, IntegerView connectivityView, IntegerView zonesView, OffsetsView offsets_view) const { // Run through the data one more time to build the nodes and zones arrays. +#if 1 + const TopologyView deviceTopologyView(topoView); + axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = deviceTopologyView.zone(zoneIndex); + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); + i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); +#else using ZoneType = typename TopologyView::ShapeType; topoView.template for_all_zones( AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { @@ -405,6 +431,7 @@ class NodeToZoneRelationBuilder zonesView[destIdx] = zoneIndex; } }); +#endif } /*! diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index 278652d497..a13141fee8 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -76,6 +76,22 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = deviceTopologyView.zone(zoneIndex); + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); +#else m_topologyView.template for_all_zones( AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); @@ -88,6 +104,7 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); +#endif AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all zones that have 1 mat per node as clean. @@ -96,6 +113,24 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); +#if 1 + axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + const auto zone = deviceTopologyView.zone(zoneIndex); + + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[zoneIndex] = ival; + mask_reduce += ival; + }); +#else m_topologyView.template for_all_zones( AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { bool clean = true; @@ -110,6 +145,7 @@ class ZoneListBuilder maskView[zoneIndex] = ival; mask_reduce += ival; }); +#endif AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); @@ -206,6 +242,24 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); +#if 1 + const TopologyView deviceTopologyView(m_topologyView); + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), axom::IndexType zoneIndex, const ZoneType &zone) { @@ -219,6 +273,7 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); +#endif AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all selected zones that have 1 mat per node as clean. @@ -227,6 +282,25 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); +#if 1 + axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) + { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[szIndex] = ival; + mask_reduce += ival; + }); +#else m_topologyView.template for_selected_zones( selectedZonesView, AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ZoneType &zone) { @@ -242,6 +316,7 @@ class ZoneListBuilder maskView[szIndex] = ival; mask_reduce += ival; }); +#endif AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 02749afb7f..516643a3be 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -602,13 +602,18 @@ void braid2d_clip_test(const std::string &type, const std::string &name) const auto offsetsView = bputils::make_array_view( n_device_topo.fetch_existing("elements/offsets")); + // Make the shape map. + axom::Array values, ids; + auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, axom::execution_space::allocatorID()); + using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; MixedTopoView mixedTopoView(n_device_topo, connView, shapesView, sizesView, - offsetsView); + offsetsView, + shapeMap); // Clip the data axom::mir::clipping::ClipField @@ -996,7 +1001,12 @@ void braid3d_mixed_clip_test(const std::string &name) axom::ArrayView offsetsView( static_cast(n_offsets.data_ptr()), n_offsets.dtype().number_of_elements()); - TopoView topoView(n_device_topo, connView, shapesView, sizesView, offsetsView); + + // Make the shape map. + axom::Array values, ids; + auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, axom::execution_space::allocatorID()); + + TopoView topoView(n_device_topo, connView, shapesView, sizesView, offsetsView, shapeMap); // Create options to control the clipping. conduit::Node options; diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 1592274b4e..ad55960e2a 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -12,13 +12,13 @@ //------------------------------------------------------------------------------ -#define DEBUGGING_TEST_CASES +//#define DEBUGGING_TEST_CASES // Uncomment to generate baselines -#define AXOM_TESTING_GENERATE_BASELINES +//#define AXOM_TESTING_GENERATE_BASELINES // Uncomment to save visualization files for debugging (when making baselines) -#define AXOM_TESTING_SAVE_VISUALIZATION +//#define AXOM_TESTING_SAVE_VISUALIZATION #include "axom/mir/tests/mir_testing_helpers.hpp" diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index fd3920736e..4c66e8136c 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -16,6 +16,8 @@ namespace bputils = axom::mir::utilities::blueprint; //------------------------------------------------------------------------------ +#define DEBUGGING_TEST_CASES + // Uncomment to generate baselines //#define AXOM_TESTING_GENERATE_BASELINES @@ -172,6 +174,80 @@ TEST(mir_views, explicit_coordsetview) } } +//------------------------------------------------------------------------------ +template +struct test_structured_topology_view_rectilinear +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Make results view on device. + constexpr int nzones = 9; + axom::Array results(nzones, nzones, axom::execution_space::allocatorID()); + auto resultsView = results.view(); + + // Execute the kernel for each zone (find max node number in zone). + auto topoView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); + using ZoneType = typename decltype(topoView)::ShapeType; + topoView.template for_all_zones(AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) + { + axom::IndexType m = -1; + for(const auto &id : zone.getIds()) + { + m = axom::utilities::max(static_cast(id), m); + } + resultsView[zoneIndex] = m; + }); + + // device->host + axom::Array hostResults(nzones, nzones, axom::execution_space::allocatorID()); + axom::copy(hostResults.data(), results.data(), nzones * sizeof(axom::IndexType)); + + // Compare. + const axom::IndexType expected[] = {5, 6, 7, 9, 10, 11, 13, 14, 15}; + for(int i = 0; i < nzones; i++) + { + EXPECT_EQ(hostResults[i], expected[i]); + } + } + + static void create(conduit::Node &mesh) + { + std::vector dims{4,4}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + } +}; + + +TEST(mir_views, stopo_rectilinear_2d_seq) +{ + test_structured_topology_view_rectilinear::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_views, stopo_rectilinear_2d_omp) +{ + test_structured_topology_view_rectilinear::test(); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_views, stopo_rectilinear_2d_cuda) +{ + test_structured_topology_view_rectilinear::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_views, stopo_rectilinear_2d_hip) +{ + test_structured_topology_view_rectilinear::test(); +} +#endif + //------------------------------------------------------------------------------ struct test_strided_structured { @@ -395,14 +471,25 @@ TEST(mir_views, matset_unibuffer_hip) #endif //------------------------------------------------------------------------------ - +#if defined(DEBUGGING_TEST_CASES) +void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) +{ + std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; + // This is on purpose. + while(1) + ; +} +#endif +//------------------------------------------------------------------------------ int main(int argc, char *argv[]) { int result = 0; ::testing::InitGoogleTest(&argc, argv); axom::slic::SimpleLogger logger; // create & initialize test logger, - +#if defined(DEBUGGING_TEST_CASES) + conduit::utils::set_error_handler(conduit_debug_err_handler); +#endif result = RUN_ALL_TESTS(); return result; } diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index 378df87c07..d0a1440b3e 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -49,7 +49,7 @@ class ExplicitCoordsetView { #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(x.size() == y.size(), "Coordinate size mismatch."); + assert(x.size() == y.size()); #else SLIC_ASSERT_MSG(x.size() == y.size(), "Coordinate size mismatch."); #endif @@ -81,7 +81,7 @@ class ExplicitCoordsetView { #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(vertex_index < size(), "Out of range index."); + assert(vertex_index < size()); #else SLIC_ASSERT_MSG( vertex_index < size(), @@ -138,8 +138,7 @@ class ExplicitCoordsetView { #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(x.size() == y.size() && x.size() == z.size(), - "Coordinate size mismatch."); + assert(x.size() == y.size() && x.size() == z.size()); #else SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), "Coordinate size mismatch."); @@ -172,7 +171,7 @@ class ExplicitCoordsetView { #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(vertex_index < size(), "Out of range index."); + assert(vertex_index < size()); #else SLIC_ASSERT_MSG( vertex_index < size(), diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 5852c2e01f..0be080522f 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -20,8 +20,10 @@ namespace mir { namespace views { -// Shape ids. These are used to identify shapes. These are used as indices -// in bit fields in some algorithms. +/*! + * \brief Shape ids. These are used to identify shapes. These are used as + * indices in bit fields in some algorithms. + */ enum { Point_ShapeID = 0, @@ -39,12 +41,56 @@ enum Invalid_ShapeID = 20 }; -// TODO: PointTraits +/*! + \brief Point type traits. + +\verbatim + + 0* + +\endverbatim + */ +struct PointTraits +{ + AXOM_HOST_DEVICE constexpr static int id() { return Point_ShapeID; } + AXOM_HOST_DEVICE constexpr static bool is_polyhedral() { return false; } + AXOM_HOST_DEVICE constexpr static bool is_variable_size() { return false; } + + AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 0; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 1; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + { + return 1; + } + AXOM_HOST_DEVICE constexpr static IndexType maxNodesInFace() { return 1; } + + AXOM_HOST_DEVICE constexpr static IndexType numberOfFaces() { return 0; } + AXOM_HOST_DEVICE constexpr static IndexType numberOfEdges() { return 0; } + AXOM_HOST_DEVICE constexpr static IndexType zoneOffset(int zoneIndex) + { + return zoneIndex; + } + + constexpr static IndexType faces[][1] = {{0}}; + + AXOM_HOST_DEVICE constexpr static axom::StackArray getEdge( + int AXOM_UNUSED_PARAM(edgeIndex)) + { + return axom::StackArray(); + } + + AXOM_HOST_DEVICE constexpr static const char *name() { return "point"; } +}; + +/*! + \brief Line type traits. -/* +\verbatim 0*-----------* 1 +\endverbatim */ struct LineTraits { @@ -55,7 +101,7 @@ struct LineTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 2; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) { return 2; } @@ -79,7 +125,11 @@ struct LineTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "line"; } }; -/* +/*! + \brief Triangle type traits. + +\verbatim + 2* |\ | \ @@ -88,6 +138,7 @@ struct LineTraits | \ 0*-----* 1 +\endverbatim */ struct TriTraits { @@ -98,7 +149,7 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 3; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) { return 3; } @@ -123,7 +174,11 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "tri"; } }; -/* +/*! + \brief Quad type traits. + +\verbatim + 3*-----------* 2 | | | | @@ -132,6 +187,7 @@ struct TriTraits | | 0*-----------* 1 +\endverbatim */ struct QuadTraits { @@ -142,7 +198,7 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) { return 4; } @@ -167,7 +223,11 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "quad"; } }; -/* +/*! + \brief Tet type traits. + +\verbatim + 3 * /|\ face 0: 0,2,1 @@ -182,6 +242,7 @@ struct QuadTraits * edge 4: 1,3 1 edge 5: 2,3 +\endverbatim */ struct TetTraits { @@ -192,7 +253,7 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) { return 3; } @@ -221,7 +282,10 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "tet"; } }; -/* +/*! + \brief Pyramid type traits. + +\verbatim 3*-----------* 2 face 0: 3,2,1,0 |\ /| face 1: 0,1,4 @@ -238,6 +302,7 @@ struct TetTraits 0*-----------* 1 edge 6: 2,4 edge 7: 3,4 +\endverbatim */ struct PyramidTraits { @@ -278,7 +343,10 @@ struct PyramidTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "pyramid"; } }; -/* +/*! + \brief Wedge type traits. + +\verbatim 3*---------* 5 face 0: 0,2,1 |\ /| face 1: 3,4,5 @@ -296,6 +364,7 @@ struct PyramidTraits edge 7: 1,4 edge 8: 2,3 +\endverbatim */ struct WedgeTraits { @@ -337,7 +406,11 @@ struct WedgeTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "wedge"; } }; -/* +/*! + \brief Hex type traits. + +\verbatim + 4*------------* 7 /| /| / | / | @@ -352,6 +425,7 @@ struct WedgeTraits *------------* 1 2 +\endverbatim */ struct HexTraits { @@ -363,7 +437,7 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 8; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int /*faceIndex*/) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) { return 4; } @@ -404,13 +478,16 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "hex"; } }; -/* - +/*! + +\verbatim + n-1 *-... * 2 | | | | 0 *-----* 1 +\endverbatim */ struct PolygonTraits { @@ -424,6 +501,9 @@ struct PolygonTraits AXOM_HOST_DEVICE constexpr static const char *name() { return "polygon"; } }; +/*! + * \brief This struct represents a polygon zone. + */ template struct PolygonShape : public PolygonTraits { @@ -450,7 +530,7 @@ struct PolygonShape : public PolygonTraits * * \return An array view (wrapping m_faceIds) that contains the ids for the face. */ - AXOM_HOST_DEVICE ConnectivityView getFace(int /*faceIndex*/) const + AXOM_HOST_DEVICE ConnectivityView getFace(int AXOM_UNUSED_PARAM(faceIndex)) const { return m_ids; } @@ -880,7 +960,7 @@ struct VariableShape */ inline int shapeNameToID(const std::string &name) { - int id = 0; + int id = Invalid_ShapeID; if(name == LineTraits::name()) id = Line_ShapeID; else if(name == TriTraits::name()) @@ -897,6 +977,10 @@ inline int shapeNameToID(const std::string &name) id = Wedge_ShapeID; else if(name == HexTraits::name()) id = Hex_ShapeID; + else if(name == "polyhedral") + id = Polyhedron_ShapeID; + else if(name == "mixed") + id = Mixed_ShapeID; return id; } diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 7b21e94389..90ee676721 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -166,7 +166,7 @@ class UnstructuredTopologyMixedShapeView * * \return The requested zone. */ - ShapeType zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE ShapeType zone(axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) @@ -181,9 +181,9 @@ class UnstructuredTopologyMixedShapeView const auto shapeID = m_shapeMap[m_shapes[zoneIndex]]; #if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) - assert(shapeID > 0); + assert(shapeID >= Point_ShapeID && shapeID <= Mixed_ShapeID); #else - SLIC_ASSERT(shapeID > 0); + SLIC_ASSERT(shapeID >= Point_ShapeID && shapeID <= Mixed_ShapeID); #endif #endif diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 4be9c6546d..9d3a3acec4 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -188,6 +188,7 @@ class UnstructuredTopologyPolyhedralView /*! * \brief Constructor. */ + AXOM_HOST_DEVICE UnstructuredTopologyPolyhedralView(const ConnectivityView &subelement_conn, const ConnectivityView &subelement_sizes, const ConnectivityView &subelement_offsets, @@ -207,6 +208,7 @@ class UnstructuredTopologyPolyhedralView * * \return The number of zones. */ + AXOM_HOST_DEVICE IndexType numberOfZones() const { return m_data.m_element_sizes.size(); } /*! @@ -214,6 +216,7 @@ class UnstructuredTopologyPolyhedralView * * \return The size of the connectivity. */ + AXOM_HOST_DEVICE IndexType connectivitySize() const { return m_data.element_conn.size(); } /*! @@ -223,6 +226,26 @@ class UnstructuredTopologyPolyhedralView */ AXOM_HOST_DEVICE static constexpr int dimension() { return 3; } + /*! + * \brief Return a zone. + * + * \param zoneIndex The requested zone. + * + * \return The requested zone. + */ + AXOM_HOST_DEVICE ShapeType zone(axom::IndexType zoneIndex) const + { +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) + assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif +#endif + + return ShapeType(m_data, zoneIndex); + } + /*! * \brief Execute a function for each zone in the mesh. * From 32e5f5e220a5d68b52c09703eee45d9ad2eb0fe1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 15:12:06 -0700 Subject: [PATCH 246/290] Removed for_all_zones. --- src/axom/mir/ClipField.hpp | 40 +++-- src/axom/mir/blueprint_utilities.hpp | 140 ++++++++++-------- .../mir/examples/mir_concentric_circles.cpp | 1 - src/axom/mir/tests/mir_clipfield.cpp | 24 +-- src/axom/mir/tests/mir_equiz.cpp | 2 +- src/axom/mir/tests/mir_testing_helpers.hpp | 4 +- src/axom/mir/tests/mir_views.cpp | 5 +- .../views/dispatch_structured_topology.hpp | 44 ++---- src/axom/mir/views/view_traits.hpp | 3 + 9 files changed, 137 insertions(+), 126 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index bf9672827b..27d0cc8159 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1642,23 +1642,8 @@ class ClipField } } #endif - // Figure out which shapes were used. - BitSet shapesUsed {}; - { - AXOM_ANNOTATE_SCOPE("shapesUsed"); - RAJA::ReduceBitOr shapesUsed_reduce(0); - const axom::IndexType nShapes = shapesView.size(); - axom::for_all( - nShapes, - AXOM_LAMBDA(axom::IndexType index) { -// BitSet shapeBit {}; -// axom::utilities::setBitOn(shapeBit, shapesView[index]); - BitSet shapeBit = 1 << shapesView[index]; - shapesUsed_reduce |= shapeBit; - }); - shapesUsed = shapesUsed_reduce.get(); - } + BitSet shapesUsed = findUsedShapes(shapesView); #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -1747,6 +1732,29 @@ class ClipField } } + /*! + * \brief Find the shapes that were used. + * + * \param shapesView The view that contains the shapes. + * + * \return A BitSet where bits are marked for each shape used. + */ + BitSet findUsedShapes(axom::ArrayView shapesView) const + { + AXOM_ANNOTATE_SCOPE("findUsedShapes"); + + RAJA::ReduceBitOr shapesUsed_reduce(0); + const axom::IndexType nShapes = shapesView.size(); + axom::for_all( + nShapes, + AXOM_LAMBDA(axom::IndexType index) { + BitSet shapeBit = 1 << shapesView[index]; + shapesUsed_reduce |= shapeBit; + }); + BitSet shapesUsed = shapesUsed_reduce.get(); + return shapesUsed; + } + #if defined(AXOM_CLIP_FILTER_DEGENERATES) /*! * \brief Replace data in the input Conduit node with a denser version using the mask. diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 7ab89a267b..e31a01bf63 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -11,7 +11,6 @@ #include "axom/core/ArrayView.hpp" #include "axom/core/NumericLimits.hpp" #include "axom/core/memory_management.hpp" -#include "axom/mir/views/dispatch_structured_topology.hpp" #include "axom/mir/views/NodeArrayView.hpp" #include @@ -214,6 +213,58 @@ class ConduitAllocateThroughAxom } }; +//------------------------------------------------------------------------------ +/** + * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, + * making sure to allocate array data in the appropriate memory space for + * the execution space. + * + * \tparam The destination execution space (e.g. axom::SEQ_EXEC). + * + * \param dest The conduit node that will receive the copied data. + * \param src The source data to be copied. + */ +template +void copy(conduit::Node &dest, const conduit::Node &src) +{ + ConduitAllocateThroughAxom c2a; + dest.reset(); + if(src.number_of_children() > 0) + { + for(conduit::index_t i = 0; i < src.number_of_children(); i++) + { + copy(dest[src[i].name()], src[i]); + } + } + else + { + if(!src.dtype().is_string() && src.dtype().number_of_elements() > 1) + { + // Allocate the node's memory in the right place. + dest.reset(); + dest.set_allocator(c2a.getConduitAllocatorID()); + dest.set( + conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); + + // Copy the data to the destination node. Axom uses Umpire to manage that. + if(src.is_compact()) + axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); + else + { + // NOTE: This assumes that src is on the host. + conduit::Node tmp; + src.compact_to(tmp); + axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); + } + } + else + { + // The node data fits in the node. It's on the host. + dest.set(src); + } + } +} + //------------------------------------------------------------------------------ /** * \brief Fill an array with int values from a Conduit node. @@ -223,17 +274,33 @@ class ConduitAllocateThroughAxom * \param n The node that contains the data. * \param key The name of the node that contains the data in \a n. * \param[out] arr The array being filled. + * \param moveToHost Sometimes data are on device and need to be moved to host first. */ template -bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr) +bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr, bool moveToHost = false) { bool found = false; if((found = n.has_path(key)) == true) { - const auto acc = n.fetch_existing(key).as_int_accessor(); - for(int i = 0; i < arr.size(); i++) + if(moveToHost) { - arr[i] = acc[i]; + // Make sure data are on host. + conduit::Node hostNode; + copy(hostNode, n.fetch_existing(key)); + + const auto acc = hostNode.as_int_accessor(); + for(int i = 0; i < arr.size(); i++) + { + arr[i] = acc[i]; + } + } + else + { + const auto acc = n.fetch_existing(key).as_int_accessor(); + for(int i = 0; i < arr.size(); i++) + { + arr[i] = acc[i]; + } } } return found; @@ -268,11 +335,13 @@ struct SSElementFieldIndexing /** * \brief Update the indexing offsets/strides from a Conduit node. * \param field The Conduit node for a field. + * + * \note Executes on the host. */ void update(const conduit::Node &field) { - fillFromNode(field, "offsets", m_indexing.m_offsets); - fillFromNode(field, "strides", m_indexing.m_strides); + fillFromNode(field, "offsets", m_indexing.m_offsets, true); + fillFromNode(field, "strides", m_indexing.m_strides, true); } /** @@ -300,11 +369,13 @@ struct SSVertexFieldIndexing /** * \brief Update the indexing offsets/strides from a Conduit node. * \param field The Conduit node for a field. + * + * \note Executes on the host. */ void update(const conduit::Node &field) { - fillFromNode(field, "offsets", m_fieldIndexing.m_offsets); - fillFromNode(field, "strides", m_fieldIndexing.m_strides); + fillFromNode(field, "offsets", m_fieldIndexing.m_offsets, true); + fillFromNode(field, "strides", m_fieldIndexing.m_strides, true); } /** @@ -332,57 +403,6 @@ struct SSVertexFieldIndexing Indexing m_fieldIndexing {}; }; -/** - * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, - * making sure to allocate array data in the appropriate memory space for - * the execution space. - * - * \tparam The destination execution space (e.g. axom::SEQ_EXEC). - * - * \param dest The conduit node that will receive the copied data. - * \param src The source data to be copied. - */ -template -void copy(conduit::Node &dest, const conduit::Node &src) -{ - ConduitAllocateThroughAxom c2a; - dest.reset(); - if(src.number_of_children() > 0) - { - for(conduit::index_t i = 0; i < src.number_of_children(); i++) - { - copy(dest[src[i].name()], src[i]); - } - } - else - { - if(!src.dtype().is_string() && src.dtype().number_of_elements() > 1) - { - // Allocate the node's memory in the right place. - dest.reset(); - dest.set_allocator(c2a.getConduitAllocatorID()); - dest.set( - conduit::DataType(src.dtype().id(), src.dtype().number_of_elements())); - - // Copy the data to the destination node. Axom uses Umpire to manage that. - if(src.is_compact()) - axom::copy(dest.data_ptr(), src.data_ptr(), src.dtype().bytes_compact()); - else - { - // NOTE: This assumes that src is on the host. - conduit::Node tmp; - src.compact_to(tmp); - axom::copy(dest.data_ptr(), tmp.data_ptr(), tmp.dtype().bytes_compact()); - } - } - else - { - // The node data fits in the node. It's on the host. - dest.set(src); - } - } -} - /*! * \brief Get the min/max values for the data in a Conduit node or ArrayView. */ diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index ef3d833658..1544b1eea4 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -128,7 +128,6 @@ int runMIR(RuntimePolicy policy, options["matset"] = "mat"; int retval = 0; -std::cout << axom::fmt::format("policy={}", policy) << std::endl; if(policy == RuntimePolicy::seq) { #pragma message "SEQ supported" diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 516643a3be..3243d22fdb 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -363,7 +363,7 @@ TEST(mir_clipfield, unique_seq) { test_unique::test(); } #if defined(AXOM_USE_OPENMP) TEST(mir_clipfield, unique_omp) { test_unique::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_clipfield, unique_cuda) { test_unique::test(); } #endif #if defined(AXOM_USE_HIP) @@ -459,7 +459,7 @@ void test_one_shape_exec(const conduit::Node &hostMesh, const std::string &name) test_one_shape(hostMesh, name); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) test_one_shape(hostMesh, name); #endif @@ -666,7 +666,7 @@ TEST(mir_clipfield, uniform2d) braid2d_clip_test("uniform", "uniform2d"); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) braid2d_clip_test("uniform", "uniform2d"); #endif @@ -743,7 +743,7 @@ TEST(mir_clipfield, rectilinear2d) braid_rectilinear_clip_test("rectilinear2d"); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) braid_rectilinear_clip_test("rectilinear2d"); #endif @@ -760,7 +760,7 @@ TEST(mir_clipfield, rectilinear3d) braid_rectilinear_clip_test("rectilinear3d"); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) braid_rectilinear_clip_test("rectilinear3d"); #endif @@ -832,7 +832,7 @@ void strided_structured_clip_test_exec(const std::string &name, strided_structured_clip_test(name, options); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) strided_structured_clip_test(name, options); #endif @@ -928,7 +928,7 @@ void braid3d_clip_test_exec(const std::string &type, const std::string &name) braid3d_clip_test(type, name); #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) braid3d_clip_test(type, name); #endif @@ -1040,7 +1040,7 @@ TEST(mir_clipfield, mixed_seq) { braid3d_mixed_clip_test("mixed"); } #if defined(AXOM_USE_OPENMP) TEST(mir_clipfield, mixed_omp) { braid3d_mixed_clip_test("mixed"); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_clipfield, mixed_cuda) { braid3d_mixed_clip_test("mixed"); } #endif #if defined(AXOM_USE_HIP) @@ -1088,6 +1088,7 @@ struct point_merge_test conduit::Node hostMesh; create(hostMesh); + // host->device conduit::Node deviceMesh; bputils::copy(deviceMesh, hostMesh); @@ -1109,10 +1110,11 @@ struct point_merge_test options["clipField"] = "clip"; options["clipValue"] = 0.5; using Clip = - axom::mir::clipping::ClipField; + axom::mir::clipping::ClipField; Clip clip(topologyView, coordsetView); clip.execute(deviceMesh, options, deviceClipMesh); + // device->host conduit::Node hostClipMesh; bputils::copy(hostClipMesh, deviceClipMesh); //printNode(hostClipMesh); @@ -1152,7 +1154,7 @@ TEST(mir_clipfield, pointmerging_seq) { point_merge_test::test(); } #if defined(AXOM_USE_OPENMP) TEST(mir_clipfield, pointmerging_omp) { point_merge_test::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_clipfield, pointmerging_cuda) { point_merge_test::test(); } #endif #if defined(AXOM_USE_HIP) @@ -1274,7 +1276,7 @@ TEST(mir_clipfield, selectedzones_seq) { test_selectedzones::test(); } #if defined(AXOM_USE_OPENMP) TEST(mir_clipfield, selectedzones_omp) { test_selectedzones::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_clipfield, selectedzones_cuda) { test_selectedzones::test(); diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index ad55960e2a..509139c6e1 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -137,7 +137,7 @@ TEST(mir_equiz, equiz_uniform_unibuffer_omp) } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_equiz, equiz_uniform_unibuffer_cuda) { AXOM_ANNOTATE_SCOPE("equiz_uniform_unibuffer_cuda"); diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index 926e022de9..bed150fbc6 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -24,7 +24,7 @@ using omp_exec = seq_exec; #endif - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + #if defined(AXOM_USE_CUDA) constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; #else @@ -57,7 +57,7 @@ struct execution_name static std::string name() { return "omp"; } }; #endif - #if defined(AXOM_USE_CUDA) && defined(__CUDACC__) + #if defined(AXOM_USE_CUDA) template <> struct execution_name { diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 4c66e8136c..5e472b8c73 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -133,7 +133,7 @@ TEST(mir_views, node_to_arrayview_omp) test_node_to_arrayview::test(); } #endif -#if defined(AXOM_USE_CUDA) && defined(__CUDACC__) +#if defined(AXOM_USE_CUDA) TEST(mir_views, node_to_arrayview_cuda) { test_node_to_arrayview::test(); @@ -195,8 +195,9 @@ struct test_structured_topology_view_rectilinear // Execute the kernel for each zone (find max node number in zone). auto topoView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); using ZoneType = typename decltype(topoView)::ShapeType; - topoView.template for_all_zones(AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) + axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = topoView.zone(zoneIndex); axom::IndexType m = -1; for(const auto &id : zone.getIds()) { diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index 4638b375d3..831b207fc4 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -6,12 +6,14 @@ #ifndef AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ #define AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ +#include #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/StructuredIndexing.hpp" #include "axom/mir/views/StridedStructuredIndexing.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/views/dispatch_uniform_topology.hpp" #include "axom/mir/views/dispatch_rectilinear_topology.hpp" +#include "axom/mir/blueprint_utilities.hpp" #include @@ -23,33 +25,6 @@ namespace mir { namespace views { -//------------------------------------------------------------------------------ -/*! - * \brief Fill an array from a Conduit node, filling the destination array if - * the values do not exist. - * - * \tparam ArrayType The array type to use. - * - * \param n The conduit node that contains the named array, if it exists. - * \param key The name of the node. - * \param[out] The array to be filled. - * \param fillValue the value to use if the array is not found. - */ -template -bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr) -{ - bool found = false; - if((found = n.has_path(key)) == true) - { - const auto acc = n.fetch_existing(key).as_int_accessor(); - for(int i = 0; i < arr.size(); i++) - { - arr[i] = acc[i]; - } - } - - return found; -} /*! * \brief Base template for strided structured topology creation @@ -75,6 +50,7 @@ struct make_strided_structured<3> */ static Indexing indexing(const conduit::Node &topo) { + namespace bputils = axom::mir::utilities::blueprint; const std::string offsetsKey("elements/dims/offsets"); const std::string stridesKey("elements/dims/strides"); @@ -84,8 +60,8 @@ struct make_strided_structured<3> zoneDims[2] = topo.fetch_existing("elements/dims/k").as_int(); LogicalIndex offsets {{0, 0, 0}}, strides {{1, 1, 1}}; - fillFromNode(topo, offsetsKey, offsets); - if(fillFromNode(topo, stridesKey, strides)) + bputils::fillFromNode(topo, offsetsKey, offsets, true); + if(bputils::fillFromNode(topo, stridesKey, strides, true)) { // Make zone striding. strides[1]--; @@ -128,6 +104,7 @@ struct make_strided_structured<2> */ static Indexing indexing(const conduit::Node &topo) { + namespace bputils = axom::mir::utilities::blueprint; const std::string offsetsKey("elements/dims/offsets"); const std::string stridesKey("elements/dims/strides"); LogicalIndex zoneDims; @@ -135,8 +112,8 @@ struct make_strided_structured<2> zoneDims[1] = topo.fetch_existing("elements/dims/j").as_int(); LogicalIndex offsets {{0, 0}}, strides {{1, 1}}; - fillFromNode(topo, offsetsKey, offsets); - if(fillFromNode(topo, stridesKey, strides)) + bputils::fillFromNode(topo, offsetsKey, offsets, true); + if(bputils::fillFromNode(topo, stridesKey, strides, true)) { // Make zone striding. strides[1]--; @@ -177,6 +154,7 @@ struct make_strided_structured<1> */ static Indexing indexing(const conduit::Node &topo) { + namespace bputils = axom::mir::utilities::blueprint; const std::string offsetsKey("elements/dims/offsets"); const std::string stridesKey("elements/dims/strides"); @@ -184,8 +162,8 @@ struct make_strided_structured<1> zoneDims[0] = topo.fetch_existing("elements/dims/i").as_int(); LogicalIndex offsets {0}, strides {1}; - fillFromNode(topo, offsetsKey, offsets); - fillFromNode(topo, stridesKey, strides); + bputils::fillFromNode(topo, offsetsKey, offsets, true); + bputils::fillFromNode(topo, stridesKey, strides, true); return Indexing(zoneDims, offsets, strides); } diff --git a/src/axom/mir/views/view_traits.hpp b/src/axom/mir/views/view_traits.hpp index 7f63505da8..176a9e3ad2 100644 --- a/src/axom/mir/views/view_traits.hpp +++ b/src/axom/mir/views/view_traits.hpp @@ -6,7 +6,10 @@ #ifndef AXOM_MIR_VIEW_TRAITS_HPP_ #define AXOM_MIR_VIEW_TRAITS_HPP_ +#include "axom/core/utilities/BitUtilities.hpp" #include "axom/mir/views/StructuredTopologyView.hpp" +#include "axom/mir/views/StructuredIndexing.hpp" +#include "axom/mir/views/StridedStructuredIndexing.hpp" #include "axom/mir/views/Shapes.hpp" namespace axom From 583b3da1df7439ee72bb13e024a9d3dbfc942bbe Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 16:54:40 -0700 Subject: [PATCH 247/290] Removed for_all_zones from views. --- src/axom/mir/ClipField.hpp | 96 +-------- src/axom/mir/ExtractZones.hpp | 68 +----- src/axom/mir/MakeUnstructured.hpp | 16 -- src/axom/mir/MeshTester.cpp | 23 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 25 --- src/axom/mir/ZoneListBuilder.hpp | 66 ------ src/axom/mir/tests/mir_clipfield.cpp | 5 +- src/axom/mir/tests/mir_views.cpp | 7 +- src/axom/mir/views/StructuredTopologyView.hpp | 198 ++---------------- .../UnstructuredTopologyMixedShapeView.hpp | 103 +-------- .../UnstructuredTopologyPolyhedralView.hpp | 52 +---- .../UnstructuredTopologySingleShapeView.hpp | 118 +---------- .../views/dispatch_unstructured_topology.hpp | 6 +- 13 files changed, 45 insertions(+), 738 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 27d0cc8159..fd8768fba7 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -342,41 +342,6 @@ class FieldIntersector View m_view {}; }; -#if 1 - using BitSet = std::uint32_t; - /*! - * \brief Contains data that describes the number and size of zone fragments in the output. - */ - struct FragmentData - { - IndexType m_finalNumZones {0}; - IndexType m_finalConnSize {0}; - axom::ArrayView m_fragmentsView {}; - axom::ArrayView m_fragmentsSizeView {}; - axom::ArrayView m_fragmentOffsetsView {}; - axom::ArrayView m_fragmentSizeOffsetsView {}; - }; - - /*! - * \brief Contains some per-zone data that we want to hold onto between methods. - */ - struct ZoneData - { - axom::ArrayView m_clipCasesView {}; - axom::ArrayView m_pointsUsedView {}; - }; - - /*! - * \brief Contains some per-node data that we want to hold onto between methods. - */ - struct NodeData - { - axom::ArrayView m_nodeUsedView {}; - axom::ArrayView m_oldNodeToNewNodeView {}; - axom::ArrayView m_originalIdsView {}; - }; -#endif - //------------------------------------------------------------------------------ /*! * \accelerated @@ -402,7 +367,7 @@ class ClipField using ClipTableViews = axom::StackArray; using Intersector = IntersectPolicy; -// using BitSet = std::uint32_t; + using BitSet = std::uint32_t; using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -601,35 +566,7 @@ class ClipField nodeData.m_originalIdsView = compactNodes.view(); nodeData.m_oldNodeToNewNodeView = oldNodeToNewNode.view(); createNodeMaps(nodeData); -#endif -#if 0 - conduit::Node tmpMesh; - tmpMesh[n_coordset.path()].set_external(n_coordset); - tmpMesh[n_topo.path()].set_external(n_topo); - tmpMesh["fields/nodeUsed/topology"] = n_topo.name(); - tmpMesh["fields/nodeUsed/association"] = "vertex"; - tmpMesh["fields/nodeUsed/values"].set_external(nodeData.m_nodeUsedView.data(), nodeData.m_nodeUsedView.size()); - - tmpMesh["fields/oldNodeToNewNode/topology"] = n_topo.name(); - tmpMesh["fields/oldNodeToNewNode/association"] = "vertex"; - tmpMesh["fields/oldNodeToNewNode/values"].set_external(nodeData.m_oldNodeToNewNodeView.data(), nodeData.m_oldNodeToNewNodeView.size()); - - // Make filename - int count = 0; - std::string path; - do - { - std::stringstream ss; - ss << "clipfield." << count; - path = ss.str(); - count++; - } while(axom::utilities::filesystem::pathExists(path + ".root")); - - // Save data. - conduit::relay::io::blueprint::save_mesh(tmpMesh, path, "hdf5"); - tmpMesh.print(); -#endif -#if defined(AXOM_REDUCE_BLEND_GROUPS) + nodeUsed.clear(); nodeData.m_nodeUsedView = axom::ArrayView(); #endif @@ -791,7 +728,7 @@ class ClipField #if !defined(__CUDACC__) private: #endif -#if 0 + /*! * \brief Contains data that describes the number and size of zone fragments in the output. */ @@ -823,7 +760,7 @@ class ClipField axom::ArrayView m_oldNodeToNewNodeView {}; axom::ArrayView m_originalIdsView {}; }; -#endif + /*! * \brief Make a bitset that indicates the parts of the selection that are selected. */ @@ -898,18 +835,14 @@ class ClipField AXOM_LAMBDA(axom::IndexType index) { nodeData.m_nodeUsedView[index] = 0; }); const auto deviceIntersector = m_intersector.view(); -#if 1 + const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); -#else - m_topologyView.template for_selected_zones( - selectedZones.view(), - AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { -#endif + // Get the clip case for the current zone. const auto clipcase = deviceIntersector.determineClipCase(zoneIndex, zone.getIds()); @@ -1185,18 +1118,13 @@ class ClipField const auto selection = getSelection(opts); const auto deviceIntersector = m_intersector.view(); -#if 1 const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); -#else - m_topologyView.template for_selected_zones( - selectedZones.view(), - AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType zoneIndex, const ZoneType &zone) { -#endif + // Get the clip case for the current zone. const auto clipcase = zoneData.m_clipCasesView[szIndex]; @@ -1411,20 +1339,14 @@ class ClipField { AXOM_ANNOTATE_SCOPE("build"); const auto origSize = nodeData.m_originalIdsView.size(); -#if 1 + const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); -#else - m_topologyView.template for_selected_zones( - selectedZones.view(), - AXOM_LAMBDA(axom::IndexType szIndex, - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { -#endif + // If there are no fragments, return from lambda. if(fragmentData.m_fragmentsView[szIndex] == 0) return; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 239a1e4449..e55b5f269e 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -253,7 +253,6 @@ class ExtractZones // Figure out the topology size based on selected zones. RAJA::ReduceSum connsize_reduce(0); -#if 1 const TopologyView deviceTopologyView(m_topologyView); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { @@ -261,13 +260,6 @@ class ExtractZones const auto zone = deviceTopologyView.zone(zoneIndex); connsize_reduce += zone.numberOfNodes(); }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { connsize_reduce += zone.numberOfNodes(); }); -#endif const auto newConnSize = connsize_reduce.get(); Sizes sizes {}; @@ -317,7 +309,6 @@ class ExtractZones // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. RAJA::ReduceSum connsize_reduce(0); -#if 1 TopologyView deviceTopologyView(m_topologyView); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { @@ -331,21 +322,6 @@ class ExtractZones } connsize_reduce += nids; }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { - const axom::IndexType nids = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nids; i++) - { - const auto nodeId = zone.getId(i); - maskView[nodeId] = 1; - } - connsize_reduce += nids; - }); -#endif const auto newConnSize = connsize_reduce.get(); // Count the used nodes. @@ -447,7 +423,6 @@ class ExtractZones auto offsetsView = bputils::make_array_view(n_offsets); // Fill sizes, offsets -#if 1 const TopologyView deviceTopologyView(m_topologyView); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { @@ -455,15 +430,7 @@ class ExtractZones const auto zone = deviceTopologyView.zone(zoneIndex); sizesView[szIndex] = zone.numberOfNodes(); }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType szIndex, - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { - sizesView[szIndex] = zone.numberOfNodes(); - }); -#endif + if(extra.zones > 0) { axom::for_all( @@ -477,7 +444,6 @@ class ExtractZones if(compact(n_options)) { const axom::ArrayView deviceOld2NewView(old2newView); -#if 1 axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; @@ -493,27 +459,9 @@ class ExtractZones connView[offset + i] = newNodeId; } }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType szIndex, - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - const auto oldNodeId = zone.getId(i); - // When compact, we map node ids to the compact node ids. - const auto newNodeId = deviceOld2NewView[oldNodeId]; - connView[offset + i] = newNodeId; - } - }); -#endif } else { -#if 1 axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; @@ -526,20 +474,6 @@ class ExtractZones connView[offset + i] = zone.getId(i); } }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType szIndex, - axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), - const ZoneType &zone) { - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - connView[offset + i] = zone.getId(i); - } - }); -#endif } if(extra.connectivity > 0) { diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp index fac655e1e2..17c5efafb4 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/MakeUnstructured.hpp @@ -119,7 +119,6 @@ class MakeUnstructured axom::ArrayView sizesView, axom::ArrayView offsetsView) { -#if 1 // Fill in the new connectivity. axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { @@ -134,21 +133,6 @@ class MakeUnstructured sizesView[zoneIndex] = ptsPerZone; offsetsView[zoneIndex] = start; }); -#else - // Fill in the new connectivity. - using ZoneType = typename TopologyView::ShapeType; - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < ptsPerZone; i++) - { - connView[start + i] = - static_cast(zone.getId(i)); - } - sizesView[zoneIndex] = ptsPerZone; - offsetsView[zoneIndex] = start; - }); -#endif } }; diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 50c21bd449..1043d5af96 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -654,7 +654,7 @@ static void addCircleMaterial(const TopoView& topoView, typename CoordsetView::PointType center; center[0] = circleCenter[0]; center[1] = circleCenter[1]; -#if 1 + const TopoView deviceTopologyView(topoView); axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { @@ -669,20 +669,7 @@ static void addCircleMaterial(const TopoView& topoView, greenView[zoneIndex] = vf; blueView[zoneIndex] = 1.0 - vf; }); -#else - topoView.template for_all_zones( - AXOM_LAMBDA(auto zoneIndex, const auto& zone) { - auto vf = calculatePercentOverlapMonteCarlo(numSamples, - center, - circleRadius, - coordsetView[zone.getId(0)], - coordsetView[zone.getId(1)], - coordsetView[zone.getId(2)], - coordsetView[zone.getId(3)]); - greenView[zoneIndex] = vf; - blueView[zoneIndex] = 1.0 - vf; - }); -#endif + // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; std::vector volume_fractions; @@ -1236,15 +1223,11 @@ void addConcentricCircleMaterial(const TopoView& topoView, // Use the uniform sampling method to generate volume fractions for each material // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation -#if 1 const TopoView deviceTopologyView(topoView); axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType eID) { const auto zone = deviceTopologyView.zone(eID); -#else - topoView.template for_all_zones( - AXOM_LAMBDA(auto eID, const auto& zone) { -#endif + auto v0 = coordsetView[zone.getId(0)]; auto v1 = coordsetView[zone.getId(1)]; auto v2 = coordsetView[zone.getId(2)]; diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index baa5d485e9..5935cb3b27 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -332,7 +332,6 @@ class NodeToZoneRelationBuilder // Run through the topology once to do a count of each zone's unique node ids. RAJA::ReduceSum count(0); -#if 1 const PHView deviceTopologyView(topoView); axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { @@ -341,15 +340,6 @@ class NodeToZoneRelationBuilder sizes_view[zoneIndex] = uniqueIds.size(); count += uniqueIds.size(); }); -#else - using ZoneType = typename decltype(topoView)::ShapeType; - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); -#endif const auto connSize = count.get(); // Do a scan on the size array to build an offset array. @@ -404,7 +394,6 @@ class NodeToZoneRelationBuilder void fillZonesPH(const TopologyView &topoView, IntegerView connectivityView, IntegerView zonesView, OffsetsView offsets_view) const { // Run through the data one more time to build the nodes and zones arrays. -#if 1 const TopologyView deviceTopologyView(topoView); axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { @@ -418,20 +407,6 @@ class NodeToZoneRelationBuilder zonesView[destIdx] = zoneIndex; } }); -#else - using ZoneType = typename TopologyView::ShapeType; - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); - i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } - }); -#endif } /*! diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index a13141fee8..99222889da 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -76,7 +76,6 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); -#if 1 const TopologyView deviceTopologyView(m_topologyView); axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { @@ -91,20 +90,6 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); -#else - m_topologyView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); -#endif AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all zones that have 1 mat per node as clean. @@ -113,7 +98,6 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); -#if 1 axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto zone = deviceTopologyView.zone(zoneIndex); @@ -130,22 +114,6 @@ class ZoneListBuilder maskView[zoneIndex] = ival; mask_reduce += ival; }); -#else - m_topologyView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } - - const int ival = clean ? 1 : 0; - maskView[zoneIndex] = ival; - mask_reduce += ival; - }); -#endif AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); @@ -242,7 +210,6 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); -#if 1 const TopologyView deviceTopologyView(m_topologyView); axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { @@ -259,21 +226,6 @@ class ZoneListBuilder RAJA::atomicMax(nodePtr, nmats); } }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType AXOM_UNUSED_PARAM(szIndex), axom::IndexType zoneIndex, const ZoneType &zone) { - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); -#endif AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all selected zones that have 1 mat per node as clean. @@ -282,7 +234,6 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); -#if 1 axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; @@ -300,23 +251,6 @@ class ZoneListBuilder maskView[szIndex] = ival; mask_reduce += ival; }); -#else - m_topologyView.template for_selected_zones( - selectedZonesView, - AXOM_LAMBDA(axom::IndexType szIndex, axom::IndexType AXOM_UNUSED_PARAM(zoneIndex), const ZoneType &zone) { - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } - - const int ival = clean ? 1 : 0; - maskView[szIndex] = ival; - mask_reduce += ival; - }); -#endif AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 3243d22fdb..21fabf702c 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -608,8 +608,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; - MixedTopoView mixedTopoView(n_device_topo, - connView, + MixedTopoView mixedTopoView(connView, shapesView, sizesView, offsetsView, @@ -1006,7 +1005,7 @@ void braid3d_mixed_clip_test(const std::string &name) axom::Array values, ids; auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, axom::execution_space::allocatorID()); - TopoView topoView(n_device_topo, connView, shapesView, sizesView, offsetsView, shapeMap); + TopoView topoView(connView, shapesView, sizesView, offsetsView, shapeMap); // Create options to control the clipping. conduit::Node options; diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 5e472b8c73..e58c96a417 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -194,7 +194,6 @@ struct test_structured_topology_view_rectilinear // Execute the kernel for each zone (find max node number in zone). auto topoView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); - using ZoneType = typename decltype(topoView)::ShapeType; axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto zone = topoView.zone(zoneIndex); @@ -295,9 +294,9 @@ struct test_strided_structured auto logicalNodesView = logicalNodes.view(); // Traverse the zones in the mesh and gather node ids - using ZoneType = typename TopologyView::ShapeType; - topoView.template for_all_zones( - AXOM_LAMBDA(axom::IndexType zoneIndex, const ZoneType &zone) { + axom::for_all(topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = topoView.zone(zoneIndex); const auto nodeIndexing = topoView.indexing().expand(); // Get node ids for zone. diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index e2b17c208a..2fa2a76b3f 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -7,6 +7,7 @@ #define AXOM_MIR_VIEWS_STRUCTURED_TOPOLOGY_VIEW_HPP_ #include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/mir/views/Shapes.hpp" #include @@ -122,8 +123,12 @@ class StructuredTopologyView AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, Shape3D>::type zone(axom::IndexType zoneIndex) const { -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = m_nodeIndexing.jStride(); @@ -156,8 +161,12 @@ class StructuredTopologyView AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, Shape2D>::type zone(axom::IndexType zoneIndex) const { -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = m_nodeIndexing.jStride(); @@ -185,8 +194,12 @@ class StructuredTopologyView AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, Shape1D>::type zone(axom::IndexType zoneIndex) const { -#if defined(AXOM_DEBUG) && !defined(AXOM_DEVICE_CODE) +#if defined(AXOM_DEBUG) +#if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); +#else + SLIC_ASSERT(zoneIndex < numberOfZones()); +#endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); @@ -198,185 +211,6 @@ class StructuredTopologyView return shape; } - /*! - * \brief Execute a function for each zone in the mesh using axom::for_all. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_all_zones(FuncType &&func) const - { - const auto nzones = numberOfZones(); - - // Q: Should we make a for_all() that iterates over multiple ranges? - // Q: Should the logical index be passed to the lambda? - - if constexpr(IndexingPolicy::dimension() == 3) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); - Shape3D shape; - auto &data = shape.getIdsStorage(); - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - data[4] = data[0] + kp; - data[5] = data[1] + kp; - data[6] = data[2] + kp; - data[7] = data[3] + kp; - - func(zoneIndex, shape); - }); - } - else if constexpr(IndexingPolicy::dimension() == 2) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - Shape2D shape; - auto &data = shape.getIdsStorage(); - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - - func(zoneIndex, shape); - }); - } - else if constexpr(IndexingPolicy::dimension() == 1) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - Shape1D shape; - auto &data = shape.getIdsStorage(); - data[0] = - nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - - func(zoneIndex, shape); - }); - } - } - - /*! - * \brief Execute a function for each zone in the mesh using axom::for_all. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam ViewType A concrete ArrayView that contains selected zone ids. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_selected_zones(const ViewType &selectedIdsView, - const FuncType &&func) const - { - const auto nSelectedZones = selectedIdsView.size(); - ViewType idsView(selectedIdsView); - - if constexpr(IndexingPolicy::dimension() == 3) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - - const auto zoneIndex = idsView[selectIndex]; - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - const auto kp = nodeIndexing.kStride(); - Shape3D shape; - auto &data = shape.getIdsStorage(); - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - data[4] = data[0] + kp; - data[5] = data[1] + kp; - data[6] = data[2] + kp; - data[7] = data[3] + kp; - - func(selectIndex, zoneIndex, shape); - }); - } - else if constexpr(IndexingPolicy::dimension() == 2) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - - const auto zoneIndex = idsView[selectIndex]; - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - const auto jp = nodeIndexing.jStride(); - Shape2D shape; - auto &data = shape.getIdsStorage(); - - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - data[2] = data[1] + jp; - data[3] = data[2] - 1; - - func(selectIndex, zoneIndex, shape); - }); - } - else if constexpr(IndexingPolicy::dimension() == 1) - { - const IndexingPolicy zoneIndexing = m_zoneIndexing; - const IndexingPolicy nodeIndexing = m_zoneIndexing.expand(); - - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - - const auto zoneIndex = idsView[selectIndex]; - const auto localLogical = zoneIndexing.IndexToLogicalIndex(zoneIndex); - Shape1D shape; - auto &data = shape.getIdsStorage(); - - data[0] = nodeIndexing.GlobalToGlobal(nodeIndexing.LocalToGlobal(localLogical)); - data[1] = data[0] + 1; - - func(selectIndex, zoneIndex, shape); - }); - } - } - private: IndexingPolicy m_zoneIndexing; IndexingPolicy m_nodeIndexing; diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 90ee676721..e5cb493541 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -6,6 +6,8 @@ #ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_MIXED_SHAPE_VIEW_HPP_ #define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_MIXED_SHAPE_VIEW_HPP_ +#include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/mir/views/Shapes.hpp" #include "axom/mir/utilities.hpp" @@ -102,20 +104,18 @@ class UnstructuredTopologyMixedShapeView /*! * \brief Constructor * - * \param topo A reference to the topology. * \param conn The mesh connectivity. * \param shapes The shape in each zone. * \param sizes The number of nodes in each zone. * \param offsets The offset to each zone in the connectivity. */ - UnstructuredTopologyMixedShapeView(const conduit::Node &topo, - const ConnectivityView &conn, + AXOM_HOST_DEVICE + UnstructuredTopologyMixedShapeView(const ConnectivityView &conn, const ConnectivityView &shapes, const ConnectivityView &sizes, const ConnectivityView &offsets, const ShapeMap &shapemap) - : m_topo(topo) - , m_connectivity(conn) + : m_connectivity(conn) , m_shapes(shapes) , m_sizes(sizes) , m_offsets(offsets) @@ -190,100 +190,7 @@ class UnstructuredTopologyMixedShapeView return ShapeType(shapeID, shapeData); } - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_all_zones(FuncType &&func) const - { - const auto nzones = numberOfZones(); - - // Build a ShapeMap from the Conduit shape map. - axom::Array values, ids; - const auto allocatorID = axom::execution_space::allocatorID(); - buildShapeMap(m_topo, values, ids, allocatorID); - const ShapeMap shapeMap(values.view(), ids.view()); - - const ConnectivityView connectivityView(m_connectivity); - const ConnectivityView shapes(m_shapes); - const ConnectivityView sizes(m_sizes); - const ConnectivityView offsets(m_offsets); - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - const ConnectivityView shapeData( - connectivityView.data() + offsets[zoneIndex], - sizes[zoneIndex]); - const auto shapeID = shapeMap[shapes[zoneIndex]]; -#if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) - assert(shapeID > 0); -#else - SLIC_ASSERT(shapeID > 0); -#endif -#endif - const ShapeType shape(shapeID, shapeData); - func(zoneIndex, shape); - }); - } - - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam ViewType A view type that contains zone indices. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param selectedIdsView A view that contains a list of zones to operate on. - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const - { - const auto nSelectedZones = selectedIdsView.size(); - - // Build a ShapeMap from the Conduit shape map. - axom::Array values, ids; - const auto allocatorID = axom::execution_space::allocatorID(); - buildShapeMap(m_topo, values, ids, allocatorID); - const ShapeMap shapeMap(values.view(), ids.view()); - - // Make views that can be captured. - const ConnectivityView connectivityView(m_connectivity); - const ConnectivityView shapes(m_shapes); - const ConnectivityView sizes(m_sizes); - const ConnectivityView offsets(m_offsets); - const ViewType deviceSelectedIdsView(selectedIdsView); - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - const auto zoneIndex = deviceSelectedIdsView[selectIndex]; - const ConnectivityView shapeData( - connectivityView.data() + offsets[zoneIndex], - sizes[zoneIndex]); - const auto shapeID = shapeMap[shapes[zoneIndex]]; -#if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) - assert(shapeID > 0); -#else - SLIC_ASSERT(shapeID > 0); -#endif -#endif - const ShapeType shape(shapeID, shapeData); - func(selectIndex, zoneIndex, shape); - }); - } - private: - - const conduit::Node &m_topo; ConnectivityView m_connectivity; ConnectivityView m_shapes; ConnectivityView m_sizes; diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index 9d3a3acec4..f1eb91b8ac 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -6,6 +6,8 @@ #ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_POLYHEDRAL_VIEW_HPP_ #define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_POLYHEDRAL_VIEW_HPP_ +#include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/mir/views/Shapes.hpp" namespace axom @@ -246,56 +248,6 @@ class UnstructuredTopologyPolyhedralView return ShapeType(m_data, zoneIndex); } - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_all_zones(FuncType &&func) const - { - const auto nzones = numberOfZones(); - - const PolyhedronData sd(m_data); - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - const PolyhedronShape shape(sd, zoneIndex); - func(zoneIndex, shape); - }); - } - - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam ViewType A view type that contains zone indices. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param selectedIdsView A view that contains a list of zones to operate on. - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const - { - const auto nSelectedZones = selectedIdsView.size(); - - ViewType idsView(selectedIdsView); - const PolyhedronData sd(m_data); - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - const auto zoneIndex = idsView[selectIndex]; - const PolyhedronShape shape(sd, zoneIndex); - func(selectIndex, zoneIndex, shape); - }); - } - private: PolyhedronData m_data; }; diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index 9ae8dfe015..fb7624446d 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -6,6 +6,8 @@ #ifndef AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ #define AXOM_MIR_VIEWS_UNSTRUCTURED_TOPOLOGY_SINGLE_SHAPE_VIEW_HPP_ +#include "axom/core.hpp" +#include "axom/slic.hpp" #include "axom/mir/views/Shapes.hpp" namespace axom @@ -142,122 +144,6 @@ class UnstructuredTopologySingleShapeView } /// @} - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_all_zones(FuncType &&func) const - { - const auto nzones = numberOfZones(); - - ConnectivityView connectivityView(m_connectivityView); - if constexpr(ShapeType::is_variable_size()) - { - ConnectivityView sizesView(m_sizesView); - ConnectivityView offsetsView(m_offsetsView); - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - const ConnectivityView shapeIdsView( - connectivityView.data() + offsetsView[zoneIndex], - sizesView[zoneIndex]); - const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); - }); - } - else - { - ConnectivityView sizesView(m_sizesView); - ConnectivityView offsetsView(m_offsetsView); - axom::for_all( - 0, - nzones, - AXOM_LAMBDA(axom::IndexType zoneIndex) { - ConnectivityView shapeIdsView {}; - if(sizesView.empty()) - { - shapeIdsView = ConnectivityView( - connectivityView.data() + ShapeType::zoneOffset(zoneIndex), - ShapeType::numberOfNodes()); - } - else - { - shapeIdsView = - ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], - sizesView[zoneIndex]); - } - const ShapeType shape(shapeIdsView); - func(zoneIndex, shape); - }); - } - } - - /*! - * \brief Execute a function for each zone in the mesh. - * - * \tparam ExecSpace The execution space for the function body. - * \tparam FuncType The type for the function/lambda to execute. It will accept a zone index and shape. - * - * \param selectedIdsView A view containing selected zone ids. - * \param func The function/lambda that will be executed for each zone in the mesh. - */ - template - void for_selected_zones(const ViewType &selectedIdsView, FuncType &&func) const - { - const auto nSelectedZones = selectedIdsView.size(); - - ConnectivityView connectivityView(m_connectivityView); - const ViewType localSelectedIdsView(selectedIdsView); - if constexpr(ShapeType::is_variable_size()) - { - ConnectivityView sizesView(m_sizesView); - ConnectivityView offsetsView(m_offsetsView); - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - const auto zoneIndex = localSelectedIdsView[selectIndex]; - const ConnectivityView shapeIdsView( - connectivityView.data() + offsetsView[zoneIndex], - sizesView[zoneIndex]); - const ShapeType shape(shapeIdsView); - func(selectIndex, zoneIndex, shape); - }); - } - else - { - ConnectivityView sizesView(m_sizesView); - ConnectivityView offsetsView(m_offsetsView); - axom::for_all( - 0, - nSelectedZones, - AXOM_LAMBDA(axom::IndexType selectIndex) { - const auto zoneIndex = localSelectedIdsView[selectIndex]; - ConnectivityView shapeIdsView {}; - if(sizesView.empty()) - { - shapeIdsView = ConnectivityView( - connectivityView.data() + ShapeType::zoneOffset(zoneIndex), - ShapeType::numberOfNodes()); - } - else - { - shapeIdsView = - ConnectivityView(connectivityView.data() + offsetsView[zoneIndex], - sizesView[zoneIndex]); - } - const ShapeType shape(shapeIdsView); - func(selectIndex, zoneIndex, shape); - }); - } - } - private: ConnectivityView m_connectivityView; ConnectivityView m_sizesView; diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 3427c81bb7..323e56551d 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -133,8 +133,7 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, axom::Array values, ids; auto shapeMap = buildShapeMap(topo, values, ids, allocatorID); - UnstructuredTopologyMixedShapeView ugView(topo, - connView, + UnstructuredTopologyMixedShapeView ugView(connView, shapesView, sizesView, offsetsView, @@ -168,8 +167,7 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, axom::Array values, ids; auto shapeMap = buildShapeMap(topo, values, ids, allocatorID); - UnstructuredTopologyMixedShapeView ugView(topo, - connView, + UnstructuredTopologyMixedShapeView ugView(connView, shapesView, sizesView, offsetsView, From 8e7fb0957098a28072ebb91a55da1e1519260aa5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 16:56:06 -0700 Subject: [PATCH 248/290] make style --- src/axom/core/StackArray.hpp | 5 +- src/axom/core/StaticArray.hpp | 2 +- src/axom/core/memory_management.hpp | 6 +- src/axom/mir/ClipField.hpp | 76 +++++--- src/axom/mir/EquiZAlgorithm.hpp | 9 +- src/axom/mir/ExtractZones.hpp | 107 ++++++------ src/axom/mir/FieldBlender.hpp | 8 +- src/axom/mir/FieldSlicer.hpp | 6 +- src/axom/mir/MakeUnstructured.hpp | 80 +++++---- src/axom/mir/MatsetSlicer.hpp | 4 +- src/axom/mir/MergeMeshes.hpp | 163 +++++++++++------- src/axom/mir/MeshTester.cpp | 30 ++-- src/axom/mir/NodeToZoneRelationBuilder.hpp | 92 ++++++---- src/axom/mir/RecenterField.hpp | 28 ++- src/axom/mir/SelectedZones.hpp | 8 +- src/axom/mir/ZoneListBuilder.hpp | 126 +++++++------- src/axom/mir/blueprint_utilities.hpp | 10 +- .../mir/examples/mir_concentric_circles.cpp | 6 +- .../mir/tests/mir_blueprint_utilities.cpp | 69 +++++--- src/axom/mir/tests/mir_clipfield.cpp | 12 +- src/axom/mir/tests/mir_views.cpp | 76 ++++---- src/axom/mir/utilities.hpp | 6 +- src/axom/mir/views/ExplicitCoordsetView.hpp | 34 ++-- src/axom/mir/views/NodeArrayView.hpp | 98 ++++++----- src/axom/mir/views/Shapes.hpp | 108 ++++++++---- src/axom/mir/views/StructuredTopologyView.hpp | 64 ++++--- .../UnstructuredTopologyMixedShapeView.hpp | 30 ++-- .../UnstructuredTopologyPolyhedralView.hpp | 6 +- .../UnstructuredTopologySingleShapeView.hpp | 50 +++--- .../views/dispatch_rectilinear_topology.hpp | 23 ++- .../views/dispatch_structured_topology.hpp | 44 +++-- .../mir/views/dispatch_uniform_topology.hpp | 23 ++- .../views/dispatch_unstructured_topology.hpp | 153 +++++++++++----- src/axom/mir/views/dispatch_utilities.hpp | 15 +- 34 files changed, 945 insertions(+), 632 deletions(-) diff --git a/src/axom/core/StackArray.hpp b/src/axom/core/StackArray.hpp index 209d0aa939..ec6f482ea3 100644 --- a/src/axom/core/StackArray.hpp +++ b/src/axom/core/StackArray.hpp @@ -65,12 +65,11 @@ struct StackArray AXOM_HOST_DEVICE constexpr operator const T*() const noexcept { return &m_data[0]; } - AXOM_HOST_DEVICE T *data() noexcept { return &m_data[0]; } - AXOM_HOST_DEVICE const T *data() const noexcept { return &m_data[0]; } + AXOM_HOST_DEVICE T* data() noexcept { return &m_data[0]; } + AXOM_HOST_DEVICE const T* data() const noexcept { return &m_data[0]; } /// @} - /*! * \brief Begin/end iterators */ diff --git a/src/axom/core/StaticArray.hpp b/src/axom/core/StaticArray.hpp index 2a2be16358..8bd471a957 100644 --- a/src/axom/core/StaticArray.hpp +++ b/src/axom/core/StaticArray.hpp @@ -96,7 +96,7 @@ class StaticArray : public StackArray AXOM_HOST_DEVICE void fill(const T &fill_value) { - for(T& datum : StackArray::m_data) + for(T &datum : StackArray::m_data) { datum = fill_value; } diff --git a/src/axom/core/memory_management.hpp b/src/axom/core/memory_management.hpp index 2be866114e..4ea971e2e0 100644 --- a/src/axom/core/memory_management.hpp +++ b/src/axom/core/memory_management.hpp @@ -117,7 +117,7 @@ inline int getDefaultAllocatorID() * \return ID of the allocator that allocated the memory. */ /// &{ -inline int getAllocatorIDForAddress(void *ptr) +inline int getAllocatorIDForAddress(void* ptr) { #ifdef AXOM_USE_UMPIRE umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); @@ -127,11 +127,11 @@ inline int getAllocatorIDForAddress(void *ptr) #endif } -inline int getAllocatorIDForAddress(const void *ptr) +inline int getAllocatorIDForAddress(const void* ptr) { #ifdef AXOM_USE_UMPIRE umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); - return rm.getAllocator(const_cast(ptr)).getId(); + return rm.getAllocator(const_cast(ptr)).getId(); #else return axom::getDefaultAllocatorID(); #endif diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index fd8768fba7..2fbeb940ae 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -333,8 +333,7 @@ class FieldIntersector axom::for_all( srcView.size(), AXOM_LAMBDA(axom::IndexType index) { - clipFieldView[index] = - static_cast(srcView[index]); + clipFieldView[index] = static_cast(srcView[index]); }); } @@ -838,7 +837,8 @@ class ClipField const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); - axom::for_all(selectedZonesView.size(), + axom::for_all( + selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); @@ -958,7 +958,7 @@ class ClipField // Set blend group sizes for this zone. blendGroupsView[szIndex] = thisBlendGroups; blendGroupsLenView[szIndex] = thisBlendGroupLen; - }); // for_selected_zones + }); // for_selected_zones #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -994,7 +994,9 @@ class ClipField const auto fragmentsView = fragmentData.m_fragmentsView; axom::for_all( nzones, - AXOM_LAMBDA(axom::IndexType szIndex) { fragment_sum += fragmentsView[szIndex]; }); + AXOM_LAMBDA(axom::IndexType szIndex) { + fragment_sum += fragmentsView[szIndex]; + }); fragmentData.m_finalNumZones = fragment_sum.get(); // Sum the fragment connectivity sizes. @@ -1050,7 +1052,9 @@ class ClipField const auto nodeUsedView = nodeData.m_nodeUsedView; axom::for_all( nodeUsedView.size(), - AXOM_LAMBDA(axom::IndexType index) { nUsed_reducer += nodeUsedView[index]; }); + AXOM_LAMBDA(axom::IndexType index) { + nUsed_reducer += nodeUsedView[index]; + }); return nUsed_reducer.get(); } @@ -1120,7 +1124,8 @@ class ClipField const auto deviceIntersector = m_intersector.view(); const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); - axom::for_all(selectedZonesView.size(), + axom::for_all( + selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); @@ -1342,7 +1347,8 @@ class ClipField const TopologyView deviceTopologyView(m_topologyView); const auto selectedZonesView = selectedZones.view(); - axom::for_all(selectedZonesView.size(), + axom::for_all( + selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) { const auto zoneIndex = selectedZonesView[szIndex]; const auto zone = deviceTopologyView.zone(zoneIndex); @@ -1492,7 +1498,7 @@ class ClipField // Reduce overall whether there are degenerates. degenerates_reduce |= degenerates; #endif - }); // for_selected_zones + }); // for_selected_zones #if defined(AXOM_DEBUG_CLIP_FIELD) std::cout @@ -1554,10 +1560,20 @@ class ClipField axom::exclusive_scan(maskView, maskOffsetsView); // Filter sizes, shapes, color using the mask - sizesView = filter(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); - offsetsView = filter(n_offsets, offsetsView, filteredZoneCount, maskView, maskOffsetsView); - shapesView = filter(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); - colorView = filter(n_color_values, colorView, filteredZoneCount, maskView, maskOffsetsView); + sizesView = + filter(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); + offsetsView = filter(n_offsets, + offsetsView, + filteredZoneCount, + maskView, + maskOffsetsView); + shapesView = + filter(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); + colorView = filter(n_color_values, + colorView, + filteredZoneCount, + maskView, + maskOffsetsView); // Record the filtered size. fragmentData.m_finalNumZones = filteredZoneCount; @@ -1623,7 +1639,7 @@ class ClipField connView[offset + 1] = pts[1]; connView[offset + 2] = pts[2]; // Repeat the last point (it won't be used though). - connView[offset + 3] = pts[2]; + connView[offset + 3] = pts[2]; } } @@ -1688,8 +1704,11 @@ class ClipField * \param maskOffsetsView The offsets view to indicate where to write the new data. */ template - DataView filter(conduit::Node &n_src, DataView srcView, axom::IndexType newSize, - axom::ArrayView maskView, axom::ArrayView maskOffsetsView) const + DataView filter(conduit::Node &n_src, + DataView srcView, + axom::IndexType newSize, + axom::ArrayView maskView, + axom::ArrayView maskOffsetsView) const { using value_type = typename DataView::value_type; namespace bputils = axom::mir::utilities::blueprint; @@ -1892,7 +1911,10 @@ class ClipField n_values.set(conduit::DataType(n_orig_values.dtype().id(), fragmentData.m_finalNumZones)); auto valuesView = bputils::make_array_view(n_values); - makeOriginalElements_copy(fragmentData, selectedZones, valuesView, origValuesView); + makeOriginalElements_copy(fragmentData, + selectedZones, + valuesView, + origValuesView); }); } else @@ -1938,14 +1960,14 @@ class ClipField const auto selectedZonesView = selectedZones.view(); const auto nzones = selectedZonesView.size(); axom::for_all( - nzones, - AXOM_LAMBDA(axom::IndexType index) { - const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; - const int nFragments = fragmentData.m_fragmentsView[index]; - const auto zoneIndex = selectedZonesView[index]; - for(int i = 0; i < nFragments; i++) - valuesView[sizeIndex + i] = origValuesView[zoneIndex]; - }); + nzones, + AXOM_LAMBDA(axom::IndexType index) { + const int sizeIndex = fragmentData.m_fragmentOffsetsView[index]; + const int nFragments = fragmentData.m_fragmentsView[index]; + const auto zoneIndex = selectedZonesView[index]; + for(int i = 0; i < nFragments; i++) + valuesView[sizeIndex + i] = origValuesView[zoneIndex]; + }); } /*! @@ -2027,7 +2049,9 @@ class ClipField // Update values for the blend groups only. axom::for_all( blendSize, - AXOM_LAMBDA(axom::IndexType bgid) { valuesView[origSize + bgid] = one; }); + AXOM_LAMBDA(axom::IndexType bgid) { + valuesView[origSize + bgid] = one; + }); } else { diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 42de23e338..a18ebdfb92 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -1030,10 +1030,11 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm * \param cleanMats A vector of clean materials. * \param mixedMats A vector of mixed materials. */ - void makeWorkingFields(const conduit::Node &n_topo, - conduit::Node &n_fields, - const axom::mir::views::MaterialInformation &cleanMats, - const axom::mir::views::MaterialInformation &AXOM_UNUSED_PARAM(mixedMats)) const + void makeWorkingFields( + const conduit::Node &n_topo, + conduit::Node &n_fields, + const axom::mir::views::MaterialInformation &cleanMats, + const axom::mir::views::MaterialInformation &AXOM_UNUSED_PARAM(mixedMats)) const { namespace bputils = axom::mir::utilities::blueprint; AXOM_ANNOTATE_SCOPE("makeWorkingFields"); diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index e55b5f269e..9659842173 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -254,12 +254,13 @@ class ExtractZones // Figure out the topology size based on selected zones. RAJA::ReduceSum connsize_reduce(0); const TopologyView deviceTopologyView(m_topologyView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - connsize_reduce += zone.numberOfNodes(); - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + connsize_reduce += zone.numberOfNodes(); + }); const auto newConnSize = connsize_reduce.get(); Sizes sizes {}; @@ -310,18 +311,19 @@ class ExtractZones // Mark all the selected zones' nodes as 1. Multiple threads may write 1 to the same node. RAJA::ReduceSum connsize_reduce(0); TopologyView deviceTopologyView(m_topologyView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - const axom::IndexType nids = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nids; i++) - { - const auto nodeId = zone.getId(i); - maskView[nodeId] = 1; - } - connsize_reduce += nids; - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + const axom::IndexType nids = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nids; i++) + { + const auto nodeId = zone.getId(i); + maskView[nodeId] = 1; + } + connsize_reduce += nids; + }); const auto newConnSize = connsize_reduce.get(); // Count the used nodes. @@ -424,12 +426,13 @@ class ExtractZones // Fill sizes, offsets const TopologyView deviceTopologyView(m_topologyView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - sizesView[szIndex] = zone.numberOfNodes(); - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + sizesView[szIndex] = zone.numberOfNodes(); + }); if(extra.zones > 0) { @@ -444,36 +447,38 @@ class ExtractZones if(compact(n_options)) { const axom::ArrayView deviceOld2NewView(old2newView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - const auto oldNodeId = zone.getId(i); - // When compact, we map node ids to the compact node ids. - const auto newNodeId = deviceOld2NewView[oldNodeId]; - connView[offset + i] = newNodeId; - } - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + const auto oldNodeId = zone.getId(i); + // When compact, we map node ids to the compact node ids. + const auto newNodeId = deviceOld2NewView[oldNodeId]; + connView[offset + i] = newNodeId; + } + }); } else { - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - - const int size = static_cast(sizesView[szIndex]); - const auto offset = offsetsView[szIndex]; - for(int i = 0; i < size; i++) - { - connView[offset + i] = zone.getId(i); - } - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int size = static_cast(sizesView[szIndex]); + const auto offset = offsetsView[szIndex]; + for(int i = 0; i < size; i++) + { + connView[offset + i] = zone.getId(i); + } + }); } if(extra.connectivity > 0) { diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/FieldBlender.hpp index d054bd9fde..947dcc899e 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/FieldBlender.hpp @@ -169,7 +169,7 @@ class FieldBlender n_output_values, [&](auto compView, auto outView) { blendSingleComponentImpl(blend, compView, outView); - }); + }); } /*! @@ -183,7 +183,9 @@ class FieldBlender * lambda. */ template - void blendSingleComponentImpl(const BlendData &blend, SrcView compView, OutputView outView) const + void blendSingleComponentImpl(const BlendData &blend, + SrcView compView, + OutputView outView) const { using value_type = typename decltype(compView)::value_type; using accum_type = @@ -193,7 +195,7 @@ class FieldBlender // groups. If the user did not provide that, use all blend groups. const auto origSize = blend.m_originalIdsView.size(); const auto blendSize = SelectionPolicy::size(blend); -// const auto outputSize = origSize + blendSize; + // const auto outputSize = origSize + blendSize; const IndexingPolicy deviceIndexing(m_indexing); const BlendData deviceBlend(blend); diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/FieldSlicer.hpp index ff7987a856..f72afdf6a6 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/FieldSlicer.hpp @@ -111,7 +111,7 @@ class FieldSlicer n_values, n_output_values, [&](auto valuesView, auto outputView) { - sliceSingleComponentImpl(slice, valuesView, outputView); + sliceSingleComponentImpl(slice, valuesView, outputView); }); } @@ -126,7 +126,9 @@ class FieldSlicer * lambda. */ template - void sliceSingleComponentImpl(const SliceData &slice, ValuesView valuesView, OutputView outputView) const + void sliceSingleComponentImpl(const SliceData &slice, + ValuesView valuesView, + OutputView outputView) const { IndexingPolicy deviceIndexing(m_indexing); SliceData deviceSlice(slice); diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp index 17c5efafb4..c91db72635 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/MakeUnstructured.hpp @@ -70,30 +70,28 @@ class MakeUnstructured axom::mir::views::dispatch_structured_topologies( topo, [&](const std::string &shape, auto &topoView) { - - n_newtopo["elements/shape"] = shape; - - int ptsPerZone = 2; - if(shape == "quad") - ptsPerZone = 4; - else if(shape == "hex") - ptsPerZone = 8; - - // Allocate new mesh data. - const auto nzones = topoView.numberOfZones(); - const auto connSize = nzones * ptsPerZone; - n_newconn.set(conduit::DataType::index_t(connSize)); - n_newsizes.set(conduit::DataType::index_t(nzones)); - n_newoffsets.set(conduit::DataType::index_t(nzones)); - - // Make views for the mesh data. - auto connView = make_array_view(n_newconn); - auto sizesView = make_array_view(n_newsizes); - auto offsetsView = make_array_view(n_newoffsets); - - makeUnstructured(ptsPerZone, topoView, connView, sizesView, offsetsView); - - }); + n_newtopo["elements/shape"] = shape; + + int ptsPerZone = 2; + if(shape == "quad") + ptsPerZone = 4; + else if(shape == "hex") + ptsPerZone = 8; + + // Allocate new mesh data. + const auto nzones = topoView.numberOfZones(); + const auto connSize = nzones * ptsPerZone; + n_newconn.set(conduit::DataType::index_t(connSize)); + n_newsizes.set(conduit::DataType::index_t(nzones)); + n_newoffsets.set(conduit::DataType::index_t(nzones)); + + // Make views for the mesh data. + auto connView = make_array_view(n_newconn); + auto sizesView = make_array_view(n_newsizes); + auto offsetsView = make_array_view(n_newoffsets); + + makeUnstructured(ptsPerZone, topoView, connView, sizesView, offsetsView); + }); } } @@ -114,25 +112,25 @@ class MakeUnstructured */ template static void makeUnstructured(int ptsPerZone, - TopologyView topoView, - axom::ArrayView connView, - axom::ArrayView sizesView, - axom::ArrayView offsetsView) + TopologyView topoView, + axom::ArrayView connView, + axom::ArrayView sizesView, + axom::ArrayView offsetsView) { // Fill in the new connectivity. - axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = topoView.zone(zoneIndex); - - const auto start = zoneIndex * ptsPerZone; - for(int i = 0; i < ptsPerZone; i++) - { - connView[start + i] = - static_cast(zone.getId(i)); - } - sizesView[zoneIndex] = ptsPerZone; - offsetsView[zoneIndex] = start; - }); + axom::for_all( + topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = topoView.zone(zoneIndex); + + const auto start = zoneIndex * ptsPerZone; + for(int i = 0; i < ptsPerZone; i++) + { + connView[start + i] = static_cast(zone.getId(i)); + } + sizesView[zoneIndex] = ptsPerZone; + offsetsView[zoneIndex] = start; + }); } }; diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 4918bfa555..62d87190ba 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -94,9 +94,7 @@ class MatsetSlicer RAJA::ReduceSum size_reduce(0); axom::for_all( sizesView.size(), - AXOM_LAMBDA(axom::IndexType index) { - size_reduce += sizesView[index]; - }); + AXOM_LAMBDA(axom::IndexType index) { size_reduce += sizesView[index]; }); axom::exclusive_scan(sizesView, offsetsView); // Allocate data for the rest of the matset. diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index c4bc16247f..6633af6c62 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -249,7 +249,10 @@ class MergeMeshes auto srcCompView = bputils::make_array_view(n_srcComp); auto compView = bputils::make_array_view(n_comp); - axom::IndexType size = mergeCoordset_copy(inputs[i].m_nodeSliceView, offsets[c], compView, srcCompView); + axom::IndexType size = mergeCoordset_copy(inputs[i].m_nodeSliceView, + offsets[c], + compView, + srcCompView); offsets[c] += size; } @@ -272,7 +275,11 @@ class MergeMeshes * lambda. */ template - axom::IndexType mergeCoordset_copy(const axom::ArrayView nodeSliceView, axom::IndexType offset, DataArrayView compView, DataArrayView srcCompView) const + axom::IndexType mergeCoordset_copy( + const axom::ArrayView nodeSliceView, + axom::IndexType offset, + DataArrayView compView, + DataArrayView srcCompView) const { axom::IndexType size = 0; if(nodeSliceView.size() > 0) @@ -506,7 +513,11 @@ class MergeMeshes auto connView = bputils::make_array_view(n_newConn); // Copy all sizes from the input. - mergeTopology_copy(inputs[i].m_nodeMapView, connOffset, coordOffset, connView, srcConnView); + mergeTopology_copy(inputs[i].m_nodeMapView, + connOffset, + coordOffset, + connView, + srcConnView); connOffset += srcConnView.size(); coordOffset += countNodes(inputs, static_cast(i)); @@ -584,7 +595,11 @@ class MergeMeshes * lambda. */ template - void mergeTopology_copy(axom::ArrayView nodeMapView, axom::IndexType connOffset, axom::IndexType coordOffset, ConnectivityView connView, ConnectivityView srcConnView) const + void mergeTopology_copy(axom::ArrayView nodeMapView, + axom::IndexType connOffset, + axom::IndexType coordOffset, + ConnectivityView connView, + ConnectivityView srcConnView) const { if(nodeMapView.size() > 0) { @@ -622,7 +637,9 @@ class MergeMeshes * lambda. */ template - void mergeTopology_copy_sizes(axom::IndexType sizesOffset, IntegerView sizesView, IntegerView srcSizesView) const + void mergeTopology_copy_sizes(axom::IndexType sizesOffset, + IntegerView sizesView, + IntegerView srcSizesView) const { axom::for_all( srcSizesView.size(), @@ -639,7 +656,9 @@ class MergeMeshes * \param srcShapesView The view that exposes shapes for the source mesh. */ template - void mergeTopology_copy_shapes(axom::IndexType shapesOffset, IntegerView shapesView, IntegerView srcShapesView) const + void mergeTopology_copy_shapes(axom::IndexType shapesOffset, + IntegerView shapesView, + IntegerView srcShapesView) const { axom::for_all( srcShapesView.size(), @@ -656,7 +675,10 @@ class MergeMeshes * \param srcShapesView The view that exposes shapes for the source mesh. */ template - void mergeTopology_default_shapes(axom::IndexType shapesOffset, IntegerView shapesView, axom::IndexType nzones, int shapeId) const + void mergeTopology_default_shapes(axom::IndexType shapesOffset, + IntegerView shapesView, + axom::IndexType nzones, + int shapeId) const { axom::for_all( nzones, @@ -794,7 +816,8 @@ class MergeMeshes { const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView(n_values, + axom::mir::views::Node_to_ArrayView( + n_values, n_src_values, [&](auto destView, auto srcView) { copyZonal_copy(nzones, offset, destView, srcView); @@ -823,11 +846,16 @@ class MergeMeshes * lambda. */ template - void copyZonal_copy(axom::IndexType nzones, axom::IndexType offset, DestView destView, SrcView srcView) const + void copyZonal_copy(axom::IndexType nzones, + axom::IndexType offset, + DestView destView, + SrcView srcView) const { - axom::for_all(nzones, AXOM_LAMBDA(axom::IndexType index) { - destView[offset + index] = srcView[index]; - }); + axom::for_all( + nzones, + AXOM_LAMBDA(axom::IndexType index) { + destView[offset + index] = srcView[index]; + }); } /*! @@ -842,11 +870,13 @@ class MergeMeshes * lambda. */ template - void fillValues(axom::IndexType nvalues, axom::IndexType offset, DestView destView) const + void fillValues(axom::IndexType nvalues, + axom::IndexType offset, + DestView destView) const { - axom::for_all(nvalues, AXOM_LAMBDA(axom::IndexType index) { - destView[offset + index] = 0; - }); + axom::for_all( + nvalues, + AXOM_LAMBDA(axom::IndexType index) { destView[offset + index] = 0; }); } /** @@ -870,14 +900,16 @@ class MergeMeshes const conduit::Node &n_src_values = inputs[i].m_input->fetch_existing(srcPath); - axom::mir::views::Node_to_ArrayView( - n_src_values, - n_values, - [&](auto srcView, auto destView) { - - copyNodal_copy(inputs[i].m_nodeSliceView, nnodes, offset, destView, srcView); - - }); + axom::mir::views::Node_to_ArrayView(n_src_values, + n_values, + [&](auto srcView, auto destView) { + copyNodal_copy( + inputs[i].m_nodeSliceView, + nnodes, + offset, + destView, + srcView); + }); } else { @@ -903,7 +935,11 @@ class MergeMeshes * lambda. */ template - void copyNodal_copy(axom::ArrayView nodeSliceView, axom::IndexType nnodes, axom::IndexType offset, SrcViewType destView, DestViewType srcView) const + void copyNodal_copy(axom::ArrayView nodeSliceView, + axom::IndexType nnodes, + axom::IndexType offset, + SrcViewType destView, + DestViewType srcView) const { if(nodeSliceView.empty()) { @@ -987,7 +1023,6 @@ class MergeMeshes conduit::Node &n_matset = n_matsets[0]; axom::IndexType matCount = 0; axom::mir::views::dispatch_material(n_matset, [&](auto matsetView) { - // Figure out the types to use for storing the data. using IType = typename decltype(matsetView)::IndexType; using FType = typename decltype(matsetView)::FloatType; @@ -1066,7 +1101,6 @@ class MergeMeshes axom::mir::views::dispatch_material( n_matset, [&](auto matsetView) { - mergeMatset_sizes(matsetView, sizesView, nzones, zOffset); }); } @@ -1094,16 +1128,28 @@ class MergeMeshes inputs[i].m_input->fetch_existing("matsets"); conduit::Node &n_matset = n_matsets[0]; - axom::mir::views::dispatch_material( - n_matset, - [&](auto matsetView) { - mergeMatset_copy(n_matset, allMats, materialIdsView, offsetsView, volumeFractionsView, matsetView, nzones, zOffset); - }); + axom::mir::views::dispatch_material(n_matset, + [&](auto matsetView) { + mergeMatset_copy( + n_matset, + allMats, + materialIdsView, + offsetsView, + volumeFractionsView, + matsetView, + nzones, + zOffset); + }); } else { const int dmat = allMats["default"]; - mergeMatset_default(materialIdsView, offsetsView, volumeFractionsView, dmat, nzones, zOffset); + mergeMatset_default(materialIdsView, + offsetsView, + volumeFractionsView, + dmat, + nzones, + zOffset); } zOffset += nzones; } @@ -1124,7 +1170,8 @@ class MergeMeshes * lambda. */ template - axom::IndexType mergeMatset_count(MatsetView matsetView, axom::IndexType nzones) const + axom::IndexType mergeMatset_count(MatsetView matsetView, + axom::IndexType nzones) const { using reduce_policy = typename axom::execution_space::reduce_policy; @@ -1151,13 +1198,15 @@ class MergeMeshes * lambda. */ template - void mergeMatset_sizes(const MatsetView matsetView, IntegerArrayView sizesView, axom::IndexType nzones, axom::IndexType zOffset) const + void mergeMatset_sizes(const MatsetView matsetView, + IntegerArrayView sizesView, + axom::IndexType nzones, + axom::IndexType zOffset) const { axom::for_all( nzones, AXOM_LAMBDA(axom::IndexType zoneIndex) { - sizesView[zOffset + zoneIndex] = - matsetView.numberOfMaterials(zoneIndex); + sizesView[zOffset + zoneIndex] = matsetView.numberOfMaterials(zoneIndex); }); } @@ -1173,7 +1222,9 @@ class MergeMeshes * lambda. */ template - void mergeMatset_sizes1(IntegerArrayView sizesView, axom::IndexType nzones, axom::IndexType zOffset) const + void mergeMatset_sizes1(IntegerArrayView sizesView, + axom::IndexType nzones, + axom::IndexType zOffset) const { axom::for_all( nzones, @@ -1193,7 +1244,8 @@ class MergeMeshes * lambda. */ template - void mergeMatset_indices(IntegerArrayView indicesView, axom::IndexType totalMatCount) const + void mergeMatset_indices(IntegerArrayView indicesView, + axom::IndexType totalMatCount) const { axom::for_all( totalMatCount, @@ -1231,8 +1283,7 @@ class MergeMeshes using MatID = typename decltype(matsetView)::IndexType; // Make some maps for renumbering material numbers. - const auto localMaterialMap = - axom::mir::views::materials(n_matset); + const auto localMaterialMap = axom::mir::views::materials(n_matset); std::map localToAll; for(const auto &info : localMaterialMap) { @@ -1242,27 +1293,17 @@ class MergeMeshes localToAll[info.number] = matno; } std::vector localVec, allVec; - for(auto it = localToAll.begin(); it != localToAll.end(); - it++) + for(auto it = localToAll.begin(); it != localToAll.end(); it++) { localVec.push_back(it->first); allVec.push_back(it->second); } // Put maps on device. - const int allocatorID = - axom::execution_space::allocatorID(); - axom::Array local(localVec.size(), - localVec.size(), - allocatorID); - axom::Array all(allVec.size(), - allVec.size(), - allocatorID); - axom::copy(local.data(), - localVec.data(), - sizeof(MatID) * local.size()); - axom::copy(all.data(), - allVec.data(), - sizeof(MatID) * all.size()); + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array local(localVec.size(), localVec.size(), allocatorID); + axom::Array all(allVec.size(), allVec.size(), allocatorID); + axom::copy(local.data(), localVec.data(), sizeof(MatID) * local.size()); + axom::copy(all.data(), allVec.data(), sizeof(MatID) * all.size()); const auto localView = local.view(); const auto allView = all.view(); @@ -1275,16 +1316,14 @@ class MergeMeshes matsetView.zoneMaterials(zoneIndex, ids, vfs); // Store the materials in the new material. - const auto zoneStart = - offsetsView[zOffset + zoneIndex]; + const auto zoneStart = offsetsView[zOffset + zoneIndex]; for(axom::IndexType mi = 0; mi < ids.size(); mi++) { const auto destIndex = zoneStart + mi; volumeFractionsView[destIndex] = vfs[mi]; // Get the index of the material number in the local map. - const auto mapIndex = - axom::mir::utilities::bsearch(ids[mi], localView); + const auto mapIndex = axom::mir::utilities::bsearch(ids[mi], localView); assert(mapIndex != -1); // We'll store the all materials number. const auto allMatno = allView[mapIndex]; @@ -1321,7 +1360,7 @@ class MergeMeshes const auto zoneStart = offsetsView[zOffset + zoneIndex]; volumeFractionsView[zoneStart] = 1; materialIdsView[zoneStart] = matno; - }); + }); } }; diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 1043d5af96..530d8b220b 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -656,19 +656,20 @@ static void addCircleMaterial(const TopoView& topoView, center[1] = circleCenter[1]; const TopoView deviceTopologyView(topoView); - axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = deviceTopologyView.zone(zoneIndex); - auto vf = calculatePercentOverlapMonteCarlo(numSamples, - center, - circleRadius, - coordsetView[zone.getId(0)], - coordsetView[zone.getId(1)], - coordsetView[zone.getId(2)], - coordsetView[zone.getId(3)]); - greenView[zoneIndex] = vf; - blueView[zoneIndex] = 1.0 - vf; - }); + axom::for_all( + topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = deviceTopologyView.zone(zoneIndex); + auto vf = calculatePercentOverlapMonteCarlo(numSamples, + center, + circleRadius, + coordsetView[zone.getId(0)], + coordsetView[zone.getId(1)], + coordsetView[zone.getId(2)], + coordsetView[zone.getId(3)]); + greenView[zoneIndex] = vf; + blueView[zoneIndex] = 1.0 - vf; + }); // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; @@ -1224,7 +1225,8 @@ void addConcentricCircleMaterial(const TopoView& topoView, // Use the uniform sampling method to generate volume fractions for each material // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation const TopoView deviceTopologyView(topoView); - axom::for_all(topoView.numberOfZones(), + axom::for_all( + topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType eID) { const auto zone = deviceTopologyView.zone(eID); diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index 5935cb3b27..a096b9d239 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -216,8 +216,13 @@ class NodeToZoneRelationBuilder views::dispatch_unstructured_polyhedral_topology( topo, [&](auto AXOM_UNUSED_PARAM(shape), auto topoView) { - handlePolyhedralView(topoView, n_zones, n_sizes, n_offsets, nnodes, intTypeId); - }); + handlePolyhedralView(topoView, + n_zones, + n_sizes, + n_offsets, + nnodes, + intTypeId); + }); } else if(shape.is_polygonal() || shapeType == "mixed") { @@ -287,10 +292,11 @@ class NodeToZoneRelationBuilder // These are all structured topos of some sort. Make an unstructured representation and recurse. conduit::Node mesh; - axom::mir::utilities::blueprint::MakeUnstructured::execute(topo, - coordset, - "newtopo", - mesh); + axom::mir::utilities::blueprint::MakeUnstructured::execute( + topo, + coordset, + "newtopo", + mesh); // Recurse using the unstructured mesh. execute(mesh.fetch_existing("topologies/newtopo"), coordset, relation); @@ -318,7 +324,12 @@ class NodeToZoneRelationBuilder * lambda. */ template - void handlePolyhedralView(PHView topoView, conduit::Node &n_zones, conduit::Node &n_sizes, conduit::Node &n_offsets, axom::IndexType nnodes, int intTypeId) const + void handlePolyhedralView(PHView topoView, + conduit::Node &n_zones, + conduit::Node &n_sizes, + conduit::Node &n_offsets, + axom::IndexType nnodes, + int intTypeId) const { using reduce_policy = typename axom::execution_space::reduce_policy; @@ -333,13 +344,14 @@ class NodeToZoneRelationBuilder // Run through the topology once to do a count of each zone's unique node ids. RAJA::ReduceSum count(0); const PHView deviceTopologyView(topoView); - axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = deviceTopologyView.zone(zoneIndex); - const auto uniqueIds = zone.getUniqueIds(); - sizes_view[zoneIndex] = uniqueIds.size(); - count += uniqueIds.size(); - }); + axom::for_all( + topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = deviceTopologyView.zone(zoneIndex); + const auto uniqueIds = zone.getUniqueIds(); + sizes_view[zoneIndex] = uniqueIds.size(); + count += uniqueIds.size(); + }); const auto connSize = count.get(); // Do a scan on the size array to build an offset array. @@ -362,19 +374,15 @@ class NodeToZoneRelationBuilder n_zones, n_sizes, n_offsets, - [&](auto connectivityView, - auto zonesView, - auto sizesView, - auto offsetsView) { + [&](auto connectivityView, auto zonesView, auto sizesView, auto offsetsView) { fillZonesPH(topoView, connectivityView, zonesView, offsets_view); // Make the relation. using ViewType = decltype(connectivityView); - details::BuildRelation::execute( - connectivityView, - zonesView, - sizesView, - offsetsView); + details::BuildRelation::execute(connectivityView, + zonesView, + sizesView, + offsetsView); }); } @@ -391,22 +399,25 @@ class NodeToZoneRelationBuilder * lambda. */ template - void fillZonesPH(const TopologyView &topoView, IntegerView connectivityView, IntegerView zonesView, OffsetsView offsets_view) const + void fillZonesPH(const TopologyView &topoView, + IntegerView connectivityView, + IntegerView zonesView, + OffsetsView offsets_view) const { // Run through the data one more time to build the nodes and zones arrays. const TopologyView deviceTopologyView(topoView); - axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = deviceTopologyView.zone(zoneIndex); - const auto uniqueIds = zone.getUniqueIds(); - auto destIdx = offsets_view[zoneIndex]; - for(axom::IndexType i = 0; i < uniqueIds.size(); - i++, destIdx++) - { - connectivityView[destIdx] = uniqueIds[i]; - zonesView[destIdx] = zoneIndex; - } - }); + axom::for_all( + topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = deviceTopologyView.zone(zoneIndex); + const auto uniqueIds = zone.getUniqueIds(); + auto destIdx = offsets_view[zoneIndex]; + for(axom::IndexType i = 0; i < uniqueIds.size(); i++, destIdx++) + { + connectivityView[destIdx] = uniqueIds[i]; + zonesView[destIdx] = zoneIndex; + } + }); } /*! @@ -422,7 +433,10 @@ class NodeToZoneRelationBuilder * lambda. */ template - void fillZonesMixed(axom::IndexType nzones, IntegerView zonesView, IntegerView sizesView, IntegerView offsetsView) const + void fillZonesMixed(axom::IndexType nzones, + IntegerView zonesView, + IntegerView sizesView, + IntegerView offsetsView) const { using DataType = typename decltype(zonesView)::value_type; axom::for_all( @@ -445,7 +459,9 @@ class NodeToZoneRelationBuilder * lambda. */ template - void fillZones(IntegerView zonesView, axom::IndexType connSize, axom::IndexType nodesPerShape) const + void fillZones(IntegerView zonesView, + axom::IndexType connSize, + axom::IndexType nodesPerShape) const { axom::for_all( connSize, diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/RecenterField.hpp index 46684a4d95..7ffd2a8d69 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/RecenterField.hpp @@ -41,7 +41,8 @@ class RecenterField const conduit::Node &relation, conduit::Node &outField) const { - const std::string association = field.fetch_existing("association").as_string(); + const std::string association = + field.fetch_existing("association").as_string(); // Assume that we're flipping the association. outField["association"] = (association == "element") ? "vertex" : "element"; @@ -54,7 +55,9 @@ class RecenterField for(conduit::index_t c = 0; c < n_values.number_of_children(); c++) { const conduit::Node &n_comp = n_values[c]; - recenterSingleComponent(n_comp, relation, outField["values"][n_comp.name()]); + recenterSingleComponent(n_comp, + relation, + outField["values"][n_comp.name()]); } } else @@ -81,7 +84,7 @@ class RecenterField { // Get the data field for the o2m relation. const auto data_paths = conduit::blueprint::o2mrelation::data_paths(relation); - + // Use the o2mrelation to average data from n_comp to the n_out. const conduit::Node &n_relvalues = relation[data_paths[0]]; const conduit::Node &n_sizes = relation["sizes"]; @@ -97,10 +100,15 @@ class RecenterField n_out.set_allocator(c2a.getConduitAllocatorID()); n_out.set(conduit::DataType(n_comp.dtype().id(), relSize)); - views::Node_to_ArrayView_same(n_out, n_comp, [&](auto outView, auto compView) { - - recenterSingleComponentImpl(relView, sizesView, offsetsView, outView, compView); - }); + views::Node_to_ArrayView_same(n_out, + n_comp, + [&](auto outView, auto compView) { + recenterSingleComponentImpl(relView, + sizesView, + offsetsView, + outView, + compView); + }); }); } @@ -114,7 +122,11 @@ class RecenterField * \param compView The view that contains the source data. */ template - void recenterSingleComponentImpl(IndexView relView, IndexView sizesView, IndexView offsetsView, DataView outView, DataView compView) const + void recenterSingleComponentImpl(IndexView relView, + IndexView sizesView, + IndexView offsetsView, + DataView outView, + DataView compView) const { using Precision = typename DataView::value_type; using AccumType = diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index 725374a7f2..726977d4c9 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -70,7 +70,7 @@ class SelectedZones SLIC_ASSERT(zonesView.size() <= nzones); badValueCount = buildSelectedZones(zonesView, nzones); - }); + }); if(badValueCount > 0) { @@ -103,8 +103,7 @@ class SelectedZones template int buildSelectedZones(ZonesViewType zonesView, axom::IndexType nzones) { - using loop_policy = - typename axom::execution_space::loop_policy; + using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -122,8 +121,7 @@ class SelectedZones axom::for_all( szView.size(), AXOM_LAMBDA(axom::IndexType index) { - const int err = - (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; + const int err = (szView[index] < 0 || szView[index] >= nzones) ? 1 : 0; errReduce += err; }); diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index 99222889da..5c6f4770e9 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -77,19 +77,20 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); const TopologyView deviceTopologyView(m_topologyView); - axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = deviceTopologyView.zone(zoneIndex); - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); + axom::for_all( + m_topologyView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = deviceTopologyView.zone(zoneIndex); + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all zones that have 1 mat per node as clean. @@ -98,22 +99,23 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); - axom::for_all(m_topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = deviceTopologyView.zone(zoneIndex); - - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } - - const int ival = clean ? 1 : 0; - maskView[zoneIndex] = ival; - mask_reduce += ival; - }); + axom::for_all( + m_topologyView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = deviceTopologyView.zone(zoneIndex); + + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[zoneIndex] = ival; + mask_reduce += ival; + }); AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); @@ -211,21 +213,22 @@ class ZoneListBuilder // Determine max number of materials a node might touch. MatsetView deviceMatsetView(m_matsetView); const TopologyView deviceTopologyView(m_topologyView); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - - const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); - const auto nnodesThisZone = zone.numberOfNodes(); - int *nodeData = nMatsPerNodeView.data(); - for(axom::IndexType i = 0; i < nnodesThisZone; i++) - { - const auto nodeId = zone.getId(i); - int *nodePtr = nodeData + nodeId; - RAJA::atomicMax(nodePtr, nmats); - } - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + const int nmats = deviceMatsetView.numberOfMaterials(zoneIndex); + const auto nnodesThisZone = zone.numberOfNodes(); + int *nodeData = nMatsPerNodeView.data(); + for(axom::IndexType i = 0; i < nnodesThisZone; i++) + { + const auto nodeId = zone.getId(i); + int *nodePtr = nodeData + nodeId; + RAJA::atomicMax(nodePtr, nmats); + } + }); AXOM_ANNOTATE_END("nMatsPerNode"); // Now, mark all selected zones that have 1 mat per node as clean. @@ -234,23 +237,24 @@ class ZoneListBuilder axom::Array mask(nzones, nzones, allocatorID); auto maskView = mask.view(); RAJA::ReduceSum mask_reduce(0); - axom::for_all(selectedZonesView.size(), AXOM_LAMBDA(axom::IndexType szIndex) - { - const auto zoneIndex = selectedZonesView[szIndex]; - const auto zone = deviceTopologyView.zone(zoneIndex); - - bool clean = true; - const axom::IndexType nnodesThisZone = zone.numberOfNodes(); - for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) - { - const auto nodeId = zone.getId(i); - clean &= (nMatsPerNodeView[nodeId] == 1); - } - - const int ival = clean ? 1 : 0; - maskView[szIndex] = ival; - mask_reduce += ival; - }); + axom::for_all( + selectedZonesView.size(), + AXOM_LAMBDA(axom::IndexType szIndex) { + const auto zoneIndex = selectedZonesView[szIndex]; + const auto zone = deviceTopologyView.zone(zoneIndex); + + bool clean = true; + const axom::IndexType nnodesThisZone = zone.numberOfNodes(); + for(axom::IndexType i = 0; i < nnodesThisZone && clean; i++) + { + const auto nodeId = zone.getId(i); + clean &= (nMatsPerNodeView[nodeId] == 1); + } + + const int ival = clean ? 1 : 0; + maskView[szIndex] = ival; + mask_reduce += ival; + }); AXOM_ANNOTATE_END("mask"); const int nClean = mask_reduce.get(); diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index e31a01bf63..5c7619b987 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -277,7 +277,10 @@ void copy(conduit::Node &dest, const conduit::Node &src) * \param moveToHost Sometimes data are on device and need to be moved to host first. */ template -bool fillFromNode(const conduit::Node &n, const std::string &key, ArrayType &arr, bool moveToHost = false) +bool fillFromNode(const conduit::Node &n, + const std::string &key, + ArrayType &arr, + bool moveToHost = false) { bool found = false; if((found = n.has_path(key)) == true) @@ -450,8 +453,9 @@ struct minmax vmax.max(nview[index]); }); - return std::pair {static_cast(vmin.get()), - static_cast(vmax.get())}; + return std::pair { + static_cast(vmin.get()), + static_cast(vmax.get())}; } }; diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 1544b1eea4..2967ffb638 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -137,14 +137,14 @@ int runMIR(RuntimePolicy policy, #if defined(AXOM_USE_OPENMP) else if(policy == RuntimePolicy::omp) { -#pragma message "OMP supported" + #pragma message "OMP supported" retval = runMIR(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_CUDA) else if(policy == RuntimePolicy::cuda) { -#pragma message "CUDA supported" + #pragma message "CUDA supported" constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; retval = runMIR(mesh, options, resultMesh); @@ -153,7 +153,7 @@ int runMIR(RuntimePolicy policy, #if defined(AXOM_USE_HIP) else if(policy == RuntimePolicy::hip) { -#pragma message "HIP supported" + #pragma message "HIP supported" constexpr int HIP_BLOCK_SIZE = 64; using hip_exec = axom::HIP_EXEC; retval = runMIR(mesh, options, resultMesh); diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index a12b6cbbbe..6ecdfbdebb 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -152,7 +152,10 @@ struct test_make_unstructured conduit::Node deviceResult; bputils::MakeUnstructured uns; - uns.execute(deviceMesh["topologies/mesh"], deviceMesh["coordsets/coords"], "mesh", deviceResult); + uns.execute(deviceMesh["topologies/mesh"], + deviceMesh["coordsets/coords"], + "mesh", + deviceResult); // device->host conduit::Node hostResult; @@ -165,7 +168,10 @@ struct test_make_unstructured // Compare just the topologies constexpr double tolerance = 1.e-7; conduit::Node info; - bool success = compareConduit(expectedResult["topologies/mesh"], hostResult["topologies/mesh"], tolerance, info); + bool success = compareConduit(expectedResult["topologies/mesh"], + hostResult["topologies/mesh"], + tolerance, + info); if(!success) { info.print(); @@ -175,13 +181,13 @@ struct test_make_unstructured static void create(conduit::Node &mesh) { - std::vector dims{4, 4}; + std::vector dims {4, 4}; axom::mir::testing::data::braid("uniform", dims, mesh); } static void result(conduit::Node &mesh) { - std::vector dims{4, 4}; + std::vector dims {4, 4}; axom::mir::testing::data::braid("quads", dims, mesh); } }; @@ -336,7 +342,6 @@ TEST(mir_blueprint_utilities, n2zrel_unstructured_hip) } #endif - TEST(mir_blueprint_utilities, n2zrel_rectilinear_seq) { /* @@ -380,7 +385,8 @@ TEST(mir_blueprint_utilities, n2zrel_rectilinear_hip) #endif template -struct test_node_to_zone_relation_builder_polyhedral : public test_node_to_zone_relation_builder +struct test_node_to_zone_relation_builder_polyhedral + : public test_node_to_zone_relation_builder { using SuperClass = test_node_to_zone_relation_builder; @@ -464,38 +470,45 @@ struct test_node_to_zone_relation_builder_polyhedral : public test_node_to_zone_ "topologies/mesh/subelements/connectivity", "topologies/mesh/subelements/sizes", "topologies/mesh/subelements/offsets"}}); - } }; TEST(mir_blueprint_utilities, n2zrel_polyhedral_seq) { - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create(mesh); - test_node_to_zone_relation_builder_polyhedral::test(mesh); + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); } #if defined(AXOM_USE_OPENMP) TEST(mir_blueprint_utilities, n2zrel_polyhedral_omp) { - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create(mesh); - test_node_to_zone_relation_builder_polyhedral::test(mesh); + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); } #endif #if defined(AXOM_USE_CUDA) TEST(mir_blueprint_utilities, n2zrel_polyhedral_cuda) { - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create(mesh); - test_node_to_zone_relation_builder_polyhedral::test(mesh); + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); } #endif #if defined(AXOM_USE_HIP) TEST(mir_blueprint_utilities, n2zrel_polyhedral_hip) { - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create(mesh); - test_node_to_zone_relation_builder_polyhedral::test(mesh); + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); } #endif @@ -612,16 +625,14 @@ bool compare_views(const Container1 &a, const Container2 &b) std::cout << "a={"; for(axom::IndexType i = 0; i < a.size(); i++) { - if(i > 0) - std::cout << ", "; + if(i > 0) std::cout << ", "; std::cout << a[i]; } std::cout << "}" << std::endl; std::cout << "b={"; for(axom::IndexType i = 0; i < a.size(); i++) { - if(i > 0) - std::cout << ", "; + if(i > 0) std::cout << ", "; std::cout << b[i]; } std::cout << "}" << std::endl; @@ -678,8 +689,10 @@ struct test_matset_slice const axom::Array volume_fractions { {0.5, 0.5, 1.0, 0.8, 0.2}}; - EXPECT_EQ(conduit::DataType::INT64_ID, newHostMatset["material_ids"].dtype().id()); - EXPECT_EQ(conduit::DataType::FLOAT64_ID, newHostMatset["volume_fractions"].dtype().id()); + EXPECT_EQ(conduit::DataType::INT64_ID, + newHostMatset["material_ids"].dtype().id()); + EXPECT_EQ(conduit::DataType::FLOAT64_ID, + newHostMatset["volume_fractions"].dtype().id()); EXPECT_TRUE(compare_views( sizes.view(), @@ -693,9 +706,9 @@ struct test_matset_slice EXPECT_TRUE(compare_views( material_ids.view(), bputils::make_array_view(newHostMatset["material_ids"]))); - EXPECT_TRUE(compare_views( - volume_fractions.view(), - bputils::make_array_view(newHostMatset["volume_fractions"]))); + EXPECT_TRUE(compare_views(volume_fractions.view(), + bputils::make_array_view( + newHostMatset["volume_fractions"]))); } static void create(conduit::Node &matset) diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 21fabf702c..95d3ec0a44 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -604,7 +604,11 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Make the shape map. axom::Array values, ids; - auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, axom::execution_space::allocatorID()); + auto shapeMap = axom::mir::views::buildShapeMap( + n_device_topo, + values, + ids, + axom::execution_space::allocatorID()); using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; @@ -1003,7 +1007,11 @@ void braid3d_mixed_clip_test(const std::string &name) // Make the shape map. axom::Array values, ids; - auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, axom::execution_space::allocatorID()); + auto shapeMap = axom::mir::views::buildShapeMap( + n_device_topo, + values, + ids, + axom::execution_space::allocatorID()); TopoView topoView(connView, shapesView, sizesView, offsetsView, shapeMap); diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index e58c96a417..a55e1dd127 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -189,25 +189,35 @@ struct test_structured_topology_view_rectilinear // Make results view on device. constexpr int nzones = 9; - axom::Array results(nzones, nzones, axom::execution_space::allocatorID()); + axom::Array results( + nzones, + nzones, + axom::execution_space::allocatorID()); auto resultsView = results.view(); // Execute the kernel for each zone (find max node number in zone). - auto topoView = axom::mir::views::make_rectilinear<2>::view(deviceMesh["topologies/mesh"]); - axom::for_all(topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - const auto zone = topoView.zone(zoneIndex); - axom::IndexType m = -1; - for(const auto &id : zone.getIds()) - { - m = axom::utilities::max(static_cast(id), m); - } - resultsView[zoneIndex] = m; - }); + auto topoView = axom::mir::views::make_rectilinear<2>::view( + deviceMesh["topologies/mesh"]); + axom::for_all( + topoView.numberOfZones(), + AXOM_LAMBDA(axom::IndexType zoneIndex) { + const auto zone = topoView.zone(zoneIndex); + axom::IndexType m = -1; + for(const auto &id : zone.getIds()) + { + m = axom::utilities::max(static_cast(id), m); + } + resultsView[zoneIndex] = m; + }); // device->host - axom::Array hostResults(nzones, nzones, axom::execution_space::allocatorID()); - axom::copy(hostResults.data(), results.data(), nzones * sizeof(axom::IndexType)); + axom::Array hostResults( + nzones, + nzones, + axom::execution_space::allocatorID()); + axom::copy(hostResults.data(), + results.data(), + nzones * sizeof(axom::IndexType)); // Compare. const axom::IndexType expected[] = {5, 6, 7, 9, 10, 11, 13, 14, 15}; @@ -219,12 +229,11 @@ struct test_structured_topology_view_rectilinear static void create(conduit::Node &mesh) { - std::vector dims{4,4}; + std::vector dims {4, 4}; axom::mir::testing::data::braid("rectilinear", dims, mesh); } }; - TEST(mir_views, stopo_rectilinear_2d_seq) { test_structured_topology_view_rectilinear::test(); @@ -264,7 +273,6 @@ struct test_strided_structured axom::mir::views::select_dimensions(2)>( hostMesh["topologies/mesh"], [&](const std::string &AXOM_UNUSED_PARAM(shape), auto topoView) { - execute(coordsetView, topoView); }); }); @@ -289,12 +297,13 @@ struct test_strided_structured const int allocatorID = axom::execution_space::allocatorID(); axom::Array actualNodes(n4, n4, allocatorID); - axom::Array logicalNodes(n4*2, n4*2, allocatorID); + axom::Array logicalNodes(n4 * 2, n4 * 2, allocatorID); auto actualNodesView = actualNodes.view(); auto logicalNodesView = logicalNodes.view(); // Traverse the zones in the mesh and gather node ids - axom::for_all(topoView.numberOfZones(), + axom::for_all( + topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto zone = topoView.zone(zoneIndex); const auto nodeIndexing = topoView.indexing().expand(); @@ -308,8 +317,8 @@ struct test_strided_structured // Get the logical local id for the id. const auto index = nodeIndexing.GlobalToLocal(ids[i]); const auto logical = nodeIndexing.IndexToLogicalIndex(index); - logicalNodesView[(zoneIndex * 4 + i)*2 + 0] = logical[0]; - logicalNodesView[(zoneIndex * 4 + i)*2 + 1] = logical[1]; + logicalNodesView[(zoneIndex * 4 + i) * 2 + 0] = logical[0]; + logicalNodesView[(zoneIndex * 4 + i) * 2 + 1] = logical[1]; } }); @@ -327,8 +336,8 @@ struct test_strided_structured const auto pt = coordsetView[id]; // Get the logical local id for the id. - const auto logicalI = logicalNodesView[i*2 + 0]; - const auto logicalJ = logicalNodesView[i*2 + 1]; + const auto logicalI = logicalNodesView[i * 2 + 0]; + const auto logicalJ = logicalNodesView[i * 2 + 1]; // Expected coordinate double x = (3. + 1. / 3.) * static_cast(logicalI - 1); @@ -344,10 +353,7 @@ struct test_strided_structured } }; -TEST(mir_views, strided_structured_seq) -{ - test_strided_structured::test(); -} +TEST(mir_views, strided_structured_seq) { test_strided_structured::test(); } //------------------------------------------------------------------------------ template @@ -449,24 +455,32 @@ struct test_braid2d_mat TEST(mir_views, matset_unibuffer_seq) { - test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); + test_braid2d_mat::test("uniform", + "unibuffer", + "uniform2d_unibuffer"); } #if defined(AXOM_USE_OPENMP) TEST(mir_views, matset_unibuffer_omp) { - test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); + test_braid2d_mat::test("uniform", + "unibuffer", + "uniform2d_unibuffer"); } #endif #if defined(AXOM_USE_CUDA) TEST(mir_views, matset_unibuffer_cuda) { - test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); + test_braid2d_mat::test("uniform", + "unibuffer", + "uniform2d_unibuffer"); } #endif #if defined(AXOM_USE_HIP) TEST(mir_views, matset_unibuffer_hip) { - test_braid2d_mat::test("uniform", "unibuffer", "uniform2d_unibuffer"); + test_braid2d_mat::test("uniform", + "unibuffer", + "uniform2d_unibuffer"); } #endif diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 1bc43f25ce..586ac47417 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -230,8 +230,10 @@ class HashNaming assert(static_cast(p0) <= Max31Bit && static_cast(p1) <= Max31Bit); // Store p0 and p1 both in the 64-bit key as 31-bit integers - KeyType k0 = (static_cast(axom::utilities::min(p0, p1)) & Max31Bit); - KeyType k1 = (static_cast(axom::utilities::max(p0, p1)) & Max31Bit); + KeyType k0 = + (static_cast(axom::utilities::min(p0, p1)) & Max31Bit); + KeyType k1 = + (static_cast(axom::utilities::max(p0, p1)) & Max31Bit); return KeyIDPair | (k0 << 31) | k1; } diff --git a/src/axom/mir/views/ExplicitCoordsetView.hpp b/src/axom/mir/views/ExplicitCoordsetView.hpp index d0a1440b3e..c43f52d797 100644 --- a/src/axom/mir/views/ExplicitCoordsetView.hpp +++ b/src/axom/mir/views/ExplicitCoordsetView.hpp @@ -48,11 +48,11 @@ class ExplicitCoordsetView : m_coordinates {x, y} { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(x.size() == y.size()); -#else + #else SLIC_ASSERT_MSG(x.size() == y.size(), "Coordinate size mismatch."); -#endif + #endif #endif } @@ -80,13 +80,12 @@ class ExplicitCoordsetView PointType getPoint(IndexType vertex_index) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(vertex_index < size()); -#else - SLIC_ASSERT_MSG( - vertex_index < size(), - axom::fmt::format("Out of range index {}.", vertex_index)); -#endif + #else + SLIC_ASSERT_MSG(vertex_index < size(), + axom::fmt::format("Out of range index {}.", vertex_index)); + #endif #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index]}; @@ -137,12 +136,12 @@ class ExplicitCoordsetView : m_coordinates {x, y, z} { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(x.size() == y.size() && x.size() == z.size()); -#else + #else SLIC_ASSERT_MSG(x.size() == y.size() && x.size() == z.size(), "Coordinate size mismatch."); -#endif + #endif #endif } @@ -170,13 +169,12 @@ class ExplicitCoordsetView PointType getPoint(IndexType vertex_index) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(vertex_index < size()); -#else - SLIC_ASSERT_MSG( - vertex_index < size(), - axom::fmt::format("Out of range index {}.", vertex_index)); -#endif + #else + SLIC_ASSERT_MSG(vertex_index < size(), + axom::fmt::format("Out of range index {}.", vertex_index)); + #endif #endif const DataType X[3] = {m_coordinates[0][vertex_index], m_coordinates[1][vertex_index], diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 3f30793508..954795e0f0 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -50,24 +50,28 @@ constexpr int encode_types(Args... args) } #else template -constexpr int encode_types_impl(T arg) { - return arg; +constexpr int encode_types_impl(T arg) +{ + return arg; } template -constexpr int encode_types_impl(T arg, Args... args) { - return (arg | encode_types_impl(args...)); +constexpr int encode_types_impl(T arg, Args... args) +{ + return (arg | encode_types_impl(args...)); } template -constexpr int encode_types(Args... args) { - return encode_types_impl(args...); +constexpr int encode_types(Args... args) +{ + return encode_types_impl(args...); } #endif template -constexpr int select_types(Args... args) { - return encode_types((1 << args)...); +constexpr int select_types(Args... args) +{ + return encode_types((1 << args)...); } constexpr bool type_selected(int flag, int bit) { return flag & (1 << bit); } @@ -613,13 +617,13 @@ void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) } template -void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &... views) +void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &...views) { func(views...); } template -void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_internal(const conduit::Node &first, Args &&...args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -627,7 +631,7 @@ void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) } template -void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_internal(conduit::Node &first, Args &&...args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -640,7 +644,7 @@ void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int8_ptr()), @@ -650,13 +654,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int8( template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int16_ptr()), @@ -666,13 +670,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int16( template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int32_ptr()), @@ -682,13 +686,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int32( template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int64_ptr()), @@ -698,13 +702,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int64( template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint8_ptr()), @@ -714,13 +718,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint8( template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint16_ptr()), @@ -730,13 +734,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint16( template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint32_ptr()), @@ -746,13 +750,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint32( template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint64_ptr()), @@ -762,13 +766,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint64( template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_float32_ptr()), @@ -778,13 +782,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_float32( template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_float64_ptr()), @@ -794,14 +798,14 @@ std::enable_if_t Node_to_ArrayView_same_internal_float64( template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit::Node &first, - Args &&... args) + Args &&...args) { if(first.dtype().is_int8()) { @@ -864,7 +868,7 @@ template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node &first, - Args &&... args) + Args &&...args) { if(first.dtype().is_int8()) { @@ -925,13 +929,13 @@ void Node_to_ArrayView_same_internal(FuncType &&func, /// Reorder args template -void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&...args) { Node_to_ArrayView_same_internal(args..., first); } template -void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&...args) { Node_to_ArrayView_same_internal(args..., first); } @@ -957,13 +961,13 @@ void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) * */ template -void Node_to_ArrayView(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } template -void Node_to_ArrayView(conduit::Node &first, Args &&... args) +void Node_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } @@ -984,7 +988,7 @@ void Node_to_ArrayView(conduit::Node &first, Args &&... args) * */ template -void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -992,7 +996,7 @@ void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -1004,7 +1008,7 @@ void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) //------------------------------------------------------------------------------ template -void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1013,7 +1017,7 @@ void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1022,7 +1026,7 @@ void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1031,7 +1035,7 @@ void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1043,7 +1047,7 @@ void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) // Float Node to ArrayView. Handle float types. //------------------------------------------------------------------------------ template -void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1052,7 +1056,7 @@ void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1061,7 +1065,7 @@ void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1070,7 +1074,7 @@ void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 0be080522f..14558db531 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -59,7 +59,8 @@ struct PointTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 0; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 1; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 1; } @@ -101,7 +102,8 @@ struct LineTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 1; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 2; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 2; } @@ -149,7 +151,8 @@ struct TriTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 3; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 3; } @@ -198,7 +201,8 @@ struct QuadTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 2; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 4; } @@ -253,7 +257,8 @@ struct TetTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 4; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 3; } @@ -437,7 +442,8 @@ struct HexTraits AXOM_HOST_DEVICE constexpr static IndexType dimension() { return 3; } AXOM_HOST_DEVICE constexpr static IndexType numberOfNodes() { return 8; } - AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace(int AXOM_UNUSED_PARAM(faceIndex)) + AXOM_HOST_DEVICE constexpr static IndexType numberOfNodesInFace( + int AXOM_UNUSED_PARAM(faceIndex)) { return 4; } @@ -513,8 +519,7 @@ struct PolygonShape : public PolygonTraits /*! * \brief Construct a shape. */ - AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) - : m_ids(ids) { } + AXOM_HOST_DEVICE PolygonShape(const ConnectivityView &ids) : m_ids(ids) { } /*! * \brief Get the ids that make up this shape. @@ -565,11 +570,7 @@ struct Shape : public ShapeTraits /*! * \brief Construct a shape. */ - AXOM_HOST_DEVICE Shape() - : m_ids() - , m_faceIds() - { - } + AXOM_HOST_DEVICE Shape() : m_ids(), m_faceIds() { } /*! * \brief Construct a shape. @@ -606,21 +607,31 @@ struct Shape : public ShapeTraits * * \return The container for the ids that make up this shape. */ - AXOM_HOST_DEVICE ConnectivityStorageConstRef getIdsStorage() const { return m_ids; } + AXOM_HOST_DEVICE ConnectivityStorageConstRef getIdsStorage() const + { + return m_ids; + } /*! * \brief Get the ids that make up this shape as a view. * * \return A view containing the ids that make up this shape. */ - AXOM_HOST_DEVICE ConnectivityView getIds() const { return ConnectivityView(const_cast(m_ids.data()), m_ids.size()); } + AXOM_HOST_DEVICE ConnectivityView getIds() const + { + return ConnectivityView(const_cast(m_ids.data()), + m_ids.size()); + } /*! * \brief Get the unique ids that make up this shape. For basic shapes, assume they are unique. * * \return The unique ids that make up this shape. */ - AXOM_HOST_DEVICE ConnectivityStorageConstRef getUniqueIds() const { return m_ids; } + AXOM_HOST_DEVICE ConnectivityStorageConstRef getUniqueIds() const + { + return m_ids; + } /*! * \brief Get the ids for the requested face. @@ -631,21 +642,22 @@ struct Shape : public ShapeTraits */ /// @{ template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, ConnectivityStorageConstRef>::type - getFace(axom::IndexType AXOM_UNUSED_PARAM(faceIndex)) const + AXOM_HOST_DEVICE + typename std::enable_if<_ndims == 2, ConnectivityStorageConstRef>::type + getFace(axom::IndexType AXOM_UNUSED_PARAM(faceIndex)) const { return m_ids; } -// template -// AXOM_HOST_DEVICE typename std::enable_if<_ndims > 2, ConnectivityStorage>::type -// getFace(axom::IndexType faceIndex) const -// { -// const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); -// for(IndexType i = 0; i < nnodes; i++) -// m_faceIds[i] = m_ids[ShapeTraits::faces[faceIndex][i]]; -// return ConnectivityStorage(m_faceIds.m_data, nnodes); -// } + // template + // AXOM_HOST_DEVICE typename std::enable_if<_ndims > 2, ConnectivityStorage>::type + // getFace(axom::IndexType faceIndex) const + // { + // const auto nnodes = ShapeTraits::numberOfNodesInFace(faceIndex); + // for(IndexType i = 0; i < nnodes; i++) + // m_faceIds[i] = m_ids[ShapeTraits::faces[faceIndex][i]]; + // return ConnectivityStorage(m_faceIds.m_data, nnodes); + // } /// @} private: @@ -662,25 +674,53 @@ struct Shape : public ShapeTraits */ /// @{ template -using LineShape = Shape::value, axom::ArrayView, ConnType>::type>; +using LineShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using TriShape = Shape::value, axom::ArrayView, ConnType>::type>; +using TriShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using QuadShape = Shape::value, axom::ArrayView, ConnType>::type>; +using QuadShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using TetShape = Shape::value, axom::ArrayView, ConnType>::type>; +using TetShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using PyramidShape = Shape::value, axom::ArrayView, ConnType>::type>; +using PyramidShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using WedgeShape = Shape::value, axom::ArrayView, ConnType>::type>; +using WedgeShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; template -using HexShape = Shape::value, axom::ArrayView, ConnType>::type>; +using HexShape = + Shape::value, + axom::ArrayView, + ConnType>::type>; /// @} /*! diff --git a/src/axom/mir/views/StructuredTopologyView.hpp b/src/axom/mir/views/StructuredTopologyView.hpp index 2fa2a76b3f..2cd6e4c2b9 100644 --- a/src/axom/mir/views/StructuredTopologyView.hpp +++ b/src/axom/mir/views/StructuredTopologyView.hpp @@ -34,28 +34,36 @@ class StructuredTopologyView using Shape1D = LineShape>; using Shape2D = QuadShape>; using Shape3D = HexShape>; - using ShapeType = typename std::conditional::type>::type; + using ShapeType = typename std::conditional< + IndexingPolicy::dimension() == 3, + Shape3D, + typename std::conditional::type>::type; /*! * \brief Return the number of dimensions. * * \return The number of dimensions. */ - AXOM_HOST_DEVICE constexpr static int dimension() { return IndexingPolicy::dimension(); } + AXOM_HOST_DEVICE constexpr static int dimension() + { + return IndexingPolicy::dimension(); + } /*! * \brief Constructor */ - AXOM_HOST_DEVICE StructuredTopologyView() : m_zoneIndexing(), m_nodeIndexing() { } + AXOM_HOST_DEVICE StructuredTopologyView() : m_zoneIndexing(), m_nodeIndexing() + { } /*! * \brief Constructor * * \param indexing The indexing policy for the topology (num zones in each dimension). */ - AXOM_HOST_DEVICE StructuredTopologyView(const IndexingPolicy &indexing) : m_zoneIndexing(indexing), m_nodeIndexing(indexing.expand()) - { - } + AXOM_HOST_DEVICE StructuredTopologyView(const IndexingPolicy &indexing) + : m_zoneIndexing(indexing) + , m_nodeIndexing(indexing.expand()) + { } /*! * \brief Return the number of zones. @@ -108,7 +116,10 @@ class StructuredTopologyView * * \return The indexing object. */ - AXOM_HOST_DEVICE const IndexingPolicy &indexing() const { return m_zoneIndexing; } + AXOM_HOST_DEVICE const IndexingPolicy &indexing() const + { + return m_zoneIndexing; + } /*! * \brief Return a zone. @@ -120,15 +131,15 @@ class StructuredTopologyView * \note 3D implementation. */ template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, Shape3D>::type - zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 3, Shape3D>::type zone( + axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = m_nodeIndexing.jStride(); @@ -136,7 +147,8 @@ class StructuredTopologyView Shape3D shape; auto &data = shape.getIdsStorage(); - data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -158,22 +170,23 @@ class StructuredTopologyView * \note 2D implementation. */ template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, Shape2D>::type - zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 2, Shape2D>::type zone( + axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); const auto jp = m_nodeIndexing.jStride(); Shape2D shape; auto &data = shape.getIdsStorage(); - data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; data[2] = data[1] + jp; data[3] = data[2] - 1; @@ -191,21 +204,22 @@ class StructuredTopologyView * \note 1D implementation. */ template - AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, Shape1D>::type - zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE typename std::enable_if<_ndims == 1, Shape1D>::type zone( + axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif const auto localLogical = m_zoneIndexing.IndexToLogicalIndex(zoneIndex); Shape1D shape; auto &data = shape.getIdsStorage(); - data[0] = m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); + data[0] = + m_nodeIndexing.GlobalToGlobal(m_nodeIndexing.LocalToGlobal(localLogical)); data[1] = data[0] + 1; return shape; diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index e5cb493541..0e08f1d591 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -122,19 +122,19 @@ class UnstructuredTopologyMixedShapeView , m_shapeMap(shapemap) { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(m_shapes.size() != 0); assert(m_sizes.size() != 0); assert(m_offsets.size() != 0); assert(m_offsets.size() == m_sizes.size() && m_offsets.size() == m_shapes.size()); -#else + #else SLIC_ASSERT(m_shapes.size() != 0); SLIC_ASSERT(m_sizes.size() != 0); SLIC_ASSERT(m_offsets.size() != 0); SLIC_ASSERT(m_offsets.size() == m_sizes.size() && m_offsets.size() == m_shapes.size()); -#endif + #endif #endif } @@ -157,7 +157,10 @@ class UnstructuredTopologyMixedShapeView * * \return The size of the connectivity. */ - AXOM_HOST_DEVICE IndexType connectivitySize() const { return m_connectivity.size(); } + AXOM_HOST_DEVICE IndexType connectivitySize() const + { + return m_connectivity.size(); + } /*! * \brief Return a zone. @@ -169,22 +172,21 @@ class UnstructuredTopologyMixedShapeView AXOM_HOST_DEVICE ShapeType zone(axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); + #endif #endif -#endif - const ConnectivityView shapeData( - m_connectivity.data() + m_offsets[zoneIndex], - m_sizes[zoneIndex]); + const ConnectivityView shapeData(m_connectivity.data() + m_offsets[zoneIndex], + m_sizes[zoneIndex]); const auto shapeID = m_shapeMap[m_shapes[zoneIndex]]; #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(shapeID >= Point_ShapeID && shapeID <= Mixed_ShapeID); -#else + #else SLIC_ASSERT(shapeID >= Point_ShapeID && shapeID <= Mixed_ShapeID); -#endif + #endif #endif return ShapeType(shapeID, shapeData); @@ -195,7 +197,7 @@ class UnstructuredTopologyMixedShapeView ConnectivityView m_shapes; ConnectivityView m_sizes; ConnectivityView m_offsets; - ShapeMap m_shapeMap; + ShapeMap m_shapeMap; }; } // end namespace views diff --git a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp index f1eb91b8ac..647b177250 100644 --- a/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyPolyhedralView.hpp @@ -238,11 +238,11 @@ class UnstructuredTopologyPolyhedralView AXOM_HOST_DEVICE ShapeType zone(axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif return ShapeType(m_data, zoneIndex); diff --git a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp index fb7624446d..9bf9db4bce 100644 --- a/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologySingleShapeView.hpp @@ -58,11 +58,11 @@ class UnstructuredTopologySingleShapeView , m_offsetsView(offsets) { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(m_offsetsView.size() == m_sizesView.size()); -#else + #else SLIC_ASSERT(m_offsetsView.size() == m_sizesView.size()); -#endif + #endif #endif } @@ -71,7 +71,10 @@ class UnstructuredTopologySingleShapeView * * \return The dimension of the shape. */ - AXOM_HOST_DEVICE static constexpr int dimension() { return ShapeT::dimension(); } + AXOM_HOST_DEVICE static constexpr int dimension() + { + return ShapeT::dimension(); + } /*! * \brief Return the number of zones. @@ -90,7 +93,10 @@ class UnstructuredTopologySingleShapeView * * \return The size of the connectivity. */ - AXOM_HOST_DEVICE IndexType connectivitySize() const { return m_connectivityView.size(); } + AXOM_HOST_DEVICE IndexType connectivitySize() const + { + return m_connectivityView.size(); + } /*! * \brief Return a zone. @@ -101,44 +107,46 @@ class UnstructuredTopologySingleShapeView */ /// @{ template - AXOM_HOST_DEVICE typename std::enable_if<_variable_size, ShapeType>::type - zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE typename std::enable_if<_variable_size, ShapeType>::type zone( + axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif - return ShapeType(ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], - m_sizesView[zoneIndex])); + return ShapeType( + ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], + m_sizesView[zoneIndex])); } template - AXOM_HOST_DEVICE typename std::enable_if::type - zone(axom::IndexType zoneIndex) const + AXOM_HOST_DEVICE typename std::enable_if::type zone( + axom::IndexType zoneIndex) const { #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(zoneIndex < numberOfZones()); -#else + #else SLIC_ASSERT(zoneIndex < numberOfZones()); -#endif + #endif #endif ConnectivityView shapeIdsView {}; if(m_sizesView.empty()) { shapeIdsView = ConnectivityView( - m_connectivityView.data() + ShapeType::zoneOffset(zoneIndex), - ShapeType::numberOfNodes()); + m_connectivityView.data() + ShapeType::zoneOffset(zoneIndex), + ShapeType::numberOfNodes()); } else { - shapeIdsView = ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], - m_sizesView[zoneIndex]); + shapeIdsView = + ConnectivityView(m_connectivityView.data() + m_offsetsView[zoneIndex], + m_sizesView[zoneIndex]); } return ShapeType(shapeIdsView); } diff --git a/src/axom/mir/views/dispatch_rectilinear_topology.hpp b/src/axom/mir/views/dispatch_rectilinear_topology.hpp index c6ee23964c..c1b4abb557 100644 --- a/src/axom/mir/views/dispatch_rectilinear_topology.hpp +++ b/src/axom/mir/views/dispatch_rectilinear_topology.hpp @@ -148,9 +148,9 @@ namespace internal template struct dispatch_one_rectilinear_topology { - static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), + FuncType &&AXOM_UNUSED_PARAM(func)) + { } }; /*! @@ -216,7 +216,7 @@ struct dispatch_one_rectilinear_topology } }; -} // end namespace internal +} // end namespace internal /*! * \brief Creates a topology view compatible with rectilinear topologies and passes that view to the supplied function. @@ -237,13 +237,22 @@ void dispatch_rectilinear_topology(const conduit::Node &topo, FuncType &&func) switch(axes.size()) { case 3: - internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); + internal::dispatch_one_rectilinear_topology< + dimension_selected(SelectedDimensions, 3), + 3, + FuncType>::execute(topo, std::forward(func)); break; case 2: - internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); + internal::dispatch_one_rectilinear_topology< + dimension_selected(SelectedDimensions, 2), + 2, + FuncType>::execute(topo, std::forward(func)); break; case 1: - internal::dispatch_one_rectilinear_topology::execute(topo, std::forward(func)); + internal::dispatch_one_rectilinear_topology< + dimension_selected(SelectedDimensions, 1), + 1, + FuncType>::execute(topo, std::forward(func)); break; default: break; diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index 831b207fc4..aae7bd17b2 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -299,9 +299,9 @@ namespace internal template struct dispatch_only_structured_topology { - static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), + FuncType &&AXOM_UNUSED_PARAM(func)) + { } }; /*! @@ -404,9 +404,9 @@ struct dispatch_only_structured_topology template struct dispatch_any_structured_topology { - static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), + FuncType &&AXOM_UNUSED_PARAM(func)) + { } }; /*! @@ -519,7 +519,7 @@ struct dispatch_any_structured_topology } }; -} // end namespace internal +} // end namespace internal /*! * \brief Creates a topology view compatible with structured topologies and passes that view to the supplied function. @@ -540,13 +540,22 @@ void dispatch_structured_topology(const conduit::Node &topo, FuncType &&func) switch(ndims) { case 3: - internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_only_structured_topology< + dimension_selected(SelectedDimensions, 3), + 3, + FuncType>::execute(topo, std::forward(func)); break; case 2: - internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_only_structured_topology< + dimension_selected(SelectedDimensions, 2), + 2, + FuncType>::execute(topo, std::forward(func)); break; case 1: - internal::dispatch_only_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_only_structured_topology< + dimension_selected(SelectedDimensions, 1), + 1, + FuncType>::execute(topo, std::forward(func)); break; default: break; @@ -571,13 +580,22 @@ void dispatch_structured_topologies(const conduit::Node &topo, FuncType &&func) switch(ndims) { case 3: - internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_any_structured_topology< + dimension_selected(SelectedDimensions, 3), + 3, + FuncType>::execute(topo, std::forward(func)); break; case 2: - internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_any_structured_topology< + dimension_selected(SelectedDimensions, 2), + 2, + FuncType>::execute(topo, std::forward(func)); break; case 1: - internal::dispatch_any_structured_topology::execute(topo, std::forward(func)); + internal::dispatch_any_structured_topology< + dimension_selected(SelectedDimensions, 1), + 1, + FuncType>::execute(topo, std::forward(func)); break; default: break; diff --git a/src/axom/mir/views/dispatch_uniform_topology.hpp b/src/axom/mir/views/dispatch_uniform_topology.hpp index 7441663b0d..1bb4dc121a 100644 --- a/src/axom/mir/views/dispatch_uniform_topology.hpp +++ b/src/axom/mir/views/dispatch_uniform_topology.hpp @@ -147,9 +147,9 @@ namespace internal template struct dispatch_one_uniform_topology { - static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + static void execute(const conduit::Node &AXOM_UNUSED_PARAM(topo), + FuncType &&AXOM_UNUSED_PARAM(func)) + { } }; /*! @@ -215,7 +215,7 @@ struct dispatch_one_uniform_topology } }; -} // end namespace internal +} // end namespace internal /*! * \brief Creates a topology view compatible with uniform topologies and passes that view to the supplied function. @@ -236,13 +236,22 @@ void dispatch_uniform_topology(const conduit::Node &topo, FuncType &&func) switch(n_dims.dtype().number_of_elements()) { case 3: - internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); + internal::dispatch_one_uniform_topology< + dimension_selected(SelectedDimensions, 3), + 3, + FuncType>::execute(topo, std::forward(func)); break; case 2: - internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); + internal::dispatch_one_uniform_topology< + dimension_selected(SelectedDimensions, 2), + 2, + FuncType>::execute(topo, std::forward(func)); break; case 1: - internal::dispatch_one_uniform_topology::execute(topo, std::forward(func)); + internal::dispatch_one_uniform_topology< + dimension_selected(SelectedDimensions, 1), + 1, + FuncType>::execute(topo, std::forward(func)); break; default: break; diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 323e56551d..066dd8cf83 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -127,7 +127,8 @@ void dispatch_unstructured_mixed_topology(const conduit::Node &topo, // Get the allocator that allocated the connectivity. The shape map data // need to go into the same memory space. - const int allocatorID = axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); + const int allocatorID = axom::getAllocatorIDForAddress( + topo["elements/connectivity"].data_ptr()); // Make the shape map. axom::Array values, ids; @@ -161,7 +162,8 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, // Get the allocator that allocated the connectivity. The shape map data // need to go into the same memory space. - const int allocatorID = axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); + const int allocatorID = + axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); // Make the shape map. axom::Array values, ids; @@ -186,18 +188,21 @@ constexpr int encode_shapes(Args... args) } #else template -constexpr int encode_shapes_impl(T arg) { - return arg; +constexpr int encode_shapes_impl(T arg) +{ + return arg; } template -constexpr int encode_shapes_impl(T arg, Args... args) { - return (arg | encode_shapes_impl(args...)); +constexpr int encode_shapes_impl(T arg, Args... args) +{ + return (arg | encode_shapes_impl(args...)); } template -constexpr int encode_shapes(Args... args) { - return encode_shapes_impl(args...); +constexpr int encode_shapes(Args... args) +{ + return encode_shapes_impl(args...); } #endif @@ -228,14 +233,14 @@ struct dispatch_shape /*! * \brief Execute method that gets generated when a shape is not enabled or supported. Do nothing. */ - static void execute(bool &AXOM_UNUSED_PARAM(eligible), - const std::string &AXOM_UNUSED_PARAM(shape), - const axom::ArrayView &AXOM_UNUSED_PARAM(connView), - const axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), - const axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), - FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + static void execute( + bool &AXOM_UNUSED_PARAM(eligible), + const std::string &AXOM_UNUSED_PARAM(shape), + const axom::ArrayView &AXOM_UNUSED_PARAM(connView), + const axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), + const axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), + FuncType &&AXOM_UNUSED_PARAM(func)) + { } /*! * \brief Execute method that gets generated when a shape is not enabled or supported. Do nothing. @@ -244,8 +249,7 @@ struct dispatch_shape const std::string &AXOM_UNUSED_PARAM(shape), const conduit::Node &AXOM_UNUSED_PARAM(topo), FuncType &&AXOM_UNUSED_PARAM(func)) - { - } + { } }; // Partial specializations that make views for various shape types. @@ -262,10 +266,9 @@ struct dispatch_shape, FuncType> { if(eligible && shape == "tri") { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); + UnstructuredTopologySingleShapeView> ugView(connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -306,10 +309,9 @@ struct dispatch_shape, FuncType> { if(eligible && shape == "tet") { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); + UnstructuredTopologySingleShapeView> ugView(connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } @@ -372,17 +374,17 @@ struct dispatch_shape, FuncType> { if(eligible && shape == "hex") { - UnstructuredTopologySingleShapeView> ugView( - connView, - sizesView, - offsetsView); + UnstructuredTopologySingleShapeView> ugView(connView, + sizesView, + offsetsView); func(shape, ugView); eligible = false; } } }; -struct SelectMixedShape {}; +struct SelectMixedShape +{ }; template struct dispatch_shape @@ -394,13 +396,16 @@ struct dispatch_shape { if(eligible && shape == "mixed") { - typed_dispatch_unstructured_mixed_topology(topo, std::forward(func)); + typed_dispatch_unstructured_mixed_topology( + topo, + std::forward(func)); eligible = false; } } }; -struct SelectPHShape {}; +struct SelectPHShape +{ }; template struct dispatch_shape @@ -412,13 +417,15 @@ struct dispatch_shape { if(eligible && shape == "polyhedral") { - typed_dispatch_unstructured_polyhedral_topology(topo, std::forward(func)); + typed_dispatch_unstructured_polyhedral_topology( + topo, + std::forward(func)); eligible = false; } } }; -} // end namespace internal +} // end namespace internal //------------------------------------------------------------------------------ /*! @@ -445,10 +452,22 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, bool eligible = true; // Conditionally add polyhedron support. - internal::dispatch_shape::execute4(eligible, shape, topo, std::forward(func)); + internal::dispatch_shape::execute4(eligible, + shape, + topo, + std::forward(func)); // Conditionally add mixed shape support. - internal::dispatch_shape::execute4(eligible, shape, topo, std::forward(func)); + internal::dispatch_shape::execute4(eligible, + shape, + topo, + std::forward(func)); // Make sizes / offsets views if the values are present. axom::ArrayView sizesView, offsetsView; @@ -460,12 +479,60 @@ void typed_dispatch_unstructured_topology(const conduit::Node &topo, topo.fetch_existing("elements/offsets")); // Conditionally add support for other shapes. - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); - internal::dispatch_shape, FuncType>::execute(eligible, shape, connView, sizesView, offsetsView, std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); + internal::dispatch_shape, + FuncType>::execute(eligible, + shape, + connView, + sizesView, + offsetsView, + std::forward(func)); // TODO: points, lines, polygon } diff --git a/src/axom/mir/views/dispatch_utilities.hpp b/src/axom/mir/views/dispatch_utilities.hpp index 62e456f706..b180da4d6e 100644 --- a/src/axom/mir/views/dispatch_utilities.hpp +++ b/src/axom/mir/views/dispatch_utilities.hpp @@ -22,18 +22,21 @@ constexpr int encode_dimensions(Dimensions... dims) } #else template -constexpr int encode_dimensions_impl(T arg) { - return arg; +constexpr int encode_dimensions_impl(T arg) +{ + return arg; } template -constexpr int encode_dimensions_impl(T arg, Dimensions... dims) { - return (arg | encode_dimensions_impl(dims...)); +constexpr int encode_dimensions_impl(T arg, Dimensions... dims) +{ + return (arg | encode_dimensions_impl(dims...)); } template -constexpr int encode_dimensions(Dimensions... dims) { - return encode_dimensions_impl(dims...); +constexpr int encode_dimensions(Dimensions... dims) +{ + return encode_dimensions_impl(dims...); } #endif From 6011470322c72d6e01554ba2a07c8e44c0e600b3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 21:32:46 -0700 Subject: [PATCH 249/290] Do not default construct on host if memory was device-allocated. --- src/axom/core/Array.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/axom/core/Array.hpp b/src/axom/core/Array.hpp index a868787471..b5971b4f5d 100644 --- a/src/axom/core/Array.hpp +++ b/src/axom/core/Array.hpp @@ -1081,7 +1081,8 @@ Array::Array(IndexType num_elements, #endif m_allocator_id = axom::detail::getAllocatorID(); } - initialize(num_elements, capacity); + bool default_construct = axom::detail::getAllocatorSpace(m_allocator_id) != MemorySpace::Device; + initialize(num_elements, capacity, default_construct); } //------------------------------------------------------------------------------ From ad20d9b6e3cb470a6cf24ea86f79ae22c980d426 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 21:34:25 -0700 Subject: [PATCH 250/290] nvcc compatibility --- src/axom/mir/ClipField.hpp | 28 +++++++++++++++++----------- src/axom/mir/blueprint_utilities.hpp | 7 ++++--- src/axom/mir/tests/mir_clipfield.cpp | 3 ++- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 2fbeb940ae..c7b46ddab4 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -424,13 +424,17 @@ class ClipField n_input.fetch_existing("coordsets/" + coordsetName); const conduit::Node &n_fields = n_input.fetch_existing("fields"); + conduit::Node &n_newTopo = n_output["topologies/" + opts.topologyName(topoName)]; + conduit::Node &n_newCoordset = n_output["coordsets/" + opts.coordsetName(coordsetName)]; + conduit::Node &n_newFields = n_output["fields"]; + execute(n_topo, n_coordset, n_fields, n_options, - n_output["topologies/" + opts.topologyName(topoName)], - n_output["coordsets/" + opts.coordsetName(coordsetName)], - n_output["fields"]); + n_newTopo, + n_newCoordset, + n_newFields); } /*! @@ -458,10 +462,11 @@ class ClipField const auto allocatorID = axom::execution_space::allocatorID(); AXOM_ANNOTATE_SCOPE("ClipField"); + const std::string newTopologyName = n_newTopo.name(); // Reset the output nodes just in case they've been reused. - n_newTopo.reset(); - n_newCoordset.reset(); - n_newFields.reset(); + n_newTopo = conduit::Node(); + n_newCoordset = conduit::Node(); + n_newFields = conduit::Node(); // Make the selected zones and get the size. ClipOptions opts(n_options); @@ -636,6 +641,7 @@ class ClipField fragmentData, opts, selectedZones, + newTopologyName, n_newTopo, n_newCoordset, n_newFields); @@ -708,7 +714,7 @@ class ClipField makeFields(blend, slice, - opts.topologyName(n_topo.name()), + newTopologyName, fieldsToProcess, n_fields, n_newFields); @@ -720,7 +726,7 @@ class ClipField n_newTopo, n_newFields); - markNewNodes(blend, newNodes, opts.topologyName(n_topo.name()), n_newFields); + markNewNodes(blend, newNodes, newTopologyName, n_newFields); } // The following members are private (unless using CUDA) @@ -1248,6 +1254,7 @@ class ClipField * \param[in] fragmentData This object holds views to per-fragment data. * \param[in] opts Clipping options. * \param[in] selectedZones The selected zones. + * \param[in] newTopologyName The name of the new topology. * \param[out] n_newTopo The node that will contain the new topology. * \param[out] n_newCoordset The node that will contain the new coordset. * \param[out] n_newFields The node that will contain the new fields. @@ -1261,6 +1268,7 @@ class ClipField FragmentData fragmentData, const ClipOptions &opts, const SelectedZones &selectedZones, + const std::string &newTopologyName, conduit::Node &n_newTopo, conduit::Node &n_newCoordset, conduit::Node &n_newFields) const @@ -1271,7 +1279,6 @@ class ClipField const auto selection = getSelection(opts); AXOM_ANNOTATE_BEGIN("allocation"); - n_newTopo.reset(); n_newTopo["type"] = "unstructured"; n_newTopo["coordset"] = n_newCoordset.name(); @@ -1305,7 +1312,7 @@ class ClipField // Allocate a color variable to keep track of the "color" of the fragments. conduit::Node &n_color = n_newFields[opts.colorField()]; - n_color["topology"] = opts.topologyName(n_newTopo.name()); + n_color["topology"] = newTopologyName; n_color["association"] = "element"; conduit::Node &n_color_values = n_color["values"]; n_color_values.set_allocator(conduitAllocatorID); @@ -1752,7 +1759,6 @@ class ClipField axom::mir::utilities::blueprint:: CoordsetBlender cb; - n_newCoordset.reset(); cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); } diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 5c7619b987..9c5d8c2306 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -176,8 +176,9 @@ class ConduitAllocateThroughAxom */ static conduit::index_t getConduitAllocatorID() { - static conduit::index_t conduitAllocatorID = -1; - if(conduitAllocatorID == -1) + constexpr conduit::index_t NoAllocator = -1; + static conduit::index_t conduitAllocatorID = NoAllocator; + if(conduitAllocatorID == NoAllocator) { conduitAllocatorID = conduit::utils::register_allocator(internal_allocate, internal_free); @@ -259,7 +260,7 @@ void copy(conduit::Node &dest, const conduit::Node &src) } else { - // The node data fits in the node. It's on the host. + // The data fits in the node or is a string. It's on the host. dest.set(src); } } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 95d3ec0a44..46e4631ba2 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -593,7 +593,8 @@ void braid2d_clip_test(const std::string &type, const std::string &name) options["fields/new_radial"] = "new_radial2"; conduit::Node deviceClipMixedMesh; - if(n_device_topo.fetch_existing("elements/shape").as_string() == "mixed") + if(n_device_topo.has_path("elements/shape") && + n_device_topo.fetch_existing("elements/shape").as_string() == "mixed") { auto shapesView = bputils::make_array_view( n_device_topo.fetch_existing("elements/shapes")); From e9769306b457ca350a8f07e869f7c343e3eeaf91 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Sat, 28 Sep 2024 21:37:19 -0700 Subject: [PATCH 251/290] make style --- src/axom/core/Array.hpp | 3 ++- src/axom/mir/ClipField.hpp | 13 +++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/axom/core/Array.hpp b/src/axom/core/Array.hpp index b5971b4f5d..5b61188111 100644 --- a/src/axom/core/Array.hpp +++ b/src/axom/core/Array.hpp @@ -1081,7 +1081,8 @@ Array::Array(IndexType num_elements, #endif m_allocator_id = axom::detail::getAllocatorID(); } - bool default_construct = axom::detail::getAllocatorSpace(m_allocator_id) != MemorySpace::Device; + bool default_construct = + axom::detail::getAllocatorSpace(m_allocator_id) != MemorySpace::Device; initialize(num_elements, capacity, default_construct); } diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index c7b46ddab4..c8c6224135 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -424,8 +424,10 @@ class ClipField n_input.fetch_existing("coordsets/" + coordsetName); const conduit::Node &n_fields = n_input.fetch_existing("fields"); - conduit::Node &n_newTopo = n_output["topologies/" + opts.topologyName(topoName)]; - conduit::Node &n_newCoordset = n_output["coordsets/" + opts.coordsetName(coordsetName)]; + conduit::Node &n_newTopo = + n_output["topologies/" + opts.topologyName(topoName)]; + conduit::Node &n_newCoordset = + n_output["coordsets/" + opts.coordsetName(coordsetName)]; conduit::Node &n_newFields = n_output["fields"]; execute(n_topo, @@ -712,12 +714,7 @@ class ClipField slice.m_indicesView = sliceIndicesView; } - makeFields(blend, - slice, - newTopologyName, - fieldsToProcess, - n_fields, - n_newFields); + makeFields(blend, slice, newTopologyName, fieldsToProcess, n_fields, n_newFields); makeOriginalElements(fragmentData, opts, From d5bfe00d0018ae070caa1ee0bdd9cfeecbec3ccf Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 10:19:55 -0700 Subject: [PATCH 252/290] Moved some files to future and removed from build. --- src/axom/mir/CMakeLists.txt | 3 --- src/axom/mir/EquiZAlgorithm.hpp | 1 + src/axom/mir/MakeUnstructured.hpp | 1 + src/axom/mir/{ => future}/ClipFieldFilter.cpp | 0 src/axom/mir/{ => future}/ClipFieldFilter.hpp | 0 .../mir/{ => future}/ClipFieldFilterDevice.hpp | 0 src/axom/mir/future/README.md | 17 +++++++++++++++++ 7 files changed, 19 insertions(+), 3 deletions(-) rename src/axom/mir/{ => future}/ClipFieldFilter.cpp (100%) rename src/axom/mir/{ => future}/ClipFieldFilter.hpp (100%) rename src/axom/mir/{ => future}/ClipFieldFilterDevice.hpp (100%) create mode 100644 src/axom/mir/future/README.md diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index d61e3040bd..ab8d728d71 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -29,8 +29,6 @@ set(mir_headers blueprint_utilities.hpp ClipField.hpp - ClipFieldFilterDevice.hpp - ClipFieldFilter.hpp BlendGroupBuilder.hpp CoordsetBlender.hpp @@ -86,7 +84,6 @@ set(mir_sources CellClipper.cpp CellGenerator.cpp - ClipFieldFilter.cpp blueprint_utilities.cpp MIRAlgorithm.cpp EquiZAlgorithm.cpp diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index a18ebdfb92..fe2bfed9a1 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -11,6 +11,7 @@ #include "axom/slic.hpp" // Include these directly for now. +#include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/MaterialView.hpp" #include "axom/mir/MIRAlgorithm.hpp" #include "axom/mir/RecenterField.hpp" diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/MakeUnstructured.hpp index c91db72635..44bd4917a0 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/MakeUnstructured.hpp @@ -9,6 +9,7 @@ #include "axom/mir/views/NodeArrayView.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/views/dispatch_structured_topology.hpp" #include diff --git a/src/axom/mir/ClipFieldFilter.cpp b/src/axom/mir/future/ClipFieldFilter.cpp similarity index 100% rename from src/axom/mir/ClipFieldFilter.cpp rename to src/axom/mir/future/ClipFieldFilter.cpp diff --git a/src/axom/mir/ClipFieldFilter.hpp b/src/axom/mir/future/ClipFieldFilter.hpp similarity index 100% rename from src/axom/mir/ClipFieldFilter.hpp rename to src/axom/mir/future/ClipFieldFilter.hpp diff --git a/src/axom/mir/ClipFieldFilterDevice.hpp b/src/axom/mir/future/ClipFieldFilterDevice.hpp similarity index 100% rename from src/axom/mir/ClipFieldFilterDevice.hpp rename to src/axom/mir/future/ClipFieldFilterDevice.hpp diff --git a/src/axom/mir/future/README.md b/src/axom/mir/future/README.md new file mode 100644 index 0000000000..8120d96f76 --- /dev/null +++ b/src/axom/mir/future/README.md @@ -0,0 +1,17 @@ +These classes instantiate ``axom::mir::ClipField`` for available devices and mesh +types. They were moved to the ``future`` directory for now since they can take +significant time to compile. + +====================== +ClipFieldFilterDevice +====================== + +This class instantiates ClipField for relevant mesh/coordset types on a specific device. + +====================== +ClipFieldFilter +====================== + +This class instantiates ClipFieldFilterDevice for all supported device types and +makes the policy selectable at runtime. + From 048eb1b81b00cf0bb2b3adf1aaf7fd0b7b2b120d Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 10:45:17 -0700 Subject: [PATCH 253/290] cleanup --- src/axom/mir/views/MaterialView.hpp | 203 ++++++---------------------- 1 file changed, 40 insertions(+), 163 deletions(-) diff --git a/src/axom/mir/views/MaterialView.hpp b/src/axom/mir/views/MaterialView.hpp index b854d2ad9b..a61717bfb7 100644 --- a/src/axom/mir/views/MaterialView.hpp +++ b/src/axom/mir/views/MaterialView.hpp @@ -7,7 +7,6 @@ #define AXOM_MIR_VIEWS_MATERIAL_VIEW_HPP_ #include "axom/core.hpp" -//#include "axom/mir/views/Shapes.hpp" #include @@ -51,6 +50,13 @@ MaterialInformation materials(const conduit::Node &matset); //--------------------------------------------------------------------------- /*! + \brief Material view for unibuffer matsets. + + \tparam IndexT The integer type used for material data. + \tparam FloatT The floating point type used for material data (volume fractions). + \tparam MAXMATERIALS The maximum number of materials to support. + + \verbatum matsets: matset: @@ -65,6 +71,7 @@ MaterialInformation materials(const conduit::Node &matset); offsets: [0, 2, 4] indices: [1, 4, 6, 3, 2] + \endverbatum */ template class UnibufferMaterialView @@ -168,6 +175,13 @@ class UnibufferMaterialView }; /*! + \brief View for multi-buffer matsets. + + \tparam IndexT The integer type used for material data. + \tparam FloatT The floating point type used for material data (volume fractions). + \tparam MAXMATERIALS The maximum number of materials to support. + + \verbatum matsets: matset: @@ -182,6 +196,8 @@ class UnibufferMaterialView material_map: # (optional) a: 0 b: 1 + + \endverbatum */ template class MultiBufferMaterialView @@ -279,6 +295,14 @@ class MultiBufferMaterialView }; /*! + \brief View for element-dominant matsets. + + \tparam IndexT The integer type used for material data. + \tparam FloatT The floating point type used for material data (volume fractions). + \tparam MAXMATERIALS The maximum number of materials to support. + + \verbatum + matsets: matset: topology: topology @@ -290,6 +314,8 @@ class MultiBufferMaterialView a: 0 b: 1 c: 2 + + \endverbatum */ template class ElementDominantMaterialView @@ -379,6 +405,14 @@ class ElementDominantMaterialView }; /*! + \brief View for material-dominant matsets. + + \tparam IndexT The integer type used for material data. + \tparam FloatT The floating point type used for material data (volume fractions). + \tparam MAXMATERIALS The maximum number of materials to support. + + \verbatum + matsets: matset: topology: topology @@ -394,9 +428,12 @@ class ElementDominantMaterialView a: 0 b: 1 c: 2 - */ -/// NOTES: This matset type does not seem so GPU friendly since there is some work to do for some of the queries. + \endverbatum + + \note This matset type does not seem so GPU friendly since there is some work to do for some of the queries. + + */ template class MaterialDominantMaterialView { @@ -519,166 +556,6 @@ class MaterialDominantMaterialView axom::IndexType m_nzones {0}; }; -#if 0 -//--------------------------------------------------------------------------- -// Some host-algorithms on material views. -//--------------------------------------------------------------------------- - -/*! - */ -template -axom::Array makeMatsPerZone(const MaterialDominantMaterialView &view, axom::IndexType nzones) -{ - // Figure out the number of materials per zone. - axom::Array matsPerZone(nzones, nzones, axom::getAllocatorID()); - auto matsPerZone_view = matsPerZone.view(); - axom::forall(0, nzones, AXOM_LAMBDA(int i) - { - matsPerZone_view[i] = 0; - }); - for(axom::IndexType mi = 0; mi < m_size; mi++) - { - auto element_ids_view = m_element_ids[mi].view(); - axom::forall(0, nzones, AXOM_LAMBDA(int i) - { - matsPerZone_view[element_ids_view[i]]++; - }); - } - return matsPerZone; -} - -// NOTE: This needs to be a method of MaterialDominantMaterialView to access the view data. -template -axom::Array selectZones(const MaterialDominantMaterialView &view, Predicate &&pred) -{ - const auto nzones = view.numberOfZones(); - - // Figure out the number of materials per zone. - axom::Array matsPerZone = makeMatsPerZone(view, nzones); - auto matsPerZone_view = matsPerZone.view(); - - // Count the clean zones. - RAJA::ReduceSum num_selected(0); - axom::forall(0, nzones, AXOM_LAMBDA(int i) - { - num_selected += pred(matsPerZone_view[i]) ? 1 : 0; - }); - axom::IndexType outsize = num_selected.get(); - - // Make an offset array that records where each thread can write its data. - axom::Array offsets(nzones); - RAJA::inclusive_scan(RAJA::make_span(zones, zones.size()), - RAJA::make_span(offsets, offsets.size())); - - // Make a list of the selected output zones. - axom::Array zonelist(outsize, outsize, axom::getAllocatorID()); - auto zonelist_view = zonelist.view(); - axom::forall(0, nzones, AXOM_LAMBDA(int zi) - { - if(pred(matsPerZone_view[zi])) - zonelist_view[offset[zi]] = zi; - }); - - return zonelist; -} - -template -axom::Array selectZonesContainingMaterial(const MaterialDominantMaterialView &view, MaterialIndex mat) -{ - const auto zones_view = view.selectZonesContainingMaterial(mat); - axom::Array zones(zones_view.size(), zones_view.size(), axom::getAllocatorID()); - axom::copy(zones.data(), zones_view.data(), zones_view.size() * sizeof(int)); - return zones; -} - -template -axom::Array selectCleanZones(const MaterialDominantMaterialView &view) -{ - auto predicate = [](int nmats) -> bool { return nmats == 1; }; - return selectZones(view, predicate); -} - -template -axom::Array selectMixedZones(const MaterialDominantMaterialView &view) -{ - auto predicate = [](int nmats) -> bool { return nmats > 1; }; - return selectZones(view, predicate); -} - -//--------------------------------------------------------------------------- -template -axom::Array selectZones(const UnibufferMaterialView &view, MaterialIndex mat, Predicate &&pred) const -{ -/*! - NOTE: I really do not like the code below because it forces the main Axom algorithm to use RAJA directly. - In the case of the reducer, I'd prefer to do this: - - auto reducer = axom::execution_space::make_ReduceSum(0); - - Then we could write the algorithm so we do RAJA things but we could make a reducer object for the serial non-RAJA case that does nothing. - */ - const axom::IndexType nzones = view.numberOfZones(); - - using REDUCE_POL = typename axom::execution_space::reduce_policy; - RAJA::ReduceSum num_selected(0); - axom::Array zones(nzones); - auto zones_view = zones.view(); - axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) - { - const int haveMat = pred(view, mat, zi) ? 1 : 0; - zones_view[zi] = haveMat; - num_selected += haveMat; - }); - axom::IndexType outsize = num_selected.get(); - - // Make an offset array that records where each thread can write its data. - axom::Array offsets(nzones); - RAJA::inclusive_scan(RAJA::make_span(zones, zones.size()), - RAJA::make_span(offsets, offsets.size())); - - // Make a list of the selected output zones. - axom::Array zonelist(outsize); - auto zonelist_view = zonelist.view(); - axom::forall(0, zones.size(), AXOM_LAMBDA(int zi) - { - if(zones[zi] > 0) - zonelist_view[offset[zi]] = zi; - }); - - return zonelist; -} - -template -axom::Array selectZonesContainingMaterial(const UnibufferMaterialView &view, MaterialIndex mat) -{ - auto findMaterial = [](const UnibufferMaterialView &deviceView, MaterialIndex deviceMat, int zi) - { - return deviceView.zoneContainsMaterial(zi, deviceMat); - }; - return selectZones(view, mat, findMaterial); -} - -template -axom::Array selectCleanZones(const UnibufferMaterialView &view) -{ - auto zoneIsClean = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) - { - return view.numberOfMaterials(zi) == 1; - }; - return selectZones(view, 0, zoneIsClean); -} - -template -axom::Array selectMixedZones(const UnibufferMaterialView &view) -{ - auto zoneIsMixed = [](const UnibufferMaterialView &deviceView, MaterialIndex /*mat*/, int zi) - { - return view.numberOfMaterials(zi) > 1; - }; - return selectZones(view, 0, zoneIsClean); -} -#endif - } // end namespace views } // end namespace mir } // end namespace axom From d935dad12df995a7c6ab5c71c68a02e58ec454b3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 10:45:26 -0700 Subject: [PATCH 254/290] fix warning --- src/axom/mir/MeshTester.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 530d8b220b..ae6f6c17df 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -1541,22 +1541,22 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) mir::Point2 v2 = cellData.m_mapData .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 2]]; - mir::Point2 v3 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; + //mir::Point2 v3 = + // cellData.m_mapData + // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; - mir::Point2 v4 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; + //mir::Point2 v4 = + // cellData.m_mapData + // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; mir::Point2 v5 = cellData.m_mapData .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 5]]; - mir::Point2 v6 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; - mir::Point2 v7 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; + //mir::Point2 v6 = + // cellData.m_mapData + // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; + //mir::Point2 v7 = + // cellData.m_mapData + // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; From 49725ec3509d2e4373614dc385bd7554feecc33e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 10:45:36 -0700 Subject: [PATCH 255/290] New GPU porting tips --- src/docs/sphinx/dev_guide/gpu_porting.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/docs/sphinx/dev_guide/gpu_porting.rst b/src/docs/sphinx/dev_guide/gpu_porting.rst index 4f38585555..43941c2e0e 100644 --- a/src/docs/sphinx/dev_guide/gpu_porting.rst +++ b/src/docs/sphinx/dev_guide/gpu_porting.rst @@ -310,6 +310,10 @@ Do NOT do this: } }; +Avoid calling lambdas from kernels. This can work on some systems and not on others. +For best odds at a portable algorithm, design your kernel so it is "one level deep", +and does not result in calling other functions that are also marked ``AXOM_LAMBDA``. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RAJA::kernel @@ -511,6 +515,15 @@ General, Rough Porting Tips * Utilize ``printf()`` for debugging output * Try using the ``SEQ_EXEC`` execution space +* If your kernel compiles/links but fails at runtime, the cause is often: + + * The data arrays referenced in the kernel are not in the correct memory space for the kernel. + * A reference to host memory has been captured for use in the kernel. + * There is nothing wrong with your code and the tooling has failed. In this case, try + separating the ``axom::for_all`` and your kernel into a separate method or function. + This can limit the available objects that the compile will attempt to capture and + increase the likelihood that the kernel will run correctly. + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Useful Links ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d3282afa34c1e71bc8e236a5c436968c8ba7033c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 13:35:31 -0700 Subject: [PATCH 256/290] Removed some old tests --- src/axom/mir/tests/CMakeLists.txt | 4 - src/axom/mir/tests/mir_cell_clipper.cpp | 1052 ----------------- src/axom/mir/tests/mir_cell_generator.cpp | 241 ---- .../mir/tests/mir_interface_reconstructor.cpp | 29 - src/axom/mir/tests/mir_mesh.cpp | 225 ---- 5 files changed, 1551 deletions(-) delete mode 100644 src/axom/mir/tests/mir_cell_clipper.cpp delete mode 100644 src/axom/mir/tests/mir_cell_generator.cpp delete mode 100644 src/axom/mir/tests/mir_interface_reconstructor.cpp delete mode 100644 src/axom/mir/tests/mir_mesh.cpp diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index 08f3bfd18b..b4e1b9897c 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -12,13 +12,9 @@ #------------------------------------------------------------------------------ set(gtest_mir_tests - mir_mesh.cpp - mir_cell_clipper.cpp mir_clipfield.cpp - mir_utilities.cpp mir_blueprint_utilities.cpp mir_views_indexing.cpp - mir_cell_generator.cpp mir_views.cpp mir_equiz.cpp ) diff --git a/src/axom/mir/tests/mir_cell_clipper.cpp b/src/axom/mir/tests/mir_cell_clipper.cpp deleted file mode 100644 index c95be0dd83..0000000000 --- a/src/axom/mir/tests/mir_cell_clipper.cpp +++ /dev/null @@ -1,1052 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_CELL_CLIPPER_TEST_H_ -#define MIR_CELL_CLIPPER_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -#include - -using namespace axom; - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_triangle_cases) -{ - mir::Shape shape = mir::Shape::Triangle; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(auto actualClippingCase = 0; actualClippingCase < numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_quad_cases) -{ - mir::Shape shape = mir::Shape::Quad; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(auto actualClippingCase = 0; actualClippingCase < numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_tetrahedron_cases) -{ - mir::Shape shape = mir::Shape::Tetrahedron; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < (unsigned int)numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_pyramid_cases) -{ - mir::Shape shape = mir::Shape::Pyramid; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < (unsigned int)numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_triangular_prism_cases) -{ - mir::Shape shape = mir::Shape::Triangular_Prism; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < (unsigned int)numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_case, all_hexahedron_cases) -{ - mir::Shape shape = mir::Shape::Hexahedron; - int numVerts = mir::utilities::numVerts(shape); - int numCases = pow(2, numVerts); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < (unsigned int)numCases; - ++actualClippingCase) - { - std::vector matOneVF; - std::vector matTwoVF; - - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue = 0.0; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - mir::CellClipper clipper; - unsigned int computedClippingCase = - clipper.determineClippingCase(shape, matOneVF, matTwoVF); - - EXPECT_EQ(computedClippingCase, actualClippingCase); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_place, clip_edge_when_mat_one_dominates) -{ - axom::float64 vfMatOneVertexOne = 0.0; - axom::float64 vfMatTwoVertexOne = 1.0; - - axom::float64 vfMatOneVertexTwo = 0.0; - axom::float64 vfMatTwoVertexTwo = 1.0; - - mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, - vfMatTwoVertexOne, - vfMatOneVertexTwo, - vfMatTwoVertexTwo); - - EXPECT_DOUBLE_EQ(tValue, 0.0); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_place, clip_edge_when_mat_two_dominates) -{ - axom::float64 vfMatOneVertexOne = 1.0; - axom::float64 vfMatTwoVertexOne = 0.0; - - axom::float64 vfMatOneVertexTwo = 1.0; - axom::float64 vfMatTwoVertexTwo = 0.0; - - mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, - vfMatTwoVertexOne, - vfMatOneVertexTwo, - vfMatTwoVertexTwo); - - EXPECT_DOUBLE_EQ(tValue, 0.0); -} - -// //---------------------------------------------------------------------- - -TEST(mir_clipping_place, clip_edge_in_middle) -{ - axom::float64 vfMatOneVertexOne = 1.0; - axom::float64 vfMatTwoVertexOne = 0.0; - - axom::float64 vfMatOneVertexTwo = 0.0; - axom::float64 vfMatTwoVertexTwo = 1.0; - - mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, - vfMatTwoVertexOne, - vfMatOneVertexTwo, - vfMatTwoVertexTwo); - - EXPECT_DOUBLE_EQ(tValue, 0.5); -} - -// //---------------------------------------------------------------------- - -TEST(mir_clipping_place, clip_edge_with_one_null_material) -{ - axom::float64 vfMatOneVertexOne = -1.0; - axom::float64 vfMatTwoVertexOne = 0.0; - - axom::float64 vfMatOneVertexTwo = -1.0; - axom::float64 vfMatTwoVertexTwo = 0.0; - - mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, - vfMatTwoVertexOne, - vfMatOneVertexTwo, - vfMatTwoVertexTwo); - - EXPECT_DOUBLE_EQ(tValue, 0.0); -} - -// //---------------------------------------------------------------------- - -TEST(mir_clipping_place, clip_edge_with_two_null_material) -{ - axom::float64 vfMatOneVertexOne = -1.0; - axom::float64 vfMatTwoVertexOne = -1.0; - - axom::float64 vfMatOneVertexTwo = -1.0; - axom::float64 vfMatTwoVertexTwo = -1.0; - - mir::CellClipper clipper; - axom::float64 tValue = clipper.computeTValueOnEdge(vfMatOneVertexOne, - vfMatTwoVertexOne, - vfMatOneVertexTwo, - vfMatTwoVertexTwo); - - EXPECT_DOUBLE_EQ(tValue, 0.0); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_zero) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 0.0, 0.0}; - std::vector matTwoVF = {1.0, 1.0, 1.0, 1.0}; - - std::vector> vertexVF; - vertexVF.push_back(matOneVF); - vertexVF.push_back(matTwoVF); - - std::map> newElements; - std::map> newVertices; - axom::float64 tValues[8] = {0}; - - mir::CellClipper clipper; - clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ(1, newElements.size()); - EXPECT_EQ(4, newVertices.size()); - - EXPECT_EQ(0, newElements[0][0]); - EXPECT_EQ(1, newElements[0][1]); - EXPECT_EQ(2, newElements[0][2]); - EXPECT_EQ(3, newElements[0][3]); - - EXPECT_EQ(0, newVertices[0][0]); - EXPECT_EQ(0, newVertices[1][0]); - EXPECT_EQ(0, newVertices[2][0]); - EXPECT_EQ(0, newVertices[3][0]); - - for(int i = 0; i < 8; ++i) - { - EXPECT_DOUBLE_EQ(0, tValues[i]); - } -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_one) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 0.0, 1.0}; - std::vector matTwoVF = {1.0, 1.0, 1.0, 0.0}; - - std::vector> vertexVF; - vertexVF.push_back(matOneVF); - vertexVF.push_back(matTwoVF); - - std::map> newElements; - std::map> newVertices; - axom::float64 tValues[8] = {0}; - - mir::CellClipper clipper; - clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ(3, newElements.size()); - EXPECT_EQ(6, newVertices.size()); - - // Check the first element - EXPECT_EQ(3, newElements[0][0]); - EXPECT_EQ(7, newElements[0][1]); - EXPECT_EQ(6, newElements[0][2]); - - // Check the second element - EXPECT_EQ(7, newElements[1][0]); - EXPECT_EQ(0, newElements[1][1]); - EXPECT_EQ(2, newElements[1][2]); - EXPECT_EQ(6, newElements[1][3]); - - // Check the third element - EXPECT_EQ(0, newElements[2][0]); - EXPECT_EQ(1, newElements[2][1]); - EXPECT_EQ(2, newElements[2][2]); - - // Check each vertex's associated elements - EXPECT_EQ(1, newVertices[0][0]); - EXPECT_EQ(2, newVertices[0][1]); - - EXPECT_EQ(2, newVertices[1][0]); - - EXPECT_EQ(1, newVertices[2][0]); - EXPECT_EQ(2, newVertices[2][1]); - - EXPECT_EQ(0, newVertices[3][0]); - - EXPECT_EQ(0, newVertices[6][0]); - EXPECT_EQ(1, newVertices[6][1]); - - EXPECT_EQ(0, newVertices[7][0]); - EXPECT_EQ(1, newVertices[7][1]); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_three) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 0.0, 1.0, 1.0}; - std::vector matTwoVF = {1.0, 1.0, 0.0, 0.0}; - - std::vector> vertexVF; - vertexVF.push_back(matOneVF); - vertexVF.push_back(matTwoVF); - - std::map> newElements; - std::map> newVertices; - axom::float64 tValues[8] = {0}; - - mir::CellClipper clipper; - clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ(2, newElements.size()); - EXPECT_EQ(6, newVertices.size()); - - EXPECT_EQ(0, newElements[0][0]); - EXPECT_EQ(1, newElements[0][1]); - EXPECT_EQ(5, newElements[0][2]); - EXPECT_EQ(7, newElements[0][3]); - - EXPECT_EQ(7, newElements[1][0]); - EXPECT_EQ(5, newElements[1][1]); - EXPECT_EQ(2, newElements[1][2]); - EXPECT_EQ(3, newElements[1][3]); - - EXPECT_EQ(0, newVertices[0][0]); - EXPECT_EQ(0, newVertices[1][0]); - EXPECT_EQ(1, newVertices[2][0]); - EXPECT_EQ(1, newVertices[3][0]); - EXPECT_EQ(0, newVertices[5][0]); - EXPECT_EQ(1, newVertices[5][1]); - EXPECT_EQ(0, newVertices[7][0]); - EXPECT_EQ(1, newVertices[7][1]); -} - -//---------------------------------------------------------------------- - -TEST(mir_clipping_cell_and_vertex_output, clip_quad_case_five) -{ - mir::Shape shape = mir::Shape::Quad; - std::vector matOneVF = {0.0, 1.0, 0.0, 1.0}; - std::vector matTwoVF = {1.0, 0.0, 1.0, 0.0}; - - std::vector> vertexVF; - vertexVF.push_back(matOneVF); - vertexVF.push_back(matTwoVF); - - std::map> newElements; - std::map> newVertices; - axom::float64 tValues[8] = {0}; - - mir::CellClipper clipper; - clipper.computeClippingPoints(shape, vertexVF, newElements, newVertices, tValues); - - EXPECT_EQ(4, newElements.size()); - EXPECT_EQ(8, newVertices.size()); - - EXPECT_EQ(4, newElements[0][0]); - EXPECT_EQ(1, newElements[0][1]); - EXPECT_EQ(5, newElements[0][2]); - - EXPECT_EQ(0, newElements[1][0]); - EXPECT_EQ(4, newElements[1][1]); - EXPECT_EQ(5, newElements[1][2]); - EXPECT_EQ(2, newElements[1][3]); - - EXPECT_EQ(0, newElements[2][0]); - EXPECT_EQ(2, newElements[2][1]); - EXPECT_EQ(6, newElements[2][2]); - EXPECT_EQ(7, newElements[2][3]); - - EXPECT_EQ(7, newElements[3][0]); - EXPECT_EQ(6, newElements[3][1]); - EXPECT_EQ(3, newElements[3][2]); - - EXPECT_EQ(1, newVertices[0][0]); - EXPECT_EQ(2, newVertices[0][1]); - EXPECT_EQ(0, newVertices[1][0]); - EXPECT_EQ(1, newVertices[2][0]); - EXPECT_EQ(2, newVertices[2][1]); - EXPECT_EQ(3, newVertices[3][0]); - EXPECT_EQ(0, newVertices[4][0]); - EXPECT_EQ(1, newVertices[4][1]); - EXPECT_EQ(0, newVertices[5][0]); - EXPECT_EQ(1, newVertices[5][1]); - EXPECT_EQ(2, newVertices[6][0]); - EXPECT_EQ(3, newVertices[6][1]); - EXPECT_EQ(2, newVertices[7][0]); - EXPECT_EQ(3, newVertices[7][1]); -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, triangle_meshes) -{ - mir::Shape shape = mir::Shape::Triangle; - int numVerts = mir::utilities::numVerts(shape); - - for(auto actualClippingCase = 0u; - actualClippingCase < mir::triangleClipTableVec.size(); - ++actualClippingCase) - { - // Initialize the mesh - int numElements = 1; - int numVertices = 3; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2}; - topology.m_evBegins = {0, 3}; - topology.m_veInds = {0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = {mir::Point2::make_point(0.5, 0.717), - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Triangle}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_triangle_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, quad_meshes) -{ - mir::Shape shape = mir::Shape::Quad; - int numVerts = mir::utilities::numVerts(shape); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < mir::quadClipTableVec.size(); - ++actualClippingCase) - { - // Initialize the mesh - int numElements = 1; - int numVertices = 4; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2, 3}; - topology.m_evBegins = {0, 4}; - topology.m_veInds = {0, 0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3, 4}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = {mir::Point2::make_point(0.0, 1.0), - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0), - mir::Point2::make_point(1.0, 1.0)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Quad}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_quad_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, tetrahedron_meshes) -{ - mir::Shape shape = mir::Shape::Tetrahedron; - int numVerts = mir::utilities::numVerts(shape); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < mir::tetrahedronClipTableVec.size(); - ++actualClippingCase) - { - // Initialize the mesh - int numElements = 1; - int numVertices = 4; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2, 3}; - topology.m_evBegins = {0, 4}; - topology.m_veInds = {0, 0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3, 4}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(auto bitIndex = 0; bitIndex < numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = { - mir::Point2::make_point(0.5, 0.717, 0.0), - mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(0.5, 0.3585, 0.717)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Tetrahedron}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_tetrahedron_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, pyramid_meshes) -{ - mir::Shape shape = mir::Shape::Pyramid; - int numVerts = mir::utilities::numVerts(shape); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < mir::pyramidClipTableVec.size(); - ++actualClippingCase) - { - { - // Initialize the mesh - int numElements = 1; - int numVertices = 5; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2, 3, 4}; - topology.m_evBegins = {0, 5}; - topology.m_veInds = {0, 0, 0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3, 4, 5}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = { - mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 1.0, 0.0), - mir::Point2::make_point(0.0, 1.0, 0.0), - mir::Point2::make_point(0.5, 0.5, 0.717)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Pyramid}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_pyramid_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } - } -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, triangular_prism_meshes) -{ - mir::Shape shape = mir::Shape::Triangular_Prism; - int numVerts = mir::utilities::numVerts(shape); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < mir::triangularPrismClipTableVec.size(); - ++actualClippingCase) - { - // Initialize the mesh - int numElements = 1; - int numVertices = 6; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2, 3, 4, 5}; - topology.m_evBegins = {0, 6}; - topology.m_veInds = {0, 0, 0, 0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3, 4, 5, 6}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = {mir::Point2::make_point(0.5, 0.717, 0.0), - mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(0.5, 0.717, 2.0), - mir::Point2::make_point(0.0, 0.0, 2.0), - mir::Point2::make_point(1.0, 0.0, 2.0)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Triangular_Prism}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_triangular_prism_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } -} - -//---------------------------------------------------------------------- - -TEST(clipping_table_mesh_generation, hexahedron_meshes) -{ - mir::Shape shape = mir::Shape::Hexahedron; - int numVerts = mir::utilities::numVerts(shape); - - for(unsigned int actualClippingCase = 0; - actualClippingCase < mir::hexahedronClipTableVec.size(); - ++actualClippingCase) - { - // Initialize the mesh - int numElements = 1; - int numVertices = 8; - - // Create the mesh connectivity information - mir::CellTopologyData topology; - topology.m_evInds = {0, 1, 2, 3, 4, 5, 6, 7}; - topology.m_evBegins = {0, 8}; - topology.m_veInds = {0, 0, 0, 0, 0, 0, 0, 0}; - topology.m_veBegins = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Calculate the vertex volume fractions needed to clip with the current case - std::vector matOneVF; - std::vector matTwoVF; - std::string bitString(""); - for(unsigned int bitIndex = 0; bitIndex < (unsigned int)numVerts; ++bitIndex) - { - unsigned int shiftedBit = 1; - shiftedBit = shiftedBit << (numVerts - 1 - bitIndex); - - axom::float64 matOneValue; - if(actualClippingCase & shiftedBit) - { - matOneValue = 1.0; - bitString += "1"; - } - else - { - matOneValue = 0.0; - bitString += "0"; - } - - matOneVF.push_back(matOneValue); - matTwoVF.push_back(1.0 - matOneValue); - } - - std::vector> vertexVF = {matOneVF, matTwoVF}; - - std::vector> elementVF = {{0.5}, {0.5}}; - - std::vector points = {mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 1.0, 0.0), - mir::Point2::make_point(0.0, 1.0, 0.0), - mir::Point2::make_point(0.0, 0.0, 1.0), - mir::Point2::make_point(1.0, 0.0, 1.0), - mir::Point2::make_point(1.0, 1.0, 1.0), - mir::Point2::make_point(0.0, 1.0, 1.0)}; - - mir::CellMapData mapData; - mapData.m_elementDominantMaterials = {mir::NULL_MAT}; - mapData.m_elementParents = {0}; - mapData.m_vertexPositions = points; - mapData.m_shapeTypes = {mir::Shape::Hexahedron}; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, 2, topology, mapData, elementVF); - testMesh.constructMeshVolumeFractionsVertex(vertexVF); - - // Clip the mesh using the actualClippingCase index - mir::MIRMesh outputMesh; - mir::InterfaceReconstructor interfaceReconstructor; - interfaceReconstructor.computeReconstructedInterface(testMesh, outputMesh); - - // Write out the processed mesh - std::string dirName = std::string(AXOM_BIN_DIR) + "/meshes"; - std::string fileName = "mir_clippingcase_hexahedron_" + - std::to_string(actualClippingCase) + "_" + bitString + ".vtk"; - outputMesh.writeMeshToFile(dirName, fileName, "/"); - } -} - -//---------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::SimpleLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - -#endif // MIR_CELL_CLIPPER_TEST_H_ diff --git a/src/axom/mir/tests/mir_cell_generator.cpp b/src/axom/mir/tests/mir_cell_generator.cpp deleted file mode 100644 index ec80922a70..0000000000 --- a/src/axom/mir/tests/mir_cell_generator.cpp +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_CELL_GENERATOR_TEST_H_ -#define MIR_CELL_GENERATOR_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -using namespace axom; - -//---------------------------------------------------------------------- - -TEST(mir_cell_generator, generate_quad_topology) -{ - EXPECT_EQ(true, 1); - // this function generates the evInds, evBegins, ... etc given the map of elements -> verts, and verts -> elements - std::map> elementMap; - elementMap[0] = {4, 1, 5}; - elementMap[1] = {0, 4, 5, 2}; - elementMap[2] = {0, 2, 6, 7}; - elementMap[3] = {7, 6, 3}; - - std::map> vertexMap; - vertexMap[0] = {1, 2}; - vertexMap[1] = {0}; - vertexMap[2] = {1, 2}; - vertexMap[3] = {3}; - vertexMap[4] = {0, 1}; - vertexMap[5] = {0, 1}; - vertexMap[6] = {2, 3}; - vertexMap[7] = {2, 3}; - - mir::CellData cellData; - mir::CellGenerator generator; - generator.generateTopologyData(elementMap, vertexMap, cellData); - - EXPECT_EQ(4, cellData.m_topology.m_evInds[0]); - EXPECT_EQ(1, cellData.m_topology.m_evInds[1]); - EXPECT_EQ(5, cellData.m_topology.m_evInds[2]); - EXPECT_EQ(0, cellData.m_topology.m_evInds[3]); - EXPECT_EQ(4, cellData.m_topology.m_evInds[4]); - EXPECT_EQ(5, cellData.m_topology.m_evInds[5]); - EXPECT_EQ(2, cellData.m_topology.m_evInds[6]); - EXPECT_EQ(0, cellData.m_topology.m_evInds[7]); - EXPECT_EQ(2, cellData.m_topology.m_evInds[8]); - EXPECT_EQ(6, cellData.m_topology.m_evInds[9]); - EXPECT_EQ(7, cellData.m_topology.m_evInds[10]); - EXPECT_EQ(7, cellData.m_topology.m_evInds[11]); - EXPECT_EQ(6, cellData.m_topology.m_evInds[12]); - EXPECT_EQ(3, cellData.m_topology.m_evInds[13]); - - EXPECT_EQ(1, cellData.m_topology.m_veInds[0]); - EXPECT_EQ(2, cellData.m_topology.m_veInds[1]); - EXPECT_EQ(0, cellData.m_topology.m_veInds[2]); - EXPECT_EQ(1, cellData.m_topology.m_veInds[3]); - EXPECT_EQ(2, cellData.m_topology.m_veInds[4]); - EXPECT_EQ(3, cellData.m_topology.m_veInds[5]); - EXPECT_EQ(0, cellData.m_topology.m_veInds[6]); - EXPECT_EQ(1, cellData.m_topology.m_veInds[7]); - EXPECT_EQ(0, cellData.m_topology.m_veInds[8]); - EXPECT_EQ(1, cellData.m_topology.m_veInds[9]); - EXPECT_EQ(2, cellData.m_topology.m_veInds[10]); - EXPECT_EQ(3, cellData.m_topology.m_veInds[11]); - EXPECT_EQ(2, cellData.m_topology.m_veInds[12]); - EXPECT_EQ(3, cellData.m_topology.m_veInds[13]); - - EXPECT_EQ(0, cellData.m_topology.m_evBegins[0]); - EXPECT_EQ(3, cellData.m_topology.m_evBegins[1]); - EXPECT_EQ(7, cellData.m_topology.m_evBegins[2]); - EXPECT_EQ(11, cellData.m_topology.m_evBegins[3]); - - EXPECT_EQ(0, cellData.m_topology.m_veBegins[0]); - EXPECT_EQ(2, cellData.m_topology.m_veBegins[1]); - EXPECT_EQ(3, cellData.m_topology.m_veBegins[2]); - EXPECT_EQ(5, cellData.m_topology.m_veBegins[3]); - EXPECT_EQ(6, cellData.m_topology.m_veBegins[4]); - EXPECT_EQ(8, cellData.m_topology.m_veBegins[5]); - EXPECT_EQ(10, cellData.m_topology.m_veBegins[6]); - EXPECT_EQ(12, cellData.m_topology.m_veBegins[7]); -} - -//---------------------------------------------------------------------- - -TEST(mir_cell_generator, generate_vertex_positions) -{ - mir::Shape shapeType = mir::Shape::Quad; - - std::map> vertexMap; - vertexMap[0] = {1, 2}; - vertexMap[1] = {0}; - vertexMap[2] = {1, 2}; - vertexMap[3] = {3}; - vertexMap[4] = {0, 1}; - vertexMap[5] = {0, 1}; - vertexMap[6] = {2, 3}; - vertexMap[7] = {2, 3}; - - std::vector originalVertexPositions; - originalVertexPositions.push_back(mir::Point2::make_point(0.0, 1.0)); - originalVertexPositions.push_back(mir::Point2::make_point(0.0, 0.0)); - originalVertexPositions.push_back(mir::Point2::make_point(1.0, 0.0)); - originalVertexPositions.push_back(mir::Point2::make_point(1.0, 1.0)); - - axom::float64 tValues[8] = {0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5}; - - mir::CellData cellData; - mir::CellGenerator cellGenerator; - cellGenerator.generateVertexPositions(shapeType, - vertexMap, - originalVertexPositions, - tValues, - cellData); - - const auto& positions = cellData.m_mapData.m_vertexPositions; - EXPECT_NEAR(positions[0][0], 0.0, 0.00001); - EXPECT_NEAR(positions[0][1], 1.0, 0.00001); - - EXPECT_NEAR(positions[1][0], 0.0, 0.00001); - EXPECT_NEAR(positions[1][1], 0.0, 0.00001); - - EXPECT_NEAR(positions[2][0], 1.0, 0.00001); - EXPECT_NEAR(positions[2][1], 0.0, 0.00001); - - EXPECT_NEAR(positions[3][0], 1.0, 0.00001); - EXPECT_NEAR(positions[3][1], 1.0, 0.00001); - - EXPECT_NEAR(positions[4][0], 0.0, 0.00001); - EXPECT_NEAR(positions[4][1], 0.5, 0.00001); - - EXPECT_NEAR(positions[5][0], 0.5, 0.00001); - EXPECT_NEAR(positions[5][1], 0.0, 0.00001); - - EXPECT_NEAR(positions[6][0], 1.0, 0.00001); - EXPECT_NEAR(positions[6][1], 0.5, 0.00001); - - EXPECT_NEAR(positions[7][0], 0.5, 0.00001); - EXPECT_NEAR(positions[7][1], 1.0, 0.00001); -} - -//---------------------------------------------------------------------- - -TEST(mir_cell_generator, generate_vertex_volume_fractions) -{ - mir::Shape shapeType = mir::Shape::Quad; - - std::map> vertexMap; - vertexMap[0] = {1, 2}; - vertexMap[1] = {0}; - vertexMap[2] = {1, 2}; - vertexMap[3] = {3}; - vertexMap[4] = {0, 1}; - vertexMap[5] = {0, 1}; - vertexMap[6] = {2, 3}; - vertexMap[7] = {2, 3}; - - std::vector> originalVertexVF(2); - originalVertexVF[0] = {0.0, 0.33, 0.67, 1.0}; - originalVertexVF[1] = {1.0, 0.67, 0.33, 0.0}; - - axom::float64 tValues[8] = {0, 0, 0, 0, 0.5, 0.5, 0.5, 0.5}; - - mir::CellData cellData; - mir::CellGenerator cellGenerator; - cellGenerator.generateVertexVolumeFractions(shapeType, - vertexMap, - originalVertexVF, - tValues, - cellData); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][0], 0.0, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][0], 1.0, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][1], 0.33, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][1], 0.67, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][2], 0.67, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][2], 0.33, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][3], 1.0, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][3], 0.0, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][4], 0.165, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][4], 0.835, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][5], 0.5, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][5], 0.5, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][6], 0.835, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][6], 0.165, 0.00001); - - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[0][7], 0.5, 0.00001); - EXPECT_NEAR(cellData.m_mapData.m_vertexVolumeFractions[1][7], 0.5, 0.00001); -} - -//---------------------------------------------------------------------- - -TEST(mir_cell_generator, determine_clean_cell_material) -{ - mir::Shape shapeType = mir::Shape::Quad; - - std::vector vertexIDs = {0, 1, 5, 7}; - - int matOne = 0; - int matTwo = 1; - - std::vector> originalVertexVF(2); - originalVertexVF[0] = {0.0, 0.33, 0.67, 1.0}; - originalVertexVF[1] = {1.0, 0.67, 0.33, 0.0}; - - mir::CellData cellData; - mir::CellGenerator cellGenerator; - - int dominantMaterial = - cellGenerator.determineCleanCellMaterial(shapeType, - vertexIDs, - matOne, - matTwo, - originalVertexVF); - - EXPECT_EQ(dominantMaterial, 1); -} - -//---------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::SimpleLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - -#endif // MIR_CELL_GENERATOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_interface_reconstructor.cpp b/src/axom/mir/tests/mir_interface_reconstructor.cpp deleted file mode 100644 index 98f990ae90..0000000000 --- a/src/axom/mir/tests/mir_interface_reconstructor.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ -#define MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -using namespace axom; - -//---------------------------------------------------------------------- - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::SimpleLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - -#endif // MIR_INTERFACE_RECONSTRUCTOR_TEST_H_ diff --git a/src/axom/mir/tests/mir_mesh.cpp b/src/axom/mir/tests/mir_mesh.cpp deleted file mode 100644 index 04da3f65e9..0000000000 --- a/src/axom/mir/tests/mir_mesh.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_MESH_TEST_H_ -#define MIR_MESH_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -namespace mir = axom::mir; - -class MirMeshTest : public ::testing::Test -{ -public: - template - using Vec = std::vector; - - using IndexVec = Vec; - using VolFracVec = Vec; - using VolumeFractions = Vec; - - enum - { - GREEN = 0, - BLUE = 1 - }; - -public: - mir::MIRMesh& getMesh() { return m_mesh; } - const mir::MIRMesh& getMesh() const { return m_mesh; } - -protected: - void SetUp() override - { - m_verts = mir::VertSet(16); - m_elems = mir::ElemSet(9); - m_nMats = 2; - - // Create the mesh connectivity information - setupTopoData(); - setupMapData(); - setupVolumeFractions(); - - m_mesh.initializeMesh(this->m_verts, - this->m_elems, - this->m_nMats, - this->m_topoData, - this->m_mapData, - this->m_volFracs); - } - - // Set up the mesh's topological connectivity - void setupTopoData() - { - m_topoData.m_evInds = { - 0, 4, 5, 1, // elem 0, card 4, start 0 - 1, 5, 6, 2, // elem 1, card 4, start 4 - 2, 6, 7, 3, // elem 2, card 4, start 8 - 4, 8, 9, 5, // elem 3, card 4, start 12 - 5, 9, 10, 6, // elem 4, card 4, start 16 - 6, 10, 11, 7, // elem 5, card 4, start 20 - 8, 12, 13, 9, // elem 6, card 4, start 24 - 9, 13, 14, 10, // elem 7, card 4, start 28 - 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 - }; - - m_topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; - - m_topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0, 1, // vert 1, card 2, start 1 - 1, 2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0, 3, // vert 4, card 2, start 6 - 0, 1, 3, 4, // vert 5, card 4, start 8 - 1, 2, 4, 5, // vert 6, card 4, start 12 - 2, 5, // vert 7, card 2, start 16 - 3, 6, // vert 8, card 2, start 18 - 3, 4, 6, 7, // vert 9, card 4, start 20 - 4, 5, 7, 8, // vert 10, card 4, start 24 - 5, 8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6, 7, // vert 13, card 2, start 31 - 7, 8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - - m_topoData.m_veBegins = - {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; - } - - // Set up the mesh's map data - void setupMapData() - { - m_mapData.m_vertexPositions = {mir::Point2(0.0, 3.0), - mir::Point2(1.0, 3.0), - mir::Point2(2.0, 3.0), - mir::Point2(3.0, 3.0), - - mir::Point2(0.0, 2.0), - mir::Point2(1.0, 2.0), - mir::Point2(2.0, 2.0), - mir::Point2(3.0, 2.0), - - mir::Point2(0.0, 1.0), - mir::Point2(1.0, 1.0), - mir::Point2(2.0, 1.0), - mir::Point2(3.0, 1.0), - - mir::Point2(0.0, 0.0), - mir::Point2(1.0, 0.0), - mir::Point2(2.0, 0.0), - mir::Point2(3.0, 0.0)}; - - m_mapData.m_elementDominantMaterials = - Vec(m_elems.size(), mir::NULL_MAT); - m_mapData.m_elementParents = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - m_mapData.m_shapeTypes = Vec(m_elems.size(), mir::Shape::Quad); - } - - // Set up the mesh's volume fraction data - void setupVolumeFractions() - { - m_volFracs.resize(m_nMats); - m_volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - m_volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - } - -protected: - mir::VertSet m_verts; - mir::ElemSet m_elems; - int m_nMats; - - mir::CellTopologyData m_topoData; - mir::CellMapData m_mapData; - mir::CellData m_cellData; - VolumeFractions m_volFracs; - - mir::MIRMesh m_mesh; -}; - -TEST_F(MirMeshTest, default_ctor) -{ - mir::MIRMesh mesh; - EXPECT_TRUE(mesh.isValid(true)); -} - -TEST_F(MirMeshTest, initialize) -{ - mir::MIRMesh mesh; - mesh.initializeMesh(this->m_verts, - this->m_elems, - this->m_nMats, - this->m_topoData, - this->m_mapData, - this->m_volFracs); - - EXPECT_TRUE(mesh.isValid(true)); - - mesh.print(); -} - -TEST_F(MirMeshTest, copy_ctor) -{ - // test copy constructor - { - mir::MIRMesh& mesh = this->getMesh(); - EXPECT_TRUE(mesh.isValid(true)); - - mir::MIRMesh mirCopy(mesh); - EXPECT_TRUE(mirCopy.isValid(true)); - } - - // test const copy constructor - { - const mir::MIRMesh& cmesh = this->getMesh(); - EXPECT_TRUE(cmesh.isValid(true)); - - // test copy constructor - const mir::MIRMesh mirCopy(cmesh); - EXPECT_TRUE(mirCopy.isValid(true)); - } -} - -TEST_F(MirMeshTest, copy_assign) -{ - // test copy assignment from a non-const MIRMesh - { - mir::MIRMesh& mesh = this->getMesh(); - EXPECT_TRUE(mesh.isValid(true)); - - mir::MIRMesh mirCopy; - mirCopy = mesh; - EXPECT_TRUE(mirCopy.isValid(true)); - } - - // test copy assignment from a const MIRMesh - { - const mir::MIRMesh& cmesh = this->getMesh(); - EXPECT_TRUE(cmesh.isValid(true)); - - mir::MIRMesh mirCopy; - mirCopy = cmesh; - EXPECT_TRUE(mirCopy.isValid(true)); - } -} - -//------------------------------------------------------------------------------ - -int main(int argc, char* argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::SimpleLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - -#endif // MIR_MESH_TEST_H_ From a147431df5f52948e3a4ee2232bf16491e035a8f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Mon, 30 Sep 2024 13:35:45 -0700 Subject: [PATCH 257/290] Added more GPU tips --- src/docs/sphinx/dev_guide/gpu_porting.rst | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/docs/sphinx/dev_guide/gpu_porting.rst b/src/docs/sphinx/dev_guide/gpu_porting.rst index 43941c2e0e..292cacd665 100644 --- a/src/docs/sphinx/dev_guide/gpu_porting.rst +++ b/src/docs/sphinx/dev_guide/gpu_porting.rst @@ -266,6 +266,45 @@ consider calling ``axom::for_all`` from a class member method instead. The nvcc not compile kernel invokations inside lambda functions. This pattern comes up an intermediate function is supplied a lambda that uses ``axom::for_all`` such as when handling many data types. +Do not add template specialization for a class/struct from within the scope of another +class/struct; the nvcc compiler does not allow it. Instead, it is necessary to extract +the internal class/struct from the containing class before specializing it. + +Do this: + + .. code-block:: cpp + + namespace internal + { + template + struct B { static void method() { } }; + + template <> + struct B<2> { static void method() { /* 2D-specific method*/ } }; + } + + template + struct A + { + void method() { internal::B::method(); } + }; + +Do NOT do this: + + .. code-block:: cpp + + template + struct A + { + template + struct B { static void method() { } }; + + template <> + struct B<2> { static void method() { /* 2D-specific method*/ } }; + + void method() { B::method(); } + }; + Do this: .. code-block:: cpp From f459f3dbac9ff2c1aefe3b23de2af0fa378dac43 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 00:22:48 -0700 Subject: [PATCH 258/290] Array fixes --- src/axom/core/Array.hpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/axom/core/Array.hpp b/src/axom/core/Array.hpp index 5b61188111..9eb2d7d736 100644 --- a/src/axom/core/Array.hpp +++ b/src/axom/core/Array.hpp @@ -289,6 +289,7 @@ class Array : public ArrayBase> this->clear(); static_cast>&>(*this) = other; m_allocator_id = other.m_allocator_id; + m_executeOnGPU = axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device; m_resize_ratio = other.m_resize_ratio; setCapacity(other.capacity()); // Use fill_range to ensure that copy constructors are invoked for each element @@ -328,6 +329,7 @@ class Array : public ArrayBase> m_capacity = other.m_capacity; m_resize_ratio = other.m_resize_ratio; m_allocator_id = other.m_allocator_id; + m_executeOnGPU = axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device; other.m_data = nullptr; other.m_num_elements = 0; @@ -966,8 +968,8 @@ class Array : public ArrayBase> IndexType m_num_elements = 0; IndexType m_capacity = 0; double m_resize_ratio = DEFAULT_RESIZE_RATIO; - int m_allocator_id; - bool m_executeOnGPU; + int m_allocator_id = INVALID_ALLOCATOR_ID; + bool m_executeOnGPU {false}; }; /// \brief Helper alias for multi-component arrays @@ -982,6 +984,7 @@ using MCArray = Array; template Array::Array() : m_allocator_id(axom::detail::getAllocatorID()) + , m_executeOnGPU(axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device) { } //------------------------------------------------------------------------------ @@ -1081,9 +1084,7 @@ Array::Array(IndexType num_elements, #endif m_allocator_id = axom::detail::getAllocatorID(); } - bool default_construct = - axom::detail::getAllocatorSpace(m_allocator_id) != MemorySpace::Device; - initialize(num_elements, capacity, default_construct); + initialize(num_elements, capacity); } //------------------------------------------------------------------------------ @@ -1166,6 +1167,7 @@ Array::Array(Array&& other) noexcept m_capacity = other.m_capacity; m_resize_ratio = other.m_resize_ratio; m_allocator_id = other.m_allocator_id; + m_executeOnGPU = axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device; other.m_data = nullptr; other.m_num_elements = 0; @@ -1587,6 +1589,7 @@ inline void Array::initialize(IndexType num_elements, capacity = (num_elements > MIN_DEFAULT_CAPACITY) ? num_elements : MIN_DEFAULT_CAPACITY; } + m_executeOnGPU = axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device; setCapacity(capacity); if(default_construct) { @@ -1622,6 +1625,7 @@ inline void Array::initialize_from_other( #endif m_allocator_id = axom::detail::getAllocatorID(); } + m_executeOnGPU = axom::detail::getAllocatorSpace(m_allocator_id) == MemorySpace::Device; this->setCapacity(num_elements); // Use fill_range to ensure that copy constructors are invoked for each // element. From aae8269b1400dc7601a78d9e4f3117c56ecaea7c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 00:28:58 -0700 Subject: [PATCH 259/290] Removing some if constexpr --- src/axom/mir/ClipField.hpp | 457 ++++++++++++++++++++++++------------- 1 file changed, 298 insertions(+), 159 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index c8c6224135..60f37637fa 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -171,20 +171,301 @@ inline AXOM_HOST_DEVICE int unique_count(const IdType *values, int n) return nv; } +//------------------------------------------------------------------------------ +// NOTE - These types were pulled out of ClipField so they could be used in +// some code that was moved out to handle degeneracies using partial +// specialization rather than "if constexpr". Put it all back when +// "if constexpr" is allowed. One nice side-seffect is shorter symbol +// names in the debugger. + +using BitSet = std::uint32_t; + +/*! + * \brief Contains data that describes the number and size of zone fragments in the output. + */ +struct FragmentData +{ + IndexType m_finalNumZones {0}; + IndexType m_finalConnSize {0}; + axom::ArrayView m_fragmentsView {}; + axom::ArrayView m_fragmentsSizeView {}; + axom::ArrayView m_fragmentOffsetsView {}; + axom::ArrayView m_fragmentSizeOffsetsView {}; +}; +//------------------------------------------------------------------------------ + +#if defined(AXOM_CLIP_FILTER_DEGENERATES) +/*! + * \brief Replace data in the input Conduit node with a denser version using the mask. + * + * \tparam ExecSpace The execution space. + * \tparam DataView The type of data view that is operated on. + * + * \param n_src The Conduit node that contains the data. + * \param srcView A view that wraps the input Conduit data. + * \param newSize The new array size. + * \param maskView The mask for valid data elements. + * \param maskOffsetsView The offsets view to indicate where to write the new data. + */ +template +DataView filter(conduit::Node &n_src, + DataView srcView, + axom::IndexType newSize, + axom::ArrayView maskView, + axom::ArrayView maskOffsetsView) +{ + using value_type = typename DataView::value_type; + namespace bputils = axom::mir::utilities::blueprint; + + // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + utilities::blueprint::ConduitAllocateThroughAxom c2a; + const int conduitAllocatorID = c2a.getConduitAllocatorID(); + + conduit::Node n_values; + n_values.set_allocator(conduitAllocatorID); + n_values.set(conduit::DataType(bputils::cpp2conduit::id, newSize)); + auto valuesView = bputils::make_array_view(n_values); + const auto nValues = maskView.size(); + axom::for_all( + nValues, + AXOM_LAMBDA(axom::IndexType index) { + if(maskView[index] > 0) + { + const auto destIndex = maskOffsetsView[index]; + valuesView[destIndex] = srcView[index]; + } + }); + + n_src.swap(n_values); + return bputils::make_array_view(n_src); +} + +/// NOTE - Use partial specialization (instead of the cleaner "if constexpr") +/// for now to implement some 2D-specific behavior. + +/*! + * \brief Base template for degenerate removal. + */ +template +struct DegenerateHandler +{ + /*! + * \brief In a previous stage, degenerate shapes were marked as having zero size. + * This method filters them out from the auxiliary arrays. + * + * \param fragmentData The fragments. + * \param[inout] n_sizes The node that contains the sizes. + * \param[inout] n_offsets The node that contains the offsets. + * \param[inout] n_shapes The node that contains the shapes. + * \param[inout] n_color The node that contains the color. + * \param[inout] sizesView The view that wraps sizes (can change on output). + * \param[inout] offsetsView The view that wraps offsets (can change on output). + * \param[inout] shapesView The view that wraps shapes (can change on output). + * \param[inout] colorView The view that wraps colors (can change on output). + */ + static void filterZeroSizes(FragmentData &AXOM_UNUSED_PARAM(fragmentData), + conduit::Node &AXOM_UNUSED_PARAM(n_sizes), + conduit::Node &AXOM_UNUSED_PARAM(n_offsets), + conduit::Node &AXOM_UNUSED_PARAM(n_shapes), + conduit::Node &AXOM_UNUSED_PARAM(n_color), + axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), + axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), + axom::ArrayView &AXOM_UNUSED_PARAM(shapesView), + axom::ArrayView &AXOM_UNUSED_PARAM(colorView)) + { + } + + /*! + * \brief Turns degenerate quads into triangles in-place. + * + * \param shapesUsed A BitSet that indicates which shapes are present in the mesh. + * \param connView A view that contains the connectivity. + * \param sizesView A view that contains the sizes. + * \param offsetsView A view that contains the offsets. + * \param shapesView A view that contains the shapes. + */ + static BitSet quadtri(BitSet shapesUsed, + axom::ArrayView AXOM_UNUSED_PARAM(connView), + axom::ArrayView AXOM_UNUSED_PARAM(sizesView), + axom::ArrayView AXOM_UNUSED_PARAM(offsetsView), + axom::ArrayView AXOM_UNUSED_PARAM(shapesView)) + { + return shapesUsed; + } +}; + +/*! + * \brief Partial specialization that implements some degeneracy handling for 2D meshes. + */ +template +struct DegenerateHandler<2, ExecSpace, ConnectivityType> +{ + using reduce_policy = typename axom::execution_space::reduce_policy; + + /*! + * \brief In a previous stage, degenerate shapes were marked as having zero size. + * This method filters them out from the auxiliary arrays. + * + * \param fragmentData The fragments. + * \param[inout] n_sizes The node that contains the sizes. + * \param[inout] n_offsets The node that contains the offsets. + * \param[inout] n_shapes The node that contains the shapes. + * \param[inout] n_color The node that contains the color. + * \param[inout] sizesView The view that wraps sizes (can change on output). + * \param[inout] offsetsView The view that wraps offsets (can change on output). + * \param[inout] shapesView The view that wraps shapes (can change on output). + * \param[inout] colorView The view that wraps colors (can change on output). + */ + static void filterZeroSizes(FragmentData &fragmentData, + conduit::Node &n_sizes, + conduit::Node &n_offsets, + conduit::Node &n_shapes, + conduit::Node &n_color, + axom::ArrayView &sizesView, + axom::ArrayView &offsetsView, + axom::ArrayView &shapesView, + axom::ArrayView &colorView) + { + AXOM_ANNOTATE_SCOPE("filterZeroSizes"); + + // There were degenerates so the expected number of fragments per zone (m_fragmentsView) + // was adjusted down. That means redoing the offsets. These need to be up + // to date to handle zonal fields later. + axom::exclusive_scan(fragmentData.m_fragmentsView, + fragmentData.m_fragmentOffsetsView); + + // Use sizesView to make a mask that has 1's where size > 0. + axom::IndexType nz = fragmentData.m_finalNumZones; + axom::Array mask(nz, + nz, + axom::execution_space::allocatorID()); + axom::Array maskOffsets( + nz, + nz, + axom::execution_space::allocatorID()); + auto maskView = mask.view(); + auto maskOffsetsView = maskOffsets.view(); + RAJA::ReduceSum mask_reduce(0); + const axom::ArrayView deviceSizesView = sizesView; + axom::for_all( + nz, + AXOM_LAMBDA(axom::IndexType index) { + const int ival = (deviceSizesView[index] > 0) ? 1 : 0; + maskView[index] = ival; + mask_reduce += ival; + }); + const axom::IndexType filteredZoneCount = mask_reduce.get(); + + // Make offsets + axom::exclusive_scan(maskView, maskOffsetsView); + + // Filter sizes, shapes, color using the mask + sizesView = + filter>(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); + offsetsView = filter>(n_offsets, + offsetsView, + filteredZoneCount, + maskView, + maskOffsetsView); + shapesView = + filter>(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); + colorView = filter>(n_color, + colorView, + filteredZoneCount, + maskView, + maskOffsetsView); + + // Record the filtered size. + fragmentData.m_finalNumZones = filteredZoneCount; + } + + /*! + * \brief Turns degenerate quads into triangles in-place. + * + * \param shapesUsed A BitSet that indicates which shapes are present in the mesh. + * \param connView A view that contains the connectivity. + * \param sizesView A view that contains the sizes. + * \param offsetsView A view that contains the offsets. + * \param shapesView A view that contains the shapes. + */ + static BitSet quadtri(BitSet shapesUsed, + axom::ArrayView connView, + axom::ArrayView sizesView, + axom::ArrayView offsetsView, + axom::ArrayView shapesView) + { + if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) + { + AXOM_ANNOTATE_SCOPE("quadtri"); + const axom::IndexType numOutputZones = shapesView.size(); + RAJA::ReduceBitOr shapesUsed_reduce(0); + axom::for_all( + numOutputZones, + AXOM_LAMBDA(axom::IndexType index) { + if(shapesView[index] == views::Quad_ShapeID) + { + const auto offset = offsetsView[index]; + ConnectivityType pts[4]; + int npts = 0; + for(int current = 0; current < 4; current++) + { + int next = (current + 1) % 4; + ConnectivityType curNode = connView[offset + current]; + ConnectivityType nextNode = connView[offset + next]; + if(curNode != nextNode) { pts[npts++] = curNode; } + } + + if(npts == 3) + { + shapesView[index] = views::Tri_ShapeID; + sizesView[index] = 3; + connView[offset] = pts[0]; + connView[offset + 1] = pts[1]; + connView[offset + 2] = pts[2]; + // Repeat the last point (it won't be used though). + connView[offset + 3] = pts[2]; + } + } + + BitSet shapeBit {}; + axom::utilities::setBitOn(shapeBit, shapesView[index]); + shapesUsed_reduce |= shapeBit; + }); + // We redid shapesUsed reduction in case triangles appeared. + shapesUsed = shapesUsed_reduce.get(); + } + return shapesUsed; + } +}; +#endif + #if defined(AXOM_DEBUG_CLIP_FIELD) +/*! + * \brief Print device views to std::out after moving data to the host. + * + * \param name The name of the view. + * \param view The view to print. + */ template void printHost(const std::string &name, const ViewType &deviceView) { using value_type = typename ViewType::value_type; int nn = deviceView.size(); + // Move data to host into temp array. value_type *host = new value_type[nn]; axom::copy(host, deviceView.data(), sizeof(value_type) * nn); + // Print std::cout << name << "[" << nn << "] = {"; for(int ii = 0; ii < nn; ii++) { - std::cout << ", " << host[ii]; + if(ii > 0) + { + std::cout << ", "; + } + std::cout << host[ii]; } std::cout << "}" << std::endl; + // Cleanup. delete[] host; } #endif @@ -366,7 +647,7 @@ class ClipField using ClipTableViews = axom::StackArray; using Intersector = IntersectPolicy; - using BitSet = std::uint32_t; + using BitSet = details::BitSet; using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -730,19 +1011,7 @@ class ClipField #if !defined(__CUDACC__) private: #endif - - /*! - * \brief Contains data that describes the number and size of zone fragments in the output. - */ - struct FragmentData - { - IndexType m_finalNumZones {0}; - IndexType m_finalConnSize {0}; - axom::ArrayView m_fragmentsView {}; - axom::ArrayView m_fragmentsSizeView {}; - axom::ArrayView m_fragmentOffsetsView {}; - axom::ArrayView m_fragmentSizeOffsetsView {}; - }; + using FragmentData = details::FragmentData; /*! * \brief Contains some per-zone data that we want to hold onto between methods. @@ -1280,6 +1549,7 @@ class ClipField n_newTopo["coordset"] = n_newCoordset.name(); // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. + // _mir_utilities_c2a_begin utilities::blueprint::ConduitAllocateThroughAxom c2a; const int conduitAllocatorID = c2a.getConduitAllocatorID(); @@ -1288,6 +1558,7 @@ class ClipField n_conn.set_allocator(conduitAllocatorID); n_conn.set(conduit::DataType(connTypeID, fragmentData.m_finalConnSize)); auto connView = bputils::make_array_view(n_conn); + // _mir_utilities_c2a_end // Allocate shapes. conduit::Node &n_shapes = n_newTopo["elements/shapes"]; @@ -1525,65 +1796,15 @@ class ClipField } #if defined(AXOM_CLIP_FILTER_DEGENERATES) - if constexpr(TopologyView::dimension() == 2) + // Filter out shapes that were marked as zero-size, adjusting connectivity and other arrays. + if(degenerates_reduce.get()) { - // We get into this block when degenerate zones were detected where - // all of their nodes are the same. We need to filter those out. - if(degenerates_reduce.get()) - { - AXOM_ANNOTATE_SCOPE("degenerates"); - - // There were degenerates so the expected number of fragments per zone (m_fragmentsView) - // was adjusted down. That means redoing the offsets. These need to be up - // to date to handle zonal fields later. - axom::exclusive_scan(fragmentData.m_fragmentsView, - fragmentData.m_fragmentOffsetsView); - - // Use sizesView to make a mask that has 1's where size > 0. - axom::IndexType nz = fragmentData.m_finalNumZones; - axom::Array mask(nz, - nz, - axom::execution_space::allocatorID()); - axom::Array maskOffsets( - nz, - nz, - axom::execution_space::allocatorID()); - auto maskView = mask.view(); - auto maskOffsetsView = maskOffsets.view(); - RAJA::ReduceSum mask_reduce(0); - axom::for_all( - nz, - AXOM_LAMBDA(axom::IndexType index) { - const int ival = (sizesView[index] > 0) ? 1 : 0; - maskView[index] = ival; - mask_reduce += ival; - }); - const axom::IndexType filteredZoneCount = mask_reduce.get(); - - // Make offsets - axom::exclusive_scan(maskView, maskOffsetsView); - - // Filter sizes, shapes, color using the mask - sizesView = - filter(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); - offsetsView = filter(n_offsets, - offsetsView, - filteredZoneCount, - maskView, - maskOffsetsView); - shapesView = - filter(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); - colorView = filter(n_color_values, - colorView, - filteredZoneCount, - maskView, - maskOffsetsView); - - // Record the filtered size. - fragmentData.m_finalNumZones = filteredZoneCount; - } + details::DegenerateHandler::filterZeroSizes(fragmentData, + n_sizes, n_offsets, n_shapes, n_color_values, + sizesView, offsetsView, shapesView, colorView); } #endif + // Figure out which shapes were used. BitSet shapesUsed = findUsedShapes(shapesView); @@ -1611,50 +1832,10 @@ class ClipField n_newFields.remove(opts.colorField()); } - // Handle some quad->tri degeneracies - if constexpr(TopologyView::dimension() == 2) - { - if(axom::utilities::bitIsSet(shapesUsed, views::Quad_ShapeID)) - { - AXOM_ANNOTATE_SCOPE("quadtri"); - const axom::IndexType numOutputZones = shapesView.size(); - RAJA::ReduceBitOr shapesUsed_reduce(0); - axom::for_all( - numOutputZones, - AXOM_LAMBDA(axom::IndexType index) { - if(shapesView[index] == views::Quad_ShapeID) - { - const auto offset = offsetsView[index]; - ConnectivityType pts[4]; - int npts = 0; - for(int current = 0; current < 4; current++) - { - int next = (current + 1) % 4; - ConnectivityType curNode = connView[offset + current]; - ConnectivityType nextNode = connView[offset + next]; - if(curNode != nextNode) pts[npts++] = curNode; - } - - if(npts == 3) - { - shapesView[index] = views::Tri_ShapeID; - sizesView[index] = 3; - connView[offset] = pts[0]; - connView[offset + 1] = pts[1]; - connView[offset + 2] = pts[2]; - // Repeat the last point (it won't be used though). - connView[offset + 3] = pts[2]; - } - } - - BitSet shapeBit {}; - axom::utilities::setBitOn(shapeBit, shapesView[index]); - shapesUsed_reduce |= shapeBit; - }); - // We redid shapesUsed reduction in case triangles appeared. - shapesUsed = shapesUsed_reduce.get(); - } - } +#if defined(AXOM_CLIP_FILTER_DEGENERATES) + // Handle some quad->tri degeneracies, depending on dimension. + shapesUsed = details::DegenerateHandler::quadtri(shapesUsed, connView, sizesView, offsetsView, shapesView); +#endif // Add shape information to the connectivity. SLIC_ASSERT_MSG(shapesUsed != 0, "No shapes were produced!"); @@ -1697,50 +1878,6 @@ class ClipField return shapesUsed; } -#if defined(AXOM_CLIP_FILTER_DEGENERATES) - /*! - * \brief Replace data in the input Conduit node with a denser version using the mask. - * - * \param n_src The Conduit node that contains the data. - * \param srcView A view that wraps the input Conduit data. - * \param newSize The new array size. - * \param maskView The mask for valid data elements. - * \param maskOffsetsView The offsets view to indicate where to write the new data. - */ - template - DataView filter(conduit::Node &n_src, - DataView srcView, - axom::IndexType newSize, - axom::ArrayView maskView, - axom::ArrayView maskOffsetsView) const - { - using value_type = typename DataView::value_type; - namespace bputils = axom::mir::utilities::blueprint; - - // Get the ID of a Conduit allocator that will allocate through Axom with device allocator allocatorID. - utilities::blueprint::ConduitAllocateThroughAxom c2a; - const int conduitAllocatorID = c2a.getConduitAllocatorID(); - - conduit::Node n_values; - n_values.set_allocator(conduitAllocatorID); - n_values.set(conduit::DataType(bputils::cpp2conduit::id, newSize)); - auto valuesView = bputils::make_array_view(n_values); - const auto nValues = maskView.size(); - axom::for_all( - nValues, - AXOM_LAMBDA(axom::IndexType index) { - if(maskView[index] > 0) - { - const auto destIndex = maskOffsetsView[index]; - valuesView[destIndex] = srcView[index]; - } - }); - - n_src.swap(n_values); - return bputils::make_array_view(n_src); - } -#endif - /*! * \brief Make the new coordset using the blend data and the input coordset/coordsetview. * @@ -1753,10 +1890,12 @@ class ClipField conduit::Node &n_newCoordset) const { AXOM_ANNOTATE_SCOPE("makeCoordset"); + // _mir_utilities_coordsetblender_start axom::mir::utilities::blueprint:: CoordsetBlender cb; cb.execute(blend, m_coordsetView, n_coordset, n_newCoordset); + // _mir_utilities_coordsetblender_end } /*! From ab2c73f71f455ece49208a129dda6e123fc6fc80 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 00:29:42 -0700 Subject: [PATCH 260/290] Working on docs --- src/axom/mir/ExtractZones.hpp | 2 + src/axom/mir/docs/sphinx/index.rst | 25 ++- src/axom/mir/docs/sphinx/mir_algorithms.rst | 129 ++++++++++++ src/axom/mir/docs/sphinx/mir_utilities.rst | 160 +++++++++++++++ src/axom/mir/docs/sphinx/mir_views.rst | 184 ++++++++++++++++++ .../mir/examples/mir_concentric_circles.cpp | 2 + .../mir/tests/mir_blueprint_utilities.cpp | 2 + src/axom/mir/tests/mir_clipfield.cpp | 5 +- 8 files changed, 506 insertions(+), 3 deletions(-) create mode 100644 src/axom/mir/docs/sphinx/mir_algorithms.rst create mode 100644 src/axom/mir/docs/sphinx/mir_utilities.rst create mode 100644 src/axom/mir/docs/sphinx/mir_views.rst diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 9659842173..4cfb01b36f 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -697,10 +697,12 @@ class ExtractZonesAndMatset AXOM_ANNOTATE_SCOPE("ExtractZonesAndMatset"); // Call base class to handle mesh/coordset/fields + // _mir_utilities_extractzones_begin ExtractZones::execute(selectedZonesView, n_input, n_options, n_output); + // _mir_utilities_extractzones_end // Make new matset. const std::string topoName = diff --git a/src/axom/mir/docs/sphinx/index.rst b/src/axom/mir/docs/sphinx/index.rst index 22f5487fd6..37f3fb1bef 100644 --- a/src/axom/mir/docs/sphinx/index.rst +++ b/src/axom/mir/docs/sphinx/index.rst @@ -1,4 +1,4 @@ -.. ## Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and .. ## other Axom Project Developers. See the top-level COPYRIGHT file for details. .. ## .. ## SPDX-License-Identifier: (BSD-3-Clause) @@ -9,4 +9,25 @@ Mir User Documentation Axom's Material Interface Reconstruction (MIR) component provides algorithms for reconstructing the interface surfaces between different materials in multimaterial -meshes. +meshes. The algorithms take Blueprint meshes containing a coordset, topology, and +matset as input and they output a new Blueprint node with a new coordset, topology, +and matset that contains at most 1 material per zone. + +The MIR component also contains some useful components that can be used to develop +other algorithms that process Blueprint meshes. + + +API Documentation +----------------- + +Doxygen generated API documentation can be found here: `API documentation <../../../../doxygen/html/coretop.html>`_ + + +.. toctree:: + :caption: Contents + :maxdepth: 1 + + mir_algorithms + mir_views + mir_clipping + mir_utilities diff --git a/src/axom/mir/docs/sphinx/mir_algorithms.rst b/src/axom/mir/docs/sphinx/mir_algorithms.rst new file mode 100644 index 0000000000..f391dbc519 --- /dev/null +++ b/src/axom/mir/docs/sphinx/mir_algorithms.rst @@ -0,0 +1,129 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +****************************************************** +MIR Algorithms +****************************************************** + +The MIR component contains MIR algorithms that will take a Blueprint mesh as input, +perform MIR on it, and output a new Blueprint mesh with the reconstructed output. +A Blueprint mesh is contained in a ``conduit::Node`` and it follows the [https://llnl-conduit.readthedocs.io/en/latest/blueprint_mesh.html](Blueprint protocol), +which means the node contains specific items that describe the mesh coordinates, topology, fields, and materials. + +####### +Inputs +####### + +MIR algorithms are designed to accept a Conduit node containing various options that can +alter how the algorithm operates. the MIR algorithm copies the options node to the memory space +where it will run. + ++---------------------------------+------------------------------------------------------+ +| Option | Description | ++=================================+======================================================+ +| matset: name | A required string argument that specifies the name | +| | of the matset that will be operated on. | ++---------------------------------+------------------------------------------------------+ +| matsetName: name | An optional string argument that specifies the name | +| | of the matset to create in the output. If the name | +| | is not given, the output matset will have the same | +| | name as the input matset. | ++---------------------------------+------------------------------------------------------+ +| originalElementsField: name | The name of the field in which to store the original | +| | elements map. | ++---------------------------------+------------------------------------------------------+ +| selectedZones: [zone list] | An optional argument that provides a list of zone ids| +| | to operate on. The output mesh will only have | +| | contributions from zone numbers in this list, if it | +| | is given. | ++---------------------------------+------------------------------------------------------+ + +############### +EquiZAlgorithm +############### + +The [https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.osti.gov/servlets/purl/15014510&ved=2ahUKEwittMui-euIAxUzxOYEHXTWA2kQFnoECBcQAQ&usg=AOvVaw3qbX9qgwCn4qDP0iZ3Sq0J](Equi-Z algorithm) by J. Meredith +is a useful visualization-oriented algorithm for MIR. Whereas many MIR algorithms +produce disjointed element output, Equi-Z creates output that mostly forms continuous +surfaces and shapes. Continuity is achieved by averaging material volume fractions to +the mesh nodes for each material and then performing successive clipping for each +material, using the node-averaged volume fractions to determine where clipping occurs +along each edge. The basic algorithm is lookup-based so shape decomposition for a +clipped-zone can be easily determined. The cliping stage produces numerous zone fragments +that are marked with the appropriate material number and moved onto the next material +clipping stage. This concludes when all zones are comprised of only 1 material. From, +there points are made unique and the output mesh is created with a new coordset, topology, +fields, and matset. + +Axom's implementation of Equi-Z can run on the CPU and the GPU. First, the zones of +interest are identified and they are classified as clean or mixed. The clean zones are +pulled out early into a new mesh and mixed zones are sent into the Equi-Z algorithm to +reconstruct clean zones. The two meshes are then merged together at the end in an output +Conduit node. + +Axom's implementation supports 2D/3D zones from structured or unstructured topologies +made of Finite Element Zoo elements (e.g. triangles, quadrilaterals, tetrahedra, pyramids, +wedges, hexahedra, or topologically-compatible mixtures). The MIR logic for Equi-Z is +encapsulated in ``EquizAlgorithm``, which is a class that is templated on view objects. +View objects help provide an interface between the Blueprint data and the MIR algorithm. +At a minimum, an execution space and three views are required to instantiate the +``EquiZAlgorithm`` class. The execution space determines which compute backend will +be used to execute the algorithm. The Blueprint data must exist in a compatible +memory space for the execution space. The views are: _CoordsetView_, _TopologyView_, and +_MaterialView_. The CoordsetView template argument lets the algorithm access the mesh's +coordset using concrete data types and supports queries that return points. The +TopologyView provides a set of operations that can be performed on meshes, mainly a +device-aware method for retrieving individual zones that can be used in device kernels. +The MaterialView provides an interface for matsets. + +Once view types have been created and views have been instantiated, the ``EquiZAlgorithm`` +algorithm can be instantiated and used. The EquiZAlgorithm class provides a single +``execute()`` method that takes the input mesh, an options node, and a node to contain +the output mesh. The output mesh will exist in the same memory space as the input mesh, +which again, must be compatible with the selected execution space. The ``axom::mir::utilities::blueprint::copy()`` +function can be used to copy Conduit nodes from one memory space to another. + +.. literalinclude:: ../../examples/mir_concentric_circles.cpp + :start-after: _equiz_mir_start + :end-before: _equiz_mir_end + :language: C++ + +The MIR output will contain a new field called "originalElements" that indicates which +original zone number gave rise to the reconstructed zone. This field makes it possible +to map back to the original mesh. The name of the field can be changed using options. + +##################### +Example Applications +##################### + +The mir_concentric_circles application generates a uniform mesh populated with circular mixed material shells and then it performs MIR on the input mesh before writing the reconstructed mesh. + ++--------------------+---------------------------------------------------------------+ +| Argument | Description | ++====================+===============================================================+ +| --gridsize number | The number of zones along an axis. | ++--------------------+---------------------------------------------------------------+ +| --numcircles number| The number of number of circles to use for material creation. | ++--------------------+---------------------------------------------------------------+ +| --output filepath | The file path for output files. | ++--------------------+---------------------------------------------------------------+ +| --policy policy | Set the execution policy (seq, omp, cuda, hip) | ++--------------------+---------------------------------------------------------------+ +| --caliper mode | The caliper mode (none, report) | ++--------------------+---------------------------------------------------------------+ + +To run the example program from the Axom build directory, follow these steps: + + ./examples/mir_concentric_circles --gridsize 100 --numcircles 5 --output mir + +##################### +Visualization +##################### + +The [https://visit-dav.github.io/visit-website/](VisIt software) can be used to view the Blueprint output from MIR algorithms. +Blueprint data is saved in an HDF5 format and the top level file has a ".root" extension. Open the ".root" file in VisIt to +get started and then add a "FilledBoundary" plot of the material defined on the mesh topology. Plotting the mesh lines will +reveal that there is a single material per zone. If the input mesh is visualized in a similar manner, it will be evident that +there are multiple materials in some of the zones, if viewing a mixed material dataset. diff --git a/src/axom/mir/docs/sphinx/mir_utilities.rst b/src/axom/mir/docs/sphinx/mir_utilities.rst new file mode 100644 index 0000000000..febdab8457 --- /dev/null +++ b/src/axom/mir/docs/sphinx/mir_utilities.rst @@ -0,0 +1,160 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +****************************************************** +MIR Blueprint Utilities +****************************************************** + +The MIR component contains several useful algorithm building blocks for writing algorithms +for Blueprint meshes. + +####################### +Copying Blueprint Data +####################### + +If a ``conduit::Node`` containing Blueprint data is not on the desired memory space, it can be +moved using the ``axom::mir::utilities::blueprint::copy()`` function. The ``copy`` +function moves the source ``conduit::Node`` to the destination ``conduit::Node``, making sure +to use the appropriate Axom allocator for non-string bulk arrays (e.g. arrays of ints, floats, +doubles, etc.). Data small enough to fit in a ``conduit::Node`` and strings are left in the host +memory space, which lets algorithms on the host side query them. For data that have been moved +to the device, their sizes and data types can still be queried using normal Conduit mechanisms +such as getting metadata via the ``conduit::Node::dtype()`` method. + + .. codeblock{.cpp}:: + + conduit::Node hostMesh, deviceMesh, hostMesh2; + // host->device + axom::mir::utilities::blueprint::copy>(deviceMesh, hostMesh); + // device->host + axom::mir::utilities::blueprint::copy(hostMesh2, deviceMesh); + +############################ +ConduitAllocateThroughAxom +############################ + +When writing algorithms that construct Blueprint data, it is helpful to force Conduit +to allocate its memory through Axom's allocation routines and then make an axom::ArrayView +of the Conduit node. This prevents data from having to be copied from an Axom data structure +since it can be constructed from the start inside the Conduit node. + +The ``axom::mir::utilities::blueprint::ConduitAllocateThroughAxom`` +class is a template class that takes an execution space as a template argument and it +installs an allocation routine in Conduit that can be used to allocate data through +Axom. The Conduit allocator is set on each ``conduit::Node`` before setting data into +the object. + +.. literalinclude:: ../../ClipField.cpp + :start-after: _mir_utilities_c2a_begin + :end-before: _mir_utilities_c2a_end + :language: C++ + +########## +ClipField +########## + +The ``axom::mir::clipping::ClipField`` class intersects all the zones in the input Blueprint +mesh with an implicit surface where the selected input field equals zero and produces a new +Blueprint mesh based on the selected zone fragments produced by the intersection. This can be thought +of as an isosurface algorithm but with a volumetric output mesh where the mesh is either inside or +outside of the selected isovalue. The ``ClipField`` class has multiple template arguments to +select the execution space, the type of topology view, the type of coordset view, and the +type of intersector used to determine intersections. The default intersection uses an isosurface- +based intersection method, though other intersectors could be created to perform plane +or sphere intersections. + +.. literalinclude:: ../../ClipField.cpp + :start-after: _mir_utilities_clipfield_start + :end-before: _mir_utilities_clipfield_end + :language: C++ + +################ +CoordsetBlender +################ + +The ``axom::mir::utilities::blueprint::CoordsetBlender`` class takes a "BlendGroup" and makes +a new explicit coordset where each new point corresponds to one blend group. A "BlendGroup" is +an object that groups several array views that describe a set of blend groups. Each blend group +is formed from a list of node ids and weight values and the new coordinate is formed by looking +up the points in the blend group in the source coordset and multiplying them by their weights +and summing them together to produce the new point in the output coordset. Classes such as +``ClipField`` use ``CoordsetBlender`` to make new coordsets that contain points that were a +combination of multiple points in the input coordset. + +.. literalinclude:: ../../ClipField.cpp + :start-after: _mir_utilities_coordsetblender_start + :end-before: _mir_utilities_coordsetblender_end + :language: C++ + +################ +CoordsetSlicer +################ + +The ``axom::mir::utilities::blueprint::CoordsetSlicer`` class takes ``SliceData`` and makes a +new explicit coordset where each point corresponds to a single index from the node indices +stored in SliceData. This class can be used to select a subset of a coordset, reorder nodes +in a coordset, or repeat nodes in a coordset. + + + +################## +ExtractZones +################## + +The ``axom::mir::utilities::ExtractZones`` class takes a list of selected zone ids and extracts +a new mesh from a source mesh that includes only the selected zones. There is a derived class +``ExtractZonesAndMatset`` that also extracts a matset, if present. + +.. literalinclude:: ../../ExtractZones.cpp + :start-after: _mir_utilities_extractzones_begin + :end-before: _mir_utilities_extractzones_end + :language: C++ + +############# +FieldBlender +############# + +The ``axom::mir::utilities::blueprint::FieldBlender`` class is similar to the ``CoordsetBlender`` +class, except that it operates on a field instead of coordsets. The class is used to create a +new field that includes values derived from multiple weighted source values. + + + +############ +FieldSlicer +############ + +################## +MakeUnstructured +################## + +The ``axom::mir::utilities::blueprint::MakeUnstructured`` class takes a structured topology +and creates a new unstructured topology. This class does not need views to wrap the input +structured topology. + +.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp + :start-after: _mir_utilities_makeunstructured_begin + :end-before: _mir_utilities_makeunstructured_begin + :language: C++ + +################## +MatsetSlicer +################## + +################## +MergeMeshes +################## + +########################### +NodeToZoneRelationBuilder +########################### + +################## +Unique +################## + +################## +ZoneListBuilder +################## diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst new file mode 100644 index 0000000000..f72903c4ff --- /dev/null +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -0,0 +1,184 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level COPYRIGHT file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +****** +Views +****** + +---------- +ArrayView +---------- + +Axom provides ``axom::ArrayView`` to wrap data in a non-owning data structure that can be passed to +kernels. The MIR component provides functions that help wrap arrays stored in + + + +---------- +Coordsets +---------- + +---------------- +Topology Views +---------------- + +Topology views provide a layer on top of the Blueprint mesh topology that enables Axom algorithms +to be written while not needing to care specifically about topology types and data types. +Axom provides topology views for structured meshes and unstructured meshes. + +^^^^^^^^^^^^^^^^^^^^^^ +Structured Mesh Views +^^^^^^^^^^^^^^^^^^^^^^ + +The structured mesh topology view, ``StructuredTopologyView``, pertains to any of the Blueprint +topology types. The ``StructuredTopologyView`` class is a template that takes an indexing policy +as a template argument. The indexing policy computes zone indices and converts to/from +logical/global indices. The ``StridedStructuredIndexingPolicy`` class supports indexing for +strided-structured Blueprint meshes, which are structured meshes that exist over a sub-window +of the overall mesh. There are helper functions for creating structured topology views from +a Conduit node. + + .. codeblock{.cpp}:: + + conduit::Node &n_topo1 = n_mesh["topologies/mesh2d"]; + conduit::Node &n_topo2 = n_mesh["topologies/mesh3d"]; + conduit::Node &n_topo3 = n_mesh["topologies/mesh2dss"]; + // Make a 2D structured mesh view from the topology. + auto topologyView1 = axom::mir::views::make_structured<2>::view(n_topo1); + // Make a 3D structured mesh view from the topology. + auto topologyView2 = axom::mir::views::make_structured<2>::view(n_topo2); + // Make a 2D strided-structured mesh view from the topology. + auto topologyView3 = axom::mir::views::make_strided_structured<2>::view(n_topo3); + +^^^^^^^^^^^^^^^^^^^^^^^^ +Unstructured Mesh Views +^^^^^^^^^^^^^^^^^^^^^^^^ + +There are 3 unstructured mesh views. The ``UnstructuredTopologySingleShapeView`` class wraps a +Blueprint topology that contains a single zone/shape type. The zone type is a template argument +that determines the type of zone that is held within the topology. + + .. codeblock{.cpp}:: + + // Make a topology view for a tetrahedral mesh with int connectivity. + namespace bputils = axom::mir::utilities::blueprint; + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + const auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); + axom::mir::views::UnstructuredTopologySingleShapeView> view(connView); + +There are multiple shape types defined in ``axom/mir/views/Shapes.hpp`` that can be used with +the ``UnstructuredTopologySingleShapeView``: TriShape, QuadShape, TetShape, PyramidShape, +WedgeShape, and HexShape. + +Blueprint supports "mixed" topologies that contain multiple shape types. These topologies are +handled using the ``axom::mir::views::UnstructuredTopologyMixedShapeView``. Additional array +views are needed to supply the sizes, offsets, and shapes arrays. + + .. codeblock{.cpp}:: + + // A shape map helps map values from the values used in the Blueprint topology to + // the shape ids used in Axom. + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + axom::Array ids, values; + auto shapeMap = axom::mir::views::buildShapeMap(n_topo); + + namespace bputils = axom::mir::utilities::blueprint; + axom::mir::views::UnstructuredTopologyMixedShapeView view( + bputils::make_array_view(n_topo["elements/connectivity"), + bputils::make_array_view(n_topo["elements/sizes"), + bputils::make_array_view(n_topo["elements/offsets"), + bputils::make_array_view(n_topo["elements/shapes"), + shapeMap); + + .. codeblock{.cpp}:: + + topologyView = ... + axom::for_all(topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // Get the current zone. + const auto zone = topologyView.zone(zoneIndex); + + // Iterate over this zone's nodes. + for(const auto &nodeId : zone.getIds()) + { + // Do something. + } + }); + +---------- +Matsets +---------- + +---------- +Dispatch +---------- + +There are several helper functions for converting a Conduit node to a specific view type +and passing the view to a lambda for further processing. These dispatch functions take +care of wrapping a Conduit node containing Blueprint data in various view types before +passing the views to a user-supplied lambda function. The lambda function will typically +be instantiated multiple times to handle cases when there are multiple data types and +object types (e.g. coordsets). Generic lambdas can be used to process multiple view +and it is possible to nest multiple dispatch functions. + + .. codeblock{.cpp}:: + + const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { + // Get the C++ type of the coordset. + using CoordsetView = decltype(CoordsetView); + + // Implement algorithm using coordsetView. + }); + +Dispatch functions for topologies enable creation of algorithms that can operate on multiple +topology types through a topology view. These dispatch functions can be called for specific +topology types such as unstructured topologies or they can be called to implement algorithms +that can operate on any topology. + + .. codeblock{.cpp}:: + + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + // Handle rectilinear topology type. + axom::mir::views::dispatch_rectilinear_topology(n_topo, [&](auto topologyView) { + }); + // Handle structured topology types + axom::mir::views::dispatch_structured_topology(n_topo, [&](auto topologyView) { + }); + // Handle unstructured topology types + axom::mir::views::dispatch_unstructured_topology(n_topo, [&](auto topologyView) { + }); + // Handle any topology type. + axom::mir::views::dispatch_topologies(n_topo, [&](auto topologyView) { + }); + +Nesting dispatch functions permits the calling code to handle both coordset views and +topology views using a single lambda function for the algorithm. For portability, the +algorithm should be placed in a function or class member method when instantiated from +the anonymous lambda function from the dispatch functions. + + .. codeblock{.cpp}:: + + struct Algorithm + { + void execute(const conduit::Node &n_mesh) + { + // Handle product of coordset types and topology types. + axom::mir::views::dispatch_coordset(n_mesh["coordsets/coords"], [&](auto coordsetView) + { + axom::mir::views::dispatch_topologies(n_mesh["topologies/mesh"], [&](auto topologyView) + { + implementation(coordsetView, topologyView); + }); + }); + } + + template + void implementation(CoordsetView coordsetView, TopologyView topologyView) const + { + // Do algorithm that involves coordsetView and topologyView. + } + }; + diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 2967ffb638..5de3002d26 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -65,6 +65,7 @@ int runMIR(const conduit::Node &hostMesh, bputils::copy(deviceMesh, hostMesh); AXOM_ANNOTATE_BEGIN("runMIR"); + // _equiz_mir_start // Make views (we know beforehand which types to make) using CoordsetView = ExplicitCoordsetView; CoordsetView coordsetView( @@ -89,6 +90,7 @@ int runMIR(const conduit::Node &hostMesh, MIR m(topoView, coordsetView, matsetView); conduit::Node deviceResult; m.execute(deviceMesh, options, deviceResult); + // _equiz_mir_end AXOM_ANNOTATE_END("runMIR"); diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 6ecdfbdebb..7c8d0d79fd 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -150,12 +150,14 @@ struct test_make_unstructured conduit::Node deviceMesh; bputils::copy(deviceMesh, hostMesh); + // _mir_utilities_makeunstructured_begin conduit::Node deviceResult; bputils::MakeUnstructured uns; uns.execute(deviceMesh["topologies/mesh"], deviceMesh["coordsets/coords"], "mesh", deviceResult); + // _mir_utilities_makeunstructured_end // device->host conduit::Node hostResult; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 46e4631ba2..a3cffd6614 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -407,6 +407,7 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) conduit::Node deviceMesh; bputils::copy(deviceMesh, hostMesh); + // _mir_utilities_clipfield_start // Make views for the device mesh. conduit::Node &n_x = deviceMesh.fetch_existing("coordsets/coords/values/x"); conduit::Node &n_y = deviceMesh.fetch_existing("coordsets/coords/values/y"); @@ -435,6 +436,7 @@ void test_one_shape(const conduit::Node &hostMesh, const std::string &name) options["inside"] = 1; options["outside"] = 1; clipper.execute(deviceMesh, options, deviceClipMesh); + // _mir_utilities_clipfield_end // Copy device->host conduit::Node hostClipMesh; @@ -604,12 +606,13 @@ void braid2d_clip_test(const std::string &type, const std::string &name) n_device_topo.fetch_existing("elements/offsets")); // Make the shape map. + int allocatorID = axom::execution_space::allocatorID(); axom::Array values, ids; auto shapeMap = axom::mir::views::buildShapeMap( n_device_topo, values, ids, - axom::execution_space::allocatorID()); + allocatorID); using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; From c0b9e02f5d042d4fcfd427d17095fae8a48fb777 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 00:31:18 -0700 Subject: [PATCH 261/290] Removed empty file --- src/axom/mir/CMakeLists.txt | 1 - src/axom/mir/EquiZAlgorithm.cpp | 12 ------------ 2 files changed, 13 deletions(-) delete mode 100644 src/axom/mir/EquiZAlgorithm.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index ab8d728d71..5b7291f7e4 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -86,7 +86,6 @@ set(mir_sources blueprint_utilities.cpp MIRAlgorithm.cpp - EquiZAlgorithm.cpp clipping/ClipCasesHex.cpp clipping/ClipCasesPyr.cpp clipping/ClipCasesQua.cpp diff --git a/src/axom/mir/EquiZAlgorithm.cpp b/src/axom/mir/EquiZAlgorithm.cpp deleted file mode 100644 index 7867dd7554..0000000000 --- a/src/axom/mir/EquiZAlgorithm.cpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#include "axom/mir/EquiZAlgorithm.hpp" - -namespace axom -{ -namespace mir -{ } // namespace mir -} // namespace axom From 90a41ab4b888cd511be3988e69f7054d2ce2625c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 11:01:08 -0700 Subject: [PATCH 262/290] Remove more if constexpr --- src/axom/mir/ClipField.hpp | 111 +++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 36 deletions(-) diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 60f37637fa..17db42ec82 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -175,7 +175,7 @@ inline AXOM_HOST_DEVICE int unique_count(const IdType *values, int n) // NOTE - These types were pulled out of ClipField so they could be used in // some code that was moved out to handle degeneracies using partial // specialization rather than "if constexpr". Put it all back when -// "if constexpr" is allowed. One nice side-seffect is shorter symbol +// "if constexpr" is allowed. One nice side-effect is shorter symbol // names in the debugger. using BitSet = std::uint32_t; @@ -249,6 +249,32 @@ DataView filter(conduit::Node &n_src, template struct DegenerateHandler { + /*! + * \brief Set the size for the current fragment. + * + * \param fragmentsView The number of fragments for the szIndex zone. + * \param connView The new connectivity. + * \param sizesView The new mesh sizes. + * \param szIndex The zone index currently being processed. + * \param nidsThisFragment The number of node ids in the current fragment. + * \param sizeIndex The write index for the sizes to output. + * \param outputIndex The write index for the offsets to output. + * + * \return True if the fragment is degenerate, false otherwise. + */ + AXOM_HOST_DEVICE + static bool setSize(axom::ArrayView AXOM_UNUSED_PARAM(fragmentsView), + axom::ArrayView AXOM_UNUSED_PARAM(connView), + axom::ArrayView sizesView, + axom::IndexType AXOM_UNUSED_PARAM(szIndex), + int nIdsThisFragment, + int sizeIndex, + int &AXOM_UNUSED_PARAM(outputIndex)) + { + sizesView[sizeIndex] = nIdsThisFragment; + return false; + } + /*! * \brief In a previous stage, degenerate shapes were marked as having zero size. * This method filters them out from the auxiliary arrays. @@ -302,6 +328,50 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> { using reduce_policy = typename axom::execution_space::reduce_policy; + /*! + * \brief Set the size for the current fragment. + * + * \param fragmentsView The number of fragments for the szIndex zone. + * \param connView The new connectivity. + * \param sizesView The new mesh sizes. + * \param szIndex The zone index currently being processed. + * \param nidsThisFragment The number of node ids in the current fragment. + * \param sizeIndex The write index for the sizes to output. + * \param outputIndex The write index for the offsets to output. + * + * \return True if the fragment is degenerate, false otherwise. + */ + AXOM_HOST_DEVICE + static bool setSize(axom::ArrayView fragmentsView, + axom::ArrayView connView, + axom::ArrayView sizesView, + axom::IndexType szIndex, + int nIdsThisFragment, + int sizeIndex, + int &outputIndex) + { + const int connStart = outputIndex - nIdsThisFragment; + + // Check for degenerate + const int nUniqueIds = details::unique_count(connView.data() + connStart, + nIdsThisFragment); + const bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); + + // Rewind the outputIndex so we don't emit it in the connectivity. + if(thisFragmentDegenerate) + { + outputIndex = connStart; + + // There is one less fragment than we're expecting in the output. + fragmentsView[szIndex] -= 1; + } + + // Mark empty size. + sizesView[sizeIndex] = thisFragmentDegenerate ? 0 : nIdsThisFragment; + + return thisFragmentDegenerate; + } + /*! * \brief In a previous stage, degenerate shapes were marked as having zero size. * This method filters them out from the auxiliary arrays. @@ -1723,41 +1793,10 @@ class ClipField const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) - if constexpr(TopologyView::dimension() == 2) - { - int connStart = outputIndex - nIdsThisFragment; - - // Check for degenerate - int nUniqueIds = details::unique_count( - connView.data() + connStart, - nIdsThisFragment); - bool thisFragmentDegenerate = - nUniqueIds < (nIdsThisFragment - 1); - degenerates |= thisFragmentDegenerate; - - // Rewind the outputIndex so we don't emit it in the connectivity. - if(thisFragmentDegenerate) - { - //std::cout << "degenerate " << szIndex << " {"; - //for(int i = 0; i < nIdsThisFragment; i++) - //{ - // std::cout << connView[connStart + i] << ", "; - //} - //std::cout << std::endl; - - outputIndex = connStart; - - // There is one less fragment than we're expecting in the output. - fragmentData.m_fragmentsView[szIndex] -= 1; - } - // Mark empty size. - sizesView[sizeIndex] = - thisFragmentDegenerate ? 0 : nIdsThisFragment; - } - else - { - sizesView[sizeIndex] = nIdsThisFragment; - } + // Set the output zone size, checking to see whether it is degenerate. + degenerates |= details::DegenerateHandler::setSize( + fragmentData.m_fragmentsView, connView, sizesView, szIndex, nIdsThisFragment, + sizeIndex, outputIndex); #else sizesView[sizeIndex] = nIdsThisFragment; #endif From 67182154c06c1fb0d071d59a77f56b3975ac46a5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 12:26:28 -0700 Subject: [PATCH 263/290] Guard RAJA better --- src/axom/mir/BlendGroupBuilder.hpp | 5 + src/axom/mir/CMakeLists.txt | 4 +- src/axom/mir/ClipField.hpp | 316 +++++++++++------- src/axom/mir/EquiZAlgorithm.hpp | 17 +- src/axom/mir/ExtractZones.hpp | 5 + src/axom/mir/MatsetSlicer.hpp | 5 + src/axom/mir/MergeMeshes.hpp | 7 +- src/axom/mir/NodeToZoneRelationBuilder.hpp | 5 +- src/axom/mir/SelectedZones.hpp | 5 + src/axom/mir/ZoneListBuilder.hpp | 5 + src/axom/mir/blueprint_utilities.hpp | 37 +- .../mir/examples/mir_concentric_circles.cpp | 4 - src/axom/mir/tests/mir_views.cpp | 7 +- src/axom/mir/utilities.hpp | 27 +- 14 files changed, 293 insertions(+), 156 deletions(-) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/BlendGroupBuilder.hpp index c4215301b6..42bf6b6403 100644 --- a/src/axom/mir/BlendGroupBuilder.hpp +++ b/src/axom/mir/BlendGroupBuilder.hpp @@ -8,6 +8,11 @@ #include "axom/core.hpp" #include "axom/mir/utilities.hpp" +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + namespace axom { namespace mir diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 5b7291f7e4..d344fc9010 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -11,7 +11,7 @@ #------------------------------------------------------------------------------ axom_component_requires(NAME MIR COMPONENTS SLIC SLAM PRIMAL - TPLS Conduit) + TPLS Conduit RAJA umpire) #------------------------------------------------------------------------------ # Specify all headers/sources @@ -100,7 +100,7 @@ set(mir_sources #------------------------------------------------------------------------------ # Build and install the library #------------------------------------------------------------------------------ -set(mir_depends_on core slic slam primal conduit::conduit) +set(mir_depends_on core slic slam primal conduit::conduit RAJA umpire) blt_add_library( NAME mir diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/ClipField.hpp index 17db42ec82..a9d683b3c7 100644 --- a/src/axom/mir/ClipField.hpp +++ b/src/axom/mir/ClipField.hpp @@ -1,5 +1,5 @@ // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. +// other Axom Project Developers. See the top-level LICENSE file for internal. // // SPDX-License-Identifier: (BSD-3-Clause) #ifndef AXOM_MIR_CLIP_FIELD_HPP_ @@ -26,6 +26,11 @@ #include #include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + #include #include @@ -45,7 +50,7 @@ namespace mir { namespace clipping { -namespace details +namespace internal { /*! * \brief Given an "ST_index" (e.g. ST_TET from clipping definitions), return an appropriate ShapeID value. @@ -353,7 +358,7 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> const int connStart = outputIndex - nIdsThisFragment; // Check for degenerate - const int nUniqueIds = details::unique_count(connView.data() + connStart, + const int nUniqueIds = internal::unique_count(connView.data() + connStart, nIdsThisFragment); const bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); @@ -509,6 +514,144 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> }; #endif +/*! + * \brief Base template for handling fields on a strided-structured mesh. The + * default is that the mesh is not strided-structured so do nothing. + * + * \tparam enabled Whether the mesh is strided-structured. + * \tparam ExecSpace The execution space. + * \tparam TopologyView The topology view type. + * + * \note This was extracted from ClipField to remove some "if constexpr". Put + * it back someday. + */ +template +struct StridedStructuredFields +{ + /*! + * \brief Slice an element field. + * + * \param topologyView The topology view. + * \param slice Slice data. + * \param n_field The field being sliced. + * \param n_newField The node that will contain the new field. + */ + static bool sliceElementField(const TopologyView &AXOM_UNUSED_PARAM(topologyView), + const axom::mir::utilities::blueprint::SliceData &AXOM_UNUSED_PARAM(slice), + const conduit::Node &AXOM_UNUSED_PARAM(n_field), + conduit::Node &AXOM_UNUSED_PARAM(n_newField)) + { + return false; + } + + /*! + * \brief Blend a vertex field. + * + * \param topologyView The topology view. + * \param blend Blend data. + * \param n_field The field being sliced. + * \param n_newField The node that will contain the new field. + */ + static bool blendVertexField(const TopologyView &AXOM_UNUSED_PARAM(topologyView), + const axom::mir::utilities::blueprint::BlendData &AXOM_UNUSED_PARAM(blend), + const conduit::Node &AXOM_UNUSED_PARAM(n_field), + conduit::Node &AXOM_UNUSED_PARAM(n_newField)) + { + return false; + } +}; + +/*! + * \brief Partial specialization to handle fields on strided-structured mesh. + * This is the strided-structured case. + * + * \tparam ExecSpace The execution space. + * \tparam TopologyView The topology view type. + * + * \note This was extracted from ClipField to remove some "if constexpr". Put + * it back someday. + */ +template +struct StridedStructuredFields +{ + /*! + * \brief Slice an element field if the field is strided-structured. + * + * \param topologyView The topology view. + * \param slice Slice data. + * \param n_field The field being sliced. + * \param n_newField The node that will contain the new field. + */ + static bool sliceElementField(const TopologyView &topologyView, + const axom::mir::utilities::blueprint::SliceData &slice, + const conduit::Node &n_field, + conduit::Node &n_newField) + { + bool handled = false; + if(n_field.has_path("offsets") && n_field.has_path("strides")) + { + using Indexing = typename TopologyView::IndexingPolicy; + using IndexingPolicy = + axom::mir::utilities::blueprint::SSElementFieldIndexing; + IndexingPolicy indexing; + indexing.m_indexing = topologyView.indexing(); + indexing.update(n_field); + + axom::mir::utilities::blueprint::FieldSlicer s( + indexing); + s.execute(slice, n_field, n_newField); + handled = true; + } + return handled; + } + + /*! + * \brief Blend a vertex field if the field is strided-structured. + * + * \param topologyView The topology view. + * \param blend Blend data. + * \param n_field The field being sliced. + * \param n_newField The node that will contain the new field. + */ + static bool blendVertexField(const TopologyView &topologyView, + const axom::mir::utilities::blueprint::BlendData &blend, + const conduit::Node &n_field, + conduit::Node &n_newField) + { + bool handled = false; + if(n_field.has_path("offsets") && n_field.has_path("strides")) + { + // Make node indexing that the field blender can use. + using Indexing = typename TopologyView::IndexingPolicy; + using IndexingPolicy = + axom::mir::utilities::blueprint::SSVertexFieldIndexing; + IndexingPolicy indexing; + indexing.m_topoIndexing = topologyView.indexing().expand(); + indexing.m_fieldIndexing = topologyView.indexing().expand(); + indexing.update(n_field); + + // If the topo and field offsets/strides are different then we need to go through + // SSVertexFieldIndexing. Otherwise, we can let the normal case further below + // handle the field. + if(indexing.m_topoIndexing.m_offsets != + indexing.m_fieldIndexing.m_offsets || + indexing.m_topoIndexing.m_strides != + indexing.m_fieldIndexing.m_strides) + { + // Blend the field. + axom::mir::utilities::blueprint::FieldBlender< + ExecSpace, + axom::mir::utilities::blueprint::SelectSubsetPolicy, + IndexingPolicy> + b(indexing); + b.execute(blend, n_field, n_newField); + handled = true; + } + } + return handled; + } +}; + #if defined(AXOM_DEBUG_CLIP_FIELD) /*! * \brief Print device views to std::out after moving data to the host. @@ -540,7 +683,7 @@ void printHost(const std::string &name, const ViewType &deviceView) } #endif -} // end namespace details +} // end namespace internal //------------------------------------------------------------------------------ /*! @@ -717,7 +860,7 @@ class ClipField using ClipTableViews = axom::StackArray; using Intersector = IntersectPolicy; - using BitSet = details::BitSet; + using BitSet = internal::BitSet; using KeyType = typename NamingPolicy::KeyType; using loop_policy = typename axom::execution_space::loop_policy; using reduce_policy = typename axom::execution_space::reduce_policy; @@ -1081,7 +1224,7 @@ class ClipField #if !defined(__CUDACC__) private: #endif - using FragmentData = details::FragmentData; + using FragmentData = internal::FragmentData; /*! * \brief Contains some per-zone data that we want to hold onto between methods. @@ -1125,20 +1268,20 @@ class ClipField AXOM_ANNOTATE_SCOPE("createClipTableViews"); if(dimension == -1 || dimension == 2) { - views[details::getClipTableIndex(views::Tri_ShapeID)] = + views[internal::getClipTableIndex(views::Tri_ShapeID)] = m_clipTables[ST_TRI].view(); - views[details::getClipTableIndex(views::Quad_ShapeID)] = + views[internal::getClipTableIndex(views::Quad_ShapeID)] = m_clipTables[ST_QUA].view(); } if(dimension == -1 || dimension == 3) { - views[details::getClipTableIndex(views::Tet_ShapeID)] = + views[internal::getClipTableIndex(views::Tet_ShapeID)] = m_clipTables[ST_TET].view(); - views[details::getClipTableIndex(views::Pyramid_ShapeID)] = + views[internal::getClipTableIndex(views::Pyramid_ShapeID)] = m_clipTables[ST_PYR].view(); - views[details::getClipTableIndex(views::Wedge_ShapeID)] = + views[internal::getClipTableIndex(views::Wedge_ShapeID)] = m_clipTables[ST_WDG].view(); - views[details::getClipTableIndex(views::Hex_ShapeID)] = + views[internal::getClipTableIndex(views::Hex_ShapeID)] = m_clipTables[ST_HEX].view(); } } @@ -1191,7 +1334,7 @@ class ClipField zoneData.m_clipCasesView[szIndex] = clipcase; // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto clipTableIndex = internal::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; int thisBlendGroups = @@ -1212,7 +1355,7 @@ class ClipField if(fragment[0] == ST_PNT) { - if(details::generatedPointIsSelected(fragment[2], selection)) + if(internal::generatedPointIsSelected(fragment[2], selection)) { const int nIds = static_cast(fragment[3]); @@ -1242,7 +1385,7 @@ class ClipField } else { - if(details::shapeIsSelected(fragment[1], selection)) + if(internal::shapeIsSelected(fragment[1], selection)) { thisFragments++; const int nIdsThisFragment = fragment.size() - 2; @@ -1306,14 +1449,14 @@ class ClipField std::cout << "------------------------ computeSizes ------------------------" << std::endl; - details::printHost("fragmentData.m_fragmentsView", + internal::printHost("fragmentData.m_fragmentsView", fragmentData.m_fragmentsView); - details::printHost("fragmentData.m_fragmentsSizeView", + internal::printHost("fragmentData.m_fragmentsSizeView", fragmentData.m_fragmentsSizeView); - details::printHost("blendGroupsView", blendGroupsView); - details::printHost("blendGroupsLenView", blendGroupsLenView); - details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); - details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + internal::printHost("blendGroupsView", blendGroupsView); + internal::printHost("blendGroupsLenView", blendGroupsLenView); + internal::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + internal::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); std::cout << "--------------------------------------------------------------" << std::endl; @@ -1369,9 +1512,9 @@ class ClipField std::cout << "------------------------ computeFragmentOffsets " "------------------------" << std::endl; - details::printHost("fragmentData.m_fragmentOffsetsView", + internal::printHost("fragmentData.m_fragmentOffsetsView", fragmentData.m_fragmentOffsetsView); - details::printHost("fragmentData.m_fragmentSizeOffsetsView", + internal::printHost("fragmentData.m_fragmentSizeOffsetsView", fragmentData.m_fragmentSizeOffsetsView); std::cout << "-------------------------------------------------------------" "-----------" @@ -1433,9 +1576,9 @@ class ClipField std::cout << "---------------------------- createNodeMaps " "----------------------------" << std::endl; - details::printHost("nodeData.m_nodeUsedView", nodeData.m_nodeUsedView); - details::printHost("nodeData.m_originalIdsView", nodeData.m_originalIdsView); - details::printHost("nodeData.m_oldNodeToNewNodeView", + internal::printHost("nodeData.m_nodeUsedView", nodeData.m_nodeUsedView); + internal::printHost("nodeData.m_originalIdsView", nodeData.m_originalIdsView); + internal::printHost("nodeData.m_oldNodeToNewNodeView", nodeData.m_oldNodeToNewNodeView); std::cout << "-------------------------------------------------------------" "-----------" @@ -1476,7 +1619,7 @@ class ClipField const auto clipcase = zoneData.m_clipCasesView[szIndex]; // Iterate over the shapes in this clip case to determine the number of blend groups. - const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto clipTableIndex = internal::getClipTableIndex(zone.id()); const auto &ctView = clipTableViews[clipTableIndex]; // These are the points used in this zone's fragments. @@ -1494,7 +1637,7 @@ class ClipField if(fragment[0] == ST_PNT) { - if(details::generatedPointIsSelected(fragment[2], selection)) + if(internal::generatedPointIsSelected(fragment[2], selection)) { const int nIds = static_cast(fragment[3]); const auto one_over_n = 1.f / static_cast(nIds); @@ -1771,7 +1914,7 @@ class ClipField #endif // Iterate over the selected fragments and emit connectivity for them. const auto clipcase = zoneData.m_clipCasesView[szIndex]; - const auto clipTableIndex = details::getClipTableIndex(zone.id()); + const auto clipTableIndex = internal::getClipTableIndex(zone.id()); const auto ctView = clipTableViews[clipTableIndex]; auto it = ctView.begin(clipcase); const auto end = ctView.end(clipcase); @@ -1783,7 +1926,7 @@ class ClipField if(fragmentShape != ST_PNT) { - if(details::shapeIsSelected(fragment[1], selection)) + if(internal::shapeIsSelected(fragment[1], selection)) { // Output the nodes used in this zone. const int fragmentSize = fragment.size(); @@ -1794,14 +1937,14 @@ class ClipField const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) // Set the output zone size, checking to see whether it is degenerate. - degenerates |= details::DegenerateHandler::setSize( + degenerates |= internal::DegenerateHandler::setSize( fragmentData.m_fragmentsView, connView, sizesView, szIndex, nIdsThisFragment, sizeIndex, outputIndex); #else sizesView[sizeIndex] = nIdsThisFragment; #endif shapesView[sizeIndex] = - details::ST_Index_to_ShapeID(fragmentShape); + internal::ST_Index_to_ShapeID(fragmentShape); colorView[sizeIndex] = fragment[1] - COLOR0; sizeIndex++; } @@ -1819,15 +1962,15 @@ class ClipField << "------------------------ makeTopology ------------------------" << std::endl; std::cout << "degenerates_reduce=" << degenerates_reduce.get() << std::endl; - // details::printHost("selectedZones", selectedZones.view()); - details::printHost("m_fragmentsView", fragmentData.m_fragmentsView); - // details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); - // details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); - details::printHost("conn", connView); - details::printHost("sizes", sizesView); - details::printHost("offsets", offsetsView); - details::printHost("shapes", shapesView); - details::printHost("color", colorView); + // internal::printHost("selectedZones", selectedZones.view()); + internal::printHost("m_fragmentsView", fragmentData.m_fragmentsView); + // internal::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + // internal::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + internal::printHost("conn", connView); + internal::printHost("sizes", sizesView); + internal::printHost("offsets", offsetsView); + internal::printHost("shapes", shapesView); + internal::printHost("color", colorView); std::cout << "--------------------------------------------------------------" << std::endl; @@ -1838,7 +1981,7 @@ class ClipField // Filter out shapes that were marked as zero-size, adjusting connectivity and other arrays. if(degenerates_reduce.get()) { - details::DegenerateHandler::filterZeroSizes(fragmentData, + internal::DegenerateHandler::filterZeroSizes(fragmentData, n_sizes, n_offsets, n_shapes, n_color_values, sizesView, offsetsView, shapesView, colorView); } @@ -1851,15 +1994,15 @@ class ClipField std::cout << "------------------------ makeTopology ------------------------" << std::endl; - details::printHost("selectedZones", selectedZones.view()); - details::printHost("m_fragmentsView", fragmentData.m_fragmentsView); - details::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); - details::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); - details::printHost("conn", connView); - details::printHost("sizes", sizesView); - details::printHost("offsets", offsetsView); - details::printHost("shapes", shapesView); - details::printHost("color", colorView); + internal::printHost("selectedZones", selectedZones.view()); + internal::printHost("m_fragmentsView", fragmentData.m_fragmentsView); + internal::printHost("zoneData.m_clipCasesView", zoneData.m_clipCasesView); + internal::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); + internal::printHost("conn", connView); + internal::printHost("sizes", sizesView); + internal::printHost("offsets", offsetsView); + internal::printHost("shapes", shapesView); + internal::printHost("color", colorView); std::cout << "--------------------------------------------------------------" << std::endl; @@ -1873,7 +2016,7 @@ class ClipField #if defined(AXOM_CLIP_FILTER_DEGENERATES) // Handle some quad->tri degeneracies, depending on dimension. - shapesUsed = details::DegenerateHandler::quadtri(shapesUsed, connView, sizesView, offsetsView, shapesView); + shapesUsed = internal::DegenerateHandler::quadtri(shapesUsed, connView, sizesView, offsetsView, shapesView); #endif // Add shape information to the connectivity. @@ -1955,32 +2098,17 @@ class ClipField conduit::Node &n_out_fields) const { AXOM_ANNOTATE_SCOPE("makeFields"); + constexpr bool ss = axom::mir::views::view_traits::supports_strided_structured(); + for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { const conduit::Node &n_field = n_fields.fetch_existing(it->first); const std::string association = n_field["association"].as_string(); if(association == "element") { - bool handled = false; - // Conditionally support strided-structured. - if constexpr(axom::mir::views::view_traits< - TopologyView>::supports_strided_structured()) - { - if(n_field.has_path("offsets") && n_field.has_path("strides")) - { - using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = - axom::mir::utilities::blueprint::SSElementFieldIndexing; - IndexingPolicy indexing; - indexing.m_indexing = m_topologyView.indexing(); - indexing.update(n_field); - - axom::mir::utilities::blueprint::FieldSlicer s( - indexing); - s.execute(slice, n_field, n_out_fields[it->second]); - handled = true; - } - } + // Conditionally support strided-structured. + bool handled = internal::StridedStructuredFields::sliceElementField(m_topologyView, slice, n_field, n_out_fields[it->second]); + if(!handled) { axom::mir::utilities::blueprint::FieldSlicer s; @@ -1991,50 +2119,12 @@ class ClipField } else if(association == "vertex") { - bool handled = false; + // Conditionally support strided-structured. + bool handled = internal::StridedStructuredFields::blendVertexField(m_topologyView, blend, n_field, n_out_fields[it->second]); - // Node indices in the blend groups are global indices. This means that, provided the - // field's offsets/strides match the topology's (and why would they not?), we can skip - // strided structured support for now. Enable this code if the field offsets/strides - // do not match the topo's offsets/strides. - - // Conditionally support strided-structured. - if constexpr(axom::mir::views::view_traits< - TopologyView>::supports_strided_structured()) - { - if(n_field.has_path("offsets") && n_field.has_path("strides")) - { - // Make node indexing that the field blender can use. - using Indexing = typename TopologyView::IndexingPolicy; - using IndexingPolicy = - axom::mir::utilities::blueprint::SSVertexFieldIndexing; - IndexingPolicy indexing; - indexing.m_topoIndexing = m_topologyView.indexing().expand(); - indexing.m_fieldIndexing = m_topologyView.indexing().expand(); - indexing.update(n_field); - - // If the topo and field offsets/strides are different then we need to go through - // SSVertexFieldIndexing. Otherwise, we can let the normal case further below - // handle the field. - if(indexing.m_topoIndexing.m_offsets != - indexing.m_fieldIndexing.m_offsets || - indexing.m_topoIndexing.m_strides != - indexing.m_fieldIndexing.m_strides) - { - // Blend the field. - axom::mir::utilities::blueprint::FieldBlender< - ExecSpace, - axom::mir::utilities::blueprint::SelectSubsetPolicy, - IndexingPolicy> - b(indexing); - b.execute(blend, n_field, n_out_fields[it->second]); - handled = true; - } - } - } if(!handled) { - // Blend the field. + // Blend the field normally. axom::mir::utilities::blueprint::FieldBlender< ExecSpace, axom::mir::utilities::blueprint::SelectSubsetPolicy> diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index fe2bfed9a1..43909d87e9 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -1,5 +1,5 @@ // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. +// other Axom Project Developers. See the top-level LICENSE file for internals. // // SPDX-License-Identifier: (BSD-3-Clause) #ifndef AXOM_MIR_EQUIZ_ALGORITHM_HPP_ @@ -22,13 +22,14 @@ #include -#include -#include - +// RAJA #if defined(AXOM_USE_RAJA) - #include + #include "RAJA/RAJA.hpp" #endif +#include +#include + // This macro makes the EquiZ algorithm split processing into clean/mixed stages. #define AXOM_EQUIZ_SPLIT_PROCESSING @@ -53,7 +54,7 @@ using MaterialVFView = axom::ArrayView; constexpr static int NULL_MATERIAL = -1; constexpr static MaterialVF NULL_MATERIAL_VF = -1.f; -namespace detail +namespace internal { /*! * \brief This class is an intersection policy compatible with ClipField. It @@ -282,7 +283,7 @@ class MaterialIntersector View m_view {}; }; -} // end namespace detail +} // end namespace internal /*! * \accelerated @@ -1153,7 +1154,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm //-------------------------------------------------------------------------- using ConnectivityType = typename ITopologyView::ConnectivityType; using IntersectorType = - detail::MaterialIntersector; + internal::MaterialIntersector; IntersectorType intersector; int allocatorID = axom::execution_space::allocatorID(); diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 4cfb01b36f..339f8e5f96 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -10,6 +10,11 @@ #include #include // Needed to get MatsetSlicer +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + namespace axom { namespace mir diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/MatsetSlicer.hpp index 62d87190ba..cc2eb4c4ed 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/MatsetSlicer.hpp @@ -11,6 +11,11 @@ #include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + namespace axom { namespace mir diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/MergeMeshes.hpp index 6633af6c62..48fd8f970a 100644 --- a/src/axom/mir/MergeMeshes.hpp +++ b/src/axom/mir/MergeMeshes.hpp @@ -14,12 +14,13 @@ #include -#include - +// RAJA #if defined(AXOM_USE_RAJA) - #include + #include "RAJA/RAJA.hpp" #endif +#include + namespace axom { namespace mir diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/NodeToZoneRelationBuilder.hpp index a096b9d239..9cae68a2fd 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/NodeToZoneRelationBuilder.hpp @@ -16,7 +16,10 @@ #include #include -#include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif #include #include diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/SelectedZones.hpp index 726977d4c9..2b2bd36443 100644 --- a/src/axom/mir/SelectedZones.hpp +++ b/src/axom/mir/SelectedZones.hpp @@ -9,6 +9,11 @@ #include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + namespace axom { namespace mir diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/ZoneListBuilder.hpp index 5c6f4770e9..8f877adb9e 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/ZoneListBuilder.hpp @@ -11,6 +11,11 @@ #include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif + namespace axom { namespace mir diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/blueprint_utilities.hpp index 9c5d8c2306..c5129d4075 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/blueprint_utilities.hpp @@ -16,8 +16,9 @@ #include #include +// RAJA #if defined(AXOM_USE_RAJA) - #include + #include "RAJA/RAJA.hpp" #endif #include @@ -32,7 +33,7 @@ namespace utilities namespace blueprint { //------------------------------------------------------------------------------ -/** +/*! * \brief This class provides a couple of type traits that let us map C++ types * to types / values useful in Conduit. */ @@ -122,7 +123,7 @@ struct cpp2conduit //------------------------------------------------------------------------------ -/** +/*! * \brief Make an axom::ArrayView from a Conduit node. * * \tparam T The type for the array view elements. @@ -158,7 +159,7 @@ inline axom::ArrayView make_array_view(const conduit::Node &n) /// @} //------------------------------------------------------------------------------ -/** +/*! * \brief This class registers a Conduit allocator that can make Conduit allocate * through Axom's allocate/deallocate functions using a specific allocator. * This permits Conduit to allocate through Axom's UMPIRE logic. @@ -169,7 +170,7 @@ template class ConduitAllocateThroughAxom { public: - /** + /*! * \brief Get the Conduit allocator ID for this ExecSpace. * * \return The Conduit allocator ID for this ExecSpace. @@ -187,7 +188,7 @@ class ConduitAllocateThroughAxom } private: - /** + /*! * \brief A function we register with Conduit to allocate memory. * * \param items The number of items to allocate. @@ -204,7 +205,7 @@ class ConduitAllocateThroughAxom return ptr; } - /** + /*! * \brief A deallocation function we register with Conduit. */ static void internal_free(void *ptr) @@ -215,7 +216,7 @@ class ConduitAllocateThroughAxom }; //------------------------------------------------------------------------------ -/** +/*! * \brief Copies a Conduit tree in the \a src node to a new Conduit \a dest node, * making sure to allocate array data in the appropriate memory space for * the execution space. @@ -267,7 +268,7 @@ void copy(conduit::Node &dest, const conduit::Node &src) } //------------------------------------------------------------------------------ -/** +/*! * \brief Fill an array with int values from a Conduit node. * * \tparam ArrayType The array type being filled. @@ -311,12 +312,12 @@ bool fillFromNode(const conduit::Node &n, } //------------------------------------------------------------------------------ -/** +/*! * \brief Returns the input index (no changes). */ struct DirectIndexing { - /** + /*! * \brief Return the input index (no changes). * \param index The input index. * \return The input index. @@ -329,14 +330,14 @@ struct DirectIndexing }; //------------------------------------------------------------------------------ -/** +/*! * \brief Help turn slice data zone indices into strided structured element field indices. * \tparam Indexing A StridedStructuredIndexing of some dimension. */ template struct SSElementFieldIndexing { - /** + /*! * \brief Update the indexing offsets/strides from a Conduit node. * \param field The Conduit node for a field. * @@ -348,7 +349,7 @@ struct SSElementFieldIndexing fillFromNode(field, "strides", m_indexing.m_strides, true); } - /** + /*! * \brief Transforms the index from local to global through an indexing object. * \param index The local index * \return The global index for the field. @@ -363,14 +364,14 @@ struct SSElementFieldIndexing }; //------------------------------------------------------------------------------ -/** +/*! * \brief Help turn blend group node indices (global) into vertex field indices. * \tparam Indexing A StridedStructuredIndexing of some dimension. */ template struct SSVertexFieldIndexing { - /** + /*! * \brief Update the indexing offsets/strides from a Conduit node. * \param field The Conduit node for a field. * @@ -382,7 +383,7 @@ struct SSVertexFieldIndexing fillFromNode(field, "strides", m_fieldIndexing.m_strides, true); } - /** + /*! * \brief Transforms the index from local to global through an indexing object. * \param index The global index * \return The global index for the field. @@ -460,7 +461,7 @@ struct minmax } }; -/** +/*! * \brief Save a Blueprint mesh to a legacy ASCII VTK file. * * \param node The node that contains the mesh data. diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 5de3002d26..7a0e71cfdb 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -132,21 +132,18 @@ int runMIR(RuntimePolicy policy, int retval = 0; if(policy == RuntimePolicy::seq) { -#pragma message "SEQ supported" retval = runMIR(mesh, options, resultMesh); } #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) #if defined(AXOM_USE_OPENMP) else if(policy == RuntimePolicy::omp) { - #pragma message "OMP supported" retval = runMIR(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_CUDA) else if(policy == RuntimePolicy::cuda) { - #pragma message "CUDA supported" constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; retval = runMIR(mesh, options, resultMesh); @@ -155,7 +152,6 @@ int runMIR(RuntimePolicy policy, #if defined(AXOM_USE_HIP) else if(policy == RuntimePolicy::hip) { - #pragma message "HIP supported" constexpr int HIP_BLOCK_SIZE = 64; using hip_exec = axom::HIP_EXEC; retval = runMIR(mesh, options, resultMesh); diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index a55e1dd127..21e9406325 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -361,7 +361,12 @@ struct test_braid2d_mat { static void test(const std::string &type, const std::string &mattype, - const std::string &name) +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + const std::string &name +#else + const std::string &AXOM_UNUSED_PARAM(name) +#endif + ) { namespace bputils = axom::mir::utilities::blueprint; const int allocatorID = axom::execution_space::allocatorID(); diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities.hpp index 586ac47417..e56b1f85be 100644 --- a/src/axom/mir/utilities.hpp +++ b/src/axom/mir/utilities.hpp @@ -11,7 +11,10 @@ #include #include -#include +// RAJA +#if defined(AXOM_USE_RAJA) + #include "RAJA/RAJA.hpp" +#endif #include @@ -405,19 +408,23 @@ class HashNaming //------------------------------------------------------------------------------ /*! - * \brief This function makes a unique array of values from an input list of keys. + * \brief Makes a unique array of values from an input list of values. * * \tparam ExecSpace The execution space. * \tparam KeyType The data type for the keys. * - * \param[in] keys_orig_view The input view that contains the input keys to be made unique. - * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. - * \param[out] sindices An array of indices that indicate where in the original view the keys came from. - * */ template struct Unique { + /*! + * \brief This function makes a unique array of values from an input list of keys. + * + * \param[in] keys_orig_view The input view that contains the input keys to be made unique. + * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. + * \param[out] sindices An array of indices that indicate where in the original view the keys came from. + * + */ static void execute(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) @@ -488,6 +495,14 @@ struct Unique template struct Unique { + /*! + * \brief This function makes a unique array of values from an input list of keys. + * + * \param[in] keys_orig_view The input view that contains the input keys to be made unique. + * \param[out] skeys A sorted unique array of keys produced from keys_orig_view. + * \param[out] sindices An array of indices that indicate where in the original view the keys came from. + * + */ static void execute(const axom::ArrayView &keys_orig_view, axom::Array &skeys, axom::Array &sindices) From 227130016af4b802eb82e1087a4677052e820ea6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 15:20:44 -0700 Subject: [PATCH 264/290] fieldslicer test --- .../mir/tests/mir_blueprint_utilities.cpp | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 7c8d0d79fd..3d89ef4c4d 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -1533,6 +1533,103 @@ TEST(mir_blueprint_utilities, mergemeshes_hip) } #endif +//------------------------------------------------------------------------------ +template +struct test_fieldslicer +{ + static void test() + { + conduit::Node hostData; + create(hostData); + + // host->device + conduit::Node deviceData; + bputils::copy(deviceData, hostData); + + std::vector indices{0,1,2,7,8,9}; + axom::Array sliceIndices(indices.size(), indices.size(), axom::execution_space::allocatorID()); + axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); + + bputils::SliceData slice; + slice.m_indicesView = sliceIndices.view(); + + conduit::Node slicedData; + bputils::FieldSlicer fs; + fs.execute(slice, deviceData["fields/scalar"], slicedData["fields/scalar"]); + fs.execute(slice, deviceData["fields/vector"], slicedData["fields/vector"]); + + // device->host + conduit::Node hostSlicedData; + bputils::copy(hostSlicedData, slicedData); + + std::vector resultX{0., 1., 2., 7., 8., 9.}; + std::vector resultY{0., 10., 20., 70., 80., 90.}; + + EXPECT_EQ(hostSlicedData["fields/scalar/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedData["fields/scalar/association"].as_string(), "element"); + EXPECT_EQ(hostSlicedData["fields/scalar/values"].dtype().number_of_elements(), indices.size()); + for(size_t i = 0; i < indices.size(); i++) + { + const auto acc = hostSlicedData["fields/scalar/values"].as_double_accessor(); + EXPECT_EQ(acc[i], resultX[i]); + } + + EXPECT_EQ(hostSlicedData["fields/vector/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedData["fields/vector/association"].as_string(), "element"); + EXPECT_EQ(hostSlicedData["fields/vector/values/x"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedData["fields/vector/values/y"].dtype().number_of_elements(), indices.size()); + for(size_t i = 0; i < indices.size(); i++) + { + const auto x = hostSlicedData["fields/vector/values/x"].as_double_accessor(); + const auto y = hostSlicedData["fields/vector/values/y"].as_double_accessor(); + EXPECT_EQ(x[i], resultX[i]); + EXPECT_EQ(y[i], resultY[i]); + } + } + + static void create(conduit::Node &fields) + { + const char *yaml = R"xx( +fields: + scalar: + topology: mesh + association: element + values: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] + vector: + topology: mesh + association: element + values: + x: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] + y: [0., 10., 20., 30., 40., 50., 60., 70., 80., 90.] +)xx"; + fields.parse(yaml); + } +}; + +TEST(mir_blueprint_utilities, fieldslicer_seq) +{ + test_fieldslicer::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_blueprint_utilities, fieldslicer_omp) +{ + test_fieldslicer::test(); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_blueprint_utilities, fieldslicer_cuda) +{ + test_fieldslicer::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_blueprint_utilities, fieldslicer_hip) +{ + test_fieldslicer::test(); +} +#endif + + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { From 40eea7ea182e34bf3341100c6384411d1d6cfcd0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 18:05:47 -0700 Subject: [PATCH 265/290] Added some docs --- .../sphinx/figures/mir_concentric_circles.png | Bin 0 -> 37503 bytes src/axom/mir/docs/sphinx/mir_algorithms.rst | 28 +++-- src/axom/mir/docs/sphinx/mir_utilities.rst | 91 ++++++++++++-- src/axom/mir/docs/sphinx/mir_views.rst | 114 +++++++++++++++++- .../mir/tests/mir_blueprint_utilities.cpp | 44 ++++--- src/axom/mir/tests/mir_clipfield.cpp | 2 + src/axom/mir/tests/mir_views.cpp | 2 + .../UnstructuredTopologyMixedShapeView.cpp | 54 +++++++++ 8 files changed, 297 insertions(+), 38 deletions(-) create mode 100644 src/axom/mir/docs/sphinx/figures/mir_concentric_circles.png create mode 100644 src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp diff --git a/src/axom/mir/docs/sphinx/figures/mir_concentric_circles.png b/src/axom/mir/docs/sphinx/figures/mir_concentric_circles.png new file mode 100644 index 0000000000000000000000000000000000000000..f1d76bba32e37f39acb5b4464cd3c4da0e3814b5 GIT binary patch literal 37503 zcmeFZXCPeP+b%2;5+xBaYV;C}9=!$`qD5!4QA5;--bIOEqBBU8=-ueOg=kTu1Q9)Y zCqxZr&F}v|&pGFPp69$D-mmX`!I-`G+H2qIzOU=LuQlQ7s?YA?Q{iJ_Vck_!fN5f3 z-K59D!all#4c_tT>#W4WqQ_E%$!L2TZ>RYskaneAwF$CC^@e50&niG}vhm^VE#Pz9 zB;;IYBP>B@+`;ALb(+Z#bv`vy|7s;Ho5kQV#LW1JDu{=OaWKE!=l%cRz|6z{r}^kPW5GIj$G4hv3!F)TGu5SyITI@w z^@73z{R_z#gpGp_VN*vw8^NI^{^v2=A|xckTvYn=XVV*)U;ft{yol^AnOmm#3|LqJ zjL(vW+c`VlW>ffdnJwDp+?YC^#`c0|Elu|2gUZdIKDj6Zjz*^Up}` zPr>?c6`xAP?Q_dw&Jd`%jO5-AyN@}P-qf@l^Aigj7ncC@^M8Az>P|eN6$G4^&Dr&g zdVo)|e~bnkLzOIg2YhO3{ZKSnE?MAz4gtGM*ER6j zTg(amZ}ScgdBK|vMk*bf-M?l^X4awc!R*FAbD6=c^IuG+wGXV`=VbTbR}T>dZ$Ox; z#6hRyn~R@it>>E=GQrzzb@TpB^I~CPxJ0zgE^De6Z4~j+3C2Y@b&>juCM5HkbtG~b*1nqc?qkm{ zF=?CQ5yc!N{n`4xX~R^L$6mUmkE-~NK-*g{UeEo7yu3$k+KOKUUDRpkZ~yBSAkkR_ zX~dkd!%`Ra^R0f|2R~kZqLK7IS|7<~RZ0H#?VD-1xchdpgxBFzg#`x`>VJJPuWYAu zaL_~d6)vxZzKT7C^)sKr`LZd19ttQb3-9FZI z&!z5%jyVbo*s#uqUpN0fWE5t|CHbHHy|RbK(9#1B5>K~44sU2_s z_`SdQrRwGI?)o}~nCp5F4uR0C8O)4iKZ~Aw{pGHMgTtY1e}BKBfx$gGshPGn*DXFL zbFO30kB^UErnybD2j4Dh+$^GYs+=_TaoCzD1#{xLk(<aj%0g$vE|1Pw2lS9#LdXtM)xRoSvR8DuNy^RrYgljbz^@j-(cKzP|bkt2XusbH=HsrmilY#7BqbpI0sSmHIY&eyX|Ur^l9k7SWQmE^9+z z4l%hydc?Gkg7d@1Sbmv))oL)26n)m}>S|wK-_6_iR6a@lC2uFM)N*Jq#jtp-lJgAU*jB@_ys{)u^QZEg-pp3L~{ zHoP246$P=@6GfB4|1#>B)A4H@3kj?0_C)E=5OQM?rzKc4)qUa-cuF3yJSSNBk&YFh%&y@09uR6*Lx`P%@tf`q39rD*OPp zfOh=YGXLXZk6E`Q6DXE6R<8$|{^7nJ7A!z0SVv#q=K5ZK7m}0%1aoCB5z%CH6mBmV zGHOii$*jS>GRXUnA3t_?%eQG`F_Y96`v&%4)7w{5mi(xC`SVGTWrhFW^Ww5rF+*=G zto>WNMZ60YSa(Prs_vB4FCka@ldjjFJ%O^bGh~6Kw$uE3XzP1%l8My9%{jR{xpzo4 z;KMi|-J7esW)7OBz3#xntYDB0PY-^DTklNQxU3FTv`)ABT{zUuPE1c5XliO|X;pzm zktXgTBp?8?;?)kwx%=(6=lxC)2T8e9R8+K{3xPyWF3YQ{U1#c@NJ&Y-2Yd_-4cq+x z+S=JED=U8j@nYGZNIVh_a?r0xVbu?hS3pGS)xTbhWqMXJUp8I!Qq9uR62t{qf-nA7 z1{;|@RXVUw1*yw^wy~z7!fB#Jf5{bZfblJMZZB!vDq$b8&s%$>k-~HJ4nKt^rxkYO zlGQWTbvO?Pb1Ty`CaCS?#;e$&TN?URO5AMfjPRojt{^!WIl@)r`SU?AYO041d!p$a z|5D}eiC=|TuMMT4b#!!f6HzGQ5shi{lY&i;nJVTRZ~+l25N}72l^6Uc6i=+(pV{~`v!f~EjKS+FU^<~d$Le1 zTds)Y7U~M~yCp{@nM*<)7B}uoPFoQh415&aYQ5O~S?4$pma{cGbno=y3*8m{`yczh z`HOdjXZAs%e6!~>@AG?5_^)mTOdz*F;2xF}Lji${@orxwdxWSIY}vQyvqP5;*9hZ8 zkIvIc-6;ZEj@TftN=*ux(%S)VGev{GgPG$-G=il4UH6==`IqC5O3TeMwLjn*HDJ+v z*MU0?m6~m&v3}Ilrs|Qay>MnzS5b!{B$G;dx4yPmQOfVaoU}E)rAzT~lpaG63y8Oh zxK(#DA(@OF;TKW?%C)i^^7q{;M#c@lqm*^=4WluSSS(*8x8NIf`{P9YDy#78G$98A z9UYz8P8^cw>{u{dj`cZ>q1;wl(Oioo%|r^dgN4fEiN{)w^8ykX8b%>2*z{5KL8uq^ z+k+nDnsa$b!FP{&+2vOhm{Ra+6x1q7y<}9LKKSt`sV^v1*-{ePkRi>k?WCj;c!vy- zmSz9vIH;?U2mJFzI0Y|QH3mGXo*x(C`Q?0^wcOp(zXS#i1Fu0*vI`<72n-8@IH zUS3^vRC**o#${5EU`chLRt;gJ?Oa&$jD;M+mLTO{opBZ_e*?=(Q?HD9t*?c)Ta%7) zsdGhHg!k3~UUp4N{XT!bzRRK)Bb)h=TzQ8S!srngSA-jR@}4Mkt*?Uir*`fYhX|A- z2s_%6%7}{n(+|IhSonSbFE=fOFAP9$YKS|fR>XIo?qX^iUa48rO3L{eKCaFRX(#O9 zT&3sFpIcZk5xap}V_3V5DQ*C^fXL=-^`i`^Cy8sS|6Y_fF5A=cs(-Ll}J+g z_r3%fo9vM!V+2>U#9?nJo&RYE0nh!B>x(znd-+fPZVU>~Zzm}}bbfgYN9mQ^xOjX& zQ|@okjI}zbO-832!v?SVldKGBJf#sFLftuulNlERa_;Ix08I1%lBjIg^s>E)qg0Lz z5ouMjOs7g%W@o-zr6u7H8`0AEnyX2MN!J>#xW$>7i-EUkPXQzZNwp{PAzVYl>0+l2 zfQ4g_i)w2DQgFTavr{+gZfIv`r>Lk1Qpw>;zsK&3s6}@~sbQUvkPrqv0x;9!eOx)= zK5dilcXhVbpnU7IfVMZaXrv>zc%WzIafEFEV<5q=rP7rFnWbiYF=tW%Zey0aSQ?rU z7Uk}HbAJcGY6rIglIHKV;j*%_`ASP=03LM9jAzHkwGjwEfV;r5S^)|Fbav1GrKK0Z zL&YGK*x8YD8NgsHEQRh>U>M) zLJ45O4B-Qs89}mWl07;_}O+x=bvRMqsgK}{p8k}?2`gQ_$07g1p zalOwJff!&fh>zZES38a9OLZt0Ni7 zl6(JWZ+=h7<@h_i?PSge6kTwoo4AAprKQIP@j@6{X2!-j`|>QRG6*|29@%0~Xj!Zh#R@m3M<$-gwc&GvoL&*|#J(k-(zLQ$`I}XB11F0w^e*q-Vy@St3jICXg2I=)+iNM8{$l zDgw+PY3-mQ78Dgxi8?PQ^I7Zykk#GYjYJ|34-e0NuhEG*zYVK(oR@N1>ICmN{pxlA zuxNjw9VP%)VdO*MiSh9nmtjeN7OeX?X-;rL{#!DP3mt5y+9G7M1QbvCqS~hced_0F zGt$*{@8_x;`1IrrSk@rEO`1IqHoq6=tEL$s5G~-$fc=5Xz1W>;&`ktsNsl@hC%_or z9iDK{NI!|Cp5wm9cCe`1m(LW6(Oo*1@k&%!hl~}t96xV0o}ZtCNUyAX%=l5jM$^Tm z>bQ07YdmY2VI8`qr3Hi<08cP&1A|8AmA-!NrU4hp?jR3hvT(-Dv%HE~)L;0xT!wUb z#S&Qtq%iA!Csm34*yD#(RNL(vh$i=%8B$^t3I(tLNVMQJ749~;vrXtp&H!C9n zjtHEok~)&%RGNBjBlqVg7CP=Q)7Rl9`Cq45yd?{Z>69`tr*WSRf1+1=VV1B!*<_KYtyeipW*FFTrtZG5C&dTzk_U zK#?G#jlTD~OYTkfq zXlp};wb%?Gh;@FwfgiYmB!`F`!6XNPRPaUZ*B1YFpx``9yvG8OoBpc4 zgkH~DC)5SH%1t7P{l!qa#Kgp?hr;NrtffCc9UblLKs_}9Ow-0@0|cRC(@rfwxp8rE zAO!)6wFmTx?g&c5x>pYN_NhQbh<ebKM=SJV{%36egMiqRUNGT*4o7idhRGJS~(W_kHgih{JWw+;8HVP^28A=Z1*rpYY zB$FH8v1BzDntbEJmX-dT-5vl)6)jD!ui-BpJxS#*rALa^87eHw^+2txtgI|4i3of2 z{G)*Fc){*$6UoT(*LV-W9nHSH1B(`L9$jg|69+6j@E2MEO?{U6&MB<%v#5|gOQS|Fk2?8U-+evo zL&f~10$-1NY*26=dnjtTaiCC%l5 z%sco9ai&F7@3`k6rTWi>g91o$B?AUF8@aOr)XVN*4LU3JHzsQk!6 zChvY5yKI*ep0=Yd+YiW%;jqGd*&$ZzbfgkDs`3b$faYiF9=idp0C)n0xWPi*#V&^t zxbKS%DJMsF=;aA}us)_miS^rzyYQJuP&n=G!em~SQ^mV1&5R5+@RJ@%Xs|@uOu4OY z&@rvxtKnMWKpuip`u6>yq^@^+D?BgR>kdk+xRcOm-b#fuH^^d-t zYUG$jE(GTpPPEQ4P|@!*ZtgA~246)u5% zfTZ*bo*9u0%83U_r^%5t-<%5Srt1nXw9_lnXr93HAf*-E4zR3BWVv=o$Q#ykmRx{AB_LiEIUF@)Uz*TZ=X-E~<*(zemu0{{d$hon;1ENWdTy{VLvgu(Y{`?oOP>t7g`f38^LOZWO|o5^+KIc)L zLUVYQ`rK?FJ4J{LMb&3hxuUe8Yf?}_a0}-Pf3%Z+q9ofx9yN~DlzxFus=UP9m(x&u z1tAbyEt#2_0BSGy#sa+2@Ow!7;lqbOZ0_stUmO1PX1SM`5hL()hLZs516V{Iz!5;8 zB^imJCnv7p7PH=SpZ}t})&n$S)@|~P^bV4sOB||9Jd668M50-oxitHx z+u+A8{$y%$2aO9+Y1&rhxI`vXwUJNyI8Os4DfA#An2t71rKPD03uCJ zP6Bq23zU;$c-bpk+aff&_4?|f&i=ED- zD8pL&v!(YumE<@`0yz)M>*wtFb8+9?5>dM0~BD=qQQ< zZe@5q*>-g-zFThrk}N1Vp0xrQ1?nE~2_3dvHc#@Ff^qdOL_;ZQJAwsAjU+b8Cim^q zzTO1i-WDb<-(pZv_Idb!O)oGE1J!G1Tw&gIeK~xs*AH^FKcG!30ywY1qXQSsesg`r zn&vthM$9DSILGPl10*+n6QD*Pg4i3VeL&ewO~k? z$aIjUBfaR|1U)t~XQ~a}`&zyD@lkvz0dLabLFTsASkk<21M2u%uCaQ-Iuv7h&x?zT zf$Ty@TvJ;Mq!DdZeo$~eT^;4a#^>g)zC9(1q!HIu)xshmJf>01C}(uyWu!Iu0Q9f*!@@Fv}RXQ>#@*FU)WbVU zhY&-RUn=dE7?R79AFg>YhcBb4O}!QJASR1ofYJ58qdaFTNo~gyh9E@&+Lp#=(cK%% z)H0Dk2UI&y30f9{2(C_ho;>cxsMLUq2?`1#JQqX6fesANa}2{%WABYzaF5Mb=UZhh zf2Kiw2a8~>PWL{}A&sU`h}Npn%beqGCD~jlXTZrQ@P@CB=47#3_K!M6w>(WF6xHIr z7_F$hCqmB#852lj*eWHJ2p-mbB|InL!KiYs8_q+bYXX9T4pZfe^}nLTuP=WCF;sL7 zNK0JAk{8=mx_zMgL_C7&W2J5>%h>MHDzzhPwu|+qw#pObIfn+}2KG30x2s{6?lj7n>kH2iFrWW!RofsC; zxbvvy+;iq5AUx`!;vKr05(1`@dxM;NnzV`;x!9DB+2IF3N#qnrgVH!<98ct29bj|aFm2X-dco*rLx}B zfOf8krD#LCeu*-NNZ7IEJP?))%^H21l3$F!~)49VP+p0&E0uqhrhQ1R%2KX?wmeLA$}f_?8*Nr{W)w21mi$ z4N_1g`8sjlNCde8p+xeBca>S>!{_zf*G3=Z*f{80O$igBAfh89BeM>{L?tC8P)m67 z8j{JW0!RW>pp(7%H=wuh=6viEXlw0*?i~HFv%KQ9S4Wf z`EzMKhTQpO;-@o~@uJK685WjE+2CVK%EJ&&u@Maef&m zyOEP%-k2plXEtRP+Yk{lUYs_K{Y)8#HR3<0LT$EVy@O&S|n?ipkK=W za;Tb+Z0!>CTxV(>9@267J7+Kg}TiY+-C13Qe_7Jc2V%J6Vk1qTO7#uCXA z-Y*JSGteG;o9{WP9|Ixn@y=vp_g2_ul1;>6EGjACvFR%t!IOdeI|i#7x~O3-*@0>Wt|+nRoZZ-(omEnx%HWPbW-s` zdId+4Qd^OPHa+)3iZX%pKKZ{9cBTI-WLTs7j%@=6Bb0*!$ckI*nAd$cW0@BSG{EFi zK;Qf=9*O(Tv>OmBdTpe!)#*G|)T^VKG}$G4>Wc@$I3g;s^J1kq?xUbffErrs&{L$2JF^>;AUOel`C?64AS1;+wll+k85g&pitg z(!U^xbAR1>4gaiSzxC;=l+*Ydrz}cfaS@tkVQl6c_}?jINqEeWnD`o*w2U{{_%Va} z^^UehgqKo0ndvg>DfW_HVvlo<3^m6U?{L1U#{EBmSvFPGzoec6dLF&&?F}%SV#DQ;cX0dCO zSBT;bVon`fTU*d@DqR6R#%KVFKqd!E=I_P)b-FkcFSVSBL&dymj;^lS}%aeMdM*aXfXV%kPMkmo;MWSBcfcD$24&~PQb`QDTsuBraWk*19wDmK|%fBKRQn34$LInQ4p2a2riwOwCfnzwUvL!xaPc_|>rwc1IjR8Pn-H{Jh1U|ojWcSU825A3!&gF&LE%tmu8Nn1Xn^L@OHCeY5 z8Wtx01eWkd5WlC`6@Is>tHr1Xou4IZ)}RvX;{47T=*=Gb;SVZYRgHF|*y_^=JD7mD znUc+%MJws8XY=2rtuvqbi#cVUmKWFLWqIM`&?H2Yc-3G08I+K$GhE5NPx_J*{>V%^ zX}-hxgS_{oxY2t|#==g^S?@MovHGub7HFUE0VQny@2&@sqd?<-a=cJ$MS#kQQtIqQ zFym$eX>l;gb>67L?5ioHk@qW>zaiCTKUxL#!Q4CVSrIKeS`-x2sX0NHrJj!u%j_Uq z1fdV{yKb45Qy&2!hep2w67SB=4y@XGBJIpj)t(}o!J9451fqNsEE3E+y`L1qBKHZR9>_a?OZxqJ$NS@fukhpikyf*!L ztP7?U2v1Nt=TRG;beYX!gGP~tmP~nX`oe!AR`-y1Nb@12;WR+TjyzOMHdZ*XL=SN* zjf*FtQQ3HZ|c89#F`3#=1FGorx*-! zT?YQSFd+F}Ti=EF{MaI;?1mQl=nqfn1fd|c+&#G&kc^lrMevxqM{lPTcETve4v<0$_)_miV^R@|}NG;m(O;-p_LcE?& ziJl~fD@V7mi$);uLEhxQ4T?0@Lxj|AU~t5plS}@TMJ+j6%-bim3%!9~MR!N+fefD_ zLw<5=Y}JW{*d-J5f#x!k49jy6^;_}&Y4?c3S@Xb@x_cNiguF{4MRhB@`^S=r2b0Ry zPGF0r<5*Uw-tA-4-i&YBpk#VvG8CROfz`M;s=5gC&t9;UlLzP8*OTHna_}7c*gyd) zw&K%+^hc_{(Doi(HQ#^SM4c~BQBvBiY@jbE2nFbUIQynb#;@@k%ZqcuAGBNK-oj{J zI7Z;+VVO(lqqwA|2h3waPcx;Y#J`Lg5YM1~fHtGj&f}j6kk@8#MG_WGKW%E1aWsq8 z6dFtBMleoXa8}bOMXTn4=ButvV!v6Hgo1I_pF^e{BrHUjPIgL9mNr}e+(?uognJum zlf{gn`^jG@0*$0qzGqG>k*Lf2p7*P%JOc*p{M(`W>){BykQ|}O*|X>)_QFi~=0mG` z*=54#Ut^(ccBdYC|5wwNDV4nu^5E=d-=*;e8~)*;bNR^1RrI(o%=2{P6=+&OR`V}!CL5@fl< za@ilt?~GVsdenmyKAVO4Fel)giKqaq%(CBd?Ay23zyNlhi~YGD_cLPUeOXc*DxoH* z2`ZZ-lIT$NI4CIl@gThdYaTLtbiJat`4Oj3H6Lrp=rbqI^mGkt0}?V) zvD*#6#+p%BxDl@=CAw2R7Pby32T$wi60mwMB@r!m05_>r4e~j$Tk)3DtD@+t4PDZ`R}A=s@D;l`>d8Ap2=z&$MK^M^SL;2(^cLcbp?{GDRF<%=$X zlBg*mokE;!Xz6H4Z3jgznuCy*fGy?^=yuay?&~pF_-8*N9K;2WtL^F>V{?6(>eLPH z%HiN>Os#Dt(QtJr&F=5Xznr4$KtOE(%`+er14J;KCSF%vJ+AuZU!G8pjnS?a_ZaJC zbbBrZMXR%0JEe!^z1G1^U6=<>0De$UWfTkQeVdIt{Z7T>ATQc}d~?eyJKyX_c!L(@ zVb*|_zPbAS2^ix*YuWZaAY~?1R)e?ce5GN);sG$;?R(Vqi|-yv0r6nZZ_mfE^~~ze zaiF+bPCab3uKFpWzyg=D|2vXH0S180t?pKr#Dys;lE1aGEB4xk%mb*#N~u}>&mTfkH4G; zl-RY^Ik(F1VPw{LiMjEkOHxu|HuX!$Bd!;5$>@R;tZB$~q(Vs+LG!lg*QqZLapVLg zVsCX(YQ;W1nI^3nc_@?XTaHHut6#2|i0Y8>H^^cCW zcX$WnkaM;S&r;5E>{kZHxOE+NKcLw(!ibHXSur!yRhZJuFG1ZFmUv}%qWjK4F0j*s z!!wn`k?9o;-IR4Zdohbw(kzE#Udiu3BxtFq0N)yM4ZcRW45(j22f z37-iTucK9_k=WrEy3E5C;kw2u=FK#`KP~iIVjM|6P83wgr?dmwKT~6uR8XQ3usS{UxMumd|38>Y73kM+&Y(;O5vg!2qy1jO4CIJU(Hg54gTe&I4e2RFR zm2Oxe203tqMvfQa6!%(sB@N|-sATn$qO4dLow>s7;wPvdvQQdo+(Moo*at9vy2wH! z_=+yV*76=BGox}9`F6B> z7h^+s-YNRT_jDip|29T?0!FDP zz%_LO6lc*oND2_-fR|TLSlDFaq>kK2lIN4iE1+MG$ zK8`Y9pkeu+exXByr}r!R37By=Kfk||t$zeL+5z_i4W0(_k%R}FIqv&i2t~TDFj*pV zo}Zs$APpt<$m}`v^|ZMFU>`yu_4~1;uaY(O?&>%f=HMAID6(@tS8vGD2929rYX>D+ z%OV8W@YGxvrN1>&o5)i<-3m9xVW{|TW~Gtf+GlTJt|M~f-^a(P#as)3H}~@NVB-C& zf0?ixS`sim;FUfU*qrbI<-a*zh)HbW|L+D?Wp&Jxfy9ujP-%Wnbv!2^Z74CqU$gmY zM-6-7DexhQ(UNHiqF79(1W3Tw+_*=Co{j6Od9m!A@PvBX-f+E5=|SejE2RVxP!16tZ#JBRYl>@ibf4GrfQuhbmhJWARvyf4ZXvPffQ)%^ z7qg|Es6lOB-~;l&ccPcdZ-wru6lZC5QXR;SKW)faSGwN_IQkwRC|40P7p&}SP44>{YdqfC!M zuO4{Omh961!x=yG=Z`xh=sb@Ado!W|Y<_VE&6Rkne~kiP8k2lI*JXPcJoMV3&iW0c z(MrXRw(3ZuMh`zDf~Dyk!H1a;-KQv{O76_ck2A>I&>u)WjVG%$P>nwdUK~XaiGAeA zffXW-9qmCZmQ4&j$hiZN-)Jm)^-ecL0e?zljM;?v-0E1xjE#8*|8}zr$Kh;0K&RTh z795>7lMC6QFJ;v6bCuPeurV`MWcL=c;yq;u!Hp+HJr`C3=2qb4wd4ea>cO=*@RNXk zynVQgC+~@yoZ`rP-~56GB=q^}4nUirlS6fEG||;r*boQ9W33x|y-l4t^h$iG!$qz# z+$l5eky7*Kuf{~@;2sNo*|!#@+6$cs;l(^bvzfs5!|r2_-_isV;aneY==0ruL`eTU zr0hu7AeUHjvwJj2;Hi3Hbm}7yboK5Qi#9${Ff}Pj+u@Vy2OAY4#J}rqdV_|w>&3~Z z=fCwHgAAtfN92D&Y{=#kf1t+U+pJWwJ&qlF(ch){Ml@Hd zL4#dFqCTj?#r{rzoQ!eoQjy;nEWB7MRpBt{a4@3#=flHtR*k5aCm|$BKf9|RMeqOv zEQtmmZr-AkMN!=-G*S|V&5>5{Qq3NfT8BPc1p5<|4u``e;gTG~SiSfs1ea?;EmG_ivMIj;H<6l%8%j)c?{ zklehu1f7Hz_GUp%eJBR?<;pBh1YU#q*CK^UR)>NF?)b1;HGnj2oZEEI?bUfO_nE#~f*R3I<#w#zJ3+N=ushAE8}vZF=BYRG(I}fYQvYz~fOg%^jiJvI>ULt( z6HaYKF8$CBkd@WnXfmnb*VuEA)nx1F4%7T+7u`4Z663EsXvU}994SlgaH?2kcp;JU zwPmN=Q!KH_@5#EwR;ftGi$0(Lg@o#fv1T1T4@ri};1N}D4u!Mztp(6~o0dxtgq84X zw_7kL`rag^MbEq_wBD>!A^HEa&` z*xMZT%k+J64rOz<3(ps^2?)sq^q&=d-9NnMz*h2NBs{}mWyHGh(=Sgqd)AAhvsQe zpGY_TyxD}41D{)S^6GttXz$8p!B*0TMI=5B)ao0=D4*z1IJGg;V@nKb_F1i{Vvg5t zJixs{%fSaGk7?fx9-)!2D&K{mByW9?!b#dXYNz7cl2D=$jkCf`|5&0_!$>mmHQ@)V z`-DC>1@yb{a)H$WmyqmRbXgOUg4Dy3diPOq6ShpucZ=Bl5M=h_b_@gwT_UUg0gBhsJt`Kzu2Uv($`EDcTc^!LnD`Dg(sC#gv< z2MJ^cU&FR1x3!8zRO6LgZaEg|WW4L4h@X*C>sm&m>@u1#w!1YdXOz98`;OW_5t$u3 zd^b|YSt)7P9Ui+lLaiF|l^hioYsgd==MJZL*AtJ5FTY;)E6jpL{QgQddA_d9`QC+b z^paZLpPSOmf|-bd)0F#9OPHTlt_NqrvyHJy!I0ubj$= zLp@zO5`7D1LU05dt$PQxhvoMHHjiAo`r@q43X%`>U&Ea_4EVY&ehhC69OywDEST}Y zM;YatiW4B2Z5mGSk6++k4IJ{zjl97k7AY*M2M2oiQBWmAGk5CuSq>4Mm^u4Dbtn0b zIk{fV_IuA5LAaT*6*e{c3NzV<^imS^ail|XJ0A0K9;>J?WvN>|A#uuQX6?%?faoU6 z(&qXciTES?^!3Zlr+(s5TB4aYADSinqngl%P{(+%N94E&YcEGJ6f+|D8D+USLK*8h zViY{R*PI_D8eJI5;SrTp8s(Eto|nXg4fu;QsNcpVNG7mYZp_9fLR~Z{XfzADJd`>^ zOG-d2aBbjSY1j(P_o7$g4mCTTLY2ETl%B*7gZy{ekPrU+0nvFxfnJw+nor(b;aRxRl3QqOyg8E_w4G*{~yX3ol_v@5uN2#2N`MxK->C$uw;w zb~}Xy_SOmiY=pLcDzz(^j+gHFcK&&>c~0-obVJ1^4)toi+gPlNA~Df;q`B4$yZJGB zlD89}>zP;t6r35MGaj^@G_9jyekX}|CSOq#a*upTDRDY)Hpc~+GYQf5Q>4(#AyxME zmW*UQIps{t18}vM8odw1>it*$rx$=??Uey36V5KaD|U3S44B)!iijIdB`~aDoGjek zqAD0oYu_Zo%UE5;DV|UHQBQ@6*o(!AB_goq?ha66c8S{mYhyMQ9is1e@w2Bxb9p}Tv7S5%o3AvLh_*;Wm&qZ9sGo^=&9bg{6zi`P zyH=P>CP6@DJm(@p;fMC(8E(^y%RE6=`pvf^zuT)4EK93zo!rsQ%l&7GZzd# z5ztj|M!Pvkc0J5Vpf2mMTphh$fB(zYC3h5j}ok}TeMDy+1)-)iCwT} zWj}u};It?L8vrtw*fEMvBGz*ME=1?zDY{H zp*Y7>*jDUaLO9f>L*vFz8b8fw68{1Z5-jDZ)3+V1|r&oeOH(j5m)+V61u^V;b#J5zX!2SnSz_G^qF z2TXByX(f`wz;@#b&x0l4<1l{Rjss3|0TiespjrWman{if(xm@(;%`jfOyZpc?OXkI zqa$n|_EFSLqor)j;eoQSpps;ErI6I`$_#lfEQ~8U&kAGUX@7;E>T2U}k5#; z`y;^11jHbbHL#POyWT$(xHGg739AJS%Ech=8}EBxS?8?33>^-`mxD7OCd zQ(Xm}uW7K{0ya|kT^1SDtoo%pkVDPKwzV*5ag?UhcV4+&7_ftyrD?Faq#L~H;)m-V zip>+Khlz+@8GN$Gkil^OG4ay%8y4je3qVf}b{)lRqH#66fpL z82JpCJxUV5hEq!71{J->rV&{cA_p$;4{O0A8QW+W*OC}nz}8|k+;4FHj#|><99gL@ zEZozRGe0D#N9jul`IBJk&!&{^pg9@74t&D}1qC8Nrp4@t268I#2+$#;XvBrU7WKHe z@pK?~Bt-+G9$-&v1~k1<$R?!gRR$=#RG?#CRCUnZiOyUmTw#6}-)~udd#-8JD?Vk>3S-X<+5Zf|pvU-}_wn4|SF+C6vg2D{bNF#rF4Q z@zyOUFq#K33KhMHlhCz~S~>_(64C;KnJ4pTM!~24Shh_+L;(`P69Toi%y8@87>0yeU9efceAM@(X`ThMp(5PN>yP!bW-H>yiH(0X!+ArBL>TqX zUdb=u$MTnVT>etb)O>DN{L(Ld*D4^iF9(tjFjECgJ@aQn9`EixT&-(8HEZB#(_UnpFW!(4{95H&dVF9_SEeoB zuK8xiqW===fvd{iZ|cB9!eVhykU6KMoa<6rHXpBa(o7slp=#6`cpK1^{ySjlA2?qB zaRfK-3A|*EjT?DjubIxti7VHs%1722x^HaI;#riM z-|)8<_}aNnoJrH($Du-#u=EMjZF-ZqGKuUGR(pkjDwU^O4_jKt_Sl z2jL)TqD`B8qvXTCAaa>E9^Ypf8g1C!EHFioqy;VN+#^f34IX#{P8|3G>Htr$E3HN} z-j_B%-(*5Xi6ky!wxT+#@nTBWgJ|%5Br>~r&$xVx^vxoSWE`J;v?l$o^em(NXts(! zR!R;@@KsSo{n8vRmmAZ=9r5%g--FiPS{jX4?~V$FVwnv>6t?RjKDE3MasNjrhTL+N zRUKLw&`r;{oNe^WiSB0d%t$D*ncuwLrzCxr3nDmEMZ7f0edzA#sBdlUCt@vRv^8Az z5$s?M!$w^YW)Rg}cz!ua+Fr=6Dd=8uW-JuqyFBkvY{y_7Y2Aei2|zG1Nyt5PO1{YH z%%BcDyU~9(1HFiJ6-&G1c_2P`C zC$l-$liMS|4CrzG&n-wmoV%7n<->bRDEXkMdxl?O<=TuYW*oAoN|IVHkS5#sNM(k? zE(vB7X-Iwy{o@M)lA_+ne3S>v$J*n=C)kjUHF+Av4(%|7U7_rmBW$q2n3&<)JKq^Q zYQ2Snn|CvpDh^ci!pa}%A52MFnIXn!u=fLfgc39y)&Sx#c&7Ttgc^lX9}y;euiW;E z>L9P@4!}3Jqb~^0$iK%U`y7(2?Bjd3-waNR8vhwC!2cN0Upf_gKe)U&`A6Y9&k})Y z$waAz3J91bs(_|L4FM)E(y@W}@a;bES+!INT~$TaeZ4GA&Xn}qfCJkaxd-{Qln7LO zM!3<|7Y79N;A44m_SwH0v2TR_=kI$txSO;NPaV14^?|b;`hj@22zBo{!{PfOSTQJ= z2E@?ZbUdYdA-7_Gn1pRW;E;6LoElkXlxkEw{9yZ@k=ej+Jn2sS4K+;^KkuU87I>7zLb3pk)O2|*L3r~D5Ghlv4?np_o`^VhA}I^`zKOLj znH8w~*8Aur6zbN|VG#&=jW;qLiWc0xre_qu^&p_X>O4>W$EOo5j%ZnCzR0?681h4J_BVZaz&Zbbt6V(J z`ERjlzv&yZG%ixGnJ9?sx#5$gCzXF@X5?3rG-r4{Z|c4*RQxfMbN!_+IJ(DQ1*;&@ z)Uza?;GK)1L{sUnE@~!=lz2m`>^pzz+UDgy`zK55XT|2wQ|j*ze;Kmi0m0+DJ!0F=-Q$ZzoJzh`t7d`jXAuPj;i^g5j-J!zi| zfe)u|;p)AzY&L&GIy7!BN?JWO+LVJc4l8f#PNQHUYr@G=5`0QFIpyy6Cm%QNaLX(j z26DEGJbz1}boZj>s6QtPS@N9fVhz)0?#g_V{wiXac~kyjMZNgNgyW0*&KhFJuAYLx zgWyJRjCk+_PbbImPU^*LzoQaLil5SdS z_6$m~22xlV-Ho+MtB= z=5fZH3l-2>KMpuD@Io%@mp@$kS}nO*Pvb|$G!c=>SlEE|%DibVzcI6{B1-c3&u`q< zQcCNmrv@1@0;j~#R3Y>EwO-K5?G3y}m+r)t!^=Z!Dzrl12Cw47xtA_STgY6Gn36@% z+@U#6a;xsb2Ce(Xn5*RGx97l`QTxKgO`U?p0KoTUB}TOEqJ*t4zx3gb&u267Qyv><6B_(?Lu8Wq_U@6g zzijaL-(kngDQK~e+t+gwl*vbES)UPaeLixu!ZrLZbXxzEJlF!V5H4@$FZaqIPQVS2 zw$@E9GLz7r9+g}>?n8~n#Z3WGN?u;xBE%g4uR$uFfVw%M)H^yl+SfN(Mn-b;%+X?- z8~S$gBtDF!&--LkmyCSqe5e8F$Sa7kx!OI8F-hv)ydAeHE*KR((Mp{+NT44%4}P zY25xPhVbG_U-6MVKD4^{c%w$7BTray_h-(Q*Y!QlD8G7&5NNw!UmW|gZ{P<|9bgaQ zm|$r6S7ME0BD;W^h3;ruj!tpY!CU769hly(@aq>%|L{t;kkj$()byrQ{YLH?Nq3J_ z_B-y668+N#`AunYy>Aq3=>8RPu_JUjzaR}9$R`udYp}iCab+i!jD(hs&T)OTPO~6} zM3wX?Mt&dmYguk?*ZvX|1AnjOdyC(GOwvg*aI}f`j?8w_DUwan=ctRPA(#tcSNe4T zI23QsKh38dQ;xx4-9*Da4gm@+Y|6`S)Tl^U->BM4a75ETMcBAy>D(o8*^C4UWiPBJoYkCS=EV4F`z+P zV67FSSMcvqJc@~!7mr4SIww>r|=cgU^fuk+#RAHw*o z&wMrmn*w-Ar0)TUqbxtZh2|chAlfS;g9|U%zzeh>lmI~D1vttX=JCxIgkS2$e9jR{ zhF$0+4>XcMe)1yq?g?$au&}Vzg)go^00$Egd&4=4{VjB+0%J~F6b*9K@x#UB&%cN3 z-LC*B5zlNsAe+X4M(VFcKS9~a3G3-^Hy=DLJp9x6-N{Q(XG1n*NkOy+NHZJN5fORkeB^ z>HH{Fkx-cgKDH4ud_&Sf9L{zh?~|OKEQYUfF_{$ z>kdgq4f6kh)Kh8m@wIec|3NU?o5KQ8(z+7!Ie;BxPpW|fW_ed}IWC_*N7^tnG4@%z z?K7>&y$38&2{LJX=n1a?w5oq>mT74nv5%@otb2ox9z95JL;QN>Ve^pAi@$LFBe#H6 z@8D06w?5#>0InqF_B(<7{#fN2pvk-A=tw*t?kEyfNb0VOolrYlQYT|v@a#K+CWH-B z4SZV2Gl64*v=c+mk=Ne5(Z?9!kATcS!~fCjJ9dhi12X;oXYVYBknoW*<{0Dt)sXWL ztQ_F$ssV%t05|~hc*EgJbMD4u1d60O@@FAJ2}aRGL8zxV>Tum%L7@$L#c9r7{|y}) zz{6l|Z({brI5j-r=MF*gA$zUj%M`vHbnwz<5D7|dl6A5=`X+t&vYWfbvy))8aq1r# z{3pbud;CKt@Lj7)oW9sMg_9kB)1+zb zc7f*O+5%K48?N%^VT?ZDDisyqQ9C`a4?(Hjh3-A8lI3AS0==bCgZ@vmCcK75Cw+qH z{A}g*D;8PVPfx= zwyKQD1;;8v$A>;@(sl@$Ud`_!S2W>K^1pD?H#RA~Apj?*q3_^^$L{GXB*-MgkjR+~ zNN!nD-cpEf*m}Dph2h>a8N#WmquPML6%#g0WN zb{>~OB8KZVEZME9e{dY6wdp7tNi&A#g}m50gTK*)#Bbc4iD_7vM;EydjS`>!cb$$d z!j(?_@o%3c1MCn5+fVzNC&I~s6v4LxuXATU3S#kVP*=-+FK{I9O|sl1y}Y}SDupVe z2(uBtUWQ`Y9d`@LTdW&V*^j{c<;zw(Yt!dZn?UdIrs@GP`LC@>82T*I9oHedqPaoc znhcSj$&b(PtsRc3JI>6B`^#@XJ!(vkM0aP!4>8Rb0;|J)VI=%}7$z-S+1jVTrta8E z8ai>P_VXKR;H_~o%lII0G?5=>@9Fp7C(E~FV>b}}I(Nwo5$G*JXsF?ZOpp;iAT zP1MV;j`}nIj)|vJxwtyyc1708&W)H?ctb$_$XY3O^pEw8Uftd=1~xJhQuJYbe8v=s zqt3UBXB{BnF7v8G)7*=Y#RyXw&HuIGA=>Jda<;L)@!L|_74|OyXT_F7oRUsTD{#78 zRU*$owWVc~JwI~Q*zzm}4I~{jV%q7@T~AVuQR50%`FIG)I=JzimlZ5E`@UXlxo|cU zBb@t;l(rew?&q7!_h)~sU+8eg z8|;O~)Rb&GDQ5}|fB83#_J`F-O?)w=Cq?6Ki*T-QCSYt z{4yDH`LS>7d-0aDm(toR&c2m4Z|JnM+fOhhu5pk2b~L_lqtt%52LcEP1mE(PdLMWx zJps&=Kaxg<5HNNH`esjl20!&t9@i0sm4q>yHaK;da{ZFMY+svPo~b!LH$6LKzaw?Z zn%-(QB%v~z6ZM_>n)!rqy*&I@2h_#1|L+Bg^P;bt7053{(SB&xN3Y45 z{OVEh6irm#o->oDlzaZ=^h3x@lP97TdY;X@NY5`Y9AD%fW2-l4K2&Svf58cy%UYeF z1DT^I&bEqpLo1@c_4==2?RMzfed}-89m{38F_a0*lH>x~6C!T%HAjFv0~^f(6p3V3 zkpzlx?N%48(yjh{FQR`=Fb+bh3rWD)vgEZ>I{GuNh3*XfS@?G)eUv|^{>71`! zAg%4gogPAAA(BP>XgQfmE7D|-H@h_{xU*CyWPrKM8S}z%5k${FJuVcj_=ZB zJgzi1{0@`(_-D-JC$dxeujNd%nMHay*S+ewdG;uz$GKw?X2^obUws3HiyKg5K{7QE zZ9}1O9{eS%c+YEe)aNC3f#q%#=pcX>1Ik!P&F#rg(Q&40_L1Y;KElgxE^jDM6Z5(G3D>=!wIPNZm2g|U*3RcfW(0W1oithO);BKjL-%eX;)yS%#WrNln4%KEC>?GF0jDFDYH94W4teHIM zM{$Gv_sRRzM`Z7qo-w8F#kQPPP;Tr!L3=!}Q83u1+;(Qj&ptqmNUl~9a@!xzL^c9Y z+_nr!WhDAJ8345h@_~Z~OZ#Sv8FONlS|GN?LU#9c;%Q5(73Z86~ zuH`|b$IrJ>g09mZOOA`NfbpBv6OwP!mwnwv zwnTLUCAj_7rY5~OFY>kDfi4E!&sOTiCaooH!oW(_FW{gNxdHcL2voiD_B-CTpYQn=OOxL1{^LN?arLes8AC&$pqa(VbzaVn47OZcym@ya zKi3%{=G}4nLXI~*6z0h_MB6e-v4R+9gcaI*3p-A5A(>db`wy+Lpy~b zGfgMc)rxK7wU6oPJkI}45yo=oN6xjO_wv`f6rU8l$xFLcC(VN=SDVcG#)R3_GE?aK zLm&tpDtUF|S8tY+-PVYy*`8G`w(AkU=f(8X&Xoe_5~H%4(ym5YJzAOu9>Hb|CbkHz z{7qKPZits{f8AAoQERQ)j`cIc3>*o4bU!d3e|?E}A}7gG!wO4h!+doa7aSe9lh)2R z?ppdx^D&lReZP(~en3lh<=n3#^vU0DI1RO&NA^TjMzl=2K=YNpPJ!ds1#ptQOut3X z;5hmRtnCYLKO)|Wbn!H7iK`E=8YkLC6HwYJEqGg0@#~e2 z6egvkdt?TAY-r&*?xJ3;ifk#ZsLV)-qB~f^mw5W{v<>TSx&m4frtAkJC$&mKf(!Z7 zS4=*cXTI*$QkKUmTrg%OOcPKpVSHHgoX=O9@R!V-#{$^z1+?eBDZGGosl-<-Das@r zCzsV^V!3B3k6jC%vFl#j@1?w^n{bI2WJ`m9X|-c3>wEwjkN`sRF`)qbOBk766RyPfW=GduZ3T&+3s{V4;- zv-1fd8^$V?bita^CjPX+?Kha%ot8?n;$?@TlpQ>DEva=*XqRjz9$NGe(kvpu_%kx@ zyv)&mblNtf^K}V6857=)#w)(Go&Mw8W4gY1VU-*g>AKgA)6Q%R?R~*yH;R@g77A)< z^Hxv_XnN>Bj`QI*dv)J=$%jL&e|6B6$%LaFr#&!de}qnvtFkFR zPk9W>vLPVuyE=S8*w@-C8ByXN=-j+IUV{VxjgH?H;{qj{X6&V8><=oOf$Wux{4avX?HPA87 zslhBX)udoy0iXb2U}aOL~Mc&w0zdQ1f?A z^PF;GPbVp0pGzyxa%b@{;@pwYIme zXlTkb@eXLiZJTwug`Hhc!9DF%LxDDr>BO{~+&8B2nG%b<=sDr^9-U@+_ex>cCu=+$ z*-vHajTbOK9W6IdRly3;lxbC(A3eygpbzj_$^K%&=n#!Dc*M+;#X+DYrTo5j)vP&n z#GJ!PEb6LRfV_5bjfpT>%k0q*)1aZ*tfDt*J*nTTgjWq@(@I`unnl;3HDQQ+2M9!q zIKyfi0d4&_ocjGD9yT+R#FjKnRaToie+p(^kY4Yq|I!tjGhBNiwmtn$<;N%st-EmN zdd+iY`mX8>x%EV6j4F;9(p1r{WM1P4SQRuF_8m%=`t778RT+Jzm|UX~$)QugV2a~1 z6}~;7yQWECa$+kn{ND8rjh1!jOBb?<*zQ9)e9cZY{q=`#C-tg0H4A3qeU)COXSkNO zL;!xg-B?n2`<)xN<*LD_C$%^7WY!~&9g~z9HP+CS{?5W6sRtmRzSccUOzsw+H%f zh%TYlc`n=QpNhu@Mxy(>wvUHX?J%0r3#dL1A6y24KzGR|Kbeg*6Q61S!%-o@n z3Coka^@vrEs#v;C`lFKzW8aLHr1nLFn;KR;Y^KiH)^872qr(Z0bFYeUO*?6MJf%~s zJVS4)h;=gBYUXJO;|C29zMs-lXCZ#fTT{Dea;H?>JRk z=8Qa3ZgJGQlFef_F>RSYi;Ds-&6?+3Pt&c)0`O!%t`%b!1 z581ZWkNdJpuin%a{xvW>wg~I8tJT;mhiXmBg;`!BSh>=+@kX?(YeXR?%HQ8c|M0ak zoukB@*u1&ZwkbN6E~Ckfn#As=jfi7*U#qKx?=p~5?x~()ys=w7H*3xlygbZl&-nSh zhhs~K)O9}od)l!CR8k6g8nO4inx=lVh2@8_f{A3qKKgU#;+R77bUh+1)2GK8h3+bn z<7TKu`OKpTN2hNjcH&sO9<+q0b=u<0-wv~y)930IhjED5gh$2Iq-NiMMZ2TQptG{d zNJ{B>(pyry^$t%Irx{UEs?mRXObD#SPhG($GAj)$3FNHL2OcI9t5t6`r^4euj?6>v zRCTW!bkYBF*}~`0x^Z;avX=Ed_Jef(5F;;}JTfa?I=U+B(LkH2mYppBB)VgG)6WZn zy7Hg1`Fru410M|1@_M!fqE8QqVM_7xCu2VAx4AmFGffGncHrfwI1@!momBZvi)PXd zcvDVN+tY*j$BCwU3cp@Q9O;*X$jppJjqnbHPF;=izK-QxWr5QE8#5 z>WP+%%_a~JA|mvR1k&ACbOfgeefDp%{h0MUb4n(9czRRYw;6*c%=G){$Ici$`dZyl zm6LW#@6w(*$h4@PUn!8FX)5XaF!l~2_j#`DZ zv)qS&UZB;g%vz|y=N{h)^zn7GnUJ#IjxAJ7O(<+g)j99k!p+ikR6*%XWB7EJRU+%x zumSgSRtuiGRfx;^BeWe;0EdTpFO!C^q`wHT8KX{O)Npl`@uI@oXicP>$aBXfvr# z3*2AF6tVrX^pNyX!Tx^j<6DyzaO}^X_Qu@yn?g&|N})b6jAL|Ch=)M%^4@aVu902p znIEGv7W7O?#RR)bE9cB2Ke?1xAnuD}gvl@%j_(_;))87_c~^f@u1-2=_|JuoqsLy8 z3u@0klxv9&Kpu`~N?@v0NGdgo_ zlldV^O!R-vPa4P5TG<)xlqU-#`uvuCHs(foHhJgL^ZZu~?^_gT50F2z{Q*9!K`|cZ zc|2|R<9l_lDKE{70lASc@pN?kKSkdDwLABnjQCCEKC~Sd&aVCIwbGbZO6A(J7ZoVlC(RVG}EK!FLzl972&7JY0GEEM?ebmQ zGR{N6m1_aDYP~n>q>XGEZrXD***zpO^()#%m$c%Nm1G4NIJ8y`s3n!!7yT94b1I$s zhYhrQ;Txe+u+mEWo(|n8yjVyOx+w`4reK(2zX+x84L$SWSAXKWBK>4GOW-=AcXHYb zA78KD7f**$!V8N`%ij==yqZWB*nMYx2gc>8L`pV(lUa!WKfTlt@4$zZ_|^S4R&yrmS zIkhfg9FLVu?7T{rwRq_7guRgyEoYxY_4?i-hq<%tMf=f3-t_Fb=Xgcj)^=F0mNT8K zg^Mk6O5HwAO3Bsn>&uH}#U>^tbGJQIKb=Dln8{Fr&>*4LmHl~jZQUJWf!};_T}}2d z*VpVB#IF6MAf*hLHS$6r2k-AGv_oFXJoj@TOg@GEV> zo0^FX>z|AQ1LnG?h-`;Eji?hR< zFKLN>W9Blk8l&Inn!EeqD0t$Dek+*q{hFUtM4_xOq%~uPk>RNw`ifr z5LfHhn)pK1y#4MMl^};waG~inUIV6J6Uiy3bSF5?9>KAaN|u(b+q*!q30{&d>^0*{ z;WH<-)Y{C;*Q1qt*}Jx{{vt?WweNMJ&6+c>8AM5a=>KM<5!WNs;gpGs|DKKwO>IcS zl{&S>{~|FLO<=wEZ(FHr%X#v>AVx*%|u=yp4$6n zDm2RaXo4Wb$+fJAqincSiD_5*Xm=OUpLdvC?rLBn-|w3B4zLHqT`2A&%w!5ng%E zYHY&NAGGpmz*;v;Hd9W6_nbT|%c#-3_^5e-W8lKQ?uhV7d zsib9{745=kY_V%d=IZ1U)duleHnbE&itgW5CQNxA(K)2G&(ZH6G2|}zBHH&`D zmX$(h%NmPJM^$A?iUr0@13o_}qGKw}s)|V)au8T3t_;gJ$=~lb-d`lbv&yMS36taG zhf|pBRA;r2)mb9zbvEukHM0xu8dZe5g!Qn+N_ZimS7VBo&;Fg1albRUwK5!y)PoOU zi5g_-oNr#ldt)yWe0-ZSPZG7bTE0CFD->W_pH~hxuDm*SrgX7E?EJe8o36Vpws){{ zO}JOuVd8PI=^ok3N-^&Y&&2Gc*jRDp)!pdT!lkUu8ao&4ZWY@7KP)4)cVIyxTAlkEmcZmp zH+(ujI$JLY;-u;dfw`$H`^dDXrrE8Udfz3KMp^Qgi|0H%w{eB$QKik(S;*GEfD(LP zXi%G|iAO?B)7R$sr)klOODzb=ou@IAbVWl2LVBWW>7{1zit z%^c%+mTo2sxz$i*f;yS(-x6JV2<)<*ir;Ya!e+wiqf1|B-=dh!nihxqEx3A646i*3_=2up8c$L``)fP0kPvCp zqhMdzhC`L4=g;+yVOKPxwkK!jn?&K3y(>=a&Zj*l9$Yd@gKaK=C_YZ>XT;v3(Vo!F ze>4p-Jcn%tqhR@o?kax&pBIt@Ok`K?;Y&>!_ypurArgX{ z4+=D2P?%gIbr9w)nl}Z}Yb!0LeZMalL8bNJCo&(X(JRx%~ zD%|}V4hD#6X;n|bbdvu^(9$@L);E@Ij|lp~ZMt@dn)gOWL}D(sjRlkB=IYACwOzK{ zl|81Z5~`MXvcOb%pMT(qhACvATxC;BkbvZ-LK+L^-OI1GwKJ_w%2ck7Y;IOiJ2OdMq&V4q!;c^| z(~dS{m2M2O2)-ka^*8Xl{tI{^pp=#G){Ftb{(5-XdkdgrUQhPk8}zOQu@2y;e+#;9 zz?E}$1C6>TdY;6msU(iu%GaouHV~Rm_7M6z9jmw!V9^24P@MPvmg~5u>;5N%y}3(h z@T~XlpW7_%ljnd{btds8a6n5Ml-ld8P~u@=t2I-e1c>CJ8%-#=c$GUMvU0Is{<n#4z$nB8bU-0sBb+Hz<}{R%Z8$v;h}5Oq9+A@}`0 zw3_{nw#yS)EdPP@!2!5L9C*JGmhXBRuZMuT5U#!Oq^)Q!!bQJX!q3vVZ2ahCUQk*{ z{1@G8EWewtAL@0U&8qZII;m~pX^VZ(nU?&499PJBvL6sUvXhZ!uiFl`Dn5GNk+}~P zAqaG(6^0LZ#*2VDmH*1~sK_ti;TUuC2NHaBS|v4Qa~S0?qOT{5zhd})lkNPLzdvyG{G2yB*fx22&INRWCMkEJEXNSc_WF9# ztQOy)#rnKb^i)T0#)(z+GKqFYj;Y+KA(~q5;Ocm9txsAy@n{2LxEjGRF=(b9-wOJT zE(l5RNXa}PTEy3f%w)}>ZSM+5+f_QxgDeHDuQ1RY0R3dXZb{Z3BHyWYf>!Ru;6l0P ziJ`kK&#wF(3A9ph9xaMYn+FQ9kJg8+4(716l}Jx7pkt<+fi@|iwZm||Xg41DA1pA} z(6<9^x~a{lVh%PFEcN5tmAUWSR7gfo*ym=6WT@KYgv}Fa4EOI=W#kW4=J)lftXZiv z%USd&avsj%v>z@1ll#DP9y$aOC6Hq1xkGd?p&bywbg{RyL4i|V2AWAdK&L3M^`*or z>rMsr3R|S?^<;e5m0KUQ%-v{0Tfbe`tBAc~x&$3pKkEaz23V+t6`4M5WoQF}m$y6AMOToyoK z5teZ)5^4e-5?&pwx{ON7O2bBMLV+}RFs6w zcW_BnDBrXA-RU=7Ft0hCqP|t%M@4Sy#aR9OVuzDX=B}~dtYV9>+yYH9HfCjM1xkqZ0qNFcWg>hJxUJsXcpp4dDpro*o;OR+R#T%Re?#@S zKt1g|)YvX4{5$(wBGarS8*AfOg5si~A32~ObAIvxrIwRerL4x}>|g`_=qIEQBXsCl zvaEUzVm~e95NG?aSZkk|Z*pfMa}1?YvEQPAEPJT^!wCnPNB0jU*0wCPPg;^!a@*mm z8t$+B9*#eDn5Ql%KtS7pe&Awc!BxAVH-&g_JpTgq3yG|St9;3?F~RGG@w(9#4Ml?m zn}&AVnnfAHlwPz2Bo9`ZIwaoPo>ay~3-y`jL`TZD%!!KfOo|Rxyz;!$L)__S=;?p* zgN3K=l0{29rkPYX)VQH1$HcBB#3aHm*rc;vKtMYPWs*2*q@` z({rAwSA4anFmd|T(HwdAf`mjQQ+vNI!KX%E) zPK(Nu!8{nxd$&6{xg*byK0neKE@4LP&A58%-86B?dO#<>5Py5+JYg(dD|Y-_2j`EV z5%<1LGnY?i)3TGet|DXf^S@n+48pC(WrS)6vUbz4v@~az+&Q z;~535t7cDREOz@3OKS@~;GBQGRv6{1ENfPY_-YhvX`2$2)cKjl5_APVi^^ezkJgVk z<*SsTq6u1kw;fC(Svf=p*VxS}PS19==knuI7N6l+&bE5;OqZ_!?lP=4q5hR=`u z=^cyi$>eFpa0Ra`24t6V#h6tpQH#a8M(HE$$w4Y`f0g+;t!o33wsr)m$|95}HW`mr zDp@?+i6Ue8R5DJ_o|!MeHG1ao@$;PGY}1>PvCuw^;xD`#L_5s726s@n*B_v< zo5eKG2T}U|*w@kfW8sp2Hav~Gzv@H=H11wVv2WNZltb~aZ(WF)e4D}&HRe*0FZS7E zij*_7bN|6pVvF$E&#O(F0#(=(*=Hh}H|MIzC=T;n;JcUfh}*sMj21`RF>!&a1zqD7 zif2wG57%8#pRS zyZ=#b60W$Ex0V&CsEOiQ5||P&U3*~REbRBwK!6x?M|Vg1WFC1g{|93IG2^4XH#3|5 zW<4&Tb;hn6&kHnXqA}w{EnSX@;QYnZ!7~B(y2)PIQG?3cx7V1Bz=7?44c&YH-#JH@g$`B%VN7?qypF8wu{ln1yi->TwClKe8JU+~m;0^C0Fpm{^Q~1eM zc3g`k7f<7E!<3F~Ow~CWNlr>RlbR{0?SN*YN`@=+)?1Cki4tFvE$G`6imCXx;1=7q ztW0*BhnOfmiRsxrn-^v&_@NH}zsrB(ILJA_O%5@aowKCN;$GCud1TSErLURMnS{ny zywl8>B&tF|OkQ|nvDjL1xB{&lxh zf7N8~3PyQLD0R$i^KqjR4#8jRv;#}$KO@Ff<5a&|S^3mbG+qY_M;)p#jp23NcMBA8r6EI= z>4}?Spta<8%?v{Jiw1FAoqj9Bx}L&<;sqt5dY97oD{%kWYhj1z`7>J?jD|P;SykY zR5F{j+1V$DVKqoRfWe8p;w2c4bL}iY#nXz*sP0h2J4sCUN>xge{kQ2P%T(p!PKvaF zT=aEu!{}jiVzaJQtlF$>^J0;@S5k)RA3Y>2BcU?Kf4&v5lVk73-F0w^jxFu_=hvb9 zZ--7rkA+<^^K;D~^m>=JSwj3?>lVxB%b4YfoIO8hzm$zl-v6P-{@E>_d zC_`SIXR-Yzk*&_(a^~q7Z)^$Xyh-c~m!m2EDb|Dbp{BXSg@ihlMG^+?m@aX>;oq-q zLO)z~miflZJ1CPV_~0R%kT+Vj*D3*mZ6B7zW0XF}1m`|*;|0XnF-rm|=Q{>Q>F+b1 zD?6`%^HK73 zackL|;mf>zmFDJ+jm^jBF0jLUnmC0P>Yiqx({Q~qcp*pTVa$^njuJau+g<8cQIfWo zSjz;?8RZ|k=;w3fLgWRe?1A#9M|b2}99ZoKhz^OWwa%1J9v^}WQeMIZ7mec1FV4HU zPdPN+JBMX3b;)0vclNmmS*rgyKUj$sS^nw6{+b!83J}VAb051IWFgT)`jbKBSmy~+ z%A2*pg=s$w=MVk%_c@ZV(TWR28;~N4{SBEwILj=>3<*qU3qhKsE|&6d%J6y@6(u+P zTB0<2KaxuUjD{Or4m#unAHb-5VLx238W|Zr^s1!#VC&$~#s* z7C67Z|E2|X9B%qQJ!IhaQT~L}=-ST$Oz#|pt$*JC)ZLbR?C-4n_c%C&0=Ce{YW44v z{>MBY@)Vix9)=0h$R3kdp3*?i2y7?|fbr?}JWYf<^R<3(lv; z^@fo~HrW7hq>l7hEXag`WYK&7SV&DY--_Srw33+wo%LD}n~|A>NN;caU~k+nLOL5k zn}QTBdKuIh@@0cv*5@gWHXGn4%gx(DD0$1jA<`=#R%DnSC%zpjxpj&TM0y+%vfc5W z>Tyv(og&KgyR0K3T8DXz1#X{n*7Pd+xRVDm#&Lr{^2>w%09g>|nJ(thE4F;)KMEWa zD*3*0%-8I3pkPq-b&c1o6G$jl&5BRIozb5%lknu79{QjNhd~2;m}N)W zdKC$*Lm--6AF+!7{VIq)BdUK{xW~Dy&$!m^j2(lJ6A`nl*;y2w@Mrt=(%Pz)z+bz! z@F3o8A^?Fxf2{8sC28FL_)fpo?ojp(le zkYi#u`UYLbCH|UsbX6Y%@aPKY{Qxt`6Ci#aD|h$-S&8Kv@aE%tW8;Y67m$7eyY^Ql zqwSuJ>Luu>T{tx|Tn1gOn*h%`*a9S4^x*sHdk;Lm967BIx-@L~ocfq+)~z79f(GhXpn(R_kN5v0VIt2@SOcx5pCCSur~|>^-&ptMYq{d}ClC~B zLXlfdzPSa-I1v4%;QRaVG8)!D`yhTfjwDwQBsyeq5OE3U{r`=u0l*aRNu0*s%0*$c5ssEIWNfLKL36Vf_Q5e0w{M_4je@CwUS@o&!lk&2 zXap%JI|=G!Fhkjh&Kr_Q0cG&;D@MF%glvMM|m`UBO2bqPr`V~cPs~}Lb4*=l;$ma=H&#DNIKXGIs=PTLWI6CEZ0GJ5i z5^tY>S9}`*U;CamEC<{jGTy*^>;>?CJZzC5gn!)Zwa7Hc!yt--`Q;OMz%-+1K6f7% z)CoTh26`7dtNtIX`%QWfOWcI{DY<+k;XIgrTEtpbpSmmdX9E zR>3A1Vq6&ks$OJw050wUVGGe}TNm33tlbQR#3Yj$fqw~7vm*>wwmt`Wy@Q+_Sm2U- zQY3reTl$3DIw6ACK2$J3Kd%_oJc0d|?=d#d3)#WapKZvt&eN{|aS)KFeK7Ja^WL8j z1R{ zuU+5&K={UCxgX2~?OTHlm)(Pz)+x1z`GACN1+amixc<%O`H34dR+2k9`!g_)HK5;Q z)_H!n{=BB)Z-hAqfj&S!I#0J`jv&~L1LkRxnd5K}X~rQo9&9V*oPQ&u1%wGdv$V2D z-m;>Xn_*+MU5ZC=yB)}E!E>}7%*FZx5f}PE)CmrW5gM=G&tWkmV0a{zymH%UEengV zE}$PZ(cK=(Ur#>2HzU5}9^^j?xor^MTVM+kD|) z+XS3jr9cq|9M~(=58R&bJp+h?dAJ4WDINgi!5voGKFqb)Ov3ZSH}8IWeuP8|6x){& z3ISyF;G-vqCI)VUV6MtA?9MRB)!~m`F2E`TIN~7w=@GkpR4^oNQFq0F(QGhBX9f0y z5xlxfW&6Fa?{#FjN#_yWyqVjQkKy=yGN%!LV>TtU8-(ykKb?ou{bn23Cf@4R9^^;_ z@l2o@2tjQ@tBHvtS>g%r$U-aGw zI}8J4sT2OP7@60D-!umgfp#JvS7-|E1Hj?s8*NGaDa3Ike9M`4J?D^bg!AZxxGDn5 z`>qT^W+p5)1rXZZfwCHQG^_{@I1Le@B@m2Kk#SiZ-2%dXIGXklyGjFG9-9fj-pZv5)y+i@LT&kj?`4EsDJy}{|5*Bzg@lm2OsmF9{d0Qg};Hp|5u~5 a<83ve(K>reqz29v5(U{iGI`R*&;Boc*nS8A literal 0 HcmV?d00001 diff --git a/src/axom/mir/docs/sphinx/mir_algorithms.rst b/src/axom/mir/docs/sphinx/mir_algorithms.rst index f391dbc519..97a4c47384 100644 --- a/src/axom/mir/docs/sphinx/mir_algorithms.rst +++ b/src/axom/mir/docs/sphinx/mir_algorithms.rst @@ -17,8 +17,8 @@ Inputs ####### MIR algorithms are designed to accept a Conduit node containing various options that can -alter how the algorithm operates. the MIR algorithm copies the options node to the memory space -where it will run. +influence how the algorithm operates. The MIR algorithm copies the options node to the memory +space where it will be used. +---------------------------------+------------------------------------------------------+ | Option | Description | @@ -35,7 +35,7 @@ where it will run. | | elements map. | +---------------------------------+------------------------------------------------------+ | selectedZones: [zone list] | An optional argument that provides a list of zone ids| -| | to operate on. The output mesh will only have | +| | on which to operate. The output mesh will only have | | | contributions from zone numbers in this list, if it | | | is given. | +---------------------------------+------------------------------------------------------+ @@ -69,7 +69,7 @@ wedges, hexahedra, or topologically-compatible mixtures). The MIR logic for Equi encapsulated in ``EquizAlgorithm``, which is a class that is templated on view objects. View objects help provide an interface between the Blueprint data and the MIR algorithm. At a minimum, an execution space and three views are required to instantiate the -``EquiZAlgorithm`` class. The execution space determines which compute backend will +``axom::mir::EquiZAlgorithm`` class. The execution space determines which compute backend will be used to execute the algorithm. The Blueprint data must exist in a compatible memory space for the execution space. The views are: _CoordsetView_, _TopologyView_, and _MaterialView_. The CoordsetView template argument lets the algorithm access the mesh's @@ -79,7 +79,7 @@ device-aware method for retrieving individual zones that can be used in device k The MaterialView provides an interface for matsets. Once view types have been created and views have been instantiated, the ``EquiZAlgorithm`` -algorithm can be instantiated and used. The EquiZAlgorithm class provides a single +algorithm can be instantiated and used. The ``EquiZAlgorithm`` class provides a single ``execute()`` method that takes the input mesh, an options node, and a node to contain the output mesh. The output mesh will exist in the same memory space as the input mesh, which again, must be compatible with the selected execution space. The ``axom::mir::utilities::blueprint::copy()`` @@ -98,7 +98,9 @@ to map back to the original mesh. The name of the field can be changed using opt Example Applications ##################### -The mir_concentric_circles application generates a uniform mesh populated with circular mixed material shells and then it performs MIR on the input mesh before writing the reconstructed mesh. +The mir_concentric_circles application generates a uniform mesh populated with circular +mixed material shells and then it performs MIR on the input mesh before writing the +reconstructed mesh. +--------------------+---------------------------------------------------------------+ | Argument | Description | @@ -118,12 +120,18 @@ To run the example program from the Axom build directory, follow these steps: ./examples/mir_concentric_circles --gridsize 100 --numcircles 5 --output mir +.. figure:: figures/mir_concentric_circles.png + :figwidth: 800px + :alt: Diagram showing MIR output from the mir_concentric_circles application. + ##################### Visualization ##################### -The [https://visit-dav.github.io/visit-website/](VisIt software) can be used to view the Blueprint output from MIR algorithms. -Blueprint data is saved in an HDF5 format and the top level file has a ".root" extension. Open the ".root" file in VisIt to -get started and then add a "FilledBoundary" plot of the material defined on the mesh topology. Plotting the mesh lines will -reveal that there is a single material per zone. If the input mesh is visualized in a similar manner, it will be evident that +The [https://visit-dav.github.io/visit-website/](VisIt software) can be used to +view the Blueprint output from MIR algorithms. Blueprint data is saved in an HDF5 +format and the top level file has a ".root" extension. Open the ".root" file in VisIt +to get started and then add a "FilledBoundary" plot of the material defined on the +mesh topology. Plotting the mesh lines will reveal that there is a single material +per zone. If the input mesh is visualized in a similar manner, it will be evident that there are multiple materials in some of the zones, if viewing a mixed material dataset. diff --git a/src/axom/mir/docs/sphinx/mir_utilities.rst b/src/axom/mir/docs/sphinx/mir_utilities.rst index febdab8457..0d68c15b0d 100644 --- a/src/axom/mir/docs/sphinx/mir_utilities.rst +++ b/src/axom/mir/docs/sphinx/mir_utilities.rst @@ -7,9 +7,12 @@ MIR Blueprint Utilities ****************************************************** -The MIR component contains several useful algorithm building blocks for writing algorithms +The MIR component contains several useful building blocks for writing algorithms for Blueprint meshes. + * Structured as classes with an ``execute()`` method to permit them to contain state or divide their algorithm into stages in various methods. + * Often templated on execution space and views + ####################### Copying Blueprint Data ####################### @@ -36,9 +39,10 @@ ConduitAllocateThroughAxom ############################ When writing algorithms that construct Blueprint data, it is helpful to force Conduit -to allocate its memory through Axom's allocation routines and then make an axom::ArrayView -of the Conduit node. This prevents data from having to be copied from an Axom data structure -since it can be constructed from the start inside the Conduit node. +to allocate its memory through Axom's allocation routines and then make an ``axom::ArrayView`` +of the data in the Conduit node. This prevents data from having to be copied from an Axom +data structure into a Conduit node since it can be constructed from the start inside the +Conduit node. The size of the array must be known. The ``axom::mir::utilities::blueprint::ConduitAllocateThroughAxom`` class is a template class that takes an execution space as a template argument and it @@ -97,7 +101,10 @@ new explicit coordset where each point corresponds to a single index from the no stored in SliceData. This class can be used to select a subset of a coordset, reorder nodes in a coordset, or repeat nodes in a coordset. - +.. literalinclude:: ../../ExtractZones.cpp + :start-after: _mir_utilities_coordsetslicer_begin + :end-before: _mir_utilities_coordsetslicer_end + :language: C++ ################## ExtractZones @@ -120,12 +127,18 @@ The ``axom::mir::utilities::blueprint::FieldBlender`` class is similar to the `` class, except that it operates on a field instead of coordsets. The class is used to create a new field that includes values derived from multiple weighted source values. - - ############ FieldSlicer ############ +The ``axom::mir::utilities::blueprint::FieldSlicer`` class selects specific indices from a +field and makes a new field. + +.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp + :start-after: _mir_utilities_fieldslicer_begin + :end-before: _mir_utilities_fieldslicer_end + :language: C++ + ################## MakeUnstructured ################## @@ -136,25 +149,87 @@ structured topology. .. literalinclude:: ../../tests/mir_blueprint_utilities.cpp :start-after: _mir_utilities_makeunstructured_begin - :end-before: _mir_utilities_makeunstructured_begin + :end-before: _mir_utilities_makeunstructured_end :language: C++ ################## MatsetSlicer ################## +The ``axom::mir::utilities::blueprint::MatsetSlicer`` class is similar to the ``FieldSlicer`` +class except it slices matsets instead of fields. The same ``SliceData`` can be passed to +MatsetSlicer to pull out and assemble a new matset data for a specific list of zones. + +.. literalinclude:: ../../ExtractZones.cpp + :start-after: _mir_utilities_matsetslicer_begin + :end-before: _mir_utilities_matsetslicer_end + :language: C++ + ################## MergeMeshes ################## +The ``axom::mir::utilities::blueprint::MergeMeshes`` class merges data for coordsets, +topology, and fields from multiple input meshes into a new combined mesh. The class also +supports renaming nodes using a map that converts a local mesh's node ids to the final +output node numbering, enabling meshes to be merged such that some nodes get combined. +A derived class can also merge matsets. + +.. literalinclude:: ../../EquiZAlgorithm.hpp + :start-after: _mir_utilities_mergemeshes_begin + :end-before: _mir_utilities_mergemeshes_end + :language: C++ + ########################### NodeToZoneRelationBuilder ########################### +The ``axom::mir::utilities::blueprint::NodeToZoneRelationBuilder`` class creates a Blueprint +O2M (one to many) relation that relates node numbers to the zones that contain them. This mapping +is akin to inverting the normal mesh connectivity which is a map of zones to node ids. The O2M +relation is useful for recentering data from the zones to the nodes. + +.. literalinclude:: ../../tests/mir_blueprint_utilities.hpp + :start-after: _mir_utilities_n2zrel_begin + :end-before: _mir_utilities_n2zrel_end + :language: C++ + +############### +RecenterFields +############### + +The ``axom::mir::utilities::blueprint::RecenterFields`` class uses an O2M relation to average +field data from multiple values to an averaged value. In Axom, this is used to convert a field +associated with the elements to a new field associated with the nodes. + +.. literalinclude:: ../../tests/mir_blueprint_utilities.hpp + :start-after: _mir_utilities_recenterfield_begin + :end-before: _mir_utilities_recenterfield_end + :language: C++ + ################## Unique ################## +The ``axom::mir::utilities::Unique`` class can take an unsorted list of values and produce a +sorted list of unique outputs, along with a list of offsets into the original values to identify +one representative value in the original list for each unique value. This class is used to help +merge points. + +.. literalinclude:: ../../tests/mir_clipfield.hpp + :start-after: _mir_utilities_unique_begin + :end-before: _mir_utilities_unique_end + :language: C++ + ################## ZoneListBuilder ################## + +The ``axom::mir::utilities::blueprint::ZoneListBuilder`` class takes a matset view and a list +of selected zone ids and makes two output lists of zone ids that correspond to clean zones and +mixed zones (more than 1 material in the zone). + +.. literalinclude:: ../../EquiZAlgorithm.hpp + :start-after: _mir_utilities_zlb_begin + :end-before: _mir_utilities_zlb_end + :language: C++ diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst index f72903c4ff..ef1f1787b8 100644 --- a/src/axom/mir/docs/sphinx/mir_views.rst +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -7,19 +7,56 @@ Views ****** +The MIR component provides view classes that provide lightweight, device-compatible, C++ interfaces +for Blueprint data. Blueprint data defines many object protocols and supports several data types. +Views can simplify the process of writing algorithms to support Blueprint data. Views are also +lightweight in that they can be easily copied and do not own their data. This makes them suitable +for use in device kernels. + ---------- ArrayView ---------- Axom provides ``axom::ArrayView`` to wrap data in a non-owning data structure that can be passed to -kernels. The MIR component provides functions that help wrap arrays stored in +kernels. The MIR component provides the ``axom::mir::utilities::blueprint::make_array_view()`` +function to help wrap arrays stored in ``conduit::Node`` to ``axom::ArrayView``. To use the +``make_array_view`` function, one must know the type held within the Conduit node. If that is +not the case, then consider using one of the dispatch ''Node_to_ArrayView'' functions. + + .. codeblock{.cpp}:: + // Make an axom::ArrayView for X coordinate components. + auto x = axom::mir::blueprint::make_array_view(n_mesh["coordsets/coords/values/x"]); ---------- Coordsets ---------- +Blueprint supports multiple coordset types: uniform, rectilinear, explicit. Axom provides functions +to explicitly create coordset views for each of these types. + + .. codeblock{.cpp}:: + + // Make a 2D uniform coordset view + auto view1 = axom::mir::views::make_uniform_coordset<2>::view(n_mesh["coordsets/coords"]); + + // Make a 3D uniform coordset view + auto view2 = axom::mir::views::make_uniform_coordset<3>::view(n_mesh["coordsets/coords"]); + + // Make a 2D rectilinear coordset view with float coordinates + auto view3 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + + // Make a 3D rectilinear coordset view with double coordinates + auto view4 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + + // Make a 2D explicit coordset view with float coordinates + auto view5 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + + // Make a 3D explicit coordset view with double coordinates + auto view6 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + + ---------------- Topology Views ---------------- @@ -92,6 +129,9 @@ views are needed to supply the sizes, offsets, and shapes arrays. bputils::make_array_view(n_topo["elements/shapes"), shapeMap); +Once a suitable topology view type has wrapped the Blueprint topology, it can be used in +device kernels to obtain zone information. + .. codeblock{.cpp}:: topologyView = ... @@ -111,6 +151,18 @@ views are needed to supply the sizes, offsets, and shapes arrays. Matsets ---------- +The MIR component provides material views to wrap Blueprint matsets behind an interface that +supports queries of the matset data without having to care much about its internal representation. +Blueprint provides 4 flavors of matset, each with a different representation. The +``axom::mir::views::UnibufferMaterialView`` class wraps unibuffer matsets, which consist of +several arrays that define materials for each zone in the associated topology. The view's +methods allow algorithms to query the list of materials for each zone. + +.. literalinclude:: ../../tests/mir_views.cpp + :start-after: _mir_views_matsetview_begin + :end-before: _mir_views_matsetview_end + :language: C++ + ---------- Dispatch ---------- @@ -123,6 +175,62 @@ be instantiated multiple times to handle cases when there are multiple data type object types (e.g. coordsets). Generic lambdas can be used to process multiple view and it is possible to nest multiple dispatch functions. +^^^^^^^^^^^ +Array Data +^^^^^^^^^^^ + +Blueprint data can readily be wrapped in ``axom::ArrayView`` using the ``axom::mir::utilities::blueprint::make_array_view()`` +function. There are dispatch functions for ``conduit::Node`` data arrays that automate the +wrapping to ``axom::ArrayView`` and passing the views to a user-supplied lambda. + +To generically wrap any type of datatype supported by Conduit, the ``axom::mir::views::Node_to_ArrayView()`` +function can be used. This template function takes a variable number of ``conduit::Node`` +arguments and a generic lambda function that accepts the view arguments. + + .. codeblock{.cpp}:: + + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They can have different types. + }); + +Using ``axom::mir::views::Node_to_ArrayView`` with multiple data values can instantiate +the supplied lambda many times so be careful. It is more common that when wrapping multiple +nodes that they are the same type. The ``axom::mir::views::Node_to_ArrayView_same`` function +will ensure that the lambdas get instantiated with views that wrap the Conduit nodes in +array views that of the same type. + + .. codeblock{.cpp}:: + + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView_same(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They have the same types. + }); + +When dealing with mesh data structures, it is common to have data that are using only integer +types or only floating-point types. Axom provides functions that limit the lambda instantiation +to only those selected types using the following functions: + + * ``axom::mir::views::IndexNode_to_ArrayView()`` + * ``axom::mir::views::IndexNode_to_ArrayView_same()`` + * ``axom::mir::views::FloatNode_to_ArrayView()`` + * ``axom::mir::views::FloatNode_to_ArrayView_same()`` + +The "Index" functions limit lambda instantiation to common index types signed/unsigned 32/64-bit +integers. The "Float" functions instantiate lambdas with float32 and float64 types. + + +^^^^^^^^^^^ +Coordsets +^^^^^^^^^^^ + +The ``axom::mir::views::dispatch_coordset()`` function can wrap Blueprint coordsets in an +appropriate view and pass it to a lambda function. + .. codeblock{.cpp}:: const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; @@ -133,6 +241,10 @@ and it is possible to nest multiple dispatch functions. // Implement algorithm using coordsetView. }); +^^^^^^^^^^^ +Topologies +^^^^^^^^^^^ + Dispatch functions for topologies enable creation of algorithms that can operate on multiple topology types through a topology view. These dispatch functions can be called for specific topology types such as unstructured topologies or they can be called to implement algorithms diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 3d89ef4c4d..c2188ec8e5 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -226,6 +226,7 @@ struct test_node_to_zone_relation_builder // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + // _mir_utilities_n2zrel_begin const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; @@ -233,6 +234,7 @@ struct test_node_to_zone_relation_builder conduit::Node deviceRelation; axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + // _mir_utilities_n2zrel_end // device -> host conduit::Node hostRelation; @@ -526,6 +528,7 @@ struct test_recenter_field // host -> device conduit::Node deviceMesh; axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + // _mir_utilities_recenterfield_begin const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; @@ -544,6 +547,7 @@ struct test_recenter_field r.execute(deviceMesh["fields/z2n"], deviceMesh["topologies/mesh/elements"], deviceMesh["fields/n2z"]); + // _mir_utilities_recenterfield_end // device -> host conduit::Node hostResultMesh; @@ -1539,13 +1543,14 @@ struct test_fieldslicer { static void test() { - conduit::Node hostData; - create(hostData); + conduit::Node hostMesh; + create(hostMesh); // host->device - conduit::Node deviceData; - bputils::copy(deviceData, hostData); + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + // _mir_utilities_fieldslicer_begin std::vector indices{0,1,2,7,8,9}; axom::Array sliceIndices(indices.size(), indices.size(), axom::execution_space::allocatorID()); axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); @@ -1553,35 +1558,36 @@ struct test_fieldslicer bputils::SliceData slice; slice.m_indicesView = sliceIndices.view(); - conduit::Node slicedData; + conduit::Node slicedMesh; bputils::FieldSlicer fs; - fs.execute(slice, deviceData["fields/scalar"], slicedData["fields/scalar"]); - fs.execute(slice, deviceData["fields/vector"], slicedData["fields/vector"]); + fs.execute(slice, deviceMesh["fields/scalar"], slicedMesh["fields/scalar"]); + fs.execute(slice, deviceMesh["fields/vector"], slicedMesh["fields/vector"]); + // _mir_utilities_fieldslicer_end // device->host - conduit::Node hostSlicedData; - bputils::copy(hostSlicedData, slicedData); + conduit::Node hostSlicedMesh; + bputils::copy(hostSlicedMesh, slicedMesh); std::vector resultX{0., 1., 2., 7., 8., 9.}; std::vector resultY{0., 10., 20., 70., 80., 90.}; - EXPECT_EQ(hostSlicedData["fields/scalar/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedData["fields/scalar/association"].as_string(), "element"); - EXPECT_EQ(hostSlicedData["fields/scalar/values"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedMesh["fields/scalar/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedMesh["fields/scalar/association"].as_string(), "element"); + EXPECT_EQ(hostSlicedMesh["fields/scalar/values"].dtype().number_of_elements(), indices.size()); for(size_t i = 0; i < indices.size(); i++) { - const auto acc = hostSlicedData["fields/scalar/values"].as_double_accessor(); + const auto acc = hostSlicedMesh["fields/scalar/values"].as_double_accessor(); EXPECT_EQ(acc[i], resultX[i]); } - EXPECT_EQ(hostSlicedData["fields/vector/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedData["fields/vector/association"].as_string(), "element"); - EXPECT_EQ(hostSlicedData["fields/vector/values/x"].dtype().number_of_elements(), indices.size()); - EXPECT_EQ(hostSlicedData["fields/vector/values/y"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedMesh["fields/vector/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedMesh["fields/vector/association"].as_string(), "element"); + EXPECT_EQ(hostSlicedMesh["fields/vector/values/x"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedMesh["fields/vector/values/y"].dtype().number_of_elements(), indices.size()); for(size_t i = 0; i < indices.size(); i++) { - const auto x = hostSlicedData["fields/vector/values/x"].as_double_accessor(); - const auto y = hostSlicedData["fields/vector/values/y"].as_double_accessor(); + const auto x = hostSlicedMesh["fields/vector/values/x"].as_double_accessor(); + const auto y = hostSlicedMesh["fields/vector/values/y"].as_double_accessor(); EXPECT_EQ(x[i], resultX[i]); EXPECT_EQ(y[i], resultY[i]); } diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index a3cffd6614..e009a86969 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -328,6 +328,7 @@ struct test_unique | | | | 0---1---2---3 */ + // _mir_utilities_unique_begin const int allocatorID = axom::execution_space::allocatorID(); axom::Array ids {{0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, 4, 5, 9, 8, 5, 6, 10, 9, 6, 7, 11, 10}}; @@ -340,6 +341,7 @@ struct test_unique axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); + // _mir_utilities_unique_end // device->host axom::Array hostuIds(uIds.size()), hostuIndices(uIndices.size()); diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 21e9406325..ecc468e372 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -386,6 +386,7 @@ struct test_braid2d_mat if(type == "unibuffer") { // clang-format off + // _mir_views_matsetview_begin using MatsetView = axom::mir::views::UnibufferMaterialView; MatsetView matsetView; matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), @@ -393,6 +394,7 @@ struct test_braid2d_mat bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // _mir_views_matsetview_end // clang-format on EXPECT_EQ(matsetView.numberOfZones(), zoneDims[0] * zoneDims[1]); test_matsetview(matsetView, allocatorID); diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp new file mode 100644 index 0000000000..4ca6c83f62 --- /dev/null +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp @@ -0,0 +1,54 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level LICENSE file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "axom/mir/views/UnstructuredTopologyMixedShapeView.hpp" + +namespace axom +{ +namespace mir +{ +namespace views +{ + +ShapeMap buildShapeMap(const conduit::Node &n_topo, + axom::Array &values, + axom::Array &ids, + int allocatorID) +{ + // Make the map from the Conduit shape_map. Use std::map to sort the key values. + // The shape_map nodes should be in host memory since the int values can fit + // in a Conduit::Node. + std::map sm; + const conduit::Node &n_shape_map = + n_topo.fetch_existing("elements/shape_map"); + for(conduit::index_t i = 0; i < n_shape_map.number_of_children(); i++) + { + const auto value = static_cast(n_shape_map[i].to_int()); + sm[value] = axom::mir::views::shapeNameToID(n_shape_map[i].name()); + } + + // Store the map in 2 vectors so data are contiguous. + std::vector valuesvec, idsvec; + valuesvec.reserve(sm.size()); + idsvec.reserve(sm.size()); + for(auto it = sm.begin(); it != sm.end(); it++) + { + valuesvec.push_back(it->first); + idsvec.push_back(it->second); + } + + // Copy the map values to the device memory. + const axom::IndexType n = static_cast(sm.size()); + values = axom::Array(n, n, allocatorID); + ids = axom::Array(n, n, allocatorID); + axom::copy(values.data(), valuesvec.data(), n * sizeof(IndexType)); + axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); + + return ShapeMap(values.view(), ids.view()); +} + +} // end namespace views +} // end namespace mir +} // end namespace axom From 524df5f47597464b43bfefe86eca13b9a2770329 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 18:45:10 -0700 Subject: [PATCH 266/290] Added doc --- src/axom/mir/EquiZAlgorithm.hpp | 4 + src/axom/mir/ExtractZones.hpp | 4 + src/axom/mir/docs/sphinx/mir_clipping.rst | 154 ++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 src/axom/mir/docs/sphinx/mir_clipping.rst diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 43909d87e9..42bb09a43f 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -377,6 +377,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif #if defined(AXOM_EQUIZ_SPLIT_PROCESSING) + // _mir_utilities_zlb_begin // Come up with lists of clean/mixed zones. axom::Array cleanZones, mixedZones; bputils::ZoneListBuilder zlb( @@ -395,6 +396,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm { zlb.execute(m_coordsetView.numberOfNodes(), cleanZones, mixedZones); } + // _mir_utilities_zlb_end SLIC_ASSERT((cleanZones.size() + mixedZones.size()) == m_topologyView.numberOfZones()); SLIC_INFO(axom::fmt::format("cleanZones: {}, mixedZones: {}", @@ -464,6 +466,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm #endif // Merge clean and MIR output. + // _mir_utilities_mergemeshes_begin std::vector inputs(2); inputs[0].m_input = &n_cleanOutput; @@ -475,6 +478,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mmOpts["topology"] = n_topo.name(); bputils::MergeMeshes mm; mm.execute(inputs, mmOpts, n_merged); + // _mir_utilities_mergemeshes_end #if defined(AXOM_EQUIZ_DEBUG) std::cout << "--- clean ---\n"; diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/ExtractZones.hpp index 339f8e5f96..e46045acc0 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/ExtractZones.hpp @@ -536,10 +536,12 @@ class ExtractZones conduit::Node &n_newCoordset) const { AXOM_ANNOTATE_SCOPE("makeCoordset"); + // _mir_utilities_coordsetslicer_begin axom::mir::utilities::blueprint::CoordsetSlicer cs( m_coordsetView); n_newCoordset.reset(); cs.execute(nodeSlice, n_coordset, n_newCoordset); + // _mir_utilities_coordsetslicer_end } /*! @@ -768,10 +770,12 @@ class ExtractZonesAndMatset conduit::Node &n_newMatset) const { AXOM_ANNOTATE_SCOPE("makeMatset"); + // _mir_utilities_matsetslicer_begin MatsetSlicer ms(m_matsetView); SliceData zSlice; zSlice.m_indicesView = selectedZonesView; ms.execute(zSlice, n_matset, n_newMatset); + // _mir_utilities_matsetslicer_end } MatsetView m_matsetView; diff --git a/src/axom/mir/docs/sphinx/mir_clipping.rst b/src/axom/mir/docs/sphinx/mir_clipping.rst new file mode 100644 index 0000000000..c700d7df4c --- /dev/null +++ b/src/axom/mir/docs/sphinx/mir_clipping.rst @@ -0,0 +1,154 @@ +.. ## Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +.. ## other Axom Project Developers. See the top-level LICENSE file for details. +.. ## +.. ## SPDX-License-Identifier: (BSD-3-Clause) + +************* +Clipping +************* + +The MIR component provides a clipping algorithm that can perform isosurface-based +clipping and return volumetric output for zones and partial zones that are "inside" +or "outside" the clip boundary. The clipping algorithm is implemented in the +``axom::mir::clipping::ClipField`` class. The class can be instantiated with several +template arguments that govern where it will execute, which coordset and topology +types it supports, and how it performs intersection. The input to the algorithm is +a Blueprint mesh and when instantiated with coordset and topology views appropriate +for the input data, the algorithm can operate on a wide variety of mesh types. This +includes 2D/3D structured and unstructured topologies, that can be represented using +finite elements. + +By default, the algorithm will clip using a field but other intersection routines +can be substituted via a template argument to facilitate creation of clipping using +planes, spheres, surfaces of revolution, etc. The Equi-Z algorithm uses ClipField +with an intersector that uses material volume fractions to determine the clipped geometry. + +####### +Inputs +####### + +Like the MIR algorithms, the clipping algorithm is designed to accept a Conduit node +containing various options that can influence how the algorithm operates. The clipping +algorithm copies the options node to the memory space where it will be used. + ++---------------------------------+------------------------------------------------------+ +| Option | Description | ++=================================+======================================================+ +| clipField: name | A required string argument that specifies the name | +| | of the field that is used for clipping. At present, | +| | the field must be a vertex-associated field. | ++---------------------------------+------------------------------------------------------+ +| clipValue: value | An optional numeric argument that specifies the | +| | value in the field at which the clip boundary is | +| | defined. The default is 0. | ++---------------------------------+------------------------------------------------------+ +| colorField: name | If inside=1 and outside=1 then a color field is | +| | generated so it is possible to tell apart regions of | +| | the clip output that were inside or outside the clip | +| | boundary. This field permits the user to change the | +| | name of the color field, which is called "color" by | +| | default. | ++---------------------------------+------------------------------------------------------+ +| inside: number | Indicates to the clipping algorithm that it should | +| | preserve zone fragments that were "inside" the clip | +| | boundary. Set to 1 to enable, 0 to disable. The | +| | algorithm will generate these fragments by default. | ++---------------------------------+------------------------------------------------------+ +| originalElementsField: name | The name of the field in which to store the original | +| | elements map. | ++---------------------------------+------------------------------------------------------+ +| outside: number | Indicates to the clipping algorithm that it should | +| | preserve zone fragments "outside" the clip boundary. | +| | Set to 1 to enable, 0 to disable. These fragments are| +| | not on by default. | ++---------------------------------+------------------------------------------------------+ +| selectedZones: [zone list] | An optional argument that provides a list of zone ids| +| | on which to operate. The output mesh will only have | +| | contributions from zone numbers in this list, if it | +| | is given. | ++---------------------------------+------------------------------------------------------+ + +########## +ClipField +########## + +To use the ``ClipField`` class, one must have Blueprint data with at least one vertex-associated +field. Views for the coordset and topology are created and used to instantiate the ``ClipField`` +class, which then takes an input node for the Blueprint mesh, an input node that contains +the options, and a 3rd output node that will contain the clip output. The input mesh node +needs to contain data arrays for coordinates, mesh topology, and fields that are in the +memory space of the targeted device. Other Conduit nodes that contain strings or single numbers +that can fit within a node are safe remaining in host memory. If the mesh is not in the +desired memory space, it can be moved using ``axom::mir::utilities::blueprint::copy()``. + +.. literalinclude:: ../../ClipField.cpp + :start-after: _mir_utilities_clipfield_start + :end-before: _mir_utilities_clipfield_end + :language: C++ + +############# +Intersectors +############# + +An intersector is a class that is passed as a template argument to ``ClipField``. The intersector +determines how the ``ClipField`` algorithm will generate intersection cases, for each zone +in the mesh. The ``ClipField`` algorithm default intersector uses a field to determine clip +cases, resulting in isosurface behavior for the geometry intersections. Alternative intersectors +can be provided to achieve other types of intersections. + +An intersector needs to provide an interface like the following: + + .. codeblock{.cpp}:: + + template + class CustomIntersector + { + public: + using ConnectivityView = axom::ArrayView; + + // Internal view - runs on device. + struct View + { + // Given a zone index and the node ids that comprise the zone, return + // the appropriate clip case. + AXOM_HOST_DEVICE + axom::IndexType determineClipCase(axom::IndexType zoneIndex, + const ConnectivityView &nodeIds) const + { + axom::IndexType clipcase = 0; + for(IndexType i = 0; i < nodeIds.size(); i++) + { + const auto id = nodeIds[i]; + const auto value = // Compute distance from node to surface. + clipcase |= (value > 0) ? (1 << i) : 0; + } + return clipcase; + } + + // Compute the weight[0,1] of a clip value along an edge (id0, id1) using the clip field and value. + AXOM_HOST_DEVICE + ClipFieldType computeWeight(axom::IndexType zoneIndex, + ConnectivityType id0, + ConnectivityType id1) const + { + return 1.; + } + }; + + // Initialize the object from options (on host). + void initialize(const conduit::Node &n_options, const conduit::Node &n_fields) + { + } + + // Determine the name of the topology on which to operate. + std::string getTopologyName(const conduit::Node &n_input, + const conduit::Node &n_options) const + { + return "mesh"; + } + + // Return a new instance of the view. + View view() const { return m_view; } + + View m_view; + }; From 8f73ed0ce67ba9a7705f7529cf1b87d11b8b03a1 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Tue, 1 Oct 2024 23:48:03 -0700 Subject: [PATCH 267/290] More doc --- .../mir/docs/sphinx/figures/clipfield.png | Bin 0 -> 246513 bytes src/axom/mir/docs/sphinx/mir_algorithms.rst | 29 ++- src/axom/mir/docs/sphinx/mir_clipping.rst | 132 +++++------ src/axom/mir/docs/sphinx/mir_utilities.rst | 14 +- src/axom/mir/docs/sphinx/mir_views.rst | 224 +++++++++--------- src/axom/mir/tests/mir_equiz.cpp | 102 ++++++++ 6 files changed, 297 insertions(+), 204 deletions(-) create mode 100644 src/axom/mir/docs/sphinx/figures/clipfield.png diff --git a/src/axom/mir/docs/sphinx/figures/clipfield.png b/src/axom/mir/docs/sphinx/figures/clipfield.png new file mode 100644 index 0000000000000000000000000000000000000000..7ebcd6de10f0e3ed06f2357d952823eec2cd33c7 GIT binary patch literal 246513 zcmeEtRa9L~x8)%~Ah^3ra0%}2?k>SCKp;qP2o48#cXtmEB)Gc-hv4pRz4`vWx4-+; zkNwgN2IIgecI{d+*P64cLX{LG5#jLQKp+sJw3L_%2n4+h0zq8Cz61VJy!+h)1R?=R ziwUcJH#ll@ODE_}IQ394*HCx0+1HTG_JV{85r1T)7$F-m_lwbK#9hkcY`^()3Na7+ zp$6xOb3jMPHGq$A^Za`s@-IGmh@_hSQ=Rt#wtVlQR?SR05yGL~QD7Y(R1R_7Mh$?$ zA>i`TlS&O&+12v;Y1D z_|O7@H1vPQBXm1K62$+X(Ktc-@42Y|^+1Ga#IH*KdK^w6{(AO*PX~OEZ>L_lS5-qwMo)u~bj{iav|&UXT3( zH#IU{0ZVqR$C11z#reiSs_?)+x&#>|n;_pD0|Ct+p!41g^m%(I!Rz5lyV=o(rs4?j;RWFkO z<0xxtHW~NAm0^JBhvhj}oX*yNBjR&F2_GYe8R6`31$*&!Gmq53{PWUwMSb31S6ZA$ zM@L;(-FCfhS2#Uxj?OpxeO@lJh*{0$1hUpzoDa8?3@gJqzx=88etvj)e)QSpT~l5F zKHDz;ba=cuUVE5pI{9@u*4NjkY3Ox*w$ZI&r9&LFaWGr9P{)eUJwx!1Y5IwdmH-TJ zf5+|;c)=nCx*M?&xIC&GU-fx?_J=`kKWRC{!^a=z-c6s(le|5gKb^=CMMOm8bvd&9 z@`Zv7BqXM-|Ix8I9Yw_K@bJ*Ai?)H@+1dH|;aV0s6u_o^ihmv-$^WMbnDYwCnjl`8 z@9C_&)%CRfYD$)=?P^NbuGwsBAO;c!DV^8#Btg^QzIoj1Bmi9j_Q!eUGjj+j!s>9hMwPwSnKSC0j3=IsH4S=ZPDDnc_aZz&y zsCw|HXldP!S_m!Qd0-=PzAJM*2q9*0zJ+JSo z?cy?w&`ls%ZEpeCgLH<4`rAQC(c9`lXTydsjxSI530v$YwCNeypy@y~V0tLw zYM0|hY(vlURgY8KFK=JJULFoRo{#MXU!N|sp0=o#78fgO6mbq0=Ts5epr#{@dgY4c+JnuumRRMUTV@@_<_=O>$URJ`Ec&a*S_QV=K65{ z_H^x)ckLbtG?K!)n_`jac2v_Z@OnR9rPE;D9fSzLR`cUYE8qgqXW&-=U{2?&*7_pw zo9x#Z1r_=t2_J#PFrF@y9~XEmUljoCQkBj5;KE%BprSl+sng_8mMna$S&&hr;(sy` z#Tqvr!gnx^%KlHa(eEG7Ae5k-Up&g~bF;Jinff{pH^=axx47_yfP8RaaO-zvVW?HN{VnPWsCOy8~cDyE3d71$rG+SS7yVQuw zY7X+Xb3)RBC9MmA1!)?%Z37snrm4yKbzP`{DM7}XStW6JTTL7kZB^z4g!`&U)#b6H z+ME)D4^oK<03+O$|3LLLda;9&`9@dZ&Y_r zw;egq9gqb-J?}4o5Et-zkV|GZ>hy(piRKZ!p~aZMSgg4FK-1RMzYeXD@cG zgiC$iN)+yYN)*IV1nHK~^Sn<&D2-~NTqa-huRntu-ND;b!Z2Fxp7#&ehY;%)f{zPy zRljP#Zgef#cX-W~szbg)E^WO~LCD z#RxohI8dhd!#)s7RSgZ!Ks_wVaKQuVInSy$0nzGl+SX(?j76r?;a<5h*H0 z?0M!F*1`9DH9gLMyA0&M=MYU%eANKZh)Mx*-eA-nL;^c=nkJ(t;C!H#;ss!vf%|@e zqR+$hmg?rM%K*K6A@c`K5;#5-X%(DkO695 za^@g;6ko0T^#QPWo6DV1peW6`9)YSq1Ni`tRj%La0)!eqr(ONq=tzsBVEb!o1JCo` zQ}5eWpIcX-+abCtDcKq=D3F+#7};XI)pRVSEQxnm@~&I^X_8g+Tm5tSD3^Tpxc2G| zG`oiBi=d+><|ito_7l3kA*yU5{X&zy zVGGaOqtU-UxPZ6-5C_nM!*-D(Kq*u2q6b~^ZJ=Z%LG$GpZ+`^7MZ#O>zoL+6zt&(g zkL+{T9f8k@gv%m!{3qe)yq~yZ{Re#K@;D^WrZ^}v__JiSU+^YSq;L`2Ud~o|UjvPo zP!GSyjnwed{bi9t#={!`02P{Z_4_6eGfzM(rI^AZF7O;4a&T}kb^JzS2n%=GpDSBW zbZU%y0m%EOlD4irS6*z*sha#Cx!d9FNO=p5Zl|Es$D8NJ+codUW1xg^k{$56P<{sx za;{Pze#cv21qpboa|z(BdOaiT30p3P=rqf}ZENYL!>04RJ`6~a?;HyB zbk{FP#9}%CAawgx0n_nXhtK|00VduX)mjuC^?EGVu6qkDU^V4HsbS5!N9f7ad$=#F zYJW(zsieUQe_Iqo({ip1UBlb!)%ydl2ed5TM1zsqb~8P801UTZ}XLQFI8z#WPSB}eVO1eGhG`ahy(B- zQoZyE8t3L%Zak+@IK`7e*CaU)JnhfKV}nCN&*P!hLXBoA9!`bevlnGwO8@A-^9OjT z=`x207hKO~<4Gq_9>(TTLb$oad*3L=%;y17MLI-aw*QQxL3bOxm=nS{u89tNp>0aoeT=PmpKS5(D(~ zGTc?M0Y;HTz)bn5?#mR3g@IEBjAW|vKh4kUV`x`cyk`8zkCoo0viAd?k~aX?PXEig zMI*O}o_`UJ^+iQG3RG=*&RzP`Z14$&Q?R7=Xenj&-J?sPn3K2j(fU%EnYl8Fz zende}8s;U^6%IsXQ!mIP3@|nRT2SC5aitSeQ)U617b8^Yl+BtyhWMCd8fx z>gql1nfu&;yIYWo>|doYJW~qQCiYR}*ylSa%x;ROyz*>ANI;fW+}LEq3bHyo_TJG| zc}DJJz>+KTAdx-yH@<>{_EoI+BmITcVwvTj0dr%LF%4Nl8LB+)Ubr;ngngB>m{_z+ z;ngTeO8+*s^Xe&XwlL4S0t+nR(iFXH64*Dm+M~*>rMw6#4SfGMNbC?cc8whYhTe`Yn?eRi?A$A~SeZ834C# zq0aG3MtX7MHH#_t#V-2#$I&vS3|7yD^n^Bz@Jiz*j_|r&GQHBF)>K(UzOCe-1jAE0 z;B(9MjieC@7+fi~sVFJJocOWjNL>HbIN4b^o>jjd_ovnU9zCVr#iVu`v6}>XEs!Dr z5lT7Ww)1CF@A`yS7za3Al{z4SV#FbM(~k)~81iqfG&#ZRqa~E_$T8c;cf4t(T1f8iq*O_J7OhA`uxb6_t4(DK8Zlp0_%HH3F-*DPSp)qL6X{J(Y%B?+h z?;2AAR4_OalN82Ip(O2`T_#;&{U>q{n8&<=eQ)-c9ZPr8lH^@6og^ zEHQH1d|u;m(A8T5c)zwl*xg=^g<&@{_ege(u<0J#XZ8TF zr1gi#n@y}D`;M(VRn+yVCBOLVql8bM?b$e{Tw?Br~C0BpKI4W^x9Z-DTpHvmAoIqv6ESo1zlP-SX&v|h9oT^y$6C9!JPqETg(8z)OvpIQ5o5>qaZ(TA(J zZwlKt#$%JxE%iZ%F|AlmmNDz+eZP-X*g_L)hQHV2CuGB$DOMoe;|ddnQ}}pqO#;j5 z46!o!5jrDDDR3TluM)Xddre}U158wElC^U2z|aTCxiRWK6?sy~gMT)3jlXU*n#W>- zYAsL@AyYlvXC-QDs2;UKawW)E8`3?(rY_qQ)t1jO==5Um{vOemveA5v|LCLYBF+{t zuOlqoeLXs04+H|w{aGN3{TAPwIX-n(uS)DpyHG`dCK{s#U1C7X ztrr0-8#wo=0Vc-0eJl*9rw8gbI$OP{Nt5vs44aC9KjmnX2-WuqFn^U{eIpI&6FX=x z`l+^7QuZNKAq8$iO#9f4t={xyFfD7#;yvrJua`$v_x=*L-Y47NnA7n|fdi^STb6RC zmP20%P_c#=7E`qf3pSHvSW;@Es2S>@+);XPA!>3dloHy#)M*oRh~I}UU~nCwGGJ%q z1jsV-e7nr^(GUj`F|6AD)cNb#_IFf5R;z-{o7TshUgKDazhCsqn?96=(X!)7V*q`jopL309Yqefd!|S^ z2LVHolZF=pl>K0Tbhk>$cI@sXA^vp>wth zrBU%uR)U&ox^c`E4uhscTs&p0Y>uGNrP)oA#iBIxnP2I=PcapWwJip!xpFNs705yP z=yfxVMYS$E>rM%VjvM=F#6H4hwvBzvM@t^e1fE711bN&F#b!!{oyZ7>8tFuO z-p_X2yA-y9*!DPi$?*G2;uN4*58JSYQY>0+?iuP>YPm1s4KWgQIkKbcvMwZjcL%<> zSj;^<5<>lqkW^-5>|xYge;FYs@Fp!(}+pq-6%R{A$)TvE#T0tUyRx?Z`8k{X_W_H4xsN>lafsURrt0$woqr0 z1<;fzz6)4XfCiMS(c1p0CWZYmTc)XLT{XXn)UNl{eHgk3Vn9A;F}D{2n`BghgegDU zG*}WeS;+s148=$f>)5}HuiNq?*dBzdoct%>CYIk5*DM{(P-swf&ugTlSh{>vx|%6MN{GM<~1VO--fpe^9YJ zoFnD9HhcFevE5j%KYmI5OI}Jte<7YiDQ<1O;+S2L*$a(K=Qy58cDR>^yTwaV$CJ3* zBeuc!H~B@Z-f4ddkbYV{>)Ht0j{^63xFo)fC=F-JWf)3NdS`lG3<5oH>Cqe6q1SrT ze*)pL0l_h&OtZ@Su%d~>e${h~d$(M#`NCuEaTVYG*C)@rGZw@fvgO}NvQ+hYWP)vf zBa#SzC~LMW#eWQQsn-BYSP2=Bhz=?wn~I);u3Vi`nwr`o=3nr{A8vhTAdM$xlCaC1 zTD)YkzZ&0+t3hbyFC$S;dGJ3sman*0pd!|g0( zUJdo}VKV^ev;it9=5`7&VC_J%CI-D}m1bw{5AGN$tim!APEj?gqTPRpQ>JpryK-?T-DM`&uwJcbmcoBlM4 zqU$cIC*ILGI_u1?jFaGu+}a3I_N+CZXjDCCkO-v=>nLVg0*MJF&*c7;iH~HToYS|_TY}AH z`C+j2G7$O|Mqq{mzac$w4DPp@YVTY!CSq%(oioi4k=e&WD!H`Sa%;g46UoTo%4Ga( zqO&V8`OKv=LA|V!kYah(DcldgZL7-2Zm#=j*>8_qZoSWYv9o~S00^c{(~8T|pBfX0 zY%ThCATqMQ>D=GmM8NUOlaALHxQH7*z^-EjtUCrAknvRE@lXz(?3HCnLrxbjrEc^R zb$KcWi`I-`CG>G6YV>_GT9EO?iuncXt;(g=@tC>~ER%_}ke_JxXLuiKB??mI4t|15 zC%Dz>>eE*mi+@nRAc4df>iYTau53~{)HAq0?y0P0uBIHWu3qwi&iKY}Z3uA>+_~<$ zuCM@Ohrb~yE2`@*t=qaEOd`E@t^Jxn+ur}?uBPtumHL8D8_0l=01Mj9^6}QyT`H7M z1NIK0gvYxH-R=i4Kws_$)Z~jW@lP?nktgdh3WGo)%CPTIN!sBgPMm45qaG>McwTKpZTY>iva9wYESqc`|7-T;1B5eq37}qwjz+5B1R3 zNqeZP{rP*gtQT*-%g(%kN5#dB8Bx)c=sHY0b3YM5-u~bW8})Q(U;5#C+S=33zsbJp zdk~SwQ4OFcyz2yZDBh&b*3));)YF#3N`RTC1A4aL^Sq!4Nqv^PLUAQ6EJfD6#$dWqV$Ikh^=mUzeeB_%c^v9-tNs1-nH)0O zx`k2hXU&8<1qxRKPIUhS|I2}9hdUsnko?W$_b}c6pK6BS!(>#U;#+Ad^0`b~lWY3M z=J0TXi#?w1HYiu!hAo^jY>*wMC09Pkn6Pin#xeU8qOxr$*;P+p5L4pPV@z`MWl<8% z$|VouiSSH)0LiEr(#u4EW5p zL_1be_Wm^8>a@%>6}j%TG}+tPB}UB5&K4|jr=FQHjX1#fTe@Rtb>qGk9j}#93~nvf zy?iX&>lb`Ek0uqpIayg+Ugoo&DfW5U^#MRK4PqTV>#hgb%z;5B;#=SFI;eK_&RPw5qoUaZpURHt58uT+DBUkiVJ=6Ts<;fd z6II+2({v@x_VaD7qN3f9$b8lR5p0hB)Bsx-ZUC|ab?9ZLNPyjOYv4_}d$YcduI}s0 z>8m%eVYFXp9XZ{k*s2x&ON@n=L9>0s!AahfrYv-7P&co&`x9)TfHzH9Ov#8ASmxl} zzLAuy%|C{3?MbwuTgxkPZi+j>sFG3rud@IHCn8BHkS^ZT_}!L1*J_ZMzna(=^XXLI zFED^WlcGMJ=&|YC%!M%11?`0O8Ta2O-yBQ_e4(whO$R;UdmakQ; zsRK0t0@S19eaYXrfitRHm?(RR?{o6=+v^i)CSs8#wZs)1h?O zs!ltrGj>$RJ-U_2l?F@lJ2LVMw*Dr=W=pDRwD@57IRB{=+ZeNbWQMYu8wt8)$LWK} zVH`L~o!7t76b+)^5-dXMN+jX}dE{NlljF&HR0(`)L}utQ;7v{o?OTEu z_F%9NEsd!aGG~yFV}$mt^t6^^nfmz)Ya=;cqL8-77{OtSaq1KN1Vbu1byc~6T%tZ* zE#kg}UfbUB63eJ1cfFIou6Bvx<3M^uK62!h$Tu8ZXVqFK*pH-)EeBVmL$!NXq{tm- zjUS~oO5M|_gg?7Zc1W(WG;ncMHXC+$bud36vq+ojz5WcXf9@lWioY$|!K zTY80!^27M8oW%QtsgV6#NNv-d=Roc;&%Qr{_d9EW8WQcj zP2}X2mV1f4;9*w^=9sRVGE;*pt6WfUcCN5f70|A-lNv0M?os=gh8rloM2H3Q zE$c(n5Z8>re-eCsP|zD`Z%f-RXxHxw4>oE+jhviG2edp#Z$)khKo-btW(eOA=ao>-o&KbMw=2;=|bH|yF&0mU{n6@%xNc#VTtf&v~9q2+-boQlPTB4;C)4FP@9iO%Qe z`q7D*q+GGj(8}C+r=z2|M`IyNcnwGl``lMw*92aEq--)JYs)i>GX-8efU#!qRg~4a z`aZ>)%GlsX*0^iPtgOZX2J6)`pAD~zZ&{(8dNweXV*g7JHR)ZNYdcbG32>%7yV!Xf zZ9?3ha)*r0kPf(e^s6w%)$ zQs+v=bRI!8vK&V<2d?+C`dP3cHJj=J)QJ#Xbb=mmI1>I@G}8=U!G(C%?gij^b=4z) zR&vyAUr775I}Q30WrMr!l(C>7P!nLGARtg7D9735IX*dD2H@Cde#nyu1)=69nYNY? z(xCcc#Yi`@W1KpvNm}FQvSk~M8rV`zKhs#MM7#&l!s++k;Ih?8C=8o=es2vMvKR(8 zALJ7gBbl}bPpdh)~tSB->$aTqw7b!b@9;8j~q0aF-sX%|HE8Gp2T z-yr*i`bGbErt2u%hT)rI+2_fbt>Euiiuk>sQW>|YA8%C0(%gpJ`VyFGxN1JLC#umn z6+?%t27jp3QMat;RCMW>wVcW}mn@iR;AbO- zY6z4fGyffRR%NQ8cd4K5<79}vma|f~W!Z+*6tPpV$Sp9<*ld>QoHydtX!oGp)6T8b zIo`PzNl+d`jO5FhfuJE^g(`JYI;Ha8F+!0f)s?1ET!mpY?0|MOI8qvq|7?0NqmlwM6V~uimSJ`1W%T^&q5p5dn*+#~g*hS(b z$a7Euc>ix~VCRWG#m8~@NpkNX}1(hTE`~p+5=vh{yVYV^Q5B`;N2&`lOfN8sP#>)CzkU z=P$I0&l=lQuooJW!H`wBrRU#wY(-mcxyZK)34be~zF8S@I&1V~rhtO}yqiEszn0#N?KavHFlA&coMVPj! zDK`~eo6n9UTlxkh?lv@f4YXD*ECiddd3!MF1(Q?f;T1JzEP;(9GkXP4put^|;@e!i z$lTSCi~rl#Zd4H1)$zcGyh{_hEvFTG1efzwxT z1bHzt9Mse2+DlJcK7(Sz&6{NCsJ`Gv5)wbq848pil`qPSzcC8v1JpZMq5bM@aW*1f zz8YAPJcgLw6J1+ksY2Bz5>$}oO%H79AGqI^sxe1=SNAn8bQ5+H)+o6V;k5-Fw_@?9 zBVT&hZ1&*lwML(i1)IHUeA`~WTfEO#~6B|q5HoH3w zgwRC_?1SNGamT)sLw`JGD=R{Y#FRb`gHwHO1&!t;L2a4grlpi7H&jrsPuYySyO-X+21uxrmjjnj5O>tYGatLqlsi=;*jLzxN)tROri!uB!DL$MZ5o$)SJ^u!r2 zBGH8HxQMmgYjMp7E`~3_;^euqKHX_m&dodbf25;Gtr*)PVGKaSz_UDLCcNi50VaAZ zDFII|u6!R$4Y=la*yo#>Xfe6r%-q;)KRj z+#KMP@p=V(#@OG$Ih4ce`yLq2y|h@KExH-qYtA0@bMG&DaIg5+^U&_5qa7T1Xr9fb z*?3x}$rS0E!-L6j@u+X{w(dmHJy)7yVgW?CLLdsUE0nHosTlpCK=(7rtpYwsAkg11kF5Be#7hNel1{EM5!&^4zBntD`7*~D z^ot*C$Y~wyc#ar?WVkr7D;fOFoZ>axVwY{k_;6-SLn8gV?jBh`a@d-e&!zJpNO%b5 zM+A=$h~sjL%%WPk*I5U!=U-89jhjOyFey~;l=wD0Eb>`#cI;`WsC~f7(=$zHU zLBu1D79Uts;%N5oP2zIDI$*?7xtIi*8Ir@Hikql~N_hBVG5% zVA(6ThXMJf7~bW3x@=Crj^E~+r9ZJ#s%ntB$2R86Zct6GJon%mXN>?ht@zy+R%6LXEz=iI!yF!xXy2Ts{8Ri6Byo3l3S7+<<4fLRrT;wd~ry>t7 z2@_THHG{Z41a~5LqHGwkOyca_LAn*`03iX;cWMv{iWde&mK+E+ge5ZWmpgUZ#g9&S zPpiS(itS#Oq72Tq*HFmdOaY!XCC%dDT}QuNkM>hkR21lYl7V~S^uS3OEp9&LgmDH%JIRB&K)5swL)k#rnzLw4nPCj)9G5k{#i z45al5Aty&)9Gu>R{sxpXjy{;{Ax{e}vskh;DElL%LytAn`}7SuhgAe3E^Ztya8(Ji zCi~1wt3a+*etJwT;|C#c4Ej6>^awg3o|nWGL6p@1UKM31Zi*IRAgjyOO=J_FaBiZ{ z+4#EIEly5nCQ*=&(2!J%m(mr6r%&?idxFl^stk9mYTWqKlRSvD!YQKPk}ofJlbT0s z5~mhTi6>)^TK}LjHlNqgPmWJ5Pi02OaVie7HVT2-fcb$<%txI^CL4`nG@4QAL97J# z_lJcxF-&YWWL1HuE{JZTSKlm2Rg*P4af?OvG8MUkSZd7e*-xODsBRLeL}*kH1nR#JG3303iOIm>mBD=Jp!52l+g4qDgfjqx)0hF&V z28p8%#Xj?M(lWGH{jWEH)}c%aVdE9r?AJ!oozU>PW{uRbzJ5qhNcHzcDQ7e+MZ)_D zdzZcggPlWO(Os|B>^+z(r{P~VRT932Pt&&r+gI?h@D{WZbW`uBb$g9MOO>ZY-sFvi z!kVYrR)smaUK6=^-&DecsVSn05HP#YK4pEA;F}(~SVT|GxE5k0wSb&{yf~`&%sVrgheX_}` z3}gLQ-<=y9)GjBIhw<7uPTu}6dsi~5YA@_DSO|&`C=_KavJD6~3aDJk0yI)I6q49q z1c9&(n%Q@lf7#`rK;8n91rxW=dZJ!|t7V_3B$;qL7n2{_uLFZx^hlG2p}p5({C3@7 zp)!%lD6*8k?~#)DM!|{&K6L$&-ZZ#hkxfXoz$tuxbpSN#{wxAJw9<npS9}05KisLaW?g@p#v~Rz=Kp=N640$7p5n&ELbb%%-r84#ImJ{yag@p|k}?=J zY-#Ml*ic*kJ%Uv$4lIczKt!vhpWI#iqGp`(O|22t!*S6q7h))D9!bRkhkD$BKstx3 zY?VA2H5Gz297Lk2>X`?8o#E6hF zw2DSBFdmVJos7i6mdDM(2rmMA?ltv`g9~rk^*5IfD{nxc_0THu$rM(`nOxQjOxCIB zr@4PO$p~cpn(rJF$~aJb;v%{I=%^V(xyake&go#1jA1oO zwLqOUP@c!87&)h79Ke>(naL*JvdU!=L@$%R)TsToo z1Jqs)>Mbc)H0ok8ZVEr;Y-B%b;JyHa2nsCPj{p)h3V>Ckepm-5m_uB^?#M4WOG z^(=fo?-++nt4(DbQietD(bQlFlM`G&^FV@HT>6{|pU1;Pf3ZqE&R^#r2P`Rfw8@sz z#R;TU{V@p>T0|P-gK~w51uEpFra!!4_i5OD&d6g6<%^x+N>C3@;oAnHDOnTCUA?x+ z=~D^%En%?~iS8#(BS&%Msl0em$uu$};9nkRVSQnvRtd!|##)%~e}8l{Cm@ZAFYx>& z>cuOi6XKv=Z-x}n=vmjwfz+scL=ChiaKj?igd>6v`sS@r{Xo7Q!+FK#c+4n5uUdbK8p(4j z)Z^hWg2)i01IWb0NUo&r$e>Vv>_W!Fl2DLA2^+zph)|1BQ^!xHC=KAqQ%k|3!l8*7 zg=E4InmtioO33jwD5`d}v|kI8Y8EqXU2+Sap(yv60WAV^7~BV@Gk#66`zS^?RcC0uLd1!EEl-u2mP(Vjv-sSHsv`*<#aT1(cZV zs1c$vEGe9@xm@bR9zXJ4!Hye)f|rz4dE}_K#zA#Bq*Xxvp@}3km}IZ=l>;B-YuVc& zA=td=I413Y`CPX=@UsI-Ru7~VHYAF;g`f_aE`uH!$q7_uqpbB%Tdfb(OVimxm21kU@<&Kj ztUpP^7bsN8i$nZ`9x^VcCo_|VS|pw_EolkLtbbbW`b?Rui*r%d*hCuS<#8P!Iu=k7 z+&xF0t3?%Nt414QEDcf)F+ie0fCZt1NZnz;ZTN#Q$PoNQP$+TxQ45p;NE1Xz>y(ov zLX_flmeMy%22zrIEZ6sPoSz?Ou1lC^wl4$qqT%^)QP23V_!I@eTCf0#%n49KPxZU`8j=;Z+yZN0);4M;Dg`97(W}xPMMfibzDml9t zTP1dp@b@P|G|CUD01BuNWT;`$)CllNa)b*S2;n)@K~85<=g3u#;2oTzqFzgD{P+?x zoA)^SALD+}l6UH7rBI^Ny&U7x*aOEf%f77mZian~zmFmrVWbsprA<9MN5=`A^^2U5yhI)bWEDx^2w{(IM1@XH@vpT|7b_6(@c9xrjR zY{6cG*P7%%g3pLw$-~CvTBskmZQa^uWnw}v&XKMegsS$+p+O16hwwq)Lg^z<5Qd^A zlu|Q_hFHytf%9Y|2RsROdZnc?ch5qg9S@Dy6J3UUgIM#L1CA>q=lL1NNN;gz$wTK~ z#=#`d3~{7;Ak3=K$r7&q2e{gvKD@~!{2s=|a&ZS24Q4HN7CM|Kxx2_ABEj}H*8Duy zh|P}}sqf$0UK{Bu#2<7uY9MdfGrQ$t49WNgh(wc!`H=*``pM*CsA%P?NXN^tFp|q$ z7}9*P<-J;k$;H4zZgN@w1@3TqS$D{DbiQv9xW4u}^!FS?{?&Qw-#I+H9Wwwa1cgQ> z1VTpTLxo%GM*9&QJ-jPZAwDijF@){tFOmM!!lG6onME#xxwf5AGYR>E+vh3-!Y}Gz zF2D2El&dePqc(7z@>Js;2<(X}e8sm{90G+p(2s>G?Af12LMld(niDMrOPeT0E%t}X z=RAL-Hx>x0{EGVHZGN-&BLw(P9^k^nlRvlC1P*Ji#m9qdr&lOUDeM|LtM}_?n;qWI zJ%vFfMVV(~kHH4Z=MWI`_^w?O5QtT6ZLgb`3aRcxBqTcLuL5+}%1(VR1ani9V4IIN ztkE;0tCab!YYB8I(24Xb*|ijuLqFWd&>HN?S0ew)$g;k4&eA(1Tt!c&~b(I%5c=s1T!XLG*mvvn%AwtD!<3DS=i!LO*%y8=6iB_MUUFHVo;)&)a|sq>s| zVysBndbR#av)ZkCJt76CyZ1d-bu_#lyDlLSSQGGA{<*6GO}L+(MXo*dUbd<3Dm<-M zxURXm?b5K0ak(wtwFGr_R7K^o#t9OLTjXjs6skz53Jtxpk~f|)g^g~13Cw;%W}p;T zQb@#%aX=#$Asdezh!;+>l2_j^CxaDg!7}jCcr(dFJjA&Q^cTk@atwr zaD^lvG5?51d(q98gfLg<;#v{32PvOrIztxfp&b+@^=_OTP2{_n@E8>-id4J^Tz9n= z8QJ({02K%z+65X=JtV|ihwupxe3dC*`_5jx4i7Gb1x}*~7USW!>{9_!4ox&9YvQRW zVM@{>^d1Dkhl~oMY0(|@|2|9`Lj7S>YOqHyX8>wAjB`AE21fs|2hlcTl8`@w_Vp4o&9>fY$IA2@i02 z)SjpO?@_~ECQ;I=9Xc6|^~MvLf=-Mz-w+MuL7>^VtowI!@59CWoptJD%Gtgj z{;`+<#(cC+;wmMZ_X$LiUS=NLE)+mC%~0cvp}$86b$5u6N(*KYofG_#uPV-9U@~f^ z7e80oX_ zqGeJO-8kN7uX{-C4L2kD!4^ZQ!G%-8h38gKcNyOZXgOMm>R5Rmp7EPUq zbYi!rxu?pMy2NMx-Ro5VkPQzWIkpbqyamquJCXSWvA+{Nz`8*RL7~DyQCxVTiS2n% zZrpd}p-hUd-9C4YV>)^>Qh!zI>wVJRbmZ=czEs5HW=^_v^h<*GG}Ol$fa z_U=0VwS`tiuos#H3L@J5os$qozcIk2L{Or$qnS@V$h^NeQ4=dtFl8xA zgk`#_Sq~U`-&Cte+2&Z%h5A<%6rw^RH6>=g93NsvChqg+zZS_sU5u`8pKx{Tl+&Ut$Bd+x3_!WbgBfK=fe51H&SL zw$q0tE}m~|b)agkAoHn5c$DBn`P+S89S#L*{#;os-J<{z zL(UyMvXEP#w=I>IjBhN>%_F4R)}$}Qn4BBopxjfHENSWMNhKv>8CQ#!9Zj(PO5|%o z_=i$kRzzhdUF{*xF;&qrRz8e&1*8lEa_g_KpJ46ZMhtbFRk0Vlb5gDO zW26GBYM#3Lu~W#-`fx(*u{|rPW}GElNr|SBFLP}w!vj+%ka0sGmdGKB3kZ zjT+9z8juo(+QZ37kKMY1%@)yPNfTgrR){EFjsQe`+~pXzb$M*1K{UYo70b1xVUtcSOV7Y4K$ zm_Sb?uQlXfdW;|v@W@_12WtPr(6 zB;_rgAFy4!fr|R!UGzpWwFG%?*4{+`iWrG;I0}WxJ>O@=l{r(&u8EMz0I!SQ=U{l! z44I!@w*#&0YhLd82qWimai`QZu6%g5l zv+iMs_5`ZqOnj5StE)>gv+}-F@l+PL)=H-o=+jfptf9|Gcog*y?zdX{I{SxbX5qhsMXDN$W zRBtSlF=IhAnG`o>@f$i}=NtKSoW>s$lJMA?Org{HGbyZNi3qxQ+AcW@m_!a{>bbw* z<=_3heXLY6>)+u9(O`3mM zK={=86>#hcAlv_k2@viHo~9*+RFa0BdUOi$DXZG|CV+%_{;>t|-g@`B?9DPq5qG|( zWKlY`v~WO?%}sLE@nicd1h@&2-A*FWMl~qu`WuzR3sxY%ok~KWU$V1_IdY7QlOYfc z%!P#@0Ys4lrDG;g1A>X-WP}W)P$+Ook4$jH{x(@Z2?ZTPrGAPLz(Ep2J+q*M*{elM zh)oqxhvbu$-c5FjP`zLcoW)Pb8^d(AJzo74bZ{v)pAi*dvgEmloim`!eAUg#`f>ud zdTsK4*2F8wX4=1fD8P8(K?nd`U+Z5!T}96T9IhP7IvD@}5yXa1hlhDFYj3Fgf7rTQ zq7AzrM}?+fj^ec>*KmHR9WF_eZo-}QTg1mn!7TgoheNcUhBa+`e7d7eEKxeTt3<$BZ^7p-B}|5kd?Nmka)3@4evyjWghlJ zCYeTmN&0>?{NZE*eiB8UDF~)0MCfkXK6+}YO8*U5`1dekV2F%ikC@uKI&#cquu1X# ztYrF&*vch|b+*kyt8U$4`~+ggEgk!QO2$vSITIhfdyO zQK7&aXqFEbu&F`-IkpMi6cB{CRucVtvE$MHWbgKHy@8(86@3CyDgE7rt>-{0SZ z^;^JlEiw+<6HC7pbFk6jk0HW=!g4KUAvw42nU7Ix_K6vuODRB?j~Dcfn8@uNB`(bv<%dZKM~HluV!mAhA+0`nc>+}XK_UXf z(8AlE1TQV!q&Iv*LdYYcagbZ2`9{h=gTnc?NOwH5+o$_(-;N34Mh~3jDZiD@f7bRr z%}F5Q&CXZb=l%J`dwRF?{NvY{XG`}KboLQ3Br$-5ZT?{2or@%BI2?9=%mNbt=@G7X zmd6JS8P2Zd5C9*{Uh1oq}2(LzQHJd1+2@;3K(mLA7jSZAv99P|HZ z>`h|Um0(BN&lVcms0~)({jSeV^&&^WU$~{1?RUQ&zgtv4_;DYDj0@kBZv6FQNVWRA zCB3i3>{Z8CZU39~)2ac7)n>HS$emB06Aa>GQFCNh8*3Hbw6+ho)_L&QHvEymiV=WW z$bk@{R`h)swIa(ywqT_S$!+CxBbuksV=iySmYf`VwUaD9ftt0>Y_-&m63NtC8Ds91 zdb(th%l})9K)_u@@H}i{M@C(MH7e`F+`p5*xi*&a!{BCLcd_`CX%?3;PNB+Z!tf}1 zi-pGT3ze8XP1yqWvk#I@3-?w2_f3o($ariP`w<7fAp^Qv zs9UT4@ml8}kDtk!2Rz3n5#;ZBtEGx741gpV9bX9Wbr0t=PVw?0Imj2v%9CA@($4?7 zfQqK2$tvct396PO)v#IW**h4DMF?ban}9{h-+FLTm4l)&_Z5;7`dlI)&S3V0g9Ii^ zg-IC_VW|?tXypj(=e=1(0oNfk5j4uUc{puFrNO0-g(~?GR)%-DI0v<((5jkjD+7 zy6nN3>5`W~zGT%avIneT?Xuf;_s*jLaKLCSuWx|VHteC@|9As=^&Yy?Psq40;6s== z=W-2*?({4&2`-klU%B;ROnpe8;CDlQ0;og~eG;tYJ)*t+OnZWPFIl7zuL zqsTw0!X&7@kNkzlFyyF^P(+Zx0gdVbjeaPWB$Ds|5peeIG=f3-wPmlL^dzbJw%kSRj`$?BUNG}^DIz>*RsDL z41QbP()N0pK&Fu=p40L=3jyojdB>xj-5>MX@W2jnRKMyFTS_^DYA zgTe5e#%S8Rm_O~92~52SnEwlI97R+%C73l}y1k%DV(GldIbxh+|7}=a zh~Efds8H5jve5*^z{I$y$S0?D@LJ=DlE@F7K(Z zMcc)U;u@7mG<~|ny*J$ZbKAzYj|qR2@B+e_tc0OWXEjETn;69*!Ij{jqhG{k-!5Hq!*Om^MLu7fx$vHlmsSW;1@K_+x%GztR_+l7JU5Oi zK!|Wy6cXG)LMIc0%>xzzdsWIb;n!pa7XMyJ>74G6R(<_ODBxi6>HYd}vH7~_-_f$i zSdG8#ojXhAMSGOv{m^~b8S{>ZdvfcGd-I%0*~c8{X03wmAe@kgsT4?0gp3lPv_jk} z3^$)8BSyqXUi1V*4soJTWKk`CBS8uv!5JbBIL0ImI!+-YL6M*rBgXK=N_e{`hlCID zsf-5xj28u>kGi);55p!n;@~u(2wi^(9%;{`Mkhg6f){5-wFpzTtiiVPec!0G zG+7-}{?!f*xY>#jn-%aFSBh6uyWlu6Ea;+eumrUyn z1uSv(&5sXiW>9fD__OKIo)a-qsz(yifu4v;U&UhbE^qH_6Ud*PG#gki#c76w3z(gw zjR6=e$fm0yj~2sd2`J?g$~qOx#RwdIyE9DEHRqXB2I=#Yh~{vlT52(}s4=w7C=_9s z;>?|nc9f2$^3eTBs@R&37kH{f3Gv)5Mwm0is$&QYoEK+qCC_SPNQZ-KU9MWnF1T0| zKW89&xue$@b6@p!bTPXyWi2d<)_3TaA6CvY;#JBb8YFgcU32c1!&0L+)fNh<)2PE1 zR|tIhMeZ(Gs>^Cb&7;nVD_j2OhvJK*%F7OmS1?Ugc#-2;Y30ZaUgIvo0oA;-|DKFQ zoJ=(*Hr@dmTOyp$vj{-xupGxUI(GG~J?a87dM~w>DZ8&!KMW?}fy5D&faURNEi#BYeLH!HMh47t|4axq~J={#Y;``18h|_o%nLX?|7nf(C^e7nmqoA-dh zAEF(EP_8k)C}22?40#8}wC&xJPo?q$#l?kkc>*z<;HIu$H_O8`1JTO-A$GJ?9wgDg_vlrBQX<`!-(N6 zUX+zf%8sO0e&aMW(eVRxeWulqMU}-^G^tok}^A4p$v%(T)a7Gz9#bX1vQsOVIJGGQN5P|Bo?@X zf{sv-o8N2Lhl_%NNl7EJSY})&=Ud<$dW5zz&&ufk1}EFn_B`3K*LsIdVNg&ht_{7o zAiQjo(T_O&(#+$n5%y0!D>ecdZ!Vts*vuTauM%?|zJ0|AUiC0F{hF+ZgZ>tc{AZbt zVQ!7ScI-DiH{9Ha5{`=$`djOR)pV%AHHhLzn~=eOjbm*dIBFX|rbJkqvNSfn%oWSV z{4N=-j&kKJh>2LJT8@)Tx}?keAzsjMOAyX#eM28O*-EixfZwY7xG6T4720+R>bQ2E z5K2>Acfer^am#b!OmZBT<)sv`LC0ttZ&-ORz_NZg_e)`yw}KbzF@BLN;j;laZ0P92 z1q2HEOyxXfM){vX0?^zNuZb?JN*eu7?laXl>6t**ptM;-v?x*cXm`drs9pP8Jm=m+QwC$z$-iZ8e30s49vk&nM{f0EWe-W5mdEnX0D=dCwcam!F`9*{Vd)l zy7BwNBd<{DOgu!dD1?rh?{K4jfvB%lp3uY(VhKOtXh@XUq;Sl$c!uPlP>QyLP*qN;*GgZpuFt!>yMwBS>?omFD)66kTBbgMCFD2 z#Wm&1Vwp6-=$kdJL@*EvY)!j+{z=5;u?yU;FRR8^R#2uaiY z>#%H((A|NCG<~IX(IPiyf7sFe77m1&uZc% zx9{3wNR`eZC;c7P;cKuT%Q0oJ-t6V?{+dXDOn33KUdll^JV5P1}(z5 zFP@wPWC)r`QVZf$3ZzoYBkq;Y67ofi4#yyD%AaJE2#QlyHX=vY*CCr z=R%}n^B}%KVM7!GaXWAVsOK5YRg5kTe9*8>U{7+DVB~ts4YLYpxJ$w7-;yRE?@DfG z#lk5*CO&g;-0~W34g2k07|$aDz8|0?;_nbWx{{l&<6^rnFws_1e4WeGc>Hb$lN4%v ze%&Z2**LBbQ zLI^+SX<^KIs8(3@M`3u@zwq#$5(%`woeE}&9r2L4e;V?vlx zS&BKpYprc(EdOS#iWDqRJ*LK5AC)RhhqHhYw^7S*a*|9Yvf^={yHU#K;z>-XA135) zQ6mPrpTV#;8I&gjY|P8K{1BuwK^NlW6-8G^8=sscN-Mkx0+zq{R(y6{atO-%?hP19 z>d|wgrkMhMpo|X4ASJzxML$dOE^e<8N`L?O@C6TWvT}~}rY;K+0OftYuWvyOPo`yZ zQ!Sk`T7DPFD~#33it_=H1~~Z+QD|jBY4W|EDb7ayRUeq1 zljKKde1>Di10S~x{3T0@vt-PnX?gnotty5y@_(z&NhpErJ}5$COeU`fgitAHZt(dDq+#1l#s7$iai{^D@i6DXAV zXO&M#`tuQA_=ZT!%i+mGUeYM2|8#n}6*t@Wc6E{)qGJEZj_j`K)gig|hC!MHL2bt*Ri-#T@KrlfBK;kr|5DJ9 zKsENfO6<$TZ$o%UUQw5KLf>!X?5bJ};~+Y@YkO{-NJ{YqsD5kl7@Kq`p#02eW@vxw zHi`#Zmz5$bm=E>>Q4R^#Dui5{=l2M`lGZEqng zuD)%@-B!mrWyjlP$E_j+-=}F~ceoqT;6CeIpiFY4L8R>+__+%WPm^%5xNLiu=78KWgnSgUt$GQw@`5 z+T{e&>~jm7Ad8HqJmM=zR*3?J>^_Gn?B_;ZvJ?m%k^~eWXC($DcapoCl!i`| zF9#n)g;+)kf^&NpA@g78jV$by^;i=-NmZhb4@>|+QLvK<5J@gf?IwW`E6Q7gL<1p# zGY0wq3M7IR4eUP+0l_5?V!$#Hbh1Lf`$8d5KMFT!C`%*1`2ThRtTJ&+jpbpfx>Z>% zYFOE;f6Qwx{FoHpC4oPQDPOgXjcYB@H??$sK*nWdv%V3}>FI{A$@iv{@T@AycN-a$ zSzy}p<=K1x9>vZ+hz_Drg>H3fNY1eF;TUTZ^pz|ynQHrE0k3$V4%BUI{&OA3nz+PP z6_BygY{;T1xFrBn;Rjs8uYO<1fx6mp5A9Bu)pPT6*pE}X?PZz;!sexNKr%ULE|Jbxb6KFb2S$(lf*I87-#u8$q>?R9aj zvicf%3M?aCZ6!~+#z=K57xfnd$fT$mC&R0ejVfH(UV}pVn8d%4xF$Qm`O+8WOO9#muaY|{PJ*dv_lG*cIc`Zg1b?_Z(n-z=xq*gba!in5h< z&*@8?#I$}UJPw=Lc`nLwC=d$9S4X2NlXScor#4VZibTXtTi5G$*K_*OmiwhB;cHm6 ze1$W-p2xpAMFG#(Slj;%$F;wfj(J@*)B0(bQ%JKdezUUj8MuA(vJrXXax_#NkK#|; z@v8JqeaY*Q_2nW62>d9u>T00OU`j)A;uLAlvKvHCpTmaxi*HU;aQl*7N z2R&b@CXk2xXdaurP$6kY5K|h?(TJS*1gGm; zZyaRC)b ziVSTWUQy-x0w0lK#u&^4_yK*N*VocLdp$1!rOJ0|x1Z_I^@zc&Mw!NTHS_{(gVk!0 z!KUpdVIMPb>`xD*9UZ;#LMz)GyrlWNVp-wNyJG#6(faaVNFd7K)S6J|X~1F>&R?dx zk4qu7!_X4RCR=?Pf|?6cJ9c|1`L2^}*N&QO()BfuC#D|L=CL^`%d6JcD_w!3FralOjoWjDP<%}BdTKnS! z4Pe@Jq67hnhF&OU03r`TMYCkKB!XlkzEO)y2oes6q=~2oqov$JB2;C?b&s8Kc-k}r zA%z7Y`fjB!EMx-l9&Vve110C}r_(org6%yiEcgWmi?R1)rffR=dw%bi{$@+o+0eNh zUt;D?gC>`1{&vGNhh$NkGYJh$<}#~Bp4g6xiLd5c@=>sPpODt(tE#(j=Hju2PpZ7S z#7CgcEi8pDY!uBgjFHK86x*h+&ZPaWQTc~@T-h*Q2~T`>{1SAD3sVz=U z6RKV@vHl7JSzrqL*1S|v3Kj}&k&Jnaa3T3hX>9`m@{1m{vbVbIM6Qj9E8J*j4DuK$ zBonzqaYl;85HV9oR$g718gHRgUFp-5eFb#yy34)iL2NsKD4x-#3TXw1-Sj#h+O6&L z8Y=NSi~FCYqRLS}Ppv{G!dbNI$&YVjh*V`YUCzn5fHoktXu07h3bpD$UTJuMf%ES; zv-5LnzlU8_g|%AS*WTXZk2fFt|5CRU%sZKMT6&tiQM7cVA|CXSa5;@SC|<{$6({Cq zX80RS$3YRD{P_A0d%HhPp0{tbdY;#tU%yo*Wwe~tnS&Fz|7h6|5=_T{yfeaW@N>(D z!8G38oUouYT3_gEM`0?iF_pZ~iO%4A62v|?kyToFzZM{}dnBM+>?E_amx;9xI3Hi$ z%W9;M92sYX*25?RNt;VTq)x$JSWZ)@03x1zwzqoS@E?F%4geu(DZB0pE|_G6yMv&B z@%qR=3*L-sts>MbKHk$b58=TRuO zV)Z>IrKZJ<2sctx+9QJ6EX!Kfa5Lq!h+NC?JnKafU4GU*9K(=zrmFAVE_r2*2CYZ{ zR=5eGn`6oI3vA~lP*Wg>-)T`*rovuwtzCWF=QVGs@szheV%9ODy>NxJK-C%b5Xn=ShMPE@@>a(rhZTtOf?!BHDx0J!nESVG$luYMC->& zfRBfIgi`#()QgQ|LrDiNh`<|ly&~r3LMi=1{UU`}Rx>7!1F3A-H7xU1N}}{IF;fR= zub;l%g(NN`hWiHqpb}2c{YYFk2%>=^Lb?@GX4)1~Pl9j?)x?pKis@?c5)%u3;-T*3;Ua>A1yn*(@{8rTZX}vQ)#s1DX|T zj7mtAyuyT5vskrG84lvKXN*Xl8r?YgB>qyRtTJyCFps!+)2fCW8&YmX6I?;0KDx?Y zrv#TOY3lB|laD51sI2+}1?cMJqM3ayvw}l|r+7^)4ten7ZK`YYjqQFy@K!Dyil(2p zHD>Q(S%@UoC>pF zy7MG2jf9N|ht1Ta9KfX-sX{x-boIPYThKmr`nny$=({Rg-LgGD_t$=f=Q49F?0FK6 zBgbj=uSVC$tpD3`SLSU4t0K4WQ1OH_IMds%&YwOef-g+(5rbcdUZLsf5fKSw(%ycS z7yud7z3;g-EnlFZR3KrAC?uiQWWC;9*y=pZY#Assn{DTcdv}g-Ro_CbEe1 z^5*t4Va$qM$BPz9Yp)21a#5$7OP$cruV`xgk&suLkfX?{$0s}uHE*fbZdJ0Wi zuNhmpCo{T?0E~qT<#P0es)#Jh@J9Hxp>sq4?ZpVkpj8}on)XJ+4P^Z&bfuCHzj6*g zp|D&>;t;%rWF19i%+@#6)r5butG;7Rr1)nm_cwK=qxg?a1B;?+!)P_V-8l0KxSA!V z@@SCeCoyEj|A<-fAWwJJ%(rF-3f3u+B?Bt8+Em{jFMXf^keiLYN6(C0zIR^Oz9=okmU_oB)v<>~uUpn`xWjL$uSwAA7h~Ly6v0oyxAnIa`QVTq z|JNq_+vn=n3S=%(gHdM=C=%FsVqh5>ImQiwN#whQ(~(ba*vRra10n9}c=-wKAMXor z&|cM=XMr75x~q9*unP?RuDWOcnSVEDUD|55baZ6un$FQVo|^$ zoqlX%E_qcm3IjGLXJ>xL-S7Q0C~06EHbbIC>uGj7Zkmpdbi2dENg+tblO~(^6Nwci z$LSwuT9O0q?db@}6Gh9pLl+jh1N9&rQNs zm?M*xt4=MXU;_JD^lV$z)DAlc@1^A*Swu+b!sq z#t|au$nBz!d%^zWTFJ1T5~1ZmYTW>_foct-FVJO=QD;-w-wc1_K}665OC@tPYh#jA zMJ3RvagDktM-CvMCa9Y zBCV&hL8ID3HvX9}A_5Wl&CJYUF();m0T)@#wHXE@Hg!H!+58(+$nGzfX}w58!Fpnh_?qTmVyI zV|;xOm&xvhgTwzUztE1MxJA=4bj4_73;@!_bE@8A2dn*}g}x%kezTW9lI#$kqBzVz z-UJtzFbKh0p3jV4ft>H>Vch~kkVHUXajlT~L>7fguA@v0^Q^I|PevVr6~4!${`5FO z!@1q<=B(dGFN4?@$fzUdJEEkH7t?_|##5`Fx00Q1&kI-#ba!8a+0N7T80A&xE`RGW zN~3Xv&h=kXpmm0ATs{IUjMRt}yc0^hwHp`9L=dBtjk%=d5{B0U)YzrR9k^Ks$0zP* z*;f9#CZciQjwhykLtc5JNg$I?qncXZkq<8FBmI1~rBG1qNf%SNhk*2 z_dkW9fM|hmL@LIR;>u^g#>yd|klA|b0VH~|uVpwfbfpdwR2!(UeoUk!HwYo^vZSB) zr$2YZR;Ut5Rj7jb_8`q<)iK~MlP@fa z7j`s>#%Cf5B9bD^=?7v2)omcvc>*|1)RlQAZKw(1*p|~R=jGm;nQP}XEB8F0Ei$#k z($&???W3#w|AtObG-jtclvF{-=orO-gNaqj!y_ z{etw%pRlPC;#p^MOQKMfDOr}8E85^WYO&d9{i4!5K1;oF4ab&%ar8Ky$>__^aiwOo z#UbrLeXpfP0POPp-wlAC&w{5w*N!o+UF)2+HzYZYN&t^9$Q)Mgwr02jA#A;ziNJNS zcdT2G;-dywJu-9`qfx|ON@M|@tmz?V*@v%mr)o7JG668wOa@zvbPCeEPAsW>$7H7K z@hRFEu3pJw3Ck!!mrIINlg7-)5O&_fx)8x9^qwOJR#k~4`xqjMVjV_^#1T(0xz%tO(d% znh7tD)bD{h%|sH5o~W9zP0_Nh;^MyA4h%-d2h=os`WZSBDag&9-peJV{a`yM@9;B1 zeDoUO1{*K=bqc1P-m55}CO-5<=FJ1~j6|hzpHN6-P$_C?#Gu41M<>f*d(HatCz*a< zO%&@9in$TI&!@4m_ZbS{9*#GFfBg0k+KgrJyZ>8?9wR=(k*D3`IqzHd`>SNZx@1WhS0#uC{b;PfMCTw zaPC}UFgs`UsUdxSMaA(GtB{v^DN4#ffhbUER!>Wa3grr~ev{S{gM?PTt~^nu`QR5d zRit=_M#2?_@FE#Qcw(Kdq0jCk_yoTVvWWqO0LsuPIOM=!v1EWsO;EDt0sztUmt+60 zFD?9kwqE-&umnHt?R&}{qJvhb{n-O2zkp(wmgYXGV>R ztsHS-t&7>8QP43VWXXTVXcHB^W5v>m_CJB~WD;a&M02t+;k`gHwS~j%)v_4-Uy+RDW%=I$Wmic*Y0!v*h>b#mAi5~vpkUvE&souxj?k}|*{MDbF{}Pu zsbp&ep$dy2L?Q(X_Y*-pKjBf_L={!RHENL74shtaWN_Uy55+7oQibp2fo&kzq+4ix5+Gv&1a2htl2p^bisL{vO}0 zrg1WoRc2JbH*xtbkhM#>hE?P3$#c_BE6OdNodEY^Ts#(LWnxA#K=sR-9B zKBlCIex2(Jq|xL6VG4{D{`B_(%LN;SyvEphxpaQ(Zdu~#UqjfuRC4Uz*vbH3;@rlY zvf|QFI2lgALj|#?{%SovOKxa0V;wW#v#{ZMdC?&K;*UzTr5o>euVhV@O)VH#J(wzH zE7G0e*N2#pFuN}A8rNFzgtLQ;ITK&cOgYsd10MAYiuu!hoBtdTXYfyI$B1>uzbQZb zz@(Ae@k1@U`t{E?ACISLU*y;n$#Soiz*Vd?bmkYi`~IIK}m-MAzq6jI?m$|NLxqe@@NL0?f^ zdpIBhqp3e;fi1yC28dPpb(TaVd(@o3dO}(%Vh~nlkf0X%Q;eVQbS!6SIeWo01u$>vC&x<*zUt?9wbi#T62*RyA({@z@>l8FSD%_CbU6EH#=$zxM^ zK3;YyCUq#bWz9Dm3>`9$di}SUp`9a_Bu~9&|j+K#m zIUO~Np60OD)Y8$z#1n^&zS@ZzEuit|cv4Y_O+im$0}~qi7dBEy8Ac^j)y$67fK-tp z%dhgPAi+3h@c~|>$en2TopM;EjsG|7&<_;hunF}Kt5c_S* z@6KQ=Ys-5bA$#Tb>2Ee>%b+i^|K+$)DFA}0Au3EC_2v`h<@IUm=4T{c`wN&?!nvlL z9e3lCWZpM^=;q>)7I*Wt>k%w-da?F-B^vhfPA30!r7|g}DLztusXfP&u8>&eJiP`-!6l3c)(EFe%Jyf3`T7SJkK7uJfcudPqos^2)XBj=n|+ zqEoIT=!^(~zU(MP3w`h-LtF0t!w};3`5MDl(1S=R7tedw4ueMKV_i;nRRDv6nE3fK z5w#~C4Py&Qj0sT%<=#*LtcRFE)5|P!FV3nwE8Y2PhZ_4S4YT^|R@9X@jyGrCH-!O0 z+-~?ZB1HckY$lm+4oW#pm4ju(@=~loQ6%uZ^%t>Bc;~~_hcVrF0miBHsKwvEOv8&d zk8N9mE~og&G*|5<*>y~*kWUwn$eUhmQ{0}HA3IoGV8Mm`1Va9U9nk{&jSfXnj^pBF z;@_#I@!3)R0ZO@6-YAM?#shP^zCStM#fm>ZzvkRWSTowyu%eWbg2>mZ=$%_JC{xu( zs4vM?WhgOV#%Fa{-T%QmK*_=MSLuckSYU9ntcoGlycW>IAG-UaW|dl`Qq?!GA@I)O zqd3pgSzC>zn&X9Ws13)4KKdQxfcD4Dvk>~= zfezc79euuE75#S8;s0|wzWE3)$#g#a;=cK~o%IOm^^!=~=CQtL`PaOU4td*V+H%k+ zu&MnBH^<{r$NnyY@b&t3)X_e=?3Zp5W*F^`Dex3?h!rl?v_fn2=PxaO{Er$YpjzbK zps1Rg3{^~{Ol4$-@S07*o~*U0LOgtlCal^sJbI&s6E@gp7p6Ep|K==TkeRbpd**2y zKY+i>)zl$b+XlYg@%y9+utH`p!8Ib@>ULE9c>Qk4`*<*{R(2<$Il>dCK_cz5=FwTO zp>t!8({GP6{CEPak`MOk^_i3BEIR>PFg_-g@IiO~=VB`Ou_s6nBk`Y#{(mmADozpH_`I6tlA~7;L)bnp9e=~Sj-5Xh;`Ek3+ zzD@8vy}?}0h!5clkuM0SI0H zw+k@NO<6wtZpPVyri_6f#e`H>cEqyg#k;zI%!A!$i(5-j;LSGp-_+bZwbEaiUG$@ZF0>#mkkYklF2 zH}|YPO$*o<*ZCp>7O)1TK}g&x7^MjSIR{8c#CSB*0O)>j{f>I~GWE_le_cM5_>V`8UZdeLftZkZLV(RXLK$-t|Ac#oE@lNPuw2_PNgX!n(rUJ zRySWo2Zu@OzdvmDiF)4;#En(??B`q@lOGmgW#{GlzlO9atorekD)h5`qcr}W#}sH0 z9yJJJ0~MPJ*072#eSVnxOg3T(epB6mr9YNF<4{GzJU zIOP|CmsZ%M{X4N1v%+@cFhU~?3DayUOh3ZW0z2aKYKg)^X2tPL&c^n~wjAlSkH|J6 zE{sNFC0squP#UrO_}@+;T8BX$yrzt@Tedi_%HQ^kB2o|jkDGh``?Uq{Uy(PP+)t@_ zeP`ac+TYKPaxUi{KgQ0F+P8fG$i9ysgq|03fR1xj{rC8&srw?H&HCehsg#=dn9Gj^ zo|4AB(4eg6`=Bh(e+x&3XPzNv`3+o|>8X~dFIxb3eC z!M)TiBYM_HYjA`3FZrQnkZiqyNI-NK+V8fJ1`ixD(s7A?o|gwydyhUmfxA3ph^PNJ zuAPq^iej5!_jmAzOK1o1(er)kBh=H@_8QaKya`11$xY%bueqE4K+EysKtXYti>*?+ zMhxRhEtF13Tla^I*mj?XhP)+id0*M`$=Y{o;9Gsq{@kqsuZ3!Dy)Nn&T?@VkE4II2 zF)FGil2OQW6CRAo7VM~i9a)gfxlXEx*Eik1y@&y{SG`e+r;M+6DHvqs@p?Is71Qdd_Y`FUVg(oG^|yC610#M z(rdD`<8TG!$vav|UPvTnf}txa5}GMqUv6IMq8}IpP*=ci7uca+2@Amk{Z97uC4M6F zpHeRpyYrypXyI_yhpeeWxPJ(U^VufWHK0Qu$|5#@8=V@z@3cl(0- zop)%TKwZZk<6N(;K3Rm|yKWU2K)xDZrr4h3(4d-q=6K`{uQI%1shwM}7{IOLg(Ro* za$FvK!z4)Vdw}y^CnG`gYzI`Rv*xhJWho-Mp;jPo<`IM<0FUc^v|G|kK z^`_ExlGiE(T{Ti*$UR#X*)CHM+x!qJGct+ZtA@&{dnzaR6iEYw!HYY}fYSOjiaYVZogxnPQ?+dKQ`J6 z9OgSG`dPj36FSW<32+CZnJ;5wik2}G0nte&c^cI-OB*@HZg-BwKQ9iDH(fu$$*1#q zMF%1Y|9<<+R|b#Cl_UP!Yfm#ydjdMTNqV|FSg)mX@2jKH>45P(=^fnaI?)1uW&E z9Nh<(BcgKk9jJ6Pa_HcQcY!guXYW9-ag9u$*{w}y#@L;iAaIgN(U@xH{C@XGLw`S~ z_lxYx(xu1o!PK9{%Ovio+YZ65gEeG)Y`u@@xzFdxunf$#-0Sx7<{d-&J_+NXW)-$_ z3dovM_9;j}@#*JniO)mhlsDg~mcc(*?Z^-*>Xr2W0V+Y&z8Mvjs7az}p;pw16?I@a zC2gT}$&p}nKvgmbPwn<*xppRtoK9=gs)#YQJyX##&@oMKP%x`_^KUQ<9u=`NhMg(f z)?QxJehSt-a0W-1ooU=%9+N-1g!)ZbU(K`;w{loV-+uo8@zVev{MWlqJ^!iMp+gVd zb??rz9s^+C-S`X%u=nkBzIW-@Gqz%_;%HTh{2IU zLuwd+7?=>`a?^dC`kr^xD_H_V(Lv zf7iR-g`*pHe&#EWd*1m;ny`WZW7zM=k|YMoq@0N|fkebQan@$mjWRnNjh2SX2bYG2 zmPW(u(8CAN@|mjooJ*g)eaFIRIKRA{9iHzU+Q0ne&wuCgYo6t0JgpeQWSC0N$n4iF zg|~m^%5f0_v*E0iE>=ZV)QLK$&Pi_FXp|p3xV*fa<+*iEJ%+<kZa43H<4 z;1>pwLZBL4dEjaQs-}d2`y*2SH6#E8( zUUgyoUNo}u_n*J(%8Sk@{rV_(fB*Tru6*{H1U&Yn&8O~||NAd~_q5|Ted~Ms04R`{ ziIeSWIv5>}!-UdC(S%p-=yb-)G|;X$&S3(u>*t^CGq3->e*|#t^UkW=P(!QApjKv- zUYTMHY9%_wW4Z*`WVU##9HXd3LyWNM}+KtR2J#!)jsjh$H$dx9SvJJU(c#AjU(nb~l> z%-VhX(_j6wL`n?beJ;0~PS_#a7w`Z2zsACXYUT=nh$snvV&x(+E-2>Ffw#T)8UPIZ z+FNc!lbTTg)VvX?;AMPH)itwHE$aUnV>}3B#-28jQ7S<`DWEYlQg zRYZy?pWpF;GbP5x&XhJQ(MHL1c7{UE?!W&A>e%!9KLZp_sa%wFB*&KuQ!AkvWF8ST z2Kh#SOk+(`=Zv7SQ%onkVkr~zGG1VU?uNkHeS4dWDKR%U=fwttfzNsFbDw+OdFQ?3 z9q%~*{PV|Y9X+_`Yu`HMX+HvjbIy*k{#<{sFo(ow!WJOFuy-UX&e<$?!z|BiKFV#D z=fnKaLkI4??e4OF%0$V$__V5C`BN94_uMC``p}E20Yv13I27Kc1!(OdB>0DtC>v$Y ziE{{>$%r^7&N`dhEVFrLmzT5UA9t!=T5DAQoQuUY@IsQ4VT&`zNwD46#zHlNy^9?oO^|6f_dXD-`+gm`_$L( zJL7~!r8uETJ+a_qr>il^%7?ARcl;~X36!%FdCo&;$rXh(gn?e4OWoKrmW}>A` z8wl&;+aqj$+-cwY!v7K{CqDV{`|rNLH|Xs=`!TnD;tMMBnDd^PA71+AC%zy~&OHB# zqXUP(_35v=%%1wBDqeCbrpLl)pu3ek8R(+{oHfU{lEu4@Wwa3ahmE;f#rwyn>0E3 zsb??l+&;H?L7kI4PY3<9mk<#Xas@LPf^*Jgxy`N3@_dvJ4=?Zi?)~5U=3QZDmO5H) z6XL%~uUdT7niOzOov0}sxKq{WFA~KJH_)}^p1vX~s?M)hvf}sU&2pP%`6#n_Zmo68 z%bBy1<u*NJTVBOh&me+#mG%{WM98kC(Ala_g3dd2c!MAI!G79rTl) zHN~Hrj0mexmRo`lzyu}-C4w@M|3;JNl}J^K6-)#zX%$h02AW2T^Q)X<$(s0{D4xu~ z4-yD|F%cF0>|J}&+ksg1Qo+{}s2cewTp1!Z+YPRIEIak^(1AmhPA0psDkLHE;*|)7yhY|bN~QcdhV${@wk(>Jnp2;anTI# ziaP14uE*(rzsU6nsP?<^f!$>7o zjK!KovC3q4nYg1$Ed@|IOT@1Yu>c|);c`=$olod~t^I`G$lo`9$6m@t{;c;}x7_YM zwRiyldp>XtP)q>Jj+soy#N5g%W{Op4Oe<)m5!lkW$>UjS{Ten)5w$$-z!?K)Iwdj2 zW{^Mf>`a+4ZgXFh_h-k3!5ahX=-UAR3&(9+IBpvNA{}g+X959RPTolb1{St%+IGrL z07O{Wvgx?fPeAN%3X~JnGNH4%o*1i+yAdUhf}9(NtAIw-Pf03yY6eYqCQKlU^Y!(X zAv58yFpRZ@%me|gMu?XEj*Vu3S*e-wIV!K-G=`=23`Q*iDh6c5v`$+$YH3rZ9FHg} z)0zqencW|0emi5&e}4^tDg@?-9!x6HwQdHv)K@JvW_HcPW`K-p>n@rlK&HXV6pEEf zd#Ub5#d`a801FEX{)0-FTyn`Jmt4{)z2Sx%+LA{tTy6oJ`m}RA+u`Q=Hp}xgF-gKq zq$+uCbF0?bEOWUX9UdNfX#b%H_dj&cUS}r}UR6Lw;fvn<-dB33p{mY*uR6N9RY4T? zF#-9dLjJQu3P41y4SU2m>%VyIvfSpm%|>}Pvf0RvhWT(g-@Erfo>^;Ud1>U?7F87$ zHk>5JnBqI!i800*rp3j%1DocSm$Rj%5#XGY;V4U&OfNA^n4j+*Jh-&5&?hcNpTs~- zmRTZYC^IpE;6r@G0BV>?lt3y(|}KQ>G#`jL0PP^j#lC9iM;ga|?Q10fI<1WKyum z2xA>FM9FY4s030FlT%W_kP=`)DU%^}LROWPNkIxGC8+;MHj@yM51e`72`B!;{rBM* zHZ0bYlvzs{WI#ME9hcLIac|BmMQ2^JG@yYAZ;3OaQE8iz@rm$n$l3M!=lQqU>p%E$ zFLmt;,JrDWYmAwls}oT`6gfm&uGzlF~HTA#!MrGxda2NZIGcl7DDJW9toskb#U z6`L)K%43Z)Bg8qi!nR~Hqk-L@Y*8@pzq)yeEB>Vr3It7YT*;5;VrIE@%k%08jj1yu z4Z^}3uhbjli&sQ!Sc;*qMeuTw}-#f`c7P&9vkTf%e~iTUC_uHN`63Xc|gtrYzHz zWcdu1Ph~^5^`K4HEt`xP*qLS(yZ`2^sAJEcy%s3kj<0ls6EWqVpjQB<_^cb$$q@$w zW2%b@nmAad5~y!3U`vD;5Oad`0XU~#V3?$(^5HPcGJELo@@Sanxyy4q8s)uS z;+;1U8RoQS27{hqPE#Y2TI;}hQhABhRFZrj0WExR% zR^bCr00T+3=F};_c68dG<(+mi+Hamy(XMndBhv<9 z*BdYKrPqJpV?OWN%bpl!ma!2k6eym4Q;`{!HgaM+RXT3Z_^zw{J-JAimm7uG^iyyx zENP^x!Kv2$?KH$*+^55JDccZdmRj}Fda7CfmkSbY^$&5@RK&DN{ z)Z%5v^n5&jY?C7;!rZgSjM&{m#R27%59e0QRZ^Hy#LU{_v~FdaA=ybCLZ2KJL)@=)~ zy6Qjr{F^@aj>NDdA_Gx!;=lMz1W*XZAb?cWS+TiV>s&6ml`OYeX0uT~8s(#5?zi&I zvi#7&<>4^TbDL*Y0q@a>C{4}rJGU$>^!xpUh=>v>MAX^2eftg%2ED;tZ+^bNyqpb( zBPWu3`YgII#>*}c70?NyWvkjx5mV*<4C>|oni~Z=^KY|Efdh3fIAV5Xo zFN6S0@GUZ=uCn0=K_Z2rq9y_Y{iFa4{23bqRaSy}{w45W5P}nRN`_Q~1Q3N)&EYpg z_Gj(?W+Vio3RPuF6VQuKJo)eLy9dXx0kNK>j9VANT_Z*&o)`Pau02hjahRQq&e`|a zm)kM<82=Zr3GaKIG9@}+wi}WZWtQ)*;gEK{>4iRV{cqpwORl~ANkM=LeTrt?pkR@T zq9B7JztN2N-|9C`MbN59OR;ijs9$C8yQr8DlP{&n$izz@_j8rj@}DBJom1DU^0e{W zx|HE?_buP4=VQ;VmsbZ9C>4L=olBO=Zs+=iZWDH;$^>)@g!M;-oiP0*pia#IwZNGp zM9rXXzmi-nJA=0UF)S5Yolv4(OQYqDGwuBD63zT_aMWOZe0u?Dtr%+OOofhdF+gqH zc;qpmVJniEicWS0fHxnxQh&3B3dgNZ0;p(JAz%GAR4REiRD5x9dq?cd1eKa@m1wA9 zErBy-%~&Hlv-_{VggQR?7yleqqNE<0tn>E;qt+z;&Vt8Oa(dUQ-A5NQOB3x z`4Yf0UIGA83WS&tpOy4H<<4 z6H+D!lmG(&s(v1;1|+Hk_FLS%yvWNy^x4+Nd7bY_@6wOCNx_Frnhc(Gvzw{!XxPDiN!MpZ~Cj(T870QKKU(`6P z43>3QNi+o7h^|QIESVaYm||~MM=QTnY?OrQa#5hMa66}=f~HtaQqf7l{MMDVNFQP-%8Z zT`WG+&hIYG?gWkwtfOxad;CqriZ~iA`g=yLSsE}I`U53>+IahWuknn@JKnO}=M?PB z?5mkjxJW{f^IbHRn(0z0B~$^ls2LReN@TsITRlY-*#_2&UYSLG_g{aZA@a$;{trOm zbK-xGVa*svh)e)An40bY8TFfS&KSAVSj5!H%b;rv*`QcQ-~Pjcy|>>zO=QJ5xPR%; zmihUGe!rjeQf5PGn*7DbZeA9^u%kzgm`RQD zC|A{fKg}{XnCs2W^*3#D*17`+4vDHr*tTYrv0-8&CZ_+Ny>}0|q^Ryie`{5B_ul(^ z%!7IJev1fWTmgpl%j#HDl>A`k{FEd21yAh_aTfh8f)5Fs+zC~FA@SSkV&l^)((N(?G|R8ftTOxQ`8! zc=I!0C;`4L@kHXj72)gP{Gfo-dQFWZs27wWI&jQ`E$TvBD(z$7LVKh{;o@|ICp9)j$^GO zMT8{Q{e*j6GAZA9#~(+UXQtz49CekG|Mq>GAq4dYMrQy3AOJ~3K~#{~)LNu{lcu)c zvwgp+$aK5?d@Gr5x4&o4+}^#tZm&N#*LN*8NursV_Uy9G^5s)X5i={rTCp%e$XJ^s zQ55MoiCR6A_LI4GzuQgQorD?t_I4c;l%^(4ZNHyttyG#4iBj3|k|L2NVIqPe2tzRe z2FwrjFSTNB}FaLIh;n{ff1)-aW=KCz7!<$XFe>>`(yzC+nOi{-?hQ zOZ)rpzue{Kl6#ZPK;WmJrCvdl4J$#c_m=?>_`f(wA_C_e4FJGVF!1K0z>lLs&h#sf7_qjr1X@m26`kw#rdSumT%_sr8ceM|^hc z*ngNLDUShS8Y+X{u&ZYB13kj z}A5@ZK7I6u`{To3QM3`iOuda)wdc(&A8yoE;CCA zi`VTkgj%oqtu%8qUS`pJhXq(_HCq$rp8^O{E#66IOn7#N=q<1RI5KJ`P}B&&V;(%q z;NSWrsNq?>Ol};H;W!h5$`|F-STH-Y<$v8?62J8eZvg`Tmr}<+xKKDAt{DqvYKkD# zRPYFlk)~?p0dR~@13^=Z(jul>UIr)}$pqAEwxxo_>-IyK6dt^Pdy+&^qT^U8hEkj) z3L;u56S2hH@2h|M*S8ZvF`s_yCIH{~{&mJ!Ys6X+L9f^Eb=__<#;scFb^E! zaMtC-0z__iT`SIIhBDGbEJTEf@`X+QA3$;)FA$MquJ~Z7Z14HYUvp*Tc7&<5*#X~~ z^I@8@5D7*X9=JT`sQ?fug0*gC83hj@r#^^Sk*Apm0Kp&m4I&atj+e6Rs2W%h7}f}w z1Y{vrNFa&`-j4>7tzZ&Z0jwoa1Ss-LnW$^*g=^N|_{gDsf0qm=8^+h?8N z<(HfRAyCGE5M{Rl(7*x;Usl|G5MEm*QoWLb@Ib9*v4xQAI!`K+ijl<@Y@*%0Iz9MQ%qR~M{ z@)b;B+N!h6AW0oBgM3I-1Ifaf*+KYH!BeK&4`C$ky5ooEUvx&R9YtC(!K5aMRTQZt zRyNVFYG0IUMa*Pa?)cYh`aOI0DgHq5uiX9?0=jek9gpu3L7JL*BvZu1C5Vn8J19A|Q(M^T{h1{ou6YEj5$JqC(A# zxta-!pk`2MKZ|5%Fdu@Zc5zer1H&>2qVm3?=(&IN1(C`_4`3`_x1af&uNWn@DCF#n z1iEkp1TwtKmQWgnmKGc}gCaXagseplK5PtXCP?Mf>flGkJe6esP!^T9b{{e^#~IYK zGh04!8;ZE~rgs+oUq&(thcIuD@>@1E$&5iRl^B-VRZI1O3v!TgC@o^D@-irDcOOzD zVqW|2xBsl^cFN|uhp>3u`uBIe@R!cfnwjv#<4<P?AWt+Pj_x#uiNeKesZ4`VS(04 zN2=XPrlwl6vu-1sD2h0W6cbx(AOs>NQd%j+ajcDzB#B$CwAVAnScq6-2+)d|h}nD1 zSgl0F81L}WC5j?3#t|tb1c=o-hP47=2$2xPIA&9YxJ#HEt1H2hpoNsv4?R8i@;N(=q5_am_;dfATy||_HTnlTrHyo*v{1? zwOq+KIS-pUR?$GmVAQiHkjZDBHIwszj&Z4|$jA)x8YI8|k1lgBUtniKL!8K0mE`s_ z0F3;B3&21z4vI&BytpKYDriwiAmjhdv&vHi$?JqmEX*x8{D<(ieqN10gV)B+3njY5 zm3ftsq1@N0k&*Esc?|}cH@7Z_oJ?`Z*_mc)rnH&~0R_{%nh9YEk4?>##8AmNxKI{?qB<$wjtrkSg7(tTT`Y)) z)R`6HU^!Chc1$!seX?-u41xam^&fK~m%i*A3i%cQnbMpWH3OPws$611?97%eUk#&g zz2(n*r}I5@-u1Hd&_G4-7#=HQV*e;HCb6UJhgvqLf#L}XnsJo^^7}b+VoAY9YtDcMK)F{+f&zCYmA6Un%ckq z#P9aHX}6oc>J{&I`TW5fe`)WY?%utK>2{}+wAyhT ztC^Yh6)%6s)O0d6)l%UvPawA|&90q$ue$0EVv(59Hy{=w0Z4=+wqnULTUa9$ z5rffEY()b|El!?U_JiF!aR>)CCP~UH-VJiohyHoKIj4$a>m8=e} zD~ov7$Vrj-5FJz<_)qcso2qZCRDeL5lz8 zv4Pk?Yt|{zP_vc={Oy*Tet;rwdG8f9)?|arL&(bj^Kh97QEg6DlEsPSjEqBxkr_$H zlpfUFXr(w8v@-a{(B#a^Bxe~GyqXEs5*~w^sTD);bq(wcCQ8tRFrZ|=hOmR)qxTz}aOx?1knuJ@n+=3!*z3n|f-VKLv6NfM!>bIM5+N`7$ z75npI8!-M@SwwBRAr3MSX=0^DqO^#q%FCca&0uM=FeNNrw;#e}F}JU`?(mh&BqIHO zYK`sp(sn0~GZe8#DYDG$4q&pv5DO84J>mRoO9gWOS2q!e+=3>7qi=-KQliyYVQ~L1Daqx(ii+0Ri3}#)T6=#F@S43e=5oPGJ!H{FS0Sl!%mu zHSotlfQ)052t=^L3IaxI+|z{#U=jl+XcmC1kSGhVWmqLJoUpPA0A`GR#>{dY!U2y- zk}`{QgY=N1Ib5=feJda1LE{+IwF1>)c4R&cXqY?rlCUe9O9+mS2hU>`_7y`kQspL0z=fr4fLrL?j;Nit}`uK6p$@nsZcQQ`(=Ms zo%SPmZq?;sFy$^Qpq`m63fu+ zGJ|4Sm5Z_Xifm+HH928V8vgnS$mHiI`em2Eq8inPRfFW6Dy)1awlyP)Lz>CWxz_4}sZw{!b? z|MzPyoSW-)yXnV2;TE%3n zR%}VYB*t!h!8N6Czvq_MJO3a6uvVWCo^ss>i2@{nAZ zsGN{oV8+M{{w9PWg(S{walb%$J$L;ZUm_w9yXhUDcT$(X=o!H?#<5YP_Oe_o`K<*1 zNElUSVFnaT6(Kz_m^K3l-XXLj3nts*c$sR8DiAAXS;BZDJ7XZpK4n-n{D%C0? zB^lUYo0uv!GYF~3OweTp6;cC1v6KpUBs+t;Ue>^{I4q2yDSb$(O!BBIy|tt;DcwHo z_#unQoQgdxR;1KAvey`%RWqcK^pS;T9A?{GCKJaHc4qJ$^CMXTZ_44EPF+F}fKPtn zYk@FszU6w)zbvp^mjXr(B}*N3A2^IOsiI+9nUOlRx`L^M5{VS@GRVl68fV#&wDD5F zq;&fsOdS7m``uBjHynA`;YY5VnQc$cw5F$%>FK1quP1^)rlTz$An8|a=I1%xrik$b7>%wom z^TyW`QKFq)Ed*E*SSTiIEv&#oNU}aoBHla3W$sMcft6*fh+q69;>Q_xA=z6X8-9Fv zpgJV`yP{~a$Cm_{bDc)sJSVq(3|UY<3Mz)N05lUspxCWPVgLkjJ~c$_wHX-^O(X6v&bo>lTl}5(!x$QUpa56Q_)}5&;ka=sj=Mx|<)}hC?_| zF-cOUJ2&FDk#UhX`OrkIwSrxLYOs!np*G2wj7$(nxpf(ak1!C%e%ny8l*-+X=_W(& znhQJo5&@wfO`ZKuS%nUikPwkr-u#9a!wN(;z4MDM;_{16t;wMlrSg%=RUR^staF5U z|F2+7G)(?;EnA8T4ZVq&%j3AoAgI9xMy5)~WXY>6ObI1{0Y@{Eni}DAmaHW}m_Q2Fl2G~m zNR^t&Q_PEHC`FafJZr4F$#|ZH7r@9g(J|HE`0^Mc07I!6Kk#tbWhQ4Cs=N#^hy?~Y zAb5dNi4~fh6Btg-psv@A!_HuMe+;Xswkn4?(UjPkQZpM?EQ=@jo^tFL-97{*bzT|) z8k%_(&E(F)XdS6jy0w8==pz;Y305Rc3MF=Ce8(BoENenO&HmIU%M{FwU%dudJ`?|n zpAN?21`Z_D9W^o(V+&S6wW;gqn}#B+^_nRNR8}w*oMncf%F7@dQmVVjH2=u5gz!}A z_Cr`0c09J*T`XTYy?V`TtEHE(m}<2mW35=x+OIqkk$%tY+O_ZDZBO*N>D*ku*X{TF zW_Gr-@vNg)ubG`))|r}4TCMoaZ~qTkE3MTZ{{BY*yypYIk*z->@(vs@R;;mBAO==M ztfZ+isaw!wrlwkZ_jdR0?Y28frxSnX3wPZ2@7wObzkI&Wr$2HPGZcv7L|&QJ^7&xbut26&oo=a6&dKf896 ze;Z;Jx6p}LV2KG35hBH64Va)9){rIA3IO*>B1Kj>B}p`QEia4WD) zlT+@Yky|y=QJiZv1XfM>ac1>nrW%RrY;#P6j~n(fC8K~UpFkBi831KQh6lo7CKUdP zlu2q6DW8k<-YbA$vR(SRkrSPgz<^IF z)=0slAy=x!>GVK&64U9BOhMMjIV8n`Pp)NTM$$3Eti|EpANACX6kTTWRY-PF=>vgU z-w+fFt-)mmb)97x)sla1wKf{q8B|(PZO0tea&pxER9gC+yaXCtW|kBtsoMio4`5mV zY}gE4BMREc2<#6sqYn8i4O_rH%09z=DAgN9>hj=FKPU|$RbYYt#)7teuAo5sAPUYmDu6({8t)ch6^>e&n$ytXX;3^z5>ByPdS! zQL7cjQ53~0iq!x3|6WCe-+ILdobZP}{yPK^F@c<;isO2$f!kIlO--8Gv~QGRR($<+ zp9){^nP;BxyTA2fW-uX+b)=ksh+EJk?ixT&msp6~V@0gEje7|q;wTOF^9xV4U)D-h?L-0TYQh-iyS|&&U}e7GM_dD&+EJz`8ACV69t~MNAMDSQ3b4q?QCi zOsNF{ngHkRWCT{)8f;)$5+!}%nAt*(Xmu`Jz2U~~KfzBA>vU&|BAu0uD@Y5O6Z0dp zdsOO!851RCChG^>tO;du zp+L=4U1oA`m;7V%lF36xl37)k8H}Q4P^30O!grGsCplZ8k*yKu8TxDm% z&j~-Ixh*XPOjx%A2so4wp_VG$hVmt`g(FZ5Be^n}r=QM221m_c6g%_3KY2$f;TLXt z3xJ6@!aP+R+D{KdevB=H>XhD?m~YzZ=r-RR=7V|%8FCiKQ2xbfR{!@92bprtb-YZ; zJErO~lZhQXjc|YH_Cwg;sG&sb@Cz?E^_0_&n3-+O%(gq7xZRH1?bz?^tCiMTDNb6^ zCqDoC0Ib0kFZolK@H035VZWE=WQVn44SxA$n@jn>?b<8n_VxDd?d{#$eRB6)zn{ME z&%gM4uf3$zju`?7!A&o`bx8zb7RRHAfLLJRJ!Rap1R(gGqCn)_Sez3LAmUa4xeQ$I zb)6e-nNs$`1C|9|Ok8%3&B=6{IJQH8d>K^sAg|>z-ba}HLm36PK4#c~R$?8v9Ayr*FFi7S6fGSct$95x|PX2m??g77>9JjD;n* z6UY^jVp5i4Wdz)}+*52sB9c{c@^i~q-|@tDJXP2bO|_s}q$1*2)G=y?g&4#ba9tu0 zl4t9629k>H!G6a)HY($T4F?`6Q?z_<{?1ka&pU0si@JO3uDc)FDT1?4T(x^&|Hs?+ z?A+TsddwkwE--6VMkXt+8agJ- zWF#cuT#7`AA3UorGrrKWrA*`SGQc1fp(9C!(h6fsX$NB2cg>5PL6Fd4!5qynf~J9K z%0Hx3o=Xjr(d_|t{&?H22OlC}^(n`74qFc3i63u!;zwHnS$E2D{k?NLezcV!>rXkZ zzjyAjA8jR(4NpIA+3NC{j)Iapt3ioyDI{@KLHQjKq`>~T-|9d-WEyIPIPiQa-HwTI zD4~U*IG9cfRdy!C=f3hLWNPHlb}&CREJ)APpM+fts+*sm+fNcnU?xsIvCJ?>oxo@jxxaBifo7DFEX}@RFzUlVTUN=?DO0m+cwc5RF z-`7$M^ zBwUn}5rB~Sxe%-s5qB?kz+wpohGItDeDmH%ckbyfn@Sq8ETEZ@sWoSt zZQML|Om?OjV~ROczl8{tWfCY8ALZO+C}U*2+Nj#l*4tksu-5Z3?$=5wE@JW%fZ$pL zM4Mmz92mRlfBxewfBMqFBZe_DRR@{;yGrjiauU-#Z5|K=Iju0>Pn0>DEXqdYpB8=r zQ{e--$j%I7WI{TIDn-|X{8<)2Ej3ecnUTs;rg3AYu($i4q~*Rs_cF@(a%O`AM+EJlKcwi79~rbDPAU)J<@ zU-`p%m6kImM@jt*BUGCQ2uiHV!rrcyni;;&%g8a70)nPscZ}LiCS1gXy7CSOvFe60 zr7>hlW3sy4;kMguo1UJ|Z$;$8AO7%r-}_zwpZ@fxues)$ZQHhO*|O!q2OrF@U;EnE z4$top=8cVK9drCi>t>gCW@g&$c9gWD&Q#oL$5EuRKU({9u!)(KW~G>foelr@UwbnN zU3lJ~eCd`y77=U27;B6WIX=OXqZj~YVns<3PfexWZhzJ4S*_LP_k8vZzjqlROho=~ zOTs1q=ZO1P`o`3`y*&%0~o31hFn# zzo9Py`GxpQ1lb%%`~?7FuaZOnLAHX40Q~wV?(8HgcKa_gDFOgvq?g)m-#odm|Jlnn zdZJ1Z44{audm9J<^8AaZn;mIGUSJ64qcv#6sF_k0Y-5 zNJJF;FJA&!5eqR83rS(8L_deu??d`8_NjC2`DtO2q|C%N3=6I4VwPFE6$UxMxxQ&f z++NxF;nrPu-S_ykJHnS$8xH}u^-7pO2Y zeoE=`4_=;>>m!iNP7#LpAS9=jASTNSV1jYZHJKBQ1XHMS7@7PfYw4JgTuhEoGWYTT zM$egnfoh(hvkZb)%56Fhc9U@eM4piugbZh73hg1dZbsEjCZuK>co}4$pYt+sG))~Z z1C)yn#bQUXMrsATSkNFtG_W%WdJW2Iq=2e@U7uT!UHB%o>`ayZtG2Nvg-Pjl5xAC> zHCLj%0NuY?hy|DkOnLAkr}5#ag3R1n;M@{GCBs8Fgj#q-x<4>dnKTGA?Cd*ojFVu9 z1l;_fEHnJkRI86OexxSGy-t0yd7xnt6Nj|m1b00-rUGMJoh&mkfo|MSrO4&uKna&-*`E>6-@9H1CbwgiHiMMJk8-I7bKpgfdtEt1eS;) z$Q)!K;zwaC5Wk2iOp;$_!XkNcfkpOEC!C2v1kU4z7|tJ$Gup_>`Mn(Er62tI(QBrs z65Wc`$ww@YHMt#r6q8~CV5}Hp)717-`}_ZV-;;Cwy>sc`UU;fNlq+ub+x;?=Gt8|K z5EQu$ZL%s+A~rMe31C=D1SmzuxHV!#+1!jlio_C_NGq`^86vHOEiqA~s4dDWnX_hD z6hCv>VR!DFx2!n?)rrF zv*wHq-@fm$GmaZWzt4-2S+MDyC!DWMHm@g!!sN?npzx@))Ft%4@TwBTgTf7C-D?X# zT;OG(00U$Xa_W_`AF5mq5`<)L0%Rrc0*5g&RXV1*zG_jOIoDD%;$Wq&!Wy!s884DOkMv@~>J@Qu?^Sy>sEx9@j;r9A1^CkfbPBFu-hd9dw znG}6zhVwGWv~^f@2h)Z!n2>IF7mj@V=tn<##T8fV+BG&Ue1^ z&2N75)~#DV^O?`waKjA%!s|nr2%dK8;ZHy9$eCqRQ#0)(iInE`83iwDC^$YwleH}YMhr0+|c8kTCy!>Uh$D{tX>lReK?i>I9#Gc**+xOh^OJ@Qg$$xtS04A@m zgrHcgrQA$l1#4VI2?4Cc8X`a`h(Q(tmjM$ghBcyyv?6h=8bb+b=0vF}tF7Ru$t_}9 z7k6ej+7NX*q!Ut+P)tgLHOM*0XfPx52oPfOmL6eH(C%O7K_h?>v5=H(4C%whqGG6@ z=k<@H`+fU(x6TkUX3SlnJ;u%vnY%g)=@QQ%lE;tcwu2aL5H9m^P2k%j6k) zfkoKL;U`ol=ajk0P{7D!!UIO8dQtj2%y3w%6PpRU0V%uA?O6c80(lu^%msmYP9UhPB@n7HPv2sM?Mi>a&5P8QI2wdY7W5hKcC*jlc2Ae7_aee&}JPYA&*Z zWuj{>A-z+9WU^l!+J@I36#9U=Wxuv3B5NBD$Rdcd!q9-~%sw;S0b2{qLW7 z=9&4PG7ehn-Me?c`OR;B_q*RsMBz0KVL~|JZj*?xP&;j3Q#|NQx%-|(BR`w<-3Lpj3%0G`Sd5X+!bkbq3CU zDkaCoWHvZMAmsidWZV4`M3JXv*poF(Zuye9L$ceNkg`}viguqIYQ8pL3?kBkC20+8&0l(8sOtn-u+ND8qK1u_r~ zj!9Zjv2YA^7Oy@iU1lap%1i{C{^}c>uRI$lTi-w2x~r4uV^&Xn@tY5I`{wKuSMBYl z?O3^AQIB3T{l$NIa8EaV@i|A;=Nl^hVe|azz)(9>RKvoM2J5Igg&QU?2&u{hTt<$H z)n9mk1UVD=NeWSxOUB4hb{m2+JH@}V_4bP}7*0tLK};a&vhs6#GkEl37@0!#jhJM4 zgj@iEG9R!oWAHLQ$&ep87jH09(H({ZznFqdk&&US#Hw^mp*?4X9KNKhm%**g&DAU_ z<7gJc%OEJ#VZ2Nz<6|=xBShW?N@jidrOkGY+QnW%}vrryaNJ!ELIQOs`w@z}N4x{dDaq$C+;LzT5Az zy>!E=$EDrgy|>?GdgMCtv3`lXPX~OuDRpc-1S58eD2P<_V?lg&m10@AG z4i-Pkj6}m*HBc?Jpy4J^12r>zTNwwRlz_}Bra>6l2Q?5ed7;+wG8k^7HCrFQe%IH2 zHg$W5n{U1uz~?^qxpnK--E`AUD_5@k)nEP9-~R32?)UqD@CSeJs#m>g{rdIqeeZjp z{p@GI?|tvfug^R0yipk+!f2fJe;j}ON$c8ENvo6SNGYw>tzS+|O0kXtj*Mqb3$=E2*izNj*39ms~oMuM0OAYIQ~|AuXP<+;4}r` zHvM&S*%mYzgEKgNi98JeCoDptR}MggU;Ce5KXT3VG3#e%+VONdYR9S_t0Yo!q!LAu zX60OK2yM-Z2_kcSV^aTtskQyS?e$EzZ>ExTwyl?S;^m$APf>3DtAZjo-0N{C}pj+D_tEUbwXNP!@f?Iz<1E0Fh^5fQ(mF~hCWGOpGk ziX?S41~QNowhzlhN>~e>%j<5p+eW+v6kGXoD1SyP)R6tb?anY3$8@Mk(bqQ01G>D>1PuMD|0kI><>LKGS86$ z^F)J@T7LkOL&Iw@VBuCU)e8j32CMoNOcmi8BBp_t$@4#i1>=%SE(vdQ&gF0a_HV0! zhcFR5W8=}sp0H+mwmmi7YIS0z)tYt7+&;dnR1~Q=)+*9UlVWDYtcaQ14hIAA=tzn23wwkuD-sc;HD` zueYzC)rvZaZpXSEt2k1zqFAd)vnD9TS`h=er%VzlD`Jgx zo6Gc-?P*SW94U^qiWJA16V1Q%&-ebn=Nu2f7l5}J37N_1CRoVIh#^LZAj~4h&&LSP zxyDtZTj(UrAR!`QAw?7^u|k$OR+0)^ix#VCB|U3iuzJInxBmoVV@=#%q2u*&r%kHG z%IzSNkcz-MBV{5tld;4E5>jAI1cD>gS%?7vfzVerwk+_=F~IC>YPa35`@^vAFj`l_ zETn{=&FlZsZf$sqd)p109&_iJ^%dk@44rmQHebM?ce_GZEt(q zDW{y0hdi)-&%fOJXlmto&sev5SsTEIZvNr(PhY>cYwrHZP7$1a!m9Nv+x6MWJb%zE z372oN5V%x}8P}`NEzGW62oNxD=`928|O3pVZ6+k&NAbmT5AB9 zK||bRni-kFZBEMM8RttEg89Dv zAsm<(lTsTESE{K*FJE$Zdg_0mgBeIcTsL z-JYLs4dClv|N4hM^r3FId-c^6chlZ*piSX3tJ&elK?!Lc%K8K(GzfC!QJjb{8S2x}V6eC}8?KVoW)>_Nfh_N=(HtO5H=2){!>!=yU-QWDDd;aYw zyZ-UzXFJZyy$iYBJ2S0KEHH#TKG$XpIIkIiAjCp$9TR~(cjI@iVIf6OOj;-*1%;mQ zjMf5Z;)_-v{`X@X#&mdVYKEf?ac733gjGTuL&e12K?baZB4P#7#3ZB@xu=s5yEDQa z@uvuGGGURBax0j?eyFiZtlz`fF*hKv`*t^L1wn&I@El(MwS$P4nItI#VEgv%j$HiM z$3FJF=RI$f_y9ln!4Iyw>Z*+!H@@jjZ*n1b|77ROo^^CTwV%K9!OPA$>b^&wOf*Aq z@3vjPc=nMZ_`;nJu3vFPW{=O`^c*H%3ZG}LW%$<{AytF$xwWs92bOGk@-5aw0+~FC zyBJLAvs=mVtu zxys9gNvN*+#x#*jm{${}A&iW#H7?w`GR?~>XEbu;0lp9d+Y`fi87DbDFH<76{G*5S zGH9$aFP6Pn3C-+`L#e>Ww6AN)n(FML)Wwh`EH$(IqvkueIKZ%A-M)SM_N%YH`fvW` zZ`|$ft&gxSj60A1;J#y@dycE)Cx7yYihQgMnr-Q}nZg*8;Mh;ghz?4O;o%|}A#eyq zEI=WIdUgMxX)BpflNW2d{s1P2;jB$*sA&dQRxoAB%h#oZ3!+(-(b zK+Rz6(PJrM!MZ&^-}>5Xul>@OzV!b0zkl!Ey>EEK8{Edd7hZVbg%@6U?X}lldg-M% z+;BrF_Q1!wGftUWwLI>$qfTq}(~eclN)aoiw1V@lFvj%LW$RX@bGgMLN<^tfE*m6DxPzl8$1?B$#`w*B znHT_(5`ako(3*`AAux+lY^}8xX=+U>Cbj*3%9fQ9tyLUHViCtSO-;L#%+2+gx!q1? zX4)s7csMI1VjV}2b*ijC*Y+%8@4%8zB^*S;6ppp^-9B5dltp=QWbQBU+4*b9V%$uJ8@VL`q?N%xcxENwB91$saUWNe$CL#rLPBDOMXCP;NWnV-L5ppL<0Z@cgBy=dy z1n(IHF(3^wU=5msakI~ZiIU|(?zu-EdE~~88}o#B-+lLOx83%h_q-<$F@AOj0ts-} z{g0n<%u2WhCqn+n`-&;p%+BPddMGpW8Z~I1R}UMva}_iu8Xq!ml-}9T^Mk*UyI@c- z&v&qJtv=iM8$_9%mATjmft*zo@-M~heg{tnP+??3I%W`FN5?dme;Aw7F39HXwJtM0 z;c#D%5*nh$0T|=*n%~a%N z970|OK`qqsGI=H;8mJkdiJd_sL4)CqXPgQxF*Sux01))<+GbXw|CS&YtlLjN{q&n| zy6NK||2P0E^u&GluQ=|QJod4BA4t31d%t}9;b%Xiy<+;&AO2|diAPnRK1{EV$-g)Z z9UR8ap@inqM#jjvRwm50#7s>PWAH2oHy9{#&|Xcsa2Mi~70qYxo+lQ@?>A#)o{RaQ zJo9P}4bOE^!v#D`!We%AQ!T2;Fb$&WHkUzh@Gr;&)C@+M9!nYv*6sQE)&TnbJ^<%x zkqcOBckSA>eEIU)*aH)*PCjAT5o_bA_PSF~P;tbO*0I)F5wpr%N{HM}7#7x=)JU3| zUO!znGrem0X%|0x?-RQo_}1NNw>Rohlf==-tw|D{Bsxi=IM#6-=}0NfS}RhlHM^~3 zn2Cs3D|W{$x!Zhi88}kL)e-?<_I=V?k?a>+r2r9Yg%v9UqpVU&YcY{ctTx6%pfp=6 zaT2xL$<%b~g)hJMOP{=&h@6uPM7$azf5MP^?x4pC{P4&u*7{#ha?3Q(LXMrt2^^P^ z^Nw+STL2KjT7h$uu_6K{F%}|1exn(;^-PwJK=6`x-gMT*ITcEt#BEd-8ou>S8rZzRwPff3HBF$RSzOE4~V%&BQP>kn) z_M11oJN59t>yU&`KI3kXSPwdPl#0jg|tyE$afn#C@IoBA7TZlm# z9Dx-RSvUt7r3e;G1c5*Nn<3&Cx@L7tT2bnF8Nj+tXbdz+03oFABqmq`Ay^BJAQtEl z2slTr`DgP8mOds-%3O5OMb}+--SXwj&p6|Z?|%2Y?|Rp}{_M~GY<70`FaF{$09<|b z)vtWzD_{NUS6_6|MWuwl^E$=qyuW6H2I>o8cRl-j=hqOI1+a!lf|Q{him;fR3r-uyWWe) zWTSzXK^7J8G88(?Qcr4%bBcN#jo76>8dFJrAuQ=1yo>$keS{&c%N+nHXu?D(HMb=y5Z-m_zH zH28kS%xqi7Q55Sa(NUt^F271CtywE}_9PJm1R|CB$PkfZYM7YVwR;8;XFEm_K`FK( zirA6>EMi4S7>Y@lt+L9nRiYF{krFGd762S}*v!M*b~=V=YN~bo@f+N3HSqrk00F~0 ztO^kWl94XtSQh8N<-202_ezGA)N2(9F2ZZbAVl0V-Et`rFD!WepM2@elh>?YH9g&q zW;^jzJ8CDoo#-T1ajX)}Nu(SdqZMhz3UKR~T%HiiL<~S+jS;}6#wboxs~JkM?imfo z;J~_XwZcGejRgiO%Yj+a;5MIB5xezG12NCY@L=5&0qi&$B4%Mj3^} z?IZ*u0%e>PE3#HVa7*Nrg0T`4#F9X)f-% zQ~z|&_WK`s^4kwQe*Ys+4o=0+?c9;$d&z{)Z^Q6GV8yZ~D(5f~%}J>8lxm0%uLMyL zKI*NAsitsz+Pvz0j#*bGaPbd9)`qiB%L$#qEfqq-X9Z;&hI95NTofxYGGT^;x2i)m zHY_48=8xiQl*C5gN46&b03ZNKL_t)Qt;gc$*i>|racEw+f~-No?Hv>5J)KdXG_Jh0kJo3mB|Ng*}+p@C^1)1wHJ^Z~N z?%MX~qd)x7&aH)$4u>iD+VI>k8#ye57=K1QzGM{xyKte706!xLz0ennT+vMsxvHEj z=A|FzBijHP{?LXfFED(R^k>8J&?0gv#~fzog>2*SQebvLQo7C8FoyLRh64bP_h)Hh z!Mfd@Y5k6OyaT}b=bwN5_1B+v+G*#Ub51Vsb z>(+Eut!youZLgS4mQBagon*Sxnr^qIT2Z?dwG#IyZo7+ECsC9{I*H?Ul1#PR%V(w! zTfThF%H``0TYKE$C!KrR@#j2!dc`dEGk*RTPSLTBV;#3*9cistY1W#Rb{qOS2N@6% z5lu}eiW$tzq!cMdte6y-*)4ZcOvL26KP%7P_*nmkiQ%^jR;(1WBCXxQ+YAU1X|1NG zTZ)N@W@kI?c1)R%Q4#qJBtt>3Qt98CzFivY^M1>fv`#e$gtH5RI5LLF3fvzN5s@=a zSbgmJWt~J%w%l&p%A zl{#6Wvot>acFJJ9J64ShYyES#^}spq5q%aa-v&scEI#q&i9`q^4MRNVk-V zz%BOco+5Azj+mTFP7GFH#c=k$Ql1jZSRwC;;} zz3+YPb{oL!UiZ4IuDWW~s#QC7?6~BTOEz!b{J{@?@cQeo7m<=1^H0tG;TDYAg2I!Bh#r`SvU;tnXWL`4_&p10?#{WtfT)-B8WE>O@ zRX3N(k&A_;k;{bjf~>krtC>*gGI=T0{=S-*@Hi8>QPYgkKjDTYEX5B&_APmvulma@ zWh_j$KkHe~dgB}4_~@gL0yzFf7c}RAiO1Z3+dqq#4bMFFh_g>+ zV8{JGNv2xE-eJLBC#8gvDWs}Qol*nf*R)i_0hBiPJy`epJs`YY#>Z3B>1cm?(2T@L zubGho4e&$uaK{!uERJ#{86}Vvp_EoI4IPjP7{ghHGM`-+I2g(@e;J?y@jfwPwIA%On8IMC?ctCdFE-nPu(V2qjOoxJ6lD5SSPQH$Dc& z7%@SF!Je@Y=OzOtQtaGh*eF)Y&;K2Cq-JJ2Pdu^5T01k_x%~fn?Y zBKUG}qpD*cBE=7E-?M5qdHGG>`@}Dt>dVQy4P`}>>9NeF_C8I%00w7fi{EepMDA#9 zHCeaEo1jQ>T-jf<5He!yM8DvBl08f;5)m5F`d%B;QjIp;q5sqWSjkc4G z1OdY0jd9GxXc!pL>K1ym`gY&P*?U*6NVDeUrU2 zK=Q}_-fMuVDf1+{=_ad7vUha9-GsOH+>Bx5+54PCi(u!n@C@Z#r=eflSGQO`{`>50eVuVZWZ*2i5g(glz&a(I%S%&!xW~b22D_dD$I~P7~b(e zeDFg*@z!3wqUI60ETAlIYadh<*FCZuqgv=cofk*vqm^vygK2-6Z+WmSkJPW*TvxHu z1DYgdLMWn(pr9z4mS?NlVPR(^{Rz`6DBThptPe zi+NOrwNDpj(;rnlHMVt`6L}NIJ{--du;aahtX1#rysKvO#((&~-)U0!_JHIozwFm?FBZznvf0RHe`9H~U*#8^{8 zo)By}B<&U02A=+GG62^3*OxkWLk*+LJ^%n%DkF>kr0VUtya(}k5(dTT3$UC6cK)c` zxr*{rtYXOR$2M*a9Hm-1o@LW#vekS%8li6eSExL9tgR;L_Qqxw| zY$jB*)TUN#y-KS#ty*2J(sJ1zuhRA7_S*6K`f+>Z+Ukj`%SS(R{hz$VGEp!cSVJ&otv_x7%pXBmiv>(GQz-<$8=dUSqyJ1^C18(yl zd3Kkz_c+Ynv*Iq~=6KFieo327{Dj{y0Ku|?sXa%(wLehe?HL)XS!H*M>&p;^%y4J7 zFLVf_Pmj$?0zAK?jQ0h;2P3naj_K~|uwXr8R|QhB>7Ei9vgz(W^4uSk!MjGcvSaAq zF*2Qh=L7VNOz)@|8L&iK7#T7z+_vkA=Q%Np@{9QF*-4@@Z>9j+S5}v=O$WeQS*Al@ z)I0TMM*b)%A5k-&{moue7xIoO+tzK*FxPhMcyFE5Ixpev4DLHiCJmJ<~pk9Y-&XbZ2_UltG!!2S7*7?3m3xi_Zak(bs{p-j7&7&u!?i6_CtH1D#U;hdbN+1lH^!nB1@_21_ zS%O@+xCs5eB;Q^HHCOHkAmEDzQiPf2uKObqLr^nQn2<(MQ=u4Cv}|iq2_nHl5Hl}Rh&^2mK8-KYZTv?=o>$q0h=@kx0)T1HS87L`33pqgBbW}CGm z<>|&)%6PXo*N%Z1+y*dU&$;*TMUPxMSTu)=X0d4Ie%w+cO$@OKv&dNtO(RVRQ5c1R z2!V@P6f+XE%-CX=ZKGSrd*H&2ugv<^TFs zU+Vd!!1Z5T>@thbQUZh-3`WT&W_$(bXJZ2pngW#mgltKSUH}vXO-d0QF#=d;#@rxC z$ut-gUTY6577=7&RZLAlb?q4EUyRJMo!J&xGKoY2g{okHDHxy%3Dl&DVlvDP00R^_ zO%sE(Ndc(95BGDGHA{K*5*n;0i=m<+$WV<~U~ z2DvT&ieL1MOegp9$7GriD)3EA#>^4;>cP$Z4n#S@Y6laLHAGM?Zl%h|4o0TV{^Uw) za<$Bgn(4`cAult@DP#%ZZs%(@%>8l?9*0$qfPtD!2dStTcVuOvLc!$Uijfh)fQ|tm z{Zr-L00#47zoP)PJW{@DQVK6KRdij6amz|$L^t$By$3IYA-i!ZYR1tO@BW0^sXFzx z`S|2}2f6DuE@9j+?n0bFw*Ts{{^|!l@BuS>+uPpu@|VBdJsSv!L*geFIuxH>}OikYRQ?XBsO-^8N{A7(iX)^BK!9>H9x!;XQwr*k?IHq_R^dp`D zJvGzatua_R-sL!hYn=)c>ciy_cw?KLpDB_L^`2HiG znI;Mm1!i9Sx#1)jz`2m1!JMPJ7#mTLG;<{vGXRn|DA6ZiAiQ^I_LZ{I@RcI`4Fa`1 zE<}MULkQ~Ze85_b0h%m;L0mfq4ALgw7L}WfWNJn&(Lea@pRcI8Rar zz^6X-DKjJa3%~FSZ+`Qe-MHg!eD`;Mcef=Kz&qdh&ifwyRWtmbfBfh-yz2R1{K%yb z{@EwK>;?Bcd3E*XFMs$C|Kf=&*H@PhXA?8oO`j7nS|HVzn}hA{$Tue^^*BH9FA+^n zjLxuTJHsP2y2rbfjrmVbIZZ%V{-)d!F?p_(J6so)lNwBYfsCd|{+6zI{q6fOGJ7t( zMoaxoYG9n6^H5>fq$=w6!$P&xcRMZy$P7ykTVIBI+R_EJMhTC|V(XH$=HHqKdYp~Hg_F0_lgULyET@)jB#?$GE z9Pu*yEl%!OoL07f@Pi-x@gM(jRejgH-sP4c-}%mW{>tNzk@kr{{6m7zf8DD|`}ptu z5y=<6{?(*@?05g<=z&-6j&=t+cBXT5W5UU_i@~Z^whvA<;A|#oQ!VHgV#IINcTP`* zoZ*pM3Nz(oKfPlxtyj1|ro``7>=~F-la|d;PSETKu%}YDk25kUb6B&7}wL`O>d^ z`6oa2*PnUxX3vXx#jC%tU8h+yu@=2{?f7H=_LDDu*(0u^LZVe`XatNBIxSH`@=V6J z3lTLb$s+H4`tL*uG_yE)S62qyGOI`o{-BYN!U4q?M1uLQ6f&z!R>X?)GOV^K!Bu!LR+g#er-LbNb!&RDQI|;<$!=Z-LmFjLWyaCvZud%H z)Mjej{Cc3P>rHlMMErdY@iM2C?IPm8A%xE7PygvZd*ye22YXS#fxD`;PP%Vib_B;} zu$xC6`8Mo7;G7owy!}qVp1h2IaXl#%V0yM??w-lGvoMl=`=cI@PQ;$X$BFxu9NS{^ zvNz;~P$-L^eIUee2NO@LEHgxjWI)91$;$w!Rc26$_7QVB{$PLGc-32p)5>-;`^8`U z#kaoot)0*N-uFHLZ+qL@-u>=(zvn&g0q|#k_Gb?~@PHFB?%sIqGl1v+%{O0o_~FGp zmu5!?&EY)Gn>dT132`1n6GI~*GDJ!Y07T%*9%3p4NXg>N#LVd!U;j&hDg|_PguL2K z5N3S=1W{FMQwWq8ViRHvv&HQ2($SZF)hn)i>dBA)>yK?u^$BQZVK$GGL*dg;FGC2| zuP!fMK0wEtTy*}cJl zix&?`x(ZAeIRJ!iYDAE%=c4W_Vy2#s92d%j%6egvOFX}Uxxbd8A*)Eue_UUNW@d&j ze&M}Gi)JwoM+eQk39}fQCNxcGqQodMFvJktxNpeQ?%0F?h;RNf^}TLh6imht#7xC3 zDWafNLm-sVKp5b5Z!}1ng*MqbX%xt1OW_-5S^(vWr);dB+Q6F zOGX#nK{Qzum@x)PsuBS07E;VceFLCCKuRW{vu6?nKuQ{f0&A5qK!KDXAjK3|D+rU} z_N4~aQX=G#=ge%}PMl>Xv;%K>%UeG1fe-x2pZv)yUztC6wr%^vKm5b*eeZkyY`oo! z405mT@zGB_^^%7#y89&$U;NsaKk}!4`NY(71>5E;+ormk-Zcrb<)K}ee2UUXdn%mm zsq=1D#4JgaQgXGl{z7dp@Gb$j!^_o?>K344cOoqX6Brld*M|a&v{et!Z_c z66u?S5Zqf$rgn6UjFi%rAzu?hmHoobuy8#hKzWh4$So-up?hO@Idbg4s6sJ(YE+dY zY^KDMy{&e--0f}>n-HGmZxZaPpo%=->WX5R{+n!U&m2xG+qqFM10NFb?_Lu%I9hS- zv%1_P$T*%Ij~Iv&-h=tTqT7cA9j?|!m!EsEy^8#nVIN}RPKFfmem3kWM!K$DXQ$O`{KlgJ09)JAtU;3qA`j&6`mXZ6J#q(bO)d%<9GrxR! zc68VrF2Z~s=kqYjCPU&ZNX(iK@S}8GNhbKg(fLGZ-{ynNZ*&Jn`;|xtA%xh(Wj$ex}s1;)b~(SMg^7WHaHxg_TvBtZj-LPMaO zzAPeYnv#YfBn>v3MF98Sdx6<_4LH=X@ot&Z=rg-=X8gMKbnlt2cdx46YT%%VJU&p{CW$N$%#8Vtphj`KK_ zLMr*Ua;IoYfKhFB0#Ld#1en()3`8WOun{>7;V8x#rExF6)0dMBq2l$62K@F(A@%Q#Me2#M+qL@ z550~-LdS&(LZ3)DFEgXe85+2$q`MXS4efg)#@IB?&;IPse&aWOqx&2l9=ev}z4zX` z^U(}Hlh1${1i(ihd+H^RTmtYL|KBG7JaqYB+3x)*W(U~M8>-8Ch~5al`)>qp#DK8~ zc!sf&=6r3vIJVs7-L1&fEHe?1vs0Bo?+_4MN~WI#0o8mlUzW!m^%A#^s#~s4F4_kn zyBLPSHWP-aG}N4ZbQsQ92aYNdPS#_M`i*rdZ8nc>^v4cr#?f`d$tHI$=-8RbMAl_hH`?se zx12YA_b=|H?SJRtw6Z-*a6(3z;5|o-<>3DI=}Aj?(()m zHQ%Cf{LGbIC;L=m-f8KQ(}wS`G0Ss}$~{CmzHQg#z-Ktn_3kNZru&;jclSU2$nWgg?C&(3R<{52Pyh7q{Lb&Z z?|tw4?ce_G(XANc(@#JBLqGIGKk_3#Lb7&m#&bLI+}FL~@Hr12+;{K%(uH`ii1Ybu zF^kz`Ya=mAjIjy*>RFh#Es@zkZVcl7P8RrdBZxF1G%?Ox=_ZCIN(^P?PZ3|rOA|wA zVl!)+#q8k1!Qq92`=9sVr3WrwzW>q#&wb#*=RfBKFMHw3-|&hrdc{jl$jJt_GBN;w zN z`};rryB7~<2lIHiXkw5ihNcOzi6JoWQHmnM7{yz{EJ}!>i_I+%dHK5S9uF_U`Ij(@ zGM`5g2{8y6N)yY?%E_9L!^c39vnJesVcsvpvgrs@gTdfpO)?!*gk=7fB$5dmM_JHh zG={uJ5&FuL&KXA`oy61uOSMvF$Z~CV9zW2S|{JX}l|N5`**uLa zz)Zn-;4FEN{qu_?zpF!ECLXriujFY!-o%8;;%|*QU+na}cUmiuEt{Ayd~j6$^^3Xy zm|Ade*77#&XxQf^Si8C&wed$A9-D z_r3Hbwq8E=`+ubE`hl0dIIUKXe&mnSdi~&+ym-^nik9ZS1E<>cW0RYYO6NEOMiHK2 zP&&gSr^a-)Zx*+oRq4m0&oQ{H7>UPyaH_#;zxemH_3MH2Ce~AOhscRxS<~6n4yKcy z#%Pu`8c({*$$d;fcNeJUW!ntOY~y7<2XHFc?s_r5@+-gc@WT)L-EaH0Z~Mug{K@b6 zp6~g-@B6;Dzy0lxJo3m-{KQXu1VkTC{LNs~fVgds?ZA%rlSH;bdirTZ?ej+d|gy4PO2a`matJoVV0?`0Q5OZT4~ zM^v#qUO)N7wFe%&*!2vODOm_`@+J47YKZ7(e=I|X@stho*qoadQ zed>x329MOce8|=S$b2@>U=GlyE?;+Y(z`Ir%!ApH zasxhjdl6v_k--=#Zd}G6(PIC__Z)>Frf{<|jMQX-Fr`FlGK0~C0jg#O0s=~4N{T?Y zsFSz42Bc(^e412daZNF@at8|4d$}vcz)HFaz{`si$vka>DKtTAM}jI$0kf2# z234pc={x?rU;EHceOt~{R~I6E7W(WAnVPQ5C+V0?)YH?hBKnW!gv`je14M{$H5mzgC8E=j$%-T!?X|SKoKA&V zHXLIL)$C(V%4qRK4&b){nC45A0_kpbFRBX%QzhR$yXl|rPIZ#qOM?BEBIk$`(>ZEm z7_^)>{)4+!Q|4Si@)d9U4hr%Y8yW?^vAO!1ZQ?>AUt;nSU6%rYu|qiU%mSu*Fu$w3 z(eDoA5Ug_7k=RrW{$R-hCN5D+wc^9=VNksVIGLHMK~zunzEhrlXEOYL_^AbF8tQOHug8TO&O~_YaboDE6@Jdy4(RkHY<}OgrzD0xzxvz zcqWGB$38sZn*noiR_2SpOG8gTQ)Fh@fTGW{?-qF`14%0-?7>Mg0KI^ z#idJ&d+&+!W_ENC=P}M>oW;;cXhdQNjfg?9ZM#~V+4^|3Uar>5^^L1H zZajVc>XX-={M3`5+HMT^!G|t?>)XEi!lj~xIu#}eUi3xJdD&MyuW4e;CQ)P-B}8dL z-bEAznXTvqtjTjPzWuSMe;R@?$tB+)m+k{+cqk z3dB}k27sOBGvH>(5hGIv*b!}gtFAEjANWiqe)M%_^VZGbgyJ|wiCG<)nSVg*t>hI` zGlWTzp~DF3bw=mzNS47$XfH;F9Hys+htw`@1^_Z;dbSH7r%{&aR32>OU87(awy2Jg z=`+QyqWc`>rYPlBQHSXQPcbt7unpT1i``V~vPPElZz{_a?ZCPa>f-7ObMsb*`bDFX zA3ak0=erv!Gbk-VH~jP6VK>>Q`feUwP1UKb)7kawzSVg%+pmFrUP29`92tWc$eoSL z-Tu09+R;96q1ZipV#r&8ecpZ|PODCG+VDGYj!|{k*I@A3z)DFY_gMk7_;|M^&?|EE z;p^XXM?E~93a3iXp0<7+Qo6_9Ng5oC?SoDoiJVbcW>8(d=Sfz6D<2dQKANv}ldn+` ze$H5s`Fz6*-t@N?_uaR+_r5UW{Nf=)lqO0OB?bwU;M+_@0wf^%HViXiU=YFpD`W*o z(hMxzUS@{@7)Xi)fk4a*W^Uls%xF=Q(o{jJc>ANUiDK3Oq^YUhc>21Tro+X0xmw*= z9v&^$_g=j3q1B~(FTCP!zWn(5@zp1_dAw}Z%#*KbG4h_n=IQGzk}+^Gi}OaZUJT8Q>N}VcEayH~ovYFS zUK`F4b(IOyypg)E2vvP$z`T)*5E2Fvh*5+zy3&{DBLu42JO)d~2q;-<%$3CaZ7%6U z44U%FV-^JoF`uRc2GL{)RJ9l~FB1Y%LJ%`R6(NWKDJca8Yn6nBz&3eG^Ji#-xdu>H zF(Ablg)75>+&$#R5O-_3&xmdw&FXR+BXg=r_h?{upTQ*nY^7#)<1`8BV0o}O%j&^4 z5}K#lRwsu2iY{x;;Ak=N~q=Ea4<>V)E`lIBmh`!5xoy zhxhOv%tfg$!6zS4qf{9gU7i88@K)S3Epeu?;ByTFs%874{|w-ORG5%sH-l60ERM-x zYYXyzrTqeDNX4q~gp;#x5}U^`Rkxtl562zt;+W)R0Gvq8pp3(g9c91I4DMMxQ+W8b zZ#aDDq4|X)iP9V#NNmC^NNk*uVIYG@j1)3J0_f*0k#rLwFq(p7S+dd;Agr(m1WADu zt(p8~pPkhe1AM(`c_C>e14V{QVi~f!pjIL1k08s zV`ul70wb|ZHZRt=3Y%g5pFeqmc&%5v7%C#kc zCNg*p0cPg1&eY7wHG?-j(2rv&l91NR!{ISZbRHgsmmxsxT6akHa5HOGp zB?LmjNK1eK#wOAd)K?op@+d$6`EctXFeL&sS(I$>#?NdDfhn0Ai)r&=*nssPktHxC z0~o{vSScjrW~s^Z${^-PM+p!Oi44y6fnT?H{;wWCin|SW6)983-Kkp}f~fY{F}t5+ zWJa;=cpSrp;%H!>80WWVVIQ2=l-d*w*Z{k(3=9+6i+Y0zGG7f;NgAb%0;X|hyOQjg z3-@^+sWM^ueI}fti zLh77$EKHZp(=3fX-(W_lZ8pzM0-`2U>btwyneMLbq$7G(=&bVfg!uP}c%Lx06m|ZI|1LO4jY7t@p>7g~^VgZ4-gWoBa2COFsw#S`Nm}N!_=|Jzzbs>{6DY zq2p*qQ6LjBT_H_t$5h-+U7TGn9u@EzhR|nmCt!ZheX|P};^H98W)d65ATh?do05?b zCC;M+kdz1sbhr6uFCdUY(^Ql-%p~h{TA-N<@@6H=ZO12bG`M{f^GO&$3SDi(%uEnW zm1Yz|sj5&wxe%rlXht4fyyrq%t=o00ZCbC^tL19FT>Ibhc=gcpAAI$jUb(!n{Ol*6 z{JY=!H%=-BsOoy%uGekT1QXKCj47#_5$4l+GcfcW*1Le2!C-2p$QyCYw75-+Ps#+0 zs_Vp9r;Jm!@A#iOkL9W^pqUUxGxamIO#&uj>OSbPpa1nxFEKVl(@03M1cmTQnnJs-`< zGN_YemfIu=yLQ|@bz^2ulMr!>&V~5u%p21ABV;{PXA(`AHDw^iAQWRm1V~m33NSY%lK_Dj5z!_v zKoy<67~iZE0?qYhtPnBG1X`wS0-auo0#_sgm|7KqWtAD4@5utAAeotg-Z}s?B+YW4 zfUl{7h?sX)+>N-aNEu)}#uNLZ?=%~ZQJBtuHy6`kbD=nzGmS6zou*{$mx^gjKWmdW zhUMxVduKvjPG7yk-T5&vVZWd>*OlCfEDNRF7+WLYy`|h27sx?6ZSu*Yg4>2Ffx8 zd705gP)cJPFN3N@Y^uzl8gOd)u)D7Io0~j~X8_N6)mL76;fvzpKxVUi#8QY7BSWAF zSPr@I^}Mi5zViAW00@dui%k;%fJj;R5(J%I$ZKm7FcBvQBm@S+)QJz*b1+jkOhcN9 z2~4{+IboI*swpW_YJ)Vhm{MxiaegpMZE9ERcHOR*>vpwXE!XXGeg8xEwCl8`0D73Q zU8lCqsw-w_6_GGC^^TAbGP_{495*#XPJ7R`xE72$K~s1aGgpmKb1ue77}MmYjq@+* z+8P0x%KjM3Lr10a0w6mWyqA^^?4G144G<@jEcbQ;#TO%$uodJ9m*OrU1_;C^ zTsmlCpaHYMynV^c#4Hhnni*(5^%u27%WpOinYt*SY7L-(3i{(Pm+KTW1;iH$^m_2@ zNLh*mvh)@>4@=QWZ_!d<0W4LR)i^g#;=tCIoTa86Z zuP$>R0GiC(2$6?AuVev(dFCh}OeMeht`yBTJ?XjqVD)!G$Sol{@4Vuz0(XzpltJ#A zQ-3w2(uo+|9fjN{@#l_hGYfOMuyfu~U{fEhCLCp;>&oHB{^8fW@$LW1#XZKD6xr52k!iHFpe1+?x`kI5|iJ%p(X>sNBB)f23@u0 zFcT$6k8qd;ZpqXr%x+8;`>2*jk<@)>5NxBDjo=T~f42=@(RFccN{hlj~+@XWD ziOqIB>F$5oDDB{}mDvLSbsUkbZ&e zNs^EtA%qYkLkvv}G32#8ksmKFq3?_FL$aGDHZjg(h#`a!q93pw<1EhR&1^B7FXr>Z z`QmVKaJ0B^`RLNU7w&oB(&2@J3l|UWdEnB6kKA{BysC4(O~|@A-cD)7=@sqtS+xAy zU8?=%r~k(n-Tz%$f@%M!{+m*&{XKHmj!9}MSyD}EZJRWyYwG|2gx?9^nYG=_$r6D41l?WizE5pD+PLvHk) z9k|uJ%w`Iu`WVtTj4dtgS!{@t|uPjr&)ap%4eX;o)ckWsm4JozO zOn0}#?(%q6sNb2m|I1!=`Ndx>vsswU7^1{RVq}PZ(6K~Pgn=X^fD{%TWw5O8{nFry z7~XD3gZkm>FnVJw;>=?Uk!FCI0Os3<%xEw%nwpVjrh+Iiv*gr= zZ`&~frkdLJ;9$01w|-pF^3$K*RS*R?ZnPIJnyPD5P1U(yTuDZ zh(HZAQ0VKLAVoVSKof}0K^7_pASpq<>L0xCO|N+ofu@N;M9q>}N~Xhkfcc^c0@4hs z7NT?lzyPCkYzaZSabIs2e)bmS%C+O=TA#YU&XG>ZTV}OD#2~76FpK`hNENOEqvpCZ zSsoey-|U$u;Kn?jn0b}DL77~@22dmya8A0oEF}#h=AA=~f#{6Oh^x~8Lb7Z!bGU|6 zjMN0Wj1RO*`K7ZjqeVdmfFE()X~A^2KNLA_)dq-SS~s*7mgzwy+G`Wky;TRfBBCnwio9k((Hv#kx(4XI;fI8>AA zpO`Af979deR2d=bLN4RPfRTCTF{wX-ed>L?w##I{yZw+Um7H2-X4J?hTiWLRUt!@4-Czx=U)G9s4By9B_`q>nX6;OV2FCY<~jcZowIp zUpCQ=KBq+>07^BM?Zz4FZ5pfu=SW#5uO9mu%HXn3X^vq+J7(lL7=gt`iZgYvZGX?g zVP@0m zjpU|K1Eiq}=@AE!5JW_R#3&M^i6SAy7(xuQCd4MrW=)(mvw3W0&3rMNAI#^6^MeZq zhZhbOhl^(3%x1BfHS>en;o*FC(1aL1{Goq!Qeglv55qZWb(%7R4ARDE`hTF_WG zlVP3;7^NOpomk7W^RgAe3~kc3)hjpF*u+NPF(3+)8@@HCsj{++m00P9zcS0`?*Yp# zs|2%%=b&Vl1YG~cmnLiiXCm_`a|t3|di8_;P~m037zscEb1jf83NQY+6y=wkfO5;- zZWLy8*Lw2Hsk*K1c24*@ad}#Bhss7*FOzp2wX#h2Nc~9;)!J^9$ey*?!pLx_CgTA* z{w2$OkEGMGDV`PC(QjOuTFm6H&2?R$0TXTYgzu<*$=CWLHAdw>`9*hB!kefw^~@|X zrxMv^c~nXqSw46cJ?=PLzh}h>@o%K3zjf2E-i9-%oSfm2XBKx|g3p4{0CYBt2DMXy zwON_XUt(hj+6g&@vW$Q4&5v}RQ^sXb4=*LI%>$TfPn5xAD6spB$-TcdHc6akaVH=y z7GXA*rjZaOX2J!&8V{r|zcc@W{7X`@94|kv6+-|xAb`RAuw_@A5d{=;?q7me=WLHXRd0!GWsT#DSVDfLOs2NSp-6TzIN=aACcC}om zl%Dv^(~mv&=@3NC6BvL*(n2szo;->`^TG-6`9YO|TobOGq##8IH3J|5GZ-Wkt-}C! zP{dS?uppMyNCY^CNC{%;aM7q)r+h*7b*N?vnE9U>gE`7n+=S|iPNuUc2D-hIuKGMi zGflv{)vL$re&f>QBsmbC5<`rlYOA%H!8{o-GZ^8Pk$WFQIfe;2FaQ^HsTYqf6e$LE z9Ycx$CL*p4W-vd12|~~y0vMR}XaMsjC*C`v+XV`A<2n9{=oVorrI?Ws87YQ?s=MYY zGi_^tRSGg@fGQ{ypiO1~1@u0uaItML3TZT`UxF4U`@r3RZENgj81ML*-=CP_lGC22 z_375Y^{sCGJo5LKZu_glY`W{&H@CQJu>9R5ygRx*uB~BoV{%rmnaP)WvOAvm?p|Kf zc^R3=%cep>_OHrc&b3+Yj!mYL(P7+jq;EOG>Ax;mmm`_l!2}dmUvjX%O-lcZl7@cS zD*lP!wqmqs?$W$@YZ8-n-QShzNV@XejcvX1MC|U`lYg7ooydLC|9OW0_n-Y!cc^ij ze5z++j(rxRr=q+jU#%#@^E8-z;>2in2RKz;?qp1vA)CS(=!P?q3#Wt2r96TR#dYr< zitAgAa~UY>NX|t>3DJ#cULN^FXa5R@z0&N-nI2nmlx3cht7Uju3*F+FRE?RmCbKEy zJN21uf8z(9#hn3)NNloFjBCfZ{!0)_FodEY)2nGfsOCGG++Z9jA|knUco-7hgS~=<57xG=z1|G!XOaI$}%BH5C)Naw6X^aem7)?hzAUQDv(H!*u-YuxB(goBI3Pj zzeY%=r0rU>I*e+YtZh@p%W? zwAjIMhsO)RTq&hHnv+C$lljv3TUNwq@XD2!}dRj{{wtd`W$WL-^7A1`L(001BW zNklHf0PZ?4AV! zT#d;9(%60P7K6BIT2V(JH@{Wi6*%jl{tTiUJST_HF3hIS$g^*T95}j=^&{I}x*28^ zrwjv6y7$5U;kPezs=IZVB1ixL9KNW_Gfu?*DI8(Tbe3n15WwW);|4Gy8isdA+us?m z?b$AQ+c?K(QQ;QZ`t7jEXmlR?ymtS^{yrgfgJ3!>>bLBZ{*$pY-M4uvbB5QQ;gJ*I zy|otIXPX+;@zm~o^JZJZG;(^Ym!1LjekG6OwB~zH89YNc{vD!}<)FsLn(3$EWiTwH zO=9J=L&T{9Ka1Og%P;=&cyI)O(lIiDA`)V$s!W`aLaB%qU$6IWN#1eu!robze#??| z{<$51glpwEjpEy*oO}^ye}p9bEvO=tyuYf@F?rjR-;xkRGmGrHd?>0iI4U!c11Q_z+S<(e7?64d0 zC7l5pL?zDDIN>iwr%+6$TM-8e_ zg&NdsowOf&Cq+i(XRvF_wpBY` zsTq&iU@!zwz`$-KFoWi$EFb`(Fg^Eq!fnBwy?tqabgicM+0x=C!?=X)d^-QhNNRs? z^?up8M-HjBEp(ovr*0hxz;XeZs~LBC_Exz8Q>ZJZ(P%W8{K&~YCtn;63w25ME7<*_ zI(O7Ha^hu;K~-)i5psaSykr)J)tph7TblGG(?__ye5M-YPS7IQ?7UreWVne*`mG0V{QEz*OL^^C0Jq52?F#*_N=(oGuVgL5~&A;)x&&u{Y z2l3$0XZxm_SMz?dOP+UuA!KrdG!qd(Eb9yq1QQTw?gmh{_?YHUpQxvoRoNLtA{N(_9% zfAlvt3!#QnIcApfvBOSAF+~OQYMFH9gAA6HcWTwllA0+raQr>tkZg( z+I8EuX|--wt9HFkSFau)FW2xs<)&N-Fu)RpW;B&N07`&rDRM7eg7l7^(?=o%sM6^K zwcZCtDF-sMY-JLGDt)VzC6dCj+<}R3wN7U6v!YUe+PA6nzpk8OW~y)=2R>cRb&aH1 zMa3lx)#Z{C2}r7|by{woUp|s_?k6=^TZ6lV zl<|hJtyt=TvmQmc;-8k-&R{81;h_bCoH zD+A_m%q%=H;AMzhZl_M}?aAZKW0cN%b&WEE^VTvs%^fwQnvto)Y_=#?d`(ZyY&f>) z%dnUM+IDx_t#iYUMMYPJ(|4y947;fr47sYCSZ|yI#tmkJmw6U@!;A_W3GCgh%H&3e(qfa0p_4SohIo` zEo*#aJph-)W*()$U=TuRZp2jt*3G?B0@Udh^8=6Cwq3U|%0K@S~qdO>g#C)m2?om8zBkL`~EfgsOx% zo003Cxmw6PFC_^zGq@OEx3*GSr;%)`FqF*4>vZMD`o=1K;G15h<}DBASu-l&qc{l5 zM?84|t(I3a7u6$&5~>q|Cf{ zR#YJYsWtCVrl3$$p5wI!25NG9g5E9&m=iY!tlff40Kk-#Sz*UCnZa`UX*3WP$dtRC z0j6e4q;#9-ROf^{o|NfD#L&HoZh+-5TqTIN`giWl-yEQNu&e0YNG!W29o^;4@G_I> zuhGr0H>cV*Ogt0_C1JT^x}GJ zDb~5@f(gNX<5FIVxS9;B{Pqp9<7m1Uozw=lW1xGGvtTfVDzIrX0hzX*|bZ}_LSqqXHN`pp4g{{-pOD^)10GDP0ay$n-A5&*tY^cqc~AnMqJ}& z&d7|arZe7JR|r%_6r29PenNPw;{V*pU>73Y7ga5PClFbG{)CJ3Nr zln|Dyv|Od*by~Oq9aU9LDr%P0oQsiU0jkPGe~~3Mx+)d{Mbf-6+nEx=EziQud$wsx zbWbwxfk<}c`ufT1tK)T20o0{*nZ8@5U0g55#7trNwWX+rV?|Mxp{a?ODn)1ty3t<( zMK8wNTF=E)Zu8ZlfkG&ZR%9{_`2=g%`_jtRZ|gLTQ)GdDrdLs!0f3YMF&f;auK||w zu2NO6A)3=M=5!1VYRP#hU%MJ$3RA#nN?V(@w^;}FoN#B8GCV0jhulp$xEuxGl)rHN z)Zlj&>(;wQ(hYBnot#3^8pYN{C-4mU1s18Wn)T`8UP6mo(&RF=t;fKeek z1(O{^nD#C6R%6uiv7MJeDS=b1Lr&c~{PH>&0QM1|1?6at#MyWMoD{Lx{>_bLO zvNPi_JLB0WhD{VGq)JQ>1(iKHv7`k=Hbh5O&fICJ)ao zJD7H$j--HPdNrIV6<#Jkw-+yiPGI|^-7IX1nsMwYwr}%9=2`3sYuDDcKs9IcWoWVK zt0@Hu5UMhSJhk7U=iZ8=W3bP?XSf0=Y_?p_|aY|vp zO;bD$%St*<+$6yFH&H+mAT*ij(oEHrUkHRC^Ml#{{VRX;%GZ3+L(jb@#*oV)H+j3~ zzN6K$rJ0&V4KNGIBydeg5EH=J5VtK=We`ByCX#KNEFtwLXIrN!vtG5Um9AIqdgYsy z+BWHW)n320TrStERr~2rKlRB^K1mV`F0cACc$m95k=Yr*Pl47x8T3LEn6i*wMuMa% z^DP6Nwl;Qx5rx5GVV?sf0VG^Bkrsj^y>?@D{dj%jxV>BEvDAAMS9WR5z-4TVC z0kX;r#SdKqfPO1eAPwpNXYbvEb!*bPu;1_L_3nMncbWNS7+?k_;te!hoCs>9OeBVj zu|SEEs1c@V1k|bKe!c^3hVu>+mOuSS8OH(3VMlHdpXnCO!FA*6M&aK1!KAv`-k7?wp^sij{?M@h1dkh@|Cj$bPg!|V&N0sm zoff3az+g>8TGf;=2j<2RQB9W$nLle;UNVjRNRk#h7X_BBeNyFtYJw<6Kx7)yuo#9# zY`Ro-YqD_eo6_QZadtM?kj%p8FT)M+*;%q7kqp&aF33OY=l#qN|J#>ddg1j}X9`>R)8zPNa>j?1SpPt!Dw)3nax zIF0LRJ?3$o)@AKdx$xp*eQ~iG$LYcSG|S3r5mnb8;da>Jm+t1pro` zzClcZKzU_mMaIFZKMJ#wL0Cr-;oKKQBjXsYAXzCuR5ke>zweh{uBKP-ucpa^k(shj z*#(T@x;Sgfp1p2R<(?yrP!6Rjd(N8Nr>vZP^2o|3Vao2Y9E=;X47(s!W4>7BfBxPV z9;~K6|D~TJz{AxLp#V@PU4ltKII}TRFQ!=r1kK@a^xNH;DD_7Jf)lJK1<)kP2`IuS zx1$^iE>B^uQb7ZaF2ESy4m@^=GPneWk7Nbe2}*Q_kbsD>k~sh-AkiUGFtA>W1Q}5O z9LmxJm%#})jB-K$cjaDbqLCQ%1i?)2FnnhE3ZXT3eX)u*C0ZHZh-w1QO;Z z+?mGk`t~v{^5G!>>atRKs=2gLBu9wA(vz#PO!y)zpQTDB>^|V@!AN=5p|M2hr0eNN_=2-eX2}_Wd#ZxMT ziHa*3!UiZ8sU-o7p}2PJB2Uq(9RN!j0IUv!i+vwi9Sq0ofl`W1t!+q}^2OzPxt=bT zZF`E>jmVnE?u#o6-2^ zR&NNx#ho;@!L8j6oeiS^Cgd!Hi|t0|o*)LzF|=YyO~TcZgPu z0HPubM`O`e;ZlY&Jj2HmzcD|4c#KJzhAULPE=~X2pDLY@2Ppvd)J%;5`t%`Aq393Y zTd$c$L!M zGcqMmFX5({%ob>yuk5ND0Yx;{17PF5$HZoHmg11${+hkGqM9E*y}#ujQQsT3I`WMl z=UTJ4y>~e_+32QTr##fhJx(De4)GK{nkLZ#GYN8>G}%{xhrz7VJT#7%{DwFo(;KiP zuN_V(|Md_v_|jX8x_Wc*avbcLk?y{I{A*0#^y`(P7+WituH1v^N)XVdXPGT*=F@9c zz|Pw^&dUI8DHE#v-cQY-Dm5+o3ET2I$9QpnTD|2g7I=6##Z@|MFFfXybsp)is@>&@ zX%K+yu`};h%RqJ|5-bb~L#q}o1Z9f7Wni(&1En7mgeo1wqMQjbfMK3M03bo5K}N>R zGYkvZ48SBwo;^0NSu9dM8>aO%EE1tF`V~L_yZ`3*fBL`ssqg%>x1HZvq`|^J%Cm2} zI}E0?6rOtJ4#KmPNTa7>N+K(+cqBmZAZ2on0AZ4*F|XHo95Wo9E15iwIT)E&Ub%Si zV0C%9UaiIty#Iw4p1%i@X;7X>groonQsEvALC&m4}yr9x%BtgpMedV$SrRP5$ARmvIrx1ha54rP=jR zX@y3(b1qEF7$}R6!ih{-&;$g#EifF%gnSN`$D-{5Vw{tUCb@x-#7dE|@L-Z=IZ8v$ zf)&@lU?E}%cwrFlA_FbFv&|ZmKwpTNm0z<;IlYE_X`A`Vo!Vj~} zgAQjgSntv^G6MFi_3nkH*%N;qoZfnORq#XCo>=D%S61MypjYkpC)n4!pWp;YG2h#c zuU}KHK0Jl2*+mnFJ#Ls)?UH+OEw?hqv$`2RhTO`j8z)_7EV!M=vCj3(>2mC~2anS? zuPt$NC5patnz2k2KDd>w1Y7LPCNINHr@z?bWzgjGK9d>j9bx*`bb3R0=r;HSj|k)a zd(-lAx?H96yS7-QNj8L)NJ31Lvkg{Wb@sxf2modxMnHCi=$<@0u9PW27K~_I2_b?G z#{3*AzS3AOkg6s)f9j+twoV+|%On=zMvAc`A&fxTW=3*1&z|e%K$2`o=XcM>^{DKS zf8m#X?)UtiAGo|&z2&XXyyb1rrbG~CJU?GRWow~nHcax{_qd{ z2>?New9g(AXt7}r7<(ZR2Q9xW zC_6F9k|~Gx7tlbY_k8nLe&siO*MrOT-Se=NNsGkwIwvbjsOH`trWpW1k4E5H494Qh zr}$@%U7R6F@a%x(diu^{waSCp?Dw}5;N#?%>-@L>#Ru18e(~PwFMj#E9EECOG@z`a zAjFRf)l#><4o0hh%_&UKG z`rSM`_qIOVD3v4J$`-k^2d7Q?LksHfKA$?k!t^7__NfyLh35DxAK^soE&W!KlPcrp zxK{qxO1uvQv{zWYz7eDLp4CaWPvTr*ER%>_6`}pt1FFU{Tj=wthb^ZaP3j;ogMK(V zU^0UvBh16JuP1ms@ch5{M?d9@zC5p&KCbgR`PtwTeKedWi9W*2C8Ex*L#Opz*UChz zEAv6rT`$xNUV^ze)_^dZx@3eY4Op8J0T6~l$;4gBmx0?j$06Gkn1>{W;(JcYkmg48$|f+=*SEBtb)vGAS`7bC*v!4QVkj zB}3~GCwD?aC#pbXAz3t)s0G(wnmkii60xj`5qhX_lo zN-x2-0h1;3poy92*~*LH%lDRVe)i7Y^YQ#Fou8#evXm?(v+~|b-EaUP5~C0oLq1M< ziHY$FqF_;AmjKUMF1cT>^CFobIyQ7;K)`a$-}Rmk++U8bT#P^Z;d^ez?1ckT6-%Uo zBdN`Hi;J}8;NV{A!`^+#rc0HYMQEo_d_rIsVWxV#$cQ(@KQD)P4U zAeG)So6Hb`yb&?+DNe`^SyYrrfSNJ@$siyqPmF4ipzJEHWYm6rl27op@&y0>`!C@2 ziQCcZZXKnuHf8aU))*afH`&%anep4X_%+Vgax1YFUtEg*S4169|5zn$mBcKWTg|X5 z%YA;{alqU5lUm;PFA^<`OxPWg*fZ~P!pi$-16Ma|61_YgJ{`Ug_bKIlOig?nuV9;s+RL~dZ`fN^ z{W#$CHuD6Vkfc~KeAw6TUWX1hZj5clbv%QOvn3yu&P68d7-hjRYQ~0x@3@ z)BbsS!3K5uxm1?J1X&Q39a5Aa;}#^zU;HJX^Xe;?5AH7?JXqbow|wwmxn8c9%W<_F z*Q+ty{pN8R*Ll4hSIcp^9H(&N8@_(C=P6H92C!aF?QfcVwVo~>tY3Zg^3|6wUV8EV z3oqPz^_7cpJ&ofuj?*;d)oOhHqpyDSBd`9@5B?;9x^|1Sp%kvV-3>RqW!7Zj*aj>! zwN7GlI}j>vJTgKoSe<_*V>wmZVy&nsMR@-uK`U%axclW`f8!f|#l8Eh2bb%M<#f56 zE?47nHLcduYRv2IZ@Hev$=6ekjkL!pkCU$_AKk~v$0@JJ5*f)NW;y1I)fC=><9$wf z{vzV8AA0}G-}U|fb~XBH^q+kG!C&}_&sC5f@%{~hLb_EMCjcr$g^Dl?X=(!MG}qMC zT+|lP2~_}*AA=XHetH}NTo5P+L|0H;giD}j$tkC@UWWBsPYc*1vzO6k zw`;MgPc)aLeDMVU0`W0`@o+nOuvCRa)8_j!-&AkDZEUFfr+*miUk6TpBYg|$8JVW6 zZOsSB1G9RbHghP(GVz9Yd6~Jy!N|-cPlC zn>>V8pO1rO3cHW(Tg|4S|8V^T760o0`PR?=4gck#vwLkrwuD)K*r#vt`-NYes*2G5 z8#i615GV>To;WuPer5zUW=!U%vwoPVYY@A27&T3}QS;Vz?f;R>}^3T5e-}#S!`Ed6< z4a2Y)Jjo4tbQ>g=`CNu2gxeN?f^tiECX@*T zNfo*vtT{?c28%};wv{me+t5_L8W;TzMdKEPSg(Yt-&#sx&k}dH!~_ybiOKi{U;NpB z?{EG~k_6;V$X)Bzn3A2H4NpCF=8Ke)nK5UYajH9pn=yR)21|1PUfIiuU{G7rq&)dJ zPHpq1w(rt7P9OZ>ORv6i@e}X+$nzim3sOwvAq#m3bdw<@GrAm5g0g_Hpoy+4c$I+SMdQp7LV7BnTwMFO#C%nUA&Ff*2lcjNc}@P|JCUGKbeJ}d?stn8v* zZmpzB*%8RD6m?EAd;q1&5=%xzWS(3?o;r|Ne`;osLFq59~K z38%6Lvja&EIp9!+;&-A?)R=uO|L#{lgx4(|Us9%^O?Ufw@9x@v}X)rWAet?eGw!vVsYy<-~knwlu9aHZF!#jcDps z(tLSe{u7_&0Q5*dyd0cJ@9;7>{2+bt+Uw{UnVMkFgG7^Kv#~mXjwvgc*ut$?<)!s0 zfuf>H^8{emOy@4mTEgQ1 zHbq@8`Ml1>w&EL9yT;zLI3Owgny;%!-_4<7YX#G_+{&DS{@EWE$H$A7>h`FX6SLCw z_f#sQ)~&N1B|`$;D6baoe@_!)sN&8JtuqQ%;^Y-c@#MGr;F13+?tYKJ0I!~9HnHF1 z?^cE<@G<~)mofo3otg=pHlWS8X4`y%*BTE#`2OMU9UB(57yv*R#t|TyJYxa}K%^X5 zumllVFjF)+0fHtW0bxN<5&&RnjUJo47c&pUbT&qV_{2tI3@8MlSg*uN-$W~vkuD~J z!5N?}>E&=X$R)Q}K|~`a#e|oHB^wrlk?Yl(l2H^a+@Ki0_)FjQSHJxqKlj$B-}<&^ zr!hbKCI@hSXUN&d^>pXXaDL}3B})lq%k5Yt0Mw0^1Ymi&CJ6#!?3wKu)0D?`o+h8h zJdSy_8t>m*efUE!J-EMIF2^7Hu@Alfecu-K0aB%p#+0ZodYLZI>dK}c`qN+aOa6yG^f82FCp5o zgm4^FRd>0J28A51T#SV9Y3X7lDm_B=EJ89zKSDIx?3&j!1>xdAp=><5m?Q^8IN?yg ze96N+2a3f?S(@ar>C&itscH3kHj}~YfRqXJ{co@C*dphN@#ZDmS-Z3k)%QwEz;gK( zTK8{fYID}Cr4i_OnI5&(m-rJ|fCFW|8quw-l?SEV?G|KJD7!-dVK1|tTV-TaiI}Fy zvjPa$#oG@+9K+T4Tb28aw}Z){v)EYn4H9)DY!c}{hZmq8t@HcVz_sl4H3rX>c@ zRx>qrrlom;Hw<3fWTb(GpaDaY*KcaA!?k$;>jz?ObPS=k`8p+LRpL2c z6>ez+5{!O9lBDq~e*NeF`9J+VIs4Pk+*z-tyHB0v?0NF@^TD&PSJT;9O37#(lBGne z-Vf!4JQ*h^Kr-xNiE-?k>+hPI(+->C?1YP3y@omgB{8yjV>SF2|pE{=xg7fAFV%<-0tH6E(XS0i-I6 z9e%s2bgRvUQPZj}V|wx5+r`9nrT$FN%-4h_hG>(N93b*` z^L4q(zM#39c3eKRXAhDE?q%VXJ7jQ4K#tVG$aq^M1t+FlR!9kQDAOnTT0TK*_)_^p z<0W3tc%6_kfh}!x>u{Hj;Z;gqlY%7Xg-abH)5PZ?6uCVg2dyJ-f&(Q_peWS6j?xZy zXLP$M4`EkN?_)N?O$0`Z_YGqic-i77#tKA-+fh<_^;Bx(MHh1zTWrhj z*{hu&{;1nG;Ku|fwu%#K;|T&1;c`3Ggk(!R9FlVlO~CBZAEzU(4cT48;DI{^w##|_ zn|=&%1F5l09gj8nrzZJi#uE*lR4aPeK;S9~Hc1EJL78 zGv>@^N(Jbo?g3r~-RQl+%K*D3GpIxK#vAU1kFME@P7_>D@G-=P{>k6})c^2HEHNz- zsk}ay7LW$OEu2MPRWKvI$Ky8Y$!G%aTSVz2npcD7w)+WLe8G^?4M^E^J+D%mg8zUU0kj% zFW3L}126oG_k61WxPvLC@34eM#MJGRO7(#~1Axei>}VI*wx(Kb zUce?X4DPf_Z3N>(1oaayNCeHo*RO}Ew)|)5R}Tra(weM z=K#t;HNQS2g`4EWSMTS$=Zi(6iB_8s++7Ikn6hz^D`%g=(af0FlZUsO-8PArxUnaVmWp*E@*x2s;V!@r!s%dl*^q+&-?nxNW{ zSZY)Xf7hIOnTU`>#WgR=A>5rVWhm#;+M^dmBae-%bM|QQ@?s#91wnBVzr2f0Luy zv?pzhjM~Db&P?XYXDrnBEzDA`&C3L;I6s!=HN&1xdg>JJ3%=&hoMgcD@qwUYj`V}@ z?Y|MTDZu)}UU=-4bBC}cb*^>oUL4QiS}T}NDU9zKoUXtz{z9knGT4{Tbp`bv#8_|| z9+rqH%IQ_|=i>)OGXb<|EEDn9GN0)=nk{*`Z+q~?vh1T+sEB!IA8V30LrMqz3~>v7O1f~sZ$ta=Rz0#+{t04OFNE|@_eWZ{xn z*(HxYT4JPP+Ykhb%B;nTkxu-PA&J@xiac;@1%@wsR2TqNE(Uql{ne&&3U%$$iF zrdUwC!-hrZ-98<&(F zj0HUC5Ui`!y|yOAPc|2P_c!o?H@ppjU=Ww!gz5rzB8s*XY)O19nn4l*)x$@}@nk;mCPGjWGS4s~zr)~Uu;WpMOOp{uT&mFgC8yPfn2b|BHAt?sqW*&Y=k zfhNvdpk39A#(js3??tE50|0h12^?Z!=AmXM=25R;x<#CBGE=_99ZPTxhUHp`H(;G# znAZU117)WyM|3eBImKBF%jE3plsj%^PDj(Fj zmzS&W`@TPkeIVP8*VUDbC3vO~E^0|Cdc8m3I__+Yl|8E)OppOCG%-^X z&{I9QD$kO=^vdNkPo13&WvQ1z z;WF6(O_2MLf-urFd3Il~bM}A=t2Oy@J^k1RU%gyS50>MHU%vb!U;5bu+?A5a9jVGs zpozSg0hEPF3FZLHG_f95Ureo?qFfN0ybOW~gB0-q2?yvBW2|VSgizQPU}Le8 z!`-8r%c?(j3UVMUdI&^G+~JT*hM*hca>SA)WrbBzc4-#Il9*$m6BIsuGc>wJd4@(P zQx|z!DSvca{i}PgZ!;OZ4oI0$Kl@*7&WE`ZF260Zn#uIZ;%|1XIh)Q z4BDI8hliyz53vb7HFIbpxW&jE!A3jWr_*(h+{YH}vhg)#?6Lh-J?ph9SCz;R-u05E zJ!@b3M}vdJ^NF9V`wD!>H~s{V84j7x)YgCXVG|*zur^!y>;a2VKa^X@uoEQaK^!lq zx^e6fu6~Lg+?KISV6Or_pFNpRKLuBkJNsZj`(%21*d<&xesBABA6vxZ?6~{gpSWjd z!uLT-7_Z{^A|~7CTxMUO=|ZitSqDh~S5h;;20PO(e}Xq8mM^{V?5BPjW2!8{T?)Uf z$#Ys{OA7<&*-`>T)nzv<#xj_Q!6;{jrP>KVX2m?Jw51FT3{C?CT`&p^AG)wkLvM1i zjx(yULYXLoKn@FqB2NS%tkurW7e=gvVwXWGSxl*EAQ8?`!lOc4n)G|V@mDVv>34kX zw|(+Ep1X5*arddS#gNRnSfpW*QtJCaDZ~4I{P|*-QwW7=z0S+a_40DOckl8i-uKbx zUwC)9pgc8P@gN2{0VP9rl44mpRO~@YZ4eA=igxEFKdT^Mk}?69g{=k1{hI1b&5$l3 z9NCIyD7P-$G%d-UR^AAcF}p~mbPi0J%KjI={!9Lg-~YE+=Zi7($%`~38}srk*h$%OBaTgV!A%dr}5fbTt9ibl8pg?PervxF2S@|Bo~_*H8Ii2hvf*vjK=U%w4rrb^^JkTwdU zLge?qr&1(EplsTPw8HP8W!n_}V>h}L(~rQBnbR&K6J^^nlern>Xyl;J95?`g^8@ZN zHfeIbF*0$eITDx^)l&$OTOwuDo5;*~bK}2>A@!m-Q z!N>^3xlEgSxR6MM&yAoe2oPCJjG49&ZXH<$hhQ}7*GJU$O*AdH(wpw?***M~7|+g! zJR(nk9By$hGgP-QG%}gN46(9_{NMi1uNoHV*Zrq|`0ej_?%6lpdHR|2VMurGo;~&S z8Nr-A>@|X;SuIDG-^#i_wT)W`J?as(~)_U(9BTexsd`)u@6oo15oN? z1FgHZK$F}1Vl;EEQA1S*gbhi@$Y@JwMCxWyScr-u@L;iCh)6_snk5x0l0Wx#U-TdR z_wWAnpYqmmasYCz7wK$Bi-nEJ7L%V1HW*XlM_yc4&paY|ak(z9R#>g4F?*i!YMobO zUQfOn^El?8_~`wA{ef5h%5VI9cxyb6L^HbLqrV7Kd5~g4Y}-0?T&k#s7_S7fE@E;d zQ*OM>=4RL|E60LL3kC$L3bh9#6RVRbcg7N=LczquSCBkQ*-?yR1OTy#wL>|hbPmyh zSc*50gPBWE=>u8CSSG>_IS~X+tlV>|WC*|XX_Q}j9^otb3*!=R2t3}T%&x8$EwaV; z3FgS1!`*y1voo|N|62UK9COaGWuhjLYe1S!W}9K*6XI@fpSYG=nIp-LW22K*%uLX=@&d4P#nuX@c6d}0Z}3o$ z0>_MHZV6Z3;>rXinXa*?(zO!rK~d6!g1Nnc_|c&!Q`&q(bsEE-aOobZPk${O+8cWd zyBtkbmyR!D+V5PZCEFKh8M7)5sAKFreM6k8jctdW!4rn%;|A~jJAdPyzvw^m>Fu8L z9RQl(XL3J(`mQAp6+I0mC}*TabR&Y4kTX(}#0;bm;Y7@4V~gu(^+aioId-|GylkQo zt&sxE$f7nBZ73`SSpG$A+Nyj<%pm01b1ptgL4RsaBKAec;`5y>niYfQ~w z{LbGt3~8}QLmGbdm;b@1pE*A}8^T^Dl10ux`Xe7+ucvX$>-F^D{_;nD{9BBPB&q-p zNiGR&mW)Jkcw96?qO4Z}5qmHZm@VRHC=5=j0fU@`(DbV$WMCg_6KFlytgt{ct+OeM zzz_!tE+zpPWZ9L8)$NR>gq&sgo^Se9|K5M|?Vs|A&%F8BJ8ypG{F%Fpr|z6Rb!Qlo zJ#%+)_bd&AO_RH8NES=K04d!4=Ijo+PuVY5)5UV6@S`7m<-u}#>E7~>|7Sm!1YAs{ z>SDLV*hD>+`cPf@NqI0cv}z_(c3gLrSwRAvB!YkOwpghPM||wBB!K0`6h_8DL9sx| zq3jgu(j=9|N>Azyk%rk{YdaEt5Y(HvuyXPMKpn<}$j z-!n2baj)mGj`r&A2yF8*s0u>4yiSS9?LbM>9bQ)*Y!t@+LjXGofKHw9*|SVsAk4CI z7v=EqA)M*acW?Lk=c4!%?Z?n9}SN2GU#6Sb*BvvkM`D1X){l7BkU3`TiE4F zwqlMS24+cI11;Hcks0uvZhahkGr1CTkv7MZz~0C$bdus4IL3+G>`7)^QR-9anA4I4 zu9bKLMaAD@3mz?s^gD?FdV)3DFzX73^<;D)>mnl7{XbpYKU z#(qIlpzk&}sLLDlx+i!H@c!@ld!PKS&wKWrpW@?q=eaj2=OJe^Ebc6nse$BBc$CqB zAq@hMoyi0#O9>R=QreL@G-hyU^9+Ka4FW~aCIFBK?xkrPARJ5vc=P~d6^7<7|Mjn1oDD`BQc6Qg$&#gH z_F13)o4)hAevgr6u{|{6zhd5C@rWg#xf0@efX9aenP6x2)<>wQ}O^IbX?8(_7aK_Z=T){G*Q@qK^n*Lcpq^ml&eH2G>ht=4(D z8qOB!{>6Go9Fm<4R+tD^ENBuS*Lw2RY8z@fVoJlxZjF*Q%+&xPU z_izdm&ZdD}SQTVf4jYyrVrop-K@w%LT9&3TmLVB*8SArAlWTHMG(p*wg?I^JC*%UE zfsBYr?#0oskMK+7m#6i2zxZLiQSjK4GOeHQWn|{SPWr~kuw!8w*y(TTvfT&-HB))t zjmyIYlLIVLvt+fy%V1L%sr|p`4*l<~pjW1M73=uC)jxX`=o^ce(3P{PD%+g2fS!@@ z`4P4P*L}2p+Jx?fo=DB?ox@+Z3|Hb1BeUDmdK^F7(^9)(?c-$veaarqK&G7Cs)T5$ z-lcr4aRo0ExUu2P!{bQDye@6}1Scbui7itRKRN^I&+cVs4&ivV0NPx?P{$yvWDFS} z{jypiZeF^>jh|eu{pcTUSGwOa9@Py_s)iF8mXhoyMJ4bEc#U4^FN6C|_%T5gX%E+P zk~VT}W0|5@#SElQ!L}AL&00s~a1CCjznY!;fI2*GQ8NIxoiJRbSvJq3BMtL1KvWlBz63ozGg?g9$;+v_}EJ1WiPk&&;MVH4r;DHAzvJ z9m0UDnwXk%E_X(e8=a#DEA#p-oh{xj$`08`PzK3jWs`^ukipbAVJ3**^?Sce?w|Kt z{?aGC^{G#M%Ts4Vdg{*b)SbnUY%mUk$yMu25ZpCo&)L_pWNAJ9$Om5jw%_;*0Vt@u zERd3nsjPW(6{?H0M7Y~v^%RiGnm1JEGEC+379F9f5CEƛ$)y;9U}gca+X!i}zg z%Eh?kgi9_skb|ayvLg%bawrQqYf?}!0p0|6?24V`Yx(HDa(|GgSEuo#>o>^4rNHA$%1HWyU-S7um18Z?{{pk- zHpf)*wvV(TippP2$(iek_+~uS7@0nO>1{v=qe{)FE5`OZCIDf@XESrb1A0#%e_OM@ zrm${L5o$^1WR;g;$284F@Xa!7-y?L9drT@meE34f8p~1TqTU#{)pz$)OH`=Z0Jq9wGQ*J$bNqo3MZZMHJ zdHPXD@vC0PiSPQ>aj2_4!9zgOXMN>w+!qgkyDN8gm-F;wN-rV$aFZgtfnH$m;`mu? z%BQ_Jp2OCHroFiC3Z|>srG2=9`#2=752NYjH^+voJDln?)@B3jXo!ae(KsDGPJ~PU z?f1AD&ULV1EEB$aH%@PGG~4;_kDQ05ISp#PZaMw!)9>YFPzJSLXX%wS)N#9iMScI; z>m~C_T&stEoIu+4AN1JbBk%d=Z~2*@@!U`U={`*~TeuU8@BhEP`RwUCW)>R{5%BDC z_v{Z|eemV~_wP$pMJ?-9ad$Loz;n_w1?jA&9df**N9P z)g;%H{lRivuJcRxR^RcpUr?l$i>Wpv7g@afmiv;Bn1XW&dW&>0fvO9glF^-Jl#1zm z0&<mF_=ddT1RprvDnKPDQJQwfds3mjyo!81F7_*DTp5V=vsSzbs8@{zdEho^U8~O z1L5%{Wj4@M;3~!dbd9f_t;$^My79ZKL9?FKR-XG9nLYt(_iloQwuhIArH|!s4i?eY zw1$>X6B#Xl+v8<`xp;DUGnP?_Zx-HkK{;`d@N$)dU<{#w{8s}8X+ zt&qprqJz(PZO-KSIQbq|?u&Fnf|GcbEA!dF?JP|)pxVFO4ySW6*ikCSNtRm?Q+-x3 z`0r9M6&pm%77np9{UWBQ)V7FeGx4ds3^0f5<(<}28t<%|)P}P)^gW?hK0fi%zxmg1 z`oy=cKH*IlPv0HR7Q@-_DL?zuXtCl4LjB?|$vsc``~JWm8`smgTwUJ3ymWI62`u_&8hR28P+i>!%=AxK#UXd54b5v=T8P(_A>Ge7-AcBcQ_y_I|NM* z2XZVXD!hy)Y06p$n=>EW@*Bu9?ne^?fjo<-mfvo&UW_e9Da+i@gVkUFl55L=O=6auw>sWrBM)R>UpLv3Z z#t;74Z~4q$_tmgqEcU0_2)l8a3#{{2ZBnd%i7`)E0Tb+;E1T6BgnD_rV5Hu<(;t%+>wkqgFrI5vr#Z9 z(Ot=;Y$Q^WyD>po2ARcA2#S60
v2*^?F-xJAuOwU+wXe!F8|q+NTNPKBDo(o6x& zk@4&MFNrX@-&p3*By206-B-Db{*GHXu|8iLookDEY4Em!$A;&esed{|vc{p3xkcJ_pWR~KWtQIIh%r(7`GTG&MT2JshPrv!;aWzisaa^w7 z^wwwp>VNmUUU~kNul`HlR8}z6oqhLpn`qexGnp1k7A`}84GB{QSsgA3l6pBQl{m#T zg-qs<=#&v4l#-z=Wuh34J;)$ys@E7Zm@x!A$vX8Ri_XtlVv!JlSOx<~j=JKeOdo(C zT2$uR)}Bs6^jnp53W`8d&?pfhP$)47Rs)TrA>?w=n*6N#aAbxs1u3~Bp1dS*2|-S< z$(&tdnJAkP9#sW^2)cv-XhyM;JP^VGumeLF`%nXv!9;hDlnUdX45cJ@3}D7tG66X| zjhGF?@i-Hp5Ax)0B=M*_M}XJ9jn3?vq{Iy32LA*GA+LR z?Ib4A%w!_xxwTkrFg1NqQUxYR-Abj#%Md_uE)$7`A|;wS5Up-YJJ^dOy43N>hc@Y+ zQmPVAao>%QrSn-tIC|}Y`<8ewVTBa#L~ft*lxJ+kD~BCbvHOgtWOxXtYo%9Wmn+1~ z4v?E=o>LxG0Jk`ra=6LMpk^|+rFyTen(4Lwo{^a&<8bS$1DV`lE^*Tt$PKBPz@s2& zcv@F|f`^6#U=JCw%TV-WN-sb9a1$Q80d+%1A73)*&u%3>4LkXtx`Jsh`rP`ITY~dZ z;St>CR66FgWH%}yfrn;E_K+_Rm*H{T`U<9!)k)mVF=H7khWGOA$n6ZbFWls1T4I)Q zX+JN6j+%+|Jx9~WcO9qQbwk&%UE?|$X^ zSH9-mg__BoC*TtBumw8NygOR~&+v5heliyZ`{|JV6i?LRo+y10i_1x7@=VCjur& z3}Xx_{I&{T{wsg?o8SD@o1VQJTYVFdv|Ns_yn6BD@B8p~|Bc@*sS*YP2zj~Ooi4!2 zRIAK8YJw^NNfxV`D82#V{x)QYD#5jBEJHI@VZxk|D!X0ICU_=`DGgxq!bXThN>Wy$ z+-VH&DjEz~Bs~{H7!ZZK8B8u?^6X}eHAHYpl?_aUD;6<@_ur_^vY^Q@RDsMygtr(M z2bSe$wl)fJwbw)W*&y8E&|(f*S3j7*2}q$p>%;GJnqZ!EKw z0JVfI5}PE*&G3jAnJdA{_nnINo*Ft{2Ah?Gef7MDj={{x#Mv#vLPrlz#BRHNh$%|z zp*Lh_&=EAZ#m+pzqrnlkGJDZWxZU1zJbok1(YgF}ZB@#byDf%!>+pn9HC(>{r&|4N z;cD)q4NRxv$b+6#2Ui#AI>`#yaS%5^VL;;YeyF=W9HLqdb0sIC&!o@%%1h!Y_ZrKj zJsVvYY-PU1&J^F48_s3+Tw5!64=>YS)zdM%;da=yxxTp9Xwgsbdd8=G&S$*o?Vs@M zCx62E-SfrySy~LkA`Qb}vHf%G68Wt`A5#C?%j2TO#DY{x^KXb8mj?vp(ym+&N#|IbWQg4=I^vAICh7<8rzFly^M$ zoBqdd{?|YHp}+a=uPZ}o&?_*(%ap1Jfo>`h31U9NC}oVhBCRB-AH~_rRW#{j$eD2{ zg&&!E7D$Q8BvDLS5|l-1AhVQA?pDYeN|ZcOB3Kd$q9_eyF1(D4kjzpc#7RKKqG+(@ zJ|?1LcOnb7G6R@FBPDC)aW7kCr)^#)2sA4jJ+&6&a)P0+6t6K3xs*{2WQxg*kFIsr zGUt{1HBS&kN{XlFu}*7CLc zgFHRIUj3VU_wWYAV^7N14xhq~k!hzQ&`*`gJ-=kRip@fbn`irQ0tpb7N)zgb`B>^F z?=;~)KmgrNOjQ)OE)n6{=&rz(`qLq3lkSP9D~B$GPo+K?W=R(X;{oM0&X&T5&=Dfn zpsTjZd!+o{F*WTFKyMX-hofe0R(prARR(Ssi90b@V2fl4c-{B9KebgkS7LtfLyXL3 z&79I2Uvsqje8SGWk>G-b&(`B^Wp;f1s*dRU)9;*X>04lLGGG=$JsdDqZ7^ijEI>=1 zdoMhWQ&uot8HpR+dM{35H%_>jZs;JU8yfe@!w%u=_oM=&&E&VH{wJdhzBj#m7adPc z?zokCNU&p_qDD_-pl)C+vjyv>1e=lj4^^nqV6I$LAJQjHCPkZNcpZZD9z4Mb7ch47h&d%@N85SumhQ(q?Lt?TaNExHWQXn## z3;}~6Kyr7=;qsi7J?HF;!KY~$2R|EZNW)?{yK}aDaCv_B?ELQ8xBR;Q=_5b+(XaoW zZ^^FWRwfszmh0|IUfL$JE5%x%+O3KPUD+DM=;M+*3DMG!JWtMguKcq2jVX)_YJiq# zi2!4f&&=5XOhWBvk`N{|F`ogdM z`9JS7e)i9L=biJjyLT6N@0<-o81yihuk~tNt;e@K_w>Dc%b)e>@A%KZ>YKj(PyN^i-V)c%N&)_O58%>iM#X}NG#(LIBB@6B& z))|l{7lL6)g#`f>CNNXI&|OR>mzdzLV$1?})o=H8om9}=$)K_~wuN>?LSixlEOyZz z$|4{Nm6tJON9ycl1~SVH#XB-l>Wrm2>$)+xAh<(Wa3FY@NqR8lRrblX&do-qn#Opc zV7%GL6wVBaI;RK>Mxj_yg;`N;d_X5ci%Ny&W=RfIno3#9z$jn4-_O&_)B0b&{4(CS zcW*z>_uBSVN?3Mc zYTDx?HkN770qr@Sx}NP6=qk?I5jM-f)|FdUDoKx*Q=18T3dXy=pV5`MpEK>U5`k0L zncL-Ju0;PVH$Zv&y@^oI%aqT8<)My0{`P}!GBPu2vJ)wuo8cjhWwvl@?93^%lv~9Y ze9fP^d6_>hAqV!kmB9hix;WcF;W-|TZHF+5TS1e`1j)t(SFl^S+!?(6C{!9PtiEMvt@s4->tk3+iFEE0@`oU!y z(y&OwVo1ZVI2%$jCL7FBBF&N!)l3F}u{b`Vb__jAa`|MLsKXrEZ?zvI1bJSw6^RvZz9LM#vTCOke zp5MK5c6qt_MZfSfzUDXo$v^przZwYIKmZ{JgkWIGf+>$ur98_DRdut{Orl1YhhmqCKaID`d*ObNkEC<~;!K$E)$B7=ew z;VNEfjL5)1lK{$rM3Xoa6PK7b5-bbN3Mr%V>Oc&sc_C_cI?QkY8H5PXs=p0Fh&ByT zS#Tl4*BO&^f10MO(Us++Yf@dcbPQR4zJlu05&YI}HOQknf0FH0dQ)d}`; zR9hG!LW)-juWp0a?iYD_alLxaORpUxGY?O%Azmk>Oph>fDJ`+vT&bMx6TP2pU z>T5k&!~dVXca7C%$;!i?XI1rk+55YlGjrxJGk_sPgAH*anTg2+WNdShk_cJ+2(}b3 ziWqDS0pm!)k;uhHDA);39Eb$+C5d9mxhP8F{7EDfgp7l1C$NPpw!Sa?n(XSOB9uwVOnz z8P$KBsG~#Q$+&AKBh~+NFxOlSA2-XIRmrp`30vpdtjCCXFh~IyhYT^Jdj04H&t6->oA+ng|;HI|tYF@cktMcZ?SqGov+RCVJllNlVQW`;e1=X;)k zSs`+dw-Y|{1K;(Pzvsh|7O=QnlQ1AJ7kSY%O^UUzQ?hKNSp>&5(qa-2j7+G)u87?V zca&6$%S*`xo>jctw8<>x)HRlJ%1zEqGUIZ+`04NX*MIP@{<)z_MMw}>1I>(d7!AH2 zk~;4v-iVp4AeoumrJ!1jRtXhVg@^>=28}g{Z`_GsjMc9zQv-5G()X-P9h_WFg_AnlEf`xUAJ4hv}xN?Gf zn8Uas(IQq0J?4m_Q6*+dkO8EM6t~Yl60idxT}Y5eq?M}Wm|zU|C42(>VaFXKi z#CQFe*h}VGeS=DUJ?;q4BeA!CfntfJKB}%-c_-bt+6Z-B2idAGRjQ?LOwj_O8Dzp` z1CKaBf`?fR>zO2iR%HM3`WY!Cxzx$8y!&js`9d->Zx!Alq)fakvNAHm&M_l1sC0)C zBN+rmX`>8vbMzaH#26A0g2q$a!F<{XPt_bIV*S=`2u5Q+k9t_qoDVdq2WXTA~h%lL6Q{ty0}zxDA?{^&3MXI@|?^_?;ppPrq8 z3kd`ym&*+V;R;Ts>NrK)(J`fDOKjvSG3fx&`z~3XFzLgo!96yM9pJFkx03OkcvsgV|nbV*+%eNcP3*E`YVfYGd=BMaWzHo8Zf6C3WpHD z#8RAzh&a+!Y*ynM0U*Nus=$lTL9P47-KB^l)5*hYY2&3giisksJT?MCA`{7K8C46H zP@C=WnH~V*(bhe~;1jPcY7RjWsX3PAhF^R6eB1urPk$Eg0=(@>8IGuv$h)+GemuQ&OZJn6vg@K{bO+O9QR=JPK z{zJOjl)Tu}O|?t+kX5_a9xpTWj3MVCoo=X@Y8RAL&17ziV##_H#&l)1UVaYp8dnap$JV0(U{*~<~cGk+dx=AM^%tD!!u z4ECIr0T=UCx?#2kvW2MjAiZ+=aL|p8*6TZ zlNUOf;a1ry4oRmYs3WfdFJJ}3J#VJx#vu=;lQ{E~akdE#2lt&Cyv#Z=4H)kq+sn-I zGUzpdULTpdGNB*ZW~rIs^zokY&ga0a#JR`Y2LIgme|P9gie;ilheFi9{)tb&@|7R# z&18(mWK3iz{sfFn1j6dW1$j&c3=vBs+?P6KE2((bwE&5fX=WDdA{W1We3=)?%<>}t z*f)LOzw_7rLNxLSDqKa*+Ym94t{IvpH_4toy{18K5&H!| za4{Bq3BewrE{#=cAe5&%)1fQ~r!W=);41J0is7R~u!OS`5!R73gm+Bsp`<9_`#<)R z?|=Wx?|tu+m!4ccdUW~V!RlhYT(6q-#UdGVPMI)cO6=O!%m``?DFT(K>q;s9rB|Ep z_*eep|L(u|L5bcugHup-*kX_=VJu@o#fa4)My^^Pgq0HqMn(`2F+Y1HFb)ix3>nq? zGzAYbEXl+Onqnz@ni!;AJb@bCg(|XSk}R9MiYo%jFd008(FoSZ<#nh|f4~eB&HzH08@?tG@#e2GD9wmm$754nO&5~4m7Z+SA>Kx{bG0_p{NFq=@iy6-YKH| z;>Js?gC)k95|9&9w1(cuC6E9kB{`9pN_Ef!1OgzhI|pFV5bLB`0E)u}L1qNlxyvQ$ zx|Gj%oB#b&pTWBWZ+lW^f_VAdWn|c=Yo3w78Im07*naREcX_W0{b@+j6Q@teVi>VxlznQVBfF%k)|M zn)ir9J2l**mm&>Y9Q+mX9G^#G1ZMF4oyWBu~Q*por94@As! zfG_63G&YtwDdlSUr;KH052XpQa+rUarD>+NHd|f>z@ZQ`zyXsP>`^nAV`s)Y@A1Wj z-~F-gKJ;$Ka`IFE;Md>(+rKg;retA70)bFi&=@6`0R~ARcIX%of)a8#jhI0aA(Cd+ zEb{VV`P!#nyLh;6nv_x^z%PH_kAL&We&pwVI)p*628t=Jf~%1LdFd<5@E(c=PeNvv zyp-5eTe3g_tM#IEU1>XlF^}11d<~>gaC(|%%!mUl*=ixSS=?C zbkzBhhm_lLGysHe{{4UA8@}P6dfzKg-uJ%8j~`#Imd)km>TrZuA=FO7th?CNTBebc`5_~I+R{Jlk?#441$`Wtf=IZ;;wh94G)4i-WG=-Uu)dEC<6eZJ2`tS#;(@4tlK>z$ zK=8Vd6F?NGlfTh*fAbUX7Dfgyrn$Z8sO=y+J2}7I@WVQ6HIEgpJ5ggMQvXNJfalVA;JOd ze*Ow3rQf3wv4TD=fc>bW;k@#aXg<6_k6U`wmySY z8`bc5h4=4hmXR4_wt1ZMOb+2lyQMwnGdT}i1>}6#9o6Uj`O>={$GJ20qxIhY>CT;& zt@-r%n3pjk&YR80MSYGYI8>kB(R|q1f5KxA~iY?;lu7h&8d3 zSxzaZl(QupJen-0lr80ilwvX(-bpE?l#D5*oSNlgxn4HQrdchP>t$-Pk<0aRb+KHo z7Z(rK>j&$LhwJxz$xHv<@Bdiw;9-JqRIfEFHmdw5yp*z4hT&cZk(5$ulF?js!RnwM zQnKZ0(JXR=n<4wDvk?(ihPCIg`k2%!YWo`%C=%pySMdVCUBx?hm7-FLyL<7{l~Rfq zFJ8K?l+MGjrRz%Dm9CVp|N7tY$}5kbJbAEQFE1`uPo7*pd3^cs;l;(p>cQpe;iHQO z57w`~`towwtk%oba&fUaWX-yWmCca?vGsIp|*ZNOBKfFbQ?FB?V}84r2hM8dbIzSydDxtb!sK4=vRv zos0y-oMU*>DbE;DM5b(JuuL;rrV*CR2n!l3 zkw8jjq$S3sjAZ0VB#|;11~Q;UB_lI>DNV+^7ZBt~aa9``*T%K+a^vN?_;v9Dm3{@R zWLzdTiHpQl<|=cUxNfXTTxH9~Wwur3DqAungON?*GFy|XoKD0#+eMQeEYgD}ubQ;T zY!Y)qGRBN~l~QAt4Mr5{^{)GcfBY%DoA9=`l-VauNZ20JL2mW0=_xi!_7)=JoZE8P z=Vu)AD#l0D%(h%12-zqtzW7>vSmVfvi9oQ^HZqpk(>(dSAJa}kr;KH`J4g014 zJ~;oM9J*T%rky*zQ)8Lu!`mm;@2Ok^2;9;lrW&w3Z&aE({%)_xTP^Wm)uz}>}3d|e;S5!T$$o3z^*qFMg!l_WG1(DM3ETx zt9!ij;Q)4D>F|dC(J%elSA5;qSc>U2Vis#ggXC^8-*$(p&rp$Q1|TOhHjq?G*LEpc z&Z%oVZ`=4VB%r<7KoGDlYcqy3%b)y)kN(&%{zU!(i3|8C;e$OBNvmgHOS3iFI@ZxfHaj{yj7R%Lgxmv7NizZvskY;v|HlYrC! zxO#Sdv+3H+rfKqGQ5H>p{P@9U(>;6o;DaA{xd`xxU*mvCFf4c&PD~+QMtgd}O4411 zSf0s2BIa{dto4+PncTS*O)*J{krn;p+!en72&N=24C@#p!zqYKazxaaFoFsPs0n}m7 zkIdmhFfXlpCol3aklA?H$e@!GY!Y+y3o-#_Owq7_V3!gxgqDu|#beqmfXpP>y8F(z zetLDclYp*NV#t8f>Bjx3cmK<;e**6^oK|t(TKw?O{PmjRZiwIM|9GeE{D<&}k1#^* zTO?|6yHB8YGXm&atU^{Qq_V1wOm7}DvX=?VI;N0=+sa^6&(T%LA=~cNkKni(F~+{) zHSCc_zq?0Osr#xG#nr~w+})$mS-_rl+CQP~r6-k~ZjOyRMu)U_mLD*jd8c6i?Er^o z1*6RFC#=xNm}VY7($W6e%G?TP%#`Nw=3C92%F^QwX|;Qthrj<{|EG}%%}S10>lgsO z_u12TY3)P8;8sFnwqH#PxJ`kRjy2uv6Mo>Xao}pVslp3k@6*AXg_9RLnW5rLQ5It1 zR$boaw1so|Lh2MnA0iNtu% zTe;m=e`_x@&d(j?WXEa-m>SLiTicnF@8KjPbAD0x_`>4Df8-BO_DqHq?ON+eNI9h@ zvB{~)Ier(JO_P_4<$6_*rqnE&)#d8)@q>$p7pn&s%k?TRGLzBFQc5{7r<_w1(k${~ zwYYe&UaS_Y^>Tf=ym+u)U#^y`#j9WT%D?@QkCqxZrH4z!yV7qJFX}lH0Hi{p1|Udv zp=)56XT%<}IFORqn`%^mMBO>udw&Im$w^&zTkge+O7Y?jptwr$u5?`(!nAGImC}`V z)3sgKcIEnJ)3xC~)s^CHS3dZG_pDco%gfcpdbwIJm#f8c(KJnOf<#_ku9_yLY)qMC zEQPgPEf-D7DK(3xX;QOjR?Ft%V)@cb58m_ggYWtyKT|DbihG|Z>h}ol?nO1B!DJ?8 zYf?C8(PBe8bG^RH4-z91W(YB31`;WO45N-Pfg!;}5|&amnW={o4VKIdmS|)$8=KFh z9M&$$0QW!$zl@f{7KS7&n;BD1iLt-_rYLH(CyKR9k}0RUAN}QWwVgNYj5D)hqt|@3{VM|njYDU zvZrHey7DjEvouN!=^c)tKX;tybrIExYtJiGV;O`n6;YeABnd1GC(zfnQBz1nWbJ&u4XC2rsk_X=4EVqmF>}}*sQ(G za5{G@GfcTos!(U*w#G7hxCIL^#-XRN-M?+V!1x>BTy5uBo6$=-#K`RIKb!hFf-~Fj zn`t$}TYZJT#}^iQ-mo&`c7J@c?%azeGbDtaAz>_a1{^X}*+=x?-WB#p1Tec z0YCdFZUvofbuzj1&Y{DFoGky$3@5Youmj}V95^*;IgRltw>mL!EBlwgIP=LXKA*8n zEqwQ^G#BxA8XczI0uKfK-A_!y(_s6?rXgm~D*#n{sn^`Gn!#v@oD7RcI)olvzp=lDnrS2@Sx~ zA}=zeDCg?g^`n;`rkvjUftQ0BfKX5)p+t@30_1&PQy8eZ%qUO=Ug>~D$V(vzBLyh| zB9sUytTZq%GKxU4q45y{BN}YwfILho0J@yCFvXy`0F>eY*y~aRSSK>EugJBDZc8)r z4}IsK{_uytdc9t*){EtOwOTgKqRHVJW)_}4xoHT2pgiS;L4dN)8sYGxYNvQr3Mijy5ofP&pG&*D&g8o|Out<|- zvX@>AN0DqOVpyeSptp+&%Niuor9>38MtZ$Dgqdy;3=&mPh+t$o_tw=GT^A2~78eAW z!K(2KjJ#-4W3)c9Od^d)Bt$_Av)ZRr&x)bh-6C?1k*fp+%_k9Ls;{PXZMu4CQdi#n ze^0Of{%7w=nYS~dZvY`_b`(GowCQ{}gvVGQ4;e^cQV9FmZ(PEN=)96VB%C0@v7O=7 zRp?v;FGI))*3u{-6jd8_`~DZ0Dn^G?r6VfT1QT6;tR{~yI;jKaco|?!&5X(%r+Yau zRBxO(S!c~TG2t*TGtM)?owGChc=JjgV_;#nFKEoyOf-r^{L2KBH+aq|cZb=gK8xWt z@4ANP@-maD$UWXtsOo&v7dPQ1CL&>8=1sfJ433mDAUKXVP=>5*cqG}H&3>yBlbu4 zcpD(7{C&EAA}Yt5zw+~c`*(iJ*PD?q)hbZgky$lbhm<^L6>cZHSqd@^o2sc3&AQ|9M3Pl=4rwB%Tw65!d z01-sQ=Z0bRB>?OzMiQO$>Z>oU*UO8G<#M@bnw*;^r_}%ECfj>ndO+5(+-ND8cb$;r z%1vIOt_yUrUT!wsvu8I=lh*6ym%RG8>k5N*iF=Zh;m4$S>w?o53z(A;J0~*0j4n|! zQn4O(C1h&EU&gB-DZzT5CI%N%2m82P(CB?kUkU&TB$M~+XQyoj8Kp|@NP`m-QDVI$ zz!F?YKvD2ALR6A*xp=2BQ)8yNP$#dnkb%Kv=QQv#GQ!;w7vd%-NNP*vWl~rr%L#XN zKv#JgU3X`1( z0cahRXvnk<<+}5yUH8BK=BM$l#5-hk8QymYQTCJyBWae|dpOf2Nd-b^uCVJHoJ#RP^iLJ!% z`4k<9fIHs5%{-Jdx}tWRjDF$=f8RHK?7sr`DmiQp=ZlUczj3xoVb8Zc?*onnjZrITDsr z%6YL`JbL+Iv&at~U;Nm&{ZLoDMnsS+lEvLUTyN^P;9i!JQA@CgC9@`1r=?`|KajJf zB%0N{0N|nkf-Xtu913S2S1G0IyenQq_I9P~yzR=*{o;@Qp5Of=ZQE@&ZP%94mCdGW zkZGEzhs2cBLySU4;JSC=#$2 zhr1%IiM!mrKIRDd;!sdYR{g>dtnx&1_Hw?;6*B-eYHq}8VZ{Aq{Du=#bxN`-oGkTk z$HIvzJdB2DzV&n?NYoaW%!t7$iNx4tiP;QfI6cL`AvKnbxt>B}ACNfFLN2Dd(mNm+ zWptqsB|`iHt*V(!kvdnjHzOM3GFc`wnT`){QMA zJaFdYWqPvA4;Cq0oE|OnE9=F>MQRd~VdLem{OYgc-HLaDrOYtck4X*gGBU!J^#GzMfjKyXm0>&`jVcFXDP?VRu?M`)DcTj)^dp(er03<}WXriKFdUfCAoY0a4$ z?OTZb&%(o_m^m7^cI2k!9{3SP=1y?@Tk+7+&ct0C&hX4{(f4>OpfGxH!8q%rvoF;q zd&ZkS`r0t;kHl=B(QaRw3Y(DEgN4p1SrQ%hxChg%jAdpquJa+0Uyt6!x0j$f4m`g} z&bKty?^tXe0Y_FOC$VEoVrOx89C^OdOrKAoUgFzWL0N#83zc^a{!7*&p;~!l+}6D?VaRw&pYYID=@rCt}{|*wL-} z_VUX=`#1lYf9V^|YJ;P}fn@!XH69(sgWp)LSME-CcLxe&v7QfEXVodo-E+y7n36&6 z?u%v9-n7j!H;a7v@M3e*{>V4}(I5YXpE0U<41gXnVF|@I;7%~r`HDa@M3W~-$-$gc z=MKRjRvJ@vhM(xAST(dj?Y9Cb7va^mgM_2h56BLuljfjMdb=2G+Lka9!T)U9Legk* z6!!>{?8O1Pt7&r1@f<+_!eY6MWvx=Ol#?Zc8Kj&ONm3+XhMe77ODS1r$#PDMCaso> z^?LEa_rLV(zwwz@-utL4fE12JMwq*q5pEDbryJyCqCf%0u#|y7SzW!lMgo?QNUEP| zkwMgmg!dy6jq!|Pv$LAoj}S2Y7f~pIjJq7Cuj!3WrE2Nyq{rvuJvcAPatdvgOSe(Nu)}!zLyVvQfagHi>E9{IvXYn#ZKTHjk8J94|u%*FdM%z7; zoP%)M^0agco&FRBGp3McD|3YR znLTUvirg80wuDRIcyO7~o)f%GU*`7VL_Z%*o}SM#^FyT*ug|dPxva9E50L(ui00*xn)*S3JOi$m_KZb1k`AHa|5MNak3L!7$*p@`XAXhOaz$J^AY!cPWgv%dggPFw+%v_ z+&7##9#&=+>V?^UVpgIY!$dZo!n6|~sO?}oqoZYy?|q!}IKedGuwDd5xQ`=tF2~L~ zLZ-+;J9@4Y$1(&MFYP4dTIq~a*~X}uIT{fUBgXMtaW|DEIVD$lim7=KW0`5r-I`DM zm*MDv;4-)4Wd@!_y$wL!4*)}y830pi1_#&~9D7msM9dclB*L|?E1jhB|L*_z*Z$e> z_`L*42*w0bKs7B$MnVQB=uXm{RXznHQ#{xNW~CxeN^&nLJcz7WHbJdyuG_LGi)C~1 zaJ{){|J`r-V?X+{KM8pdF&$(PV>qJ0qMsjN-0vcks*I!oiFuJXSJwuZ!2n|f6%j1> zi2jxZNfrc*BM}y>Nr$Ng7#)mN?=~|=tdPy7jmdTK)?EgRs104~uHi#&%$b;TG9v&I zB#rF4QiLoA8xXW4DK8d~5Y_ANEKDveC1V#ApfR4{OHVGB%jV{$C6H1L6JstTQZmsg zxv|=f#GK5i;)H0VJ7CoUg`mqg$YdfIupoQ5jrgkw@hgRC4**&X?#peU-voD}E;!9T ztc=`|$kGRwNhy}*u2a24xr?L=VYtHt_iebDevxeEx}OEiV9Wzt48A5AUF<1rW>b+R z^WsP}rxMT!sHQYB^Uehoz$4fLEe>4QyL1N;L z1Z<_))TI=Foz&$kFB45y4xo7KA}Ii3%n>WwcYUhxThGJJ!l4PtJhl(Nr&{(C z*n3vD*h9gt5;~n!bzUh1a@;wuDK0>8- znX738hF*S7cOOoi8B(ZHFlS}1l<`RqCXOhWBl2pNL#c+E|M}mzm+fyKxM(8|VvHkH{#Xo!fr2HIK$=4dAVJUw z58DKQK-gP#hXR;k~cSLwO+2)iwBP{+#Mc{ zOYt>W&f)u!1PydyRMml$6TBF~nC%u{ESsPPEIKB`J%u$ytmJ?}?y3vO#yy0vl>o$4 zy6Ze14q>cLNw~V&kT6SLq&}w{atV;vPT(#GUFXAsO#n(Cr)H5Y=q`B;5GPeWf;9II zmVm-2KbQf6X2BON7R_RjZ#IS1?goi54#|k7Nd}n9jEPPXB#S%3nkMA005Ps8DG!q} zg>sS5KP-VU=`cbfgS-msmH2grbqs22U{ESXsHbTv7gHrF0g$r#N?od^H&*3m=^{>W zSjtpCrdUd;vOgKYztla_z{t@1MW7G@88DL60tX7vMOYcB0KG32 z%xH$3EgMs^CRqZ4AG0_bbPQoawT~d8*)n{r2m@FnXEWkw!`UROk-oy*2G&W~bgAgG zH=Dom_kO3o7Z3mdAOJ~3K~w{84-7r<3ygOHDFamTN%jf!Q`6cTKvt4vB9FKFLAu?9 zYMi%P&7cS_Dn@zH2)PoG;36dmTVojx#xhaEae?gF*>mWX=f-V$8DJ)T2*bmiB-C5S zjBoAGT@E$eHe-%W7A`mi#yOMchS@T2Yb-PSd?ppxEp5un^hPw#$;(W30w_X1yV=U{ z5j6ws8O{vn297+vqm5;YLu$i4-j;}wP8~-%At02pI#& zEDtQ#!EppL{k&D&BOKBEex9OJa<-^C9fAZUY=uiDm z|B;{$Vh~GE=haLGPyvz=07)=qLo%w=Nv{z?ScwpTA?6<86(uhoruzZ}+-y2PQGz4p zCz87)BhBpQriH>rMx|7G<@$O9pqeF$02L#E;0jhp>J#qXwx#XbQoL=uQc5WvJclH~ zN^!v49S8%Ky4+Ibc4$DQ*2dB$Ld>*nAyMeb;K>*_ODO!IjOHj*am%Utok0TSOmG)y zj6oDWS|mG)aI06X0?Kwpq6H5UL$!rhRQ0k9f+qE3w-4Mp2!K#;K7&#-B+}JKqRB*2 zYD`^lD_*Ty;%FjOnV?{87*S?O2prk$L<8_Jx7o;&sq<(n1Yl&Wngs}OvPhkKE4|T{ zP7xPb5}L%EENo$tu`wnhnFYOKhDw@6>m(F(3{mAX;h+%&D*s|kgptdIt#dMhsEao> z#^c;iaRVAp@ z3;fSVL`tYLYlV0 z`M%Dh#f>mAmI)jz_9)J~;4!=zybOSS?&JA5myLaR8^d`)FGvLWK`zgi&71(jBC^J4q*LjHRSObBbo}vLq%EEx$ig2~;JF(o#u z{Ot8-%_4i-kunKD>ENz@>MSo3adXoqvRK3fK{9%A$tk#dDPFodvq=_ld<9^$=@jcI zM>HFNU@0zD`7iaIQT+VppKjVVmS^g^uIozIb*1y-UMe{NcXwX^3SEovQex?;pml)Yq@KJ^Td$?wE+$-` zdVed4DXB!L8dxG?*l|^+CU_jc%!<2Zs)Ge16Tyf2<}RZQMRL-~Sfi@AHoXiCLzt8- zndLM%k&#yWMsy(AFb9T1xza>n*vJ58#+a`g8?rIRa!bo(Mi|)`6WAuv@J6vuU*D55 z?+8+67#|t53wjNmK+nk7cnRv=ldB56Bbv7bo`ouKBo?8F9>Kp!rxF2ZVQ@GrHRFOa zNLWTp8Ps}KRm^}=%O(h_>VG;%ue%-2;br#IIQ4MGliDhaN56x9GgkN}df{-zle`Q@ zmDx9$!QMTNF}|;TJU1^hU{aG8#33%`_+6Z%-(&Cj*PZ8`JBvXtKbYY;NfQ9m)_4SG z&Tw||oV?7*TfWELp?)&N3Cac&=`|WLX8ZS$VVrY}SxtQ>^Pw|GnC-HY^Y%?+_A!48 z&t)w0CSg2iJs;$$?jdcy6D%+-A6tjrvHFwNo!&$5%@xr}8do0F>? z-<a{akm9pH zL4&K#FcD4ONdpCr5Y!A*ZD(Zkpl-;R$fj&wtXVW9!wMxg*|Oy(H;a7v=;Ft|{fGap z|Nh5;-tGX9RER~B3d2$Nkee2oP4FovCk|GG2NQ5a(gqR5?<@;O>$faF@Hf&aZFUoKi9x z-3pCLq$Nv+aMQnkHFtSEX`bML_s54fZl%Vt5!qeSDeVYs?^7HWf!A z1SGHs*4xpP8p0-8KM*mTWyy*cpo%!L-kK9G6;}szQVVQcIS4E=>}2AXz1vdA+bG0Y zM-l?=%EZPUz#>^98$%~_atIeqT97$gtc~4}i&(tLD#=r*RamN9Gw z`+r7j=17&{u0s2!;e1{Om~I|M3>$SL67lP!H0Jcb!Qf>(49@W~80krq@$+s?X5OBd z5Hyn~a9f;uo6m`fUOu0vzl$^GBr`atLB0t4a|rkLGH)^@eds&C@4zAAkRQ`+R@2zd z(fa*I651U(j<6vP#EJ%F%@%XeK0QMZOuFx(-h7;Yfhq z)Wh*3Ryh#C)9y}Zn!3zdksQhvg7G8~b7nR>%(K)CcWo@Q-K?{b9bq^;gj)ap$@Top z&bhcHFEdULU38L8eOjSX5x|ax&Kx@f9HdkxWX-wE%Xzupg$Q!?*k_M72M51u-6j~> z-}-NU_MiVl-{ukbIlclz)itD_@cJ$(LlQyA7>hI7B-!UNg5eH?X4$mSX3ZWxx%})W zyXAVhSYEF#mZd9@f|LPh1c*YI89Z9DmKV#I$VRc*vJokPDGX?`h!)7^aQ6-_6@iij zFMfU15=c}^X>-#WlOh?b-Hn%0gszm*mC|`BDh~JR zNOg7HZf;6zC8qR!7$3{UJuxg_kO|P;-Q>xX+)+OjBttAuPGf?}=|pcSBMELafi%VK zrJi6w>Ff4a zKl@a`hhDz?)z3V|yBKeGQU<^e|J2X_#2^1=0ASEAB#D9PAYld&mg=6!8K?pyf$FK5 zgBLo9a9nc1!CnjrMw2WWKb#>0NnzK6N#0kl|E-F72)W%W&8ko@f{CiG1H4RZ0y}$V zF&=OCt14}Off-eDpH&$!9#_ZJp49V@$qWutGqX2!d=m54;;wj^Lv86!Sd8=A>YcDN zN3L~pZnF!X)EZ}bneqK@CnvYN^gRN<_TT=c5BgdVEt!^7Hs{ohPp=Ip18V`NI7;jm@RNuK5OD>n?^ zKVLlCnY2!TCx{r1jb)BpIu6Z}L`O)RS!0>mqNba3PE6Yp`U2B|pucDGG8hfw`y9=F z8dGWpySm9#Kpen53-gXc^YEqZ8}C|`uDiL~&$d5mU{|6#fITWdXb%t?;>Z9bywD63 zVyRUp*_+8I=BZ|3aS>)RLKw1+L}RI5 zAb_g4msv6_7FngxEM*#z5;yKCrD*D1QHd}iE_b;@Qri|t$zbHCKKXi1%-NFh>T2U% zNybuCN?5``s$A#O*RNf2cQ263Jz^7AKckuuq`G-^y?J(ZbM@@zYJJl*dA;u1w%gpa zX3QzMcb?N`)0vS}#NjOtFP)cCig#VrIxq(*8#)xcGqEsNJWOVP00s&WI^7`3FTSi81ghQDKR>UYFT$Nx|mdcDV`gkAH zmxf?uLe_;R5m__1U`Fp3^)t%Rb2JI1fDo!}O^jKBtQyOhcJ)u8@-oq&l?MMNzaNDweM(0SNn!6o;)kTwB9 zn;R#PfpwF<{PBtejc;`2(IRJ~kwv<0{TblJ-`JF=U3u1)*EXedy|Qk;{hxRR?{2&U zNSS$r&_wUGG%zykDGvbXKM-kTho;f~q0J45COp|!&jA1l4!Yn1ghaw+Bpr!cW0{bh z5qhh7Fh#{i+Cs0doKpDC#8AloO$CngGT1%wP-**B9ri?Gq96`ihwgEx#t#2-9xsCf zQEqc~)=D02TfF1d<9T=vGiOiT~E{4n}5jp2am;;53YI4WMGG9Qk7q4S`G0k~1 z&F(xi!x5(D%nYY{bjXmC+0OJ};>dUDNamB}jCq!MBIcIHG6b~uG-lbH>503%%&^J+ z{YS1_@-hHUxH8SAhfVbCOkWy=scb(o8c&}68RPcxHhhn}#p3aMo69H63#J^gE-pXt zRiNTEL$1<2`pU2V%HR8u$S(nyA2r6o$R@IJtBukU!Ro0J5kCZwR>KpJ^x$iH2#h9N zC+84JGece2nF0vX%o0;hBqc0X&GoZn$#RpfpI#Z6lR1%cVm3udF>4`c!5#o$N@m8q z$UsK%rZE77^1=(`1rB(TyGkdy2hY)UBH4ANYek6UX473g^RDfJ5Aoum5_$DgLdf&a zzxLEi9emVNFrhw4kkGYVDe|IH{N|?p+yC3IJ^a?Md;I9~;&RjE{NTa*=B70>kc)+~ z0YKYz5Flqyv3u#Sh!6HQi>@ZQ9axrR&OPKKJzMx@|YlR;$Hwk%&@?;dlOy_pclKvPbJH_Xj!ObRHS5ZaM*QUA5(^E1kQ9 zRZfqV`H%gs-}Zle;VM-31D`Tf<4j%#yOn%#US>b@ac1NTiP@UW-o-3#)sS8U zw{5gzjBoHvN`-asoI4ba>b!D*=iy~u27h-5N3iPQ;u2396RLPu81|4LC$bKm_5A3 zsVql0k^O8ZrNqf>Z>q7(1e2>w?zCR0KXICQmzUWmUxo~aco_oP-H8zeGu+)iH8X5Z zj@HX^c%`R={IP=rc8Hks``bO97Z>mSvb26+tBcDIeh9g-)G&oZMZDZ#4V-Yf!@HP+ zy3Lp$ot85f0xF#%6@?LjND#58aJ7%2F*ms#!jz^&SAjs#XsKFc z)C&j_QnD5xnVV!r0RPEH|I8o#sXw00%s|i#tM#%?)?`CYIc1WyF@qqRMRqY8^0G+@ z01`mBTxQ1xtFOKseXx|WXmZYJ zv++6|3%Wo8++C&XZZ_R!)81^#O&^%%=BE41=bm4rs64%C+q7s>XBNgjZHp#b zGB!DR*z)A$k~^B5B*Ca;l4KCfRY)KPRwG;@C6(eNO?@<95@s4G^>7_BgF6W@LdlTs z>}_F&Kr@4N%pju(AoWp%-NT4R26!F*VB4U-_1CPQP>KmE2%Xghnz?(>H^Ie-l)Hkj z0g%kQE*8Cx71seU89FBcW0AsZDX19mwxHvr^@~!#5;tP8G*)Lc#c>Y20N4L9suk|fW>FwyS! zvyHEK2xI+bNc-N-Lp9xwe$U0rjPs)}2bfYbN2W(_0(j(cy&0H1fkSzY9X*$}s5tp8 zH|lfzq9<`Hg67B-Pu6yXcz4IN_9LIoCo>|O`zU>nTS8u+oNm#+$S3!;s;S zgqZZLgIz7&@Tp!1Og)&id(ysX%sByokK?$!#DOPsC(y0!Tu$BP3sB^z4QIGZHyqD) z1a_VW(@7#Gavfocj}Sw5Xe@JZ^FZcF$+zWYP{sIm=eY1O+j8b+plr3N@sxN6!K99N zOj_^oO2-N8Ug#cojfWrjP`-Gye)TKZEZ8jBENBUqVpU#C27p0~K!k_=#%xh1J(%`4 zw;DW!D9Qo4G2WoA!b zzv?YzytJk3O1}(Ksjf`GZ~n&Ty0#Seh{EgcLEm`wUGi#`Q}=kdO+9>gv0OHgKw=zHV=B+Ux7h)pd7uwYk1&f9n&U z|C+D;itFo5N|uc7?!}uXxm#B}8H%SS(Oo&4Jop!uVkF(AWG*S0M5&lDnSh?uHkt&e zB$x#2MQ~+i3>vE=0=J@=(m>WQ#QRh@vnOZ_?zE8=j2TJ^@mMX7qCOEC#wrUVxeVrj zK`s$gHDqxYjU~Qc6^ZoX4i>4jC8GJ#SRLvTt9e*_kkoeSq=-Niq@D|ca;zs$ zUM`Xg-}AK}{5!w(+An|V^;1{)qQX0hlnFT}z*u$6D*R&grBu_jUNi~_1wVR-k91Lo zG#X|HRAJW-;2fnZywWW!LXhYtxln{8sHbCUQL0r25JGyp&7O#Tk@?$+iMIe?s*elU zRVik(ol~nJ&meOu#FKb?s$a8jZG7m?aSqcl)aS+P za3(=>s0W=>+YwHf%uHq@`xm|CL>>8f62FlS317VZ1r+=tElAPR%BX zshU1VTMU_jxzS@U=S0{X-#ioJI-GATGb1nWo|idNh2yoG$4TRt`9pEmk%wFRvW@3j z&y~%3!tFSrW6;rTRTDUsVU~zFa>(Y4WjIGdP1u~ti4#I+HuF&O&?(O2Wq_U&F+zg& zc4Aj!ImyM0pF}jty#dI`tqR0&h<>;EN8AKlt0z<>S?RK4{AeE|vh88#D{1 zY&nr;$!K7#wrj%w&)&QBO0s0?<1*{3pcWn^S#eDk{~pT#@#jaCNq+T`tB0a zaH9w62!8m7zq(vCO%si@LZi4pc(6(_1T#EdhhQO;e7yjm?2zZ2x7)7W_M3IP z-gKL7w_dkxm#(h2uXX9;Pp((XczLk^*|t4^82HL77om_5(L4rs_h9b9BMmO8iG{2Q zQdXc~LCT@%K!Z_Pf)GtA{w8ujgaN8qgp88B{@E<2Rdel|K>OT#gDx6N^&iOT?BK$>w8k( z50%tO+my1@yGpNnxClS-J+J)b?|i`DQP51nQ3dGIo`V$Tp~-ql&0{!pyRoqcND zI>$H@+<#I}iQOHTZCg3l;aTo@Ew{ML-1<3vg4vnUOYCD-IUULXQ#uB~n3BMLRXYWP z(aU|#TRuJzNA(A9BA5i$FKs9@*V#iR&N9=z$6aG?H02qMALFj_&j}|TXJ$g23Z-)L zsQZLC74Tpnnio06j~sVYITYjZFD|@=h^ZX$tj;;nhFNEsS@|lte@A8yJi7OQm#O7L zooDbe^LxjIllo6nGrN*WA@&KHF%HR^W0Dj6NqD=ySa|T}SC(&lY4Q5^hUKL#R$Q#O zSaPwV1wnPJDb5!SobyuMYhEzxiuF_aFUY76MeK0w@toWu^?M zoUttJf$TRnPwmCU`=CxZ6+$RGR6pxV0wk9NESAl~S1;S^ZHyLU$iZS02oXqxk&RK> zWz}^#yKUb*T!lzLEUO05Nn|l+&wZkq86SVThP1wFLk#Qd-c#zfy@buC%YEwFzVA}s z_Fb29@|1E;xo!Kt%Q@wC+jm_C@c;epck+qlsnvP z4x%KTkqcy>vIH67RfUp;4MYz;>9d5gDs?nQBT2B#ZI!Z;6d?fLd9>N|dE4i|_5b|g z!4p0gSxT9&)3N|YKVV^pzPCbq@yT(dUuVM`{A%poS_H!RF< z0Q@|7nP-B@iJlcsvoN18?nrY~9QLJqcjL3n9O>AcLexvCVPGxNuTF!I=~cUa~OH7eDw5|HZpM@s~x4D1RN!#gfgE z%O&Fi0_gyjufFcx6)8wGg%~i{VisP_kN)7W@>?UA9Kf)-rzeFn%O#Z^q4tnM&C37) zAOJ~3K~!sk$}R|%nvoH*>4)#G(eSvivw%(FiJ@X_{q+im-9+if@9=DKaS zUEihDr>;%gb=!Ak;p*JA{q@y$yY5oD@i`)S4@Rcow}1P0e&H|uL!>6Kq`}pzt(WCV@{@}ac`OYT{@zLY;auHXnMb6$Y z@-pScB6t%exXT-54?v7k>Jg)PR@0crAhDWsfdUW>V93QU*8!LjE)R4UDK}DLU6=`Q zX|N1I6TG-Zi43egraIBMiaML{GV&p;sYF)LcD+ih{xb4hxfrteRh3hy+&UM?wo61^ zIVNWq0r$eEAiId-^5ajA&J0X*syBmqM}_Tf(cdeLUX2M@szQnxqi>YX&dnnIq%)kOI2 ztvyQ3+&X!CiKl~xg|TrEtOVfDyq+)%+ zXPILMR-R>O`LScCCpwRp!Jws&g)$B2x&t+{E149I%MWqBclBs}_370|-+k0=yLQv}ZNIthHtX$n z-L~6)eYM%HyX~fHx7~KzudlXU+v7RlH-F=w{K$`d^~ZnW>zj4^wXeOq-S#AwtHs4? zd3m{5E*esOS^m1_tSRe2<*TdhNYIu=E!b?)#A>atZSl}+u>4|Js*d|Iv3IzPgBw(TFUp`%G{VNYPkh0BJOU*x3#Q;8HR!f?Y&g1^b2f z-u%r!{P^VAUU!WdJZ2MqTxRAvtxppz9+Og;mU;_;_lk2yhe~LLP?HQmNOa~^#*yyoGZU%)Fj``)Ae9VfhKoQReIAq=OYl9?^G` z2&P-le%e`Pj1vseIco2j@-oLRYkt>Vy=m&M8scbB+jbg z4DB(4+4ELQeSg<%q3yf0RLT7@db)R?m)Y%|@G`p-?F4q!&N44MvDr|jG2y~d`DE8_ zb6hemJlr+Gt*=MHoY=qdKm6uP-Tw6WGk@Vf-)ZfVCPTx`PMasohhIU{Fhp9kXe=uR zMUuuq29PvsLP=RVCNp?el$MRKoyD}4AnxXF;V z%ZckQ2s_Cl@bMpiv{=NrXcnu*auGul7t7cz7s0rGvL@s?=afC?X3>zbZTrpjrdc#i zBM{{qaO!j4cH4EgzS>?r*`_{sZECmulSkK&Ke@hsvQAxYx7}vlZm!$%*WI6MPyXls z{F`6-$~#~G`j;O)x_&kFGbHwry9dWpi<{xV&607xBZ7pIlxnmW#Mp#HI<07~HkU-bA~3 z5DIDl02&5oiHv=6!5{)2>RyImbTOE@0LTh6&N8UDt12-Fg*CU`a8O-=Ko0D-*UwAj zz{?b;0jT~DH*GAAR3zLTkjsVaWpyfnUo)H+^13YmM7JgZ$Q`&9P^CCfG znYC<$eVb=;-u<1Q4aReqF%Au#*}Y@zI_{l%@QENMjG8wkU!IPa`ICS@Y23;&oS0Ee z#DKFT?+5Nb;(3|5Hq1_V&b+7tOsSc}8J&&3DxaFIy~KS`%$dpieL6wF#Jt|Ub-VHt zKWc57_P$%~`$Lo_ov!Mfns3zP-JNA_)s)X0nhjREjdQvCuCo@#p50Y(im53cF~@0? z`tBdEp%O9o@~JuB`DH9W!KCI=+YPY-=mgI)cP#G1%K#&WWhb^D$^;C=&4_>5E8p!J z_U@I83lDdVai-WI&X;_NllYOJ{ZC0^U%yvs1^}@A@LQMf{SZU6xTFaL1LMVoB%?xO zFbf7V0U(kbm~*j2(S&nxul4t6n9#sIW+4D5^a+)uAXN%Ol0elsrozZzm|&Mox!L7q zNoucTje8{@N{MIB!@L|)cfIvKE>c!@rKVXrq0;whC0FhOLI|`FJm;LP?-BsG^@IWI z>u&R8-FJOz`^TTIub*r->+b5&`s(re$*0%rt8KffL`>hM>#I%4?~CV;Z++_n0Dtz+ z{+X^z@4WN+<>fLqakXqhun?>)!<2JgUvJ&L@6zKZ>&<%m!H18(@h{*1+V_9&X5FTg zyDmL>vhLcZ?NZmJ<+53=8UQ&L51Uw@jU<&dswx9Q2#8YgH40z=k`w?T5Dew{lT6u3 zqf3$*DPhl@!up6%DVC6%#xvWLXmX2!INC_!X}kytYAh3iGh!=Qk%V(16TyRT!wYZ%f>!g z_igrV^20lPc>X6eSMZ$ngFc3y?G#u|N5CKIY7f%^SIi=6!`Yo5RJN3xSB6-Z-o5$Ux z4^VBU^Nf{Qj2p1~UH&^Z;4~39J|~!^Q6^K06T42-TyR?c#EEXh{OsaSIRA1hLvgFX z4&y65^N@RBcavv({$Xp2{6zRESfoAf{FwLtOFTOi^BM=gTmYME7Nca0poMjGis3~VJ^XYMGs+S2)P%0QR@^{3kYHwv* zZVy+q!R|f)Z}W)R+4M&=(){E>Jz(^26A+U1!1PJn|AFq=eXd~;@r z8_ZBMz)5yyTsSG8;QSH)e94!%7ls5EH}{!d{1~|Y_V2y^V}AiKi%{z0#f6zevVgFR z0P{c$zXU^|1p&06cYgMlKK$)p9oJl3EGJCOIT1|1{kMMo&;7N3oAqcBf*>JhH_7W1 zzwjw_{*iNGWaKWnPyQA+nM%jFa;gggyVPyFocz&;k6wHGRY*Bk@&?rbr_}E>h8SY9 z5VA*l_VsnUOt}w6s$F;Q|KSJjlIQJpd;RG8(I;0=KD}B$UT@azX5Fr@w(Yv@+J1e# zO?~#PzI!eqOULjJ|KaZf_^F@zq4(bVo|N*%#TcaT65PM5g^Nij%} zCw+mN`&NSA|@h>?0H;gy9^`C>m5ih$SO4ydp&*^SIDl)Nvs zoN!NNSt1gy zn>~^xt+f$GMuUsllw4i**On2$5594E;=C_9UI0=?(y#pTzcaIgyRHS ze4NkA>~N=v(_JesD35sNow|$5%%S8N_#Ay2zmSmhul@&rW549i3uB)!ug93EiYh$P zJtxX%j~%1PpAegE)$T0E+B_?~rw&K$9P=`FZ9kqhFQYpvFe*NhB@|8;fJweC_Bf45`?%KXt#FpsWbo~U2MF=shR*S{5$*F(x$<_M0^_16F zn|8fjUv1Y{oAuT9`l`KtvTiqB$>ZzUJu4^wj2W37{?R}B7XaRT^VN6Xee?3cO0EzL z0D-paA3wT&@?`Vf4?g|Aue{awsc9mB<+5>C2-fu>b}1$A`|5$zb?NeQ3E;_-O-i|G z!mAHgDtCJ4pp-pE11xu03MCybq>_JP7(|?8G8t9K02nFIkX@048Qo#bu3#io^YyBX zH4ti+m*VOL_v|pJ8V6+8rBV(cJy?Ep z-5xx^+wtD(m%s4!x8J;IR>2m*qM16Hj31{U9{eR^Ot^i(ZA9SMeQaxA>Je^)*`le`tj4%wBT7@f|M!Q3f! zB{w(IZ<8DhiY7|UxIL){d({Vn|;au*^Uz~;0*LC`&V^e;w{Gh?7 zC2`w{_Hkoqp8kY>zgPHAA6&4a`<$>-2DF57`;ST-D3V#dvQro*e$>PFb;)G&Ox&v5 z#&{ZMnF%K6J$-3a=GpKvvsZj{*VCkxx$`bLX~g^p44i(c4d+BK&F?zSww%~i_utW3 z=5*nN$0>VH2Qf{K#T@NY1&;GFyUKA}iWy8@W^QCKPtEKJ>|_cK@6s6O(<|pozQo!1 zv7h-*O8PK)7321egdFH*^3rxYMEziTfQ~ca30D zb{J6>RR(u+eHlDw(Y<)Pz?HKwFfTc~yDPhUO^b+wJvcbG>c1U26Nyb-Uek+fDcB zM^C!8->%!POmw-}mO_gXLG zu1kHNbC#skr;CdP0QcsRHj5^#md&zHt7X%tu!x~g`C=Iti|7(mzUX6^-~l2LjBZ9W z!F_NrG%_o@HySW4X;0E-`u1VLW-7SJm}2oRENP?$r~8A%Ql#-=Ws9gK1#LZHMDLj<$5T!iZ` zH)hMm-nd+}X?xj(t2VuVwSDt)@uOdU?bH)Gg0H@D`IF!G`fC@>(zpl~S>0X;WS0|x z3;+QmNCYzz%n-;3#*!_{7|VN15C~+jk~v3G1o(?T@XoLO{s*_U<;BCZu)&@`e)(7a z&p8{G^{QnZ&_-PycIi< zu}wTaaBN-OWfr{$Ke&W@<0-kGaesbJoXa^l^VDaGJzG@rboe(f!#T|`*WhE4z%8dZ zz|BOl^ntSZ(rUjKzVW~RPq&E_TV&?;%J&KMDhr6F8z^H~GNjbSB&&BVhFfGd2-Ia!bqsqBt_go!h zQgY8;mY^=b3nSw$ch7yw$;+D4xlfY1wlCRyJ!Mb1Z@cTq*PcD6oKtSM-S&FhZn|#U zb=!Ws-ah*D`tiq4)>oU&y4_rDH&@&B^>(vvAAS7flkYz2yR==mn@yM9bMn;Z)Teg) zIWsb6;*Y-l@#9D9w(aFkA_QA3<6_xdTr5@>iw6&ug|6CcyY;49ue+=3?e%)wb-C|z z+oo-sHruY<_FbP+^0v=ym%2W8y_YbkK6&5gKIN2^a!#(Cy)YVO(paA?(RL|=u2Se- z@zZf-Fz1poLjtpxh$lIbb79+&!9Ep2AD{`yKsm~#3?^3=Qu0(hqQK;yfj)bi)%LmV zech!^%6<0io(th(kR(z{x+n0j|G?X~Jfwa6?8HpW%3RScIgys`-IK3M+fJFncv*#@-1d0|1V~w6HKT2Dx07lmQJUcp!qw#86360^m)y_!-kr2PuVT zaJvM6kuiSAkDQO4@Ch^YR?LoIvwLTu1812r&T(%U`^%jE3Vcp&&m8jx_h;UWbHJk? zX{TZZ=LkO!oM=FK+>>5cMx@?7nNB@m|4kNcTIgHOW9{1!Ngm9TTWHW z6R}HA=PXk;x$n9%8(1_s(aqi>j5?st20&rd&S?V<{A+A@>&At<`j15_jaBqWW-7L; zAA2O_ge%$epgF{?uMmKPI$z=$q6B*EOk_&ag|Ul(>!W}CYj6Mbzej_MAzDoY%<`TB zfV-J#F!cdIM?I>iPM%ZtlylCN#z{H# zDW{ye)K})Ek~O(cufOvefSkNp#B%DK-Ll6;L$cd;8o}LDmlhWbN+2Yqwr@Ax_0=YI zeY@=+fAr+~$-3RN?Y7%)ItX3cyX4t7*H4uz@LAw{i=C*iD816$3qGMLS&u%lEuKtCEYKj^`>7gV=$`bZXsNW zijuQL6FGpkPca0@NxBO}(ZF;Em{YboQZV;9d(CZ8V%cgRl}k{T3pu;9=9ei<4cxQf zDXRzy*`0vz`nL-hQlv_i_o!!)fa+vJQexnCo3ksqC$AB2eXup(*Q)%cgfX z{u*KFrRk z2qx~w^31?+)EUQy2YZBV*$wn}+BLz1WH}|d&zQ^}a>KJs_U_-E-QHuo%#FZ^mjMor z8u2n1PH`-`@3={G)C@4k&H(#YeTq*R^EG#%UCx($iIe#8pZbq?H0I-=(hYH`m*TuRRRWQlBmsD*!Z_F{j)tVosT) zyb?^FbMDjQPoAtU7R|DOwB5AbrrTU?`#x>2+pEXxzU$jg?CvU#KN6H(fM=Ij z$R5v<*&yMO_Y=Q2gF^It!qh(;} zBCv>-9{bx5mL=Ng+mE-s`-5ftneTsl-KBr}?N1M%&RY+b@4a#Ha2db)#^o!kxG-x1 z8xl8(T+0$>g^m&HH>VqCLlQ0|$&6+Qh0r0541$q@B!V_b{#pk@-nm2q?dROIA>w(q8_-vS$_}K_6gl^qki_uixVV8F^ya$ zX&rcy^_`sh*yd9@%S?Eg`{C($nPF5M7tM1tMf#a})11msbL$#Rv#9*V98)u)HpZ~y z>3(^ev&@__nX{4(KX3T^~Or<*xy)VAHdImcxNQ)*@> zd?R0QjG#HdxkSvFVlQ!bjQwJEYc1{V1>xGIn;|JTRzDg6aAz<{N*N(kohj*ie(tY+ z@b~_=@wx8D=F2@v2J}*8(xp17mU|XGqd3dtT&bBpE9aE556nyIvnNm4bMlmOPB|s- zyFR;b*V_`il)6Nt1tS=nSO|!+C{YNOQx40<-I3I;eR;8DAW6^uJ^uJf-}c+}w%v39QkPsxeOkYOj7-5Ff9KQh{nG0+US2L^jKM5tzqnX*eY&_< zZZ=&A)^&Y$@45sG6G1>y&JHcg6Xgv%Q--Jn1cC&LN_B~3&dNb3DOv*I4na~$Lu&v@ zO1*moU64E7X{6V~0i^5z`ZlAipN-OG3bO)b&n{0nOP*Y+9okjWY^c~0B{vZy5ON6g zo#L%aXahhYac(qTX|PxADanEu(3L%jG3Q6RZjR$@kt^Mq5Q|f;CqCT*`}7-lpuzL=eJY6ckIKJPMXz zP^&BqN!pDe7o!CNG;<}F5z)8^wlG^XdWi7l2TOdh@IsL?M`nNn_o0E8@hl`~){T`M zO+?{k%vk(lxIgNv7K$kO{M=y<_aUm7gc-6?#ZyM)pvGmy?&Ignez;=*03ZNKL_t*1 zL7dz(Wk|??42%qvyW-hS1@BVZp6nqbqy5b>4tbedgP6vnmH8+zv+%ZZ!Lb&;HOrW4BnAxgs#j#VIvv-17;S)8?Ud5ikkt@0fFEb)Oc+O?bGBu~zlM_Vz z37mLlw?;6{?>SDwo=_CCM;#|(=BqkE58Z6;Q#PlAm?l)tl*GA>mpLR{%20dssF7wK z-{51(eJ7fHmuTizb_TccDLdi2&@SgvIWKWGDDAkIn&Nl=_1{G>eem~w^_@Te?}Kt@ zS}q?#f*+sok{OO;?DRcGGUxt#&>mK<;x1fWixl_y73Q_x_c)LkMPOWYdJ4wOBN0 zN~aNkoU>=|d(>-(02C6$C1Cja&cetDj3CJb#=^>!thr5KD5}N`S;a3!LK7*68w&Z7 z`rHrELm(_U2`jzF-NBM3T3KT0FN4F23&k~t^jveqK=SOlEUBKet0YM)-Yznz3D$PG z8X+j>1q{ey`K++hmPQocfx^oe;j&;#ZUiEf6aqmsU~-0FQCKv=!3VBD)ErGl0FO5P z`%kv7u9{`ERTEZ?l|;NX9q&NI>}GZ4z6;1uae}6hK}lr=C=`bZAy_53g0V4+fejhW z7B>-0g#rUYNmVSTH?Y^N>DHcuy|#)!`R;4~_&bkgdd!Q77mAcAm|9n5U@tG6JKTZf zOhOjR2gd3$6GS*sgPUjpKH_C||IDBaCz#<0G>eV98O%Y#qPa`;lwf)7t#CH1lZ2w) zkAIhYI-H|e4@I6~TPBqs8je$eylLJrGbEixD0#GwM zoFG;Xo4vbBlWQN}*jovj-LVIAr0M7Dmv~0d84F_{ax~{eFzu}EbAB9FH9@JlI?G_{ zEK_E>vt02cg6XEhr*M`TU*4{*_kRk;bFpg20a50AqV-Lpw(oRWuq6M$VNjWo^aIXla zSvFsQyH_2K;vFN(J!Qbfz~#fs5X0(n6=Dc6#3sfjSO_fM1SOtC03o37dIG7>a&J>keQGyt-}ddg zy?(MzeRfyB?Ie`UHC@}aoBOP7{3i;3_%A>F+7Enh3}!1DEH)-bahYk`v|6!k`-JT7 zDd)BY>d>~twsYMuGF9P}z6TMJ%tYO5M3>wwyGv0RR%Xp+fdq{#YupDzBxAHl24iD|k+~VF4T?s= z1SfIXgkE~Mh(Gw|1AKw-LXk3Z*pLB?y!l)C;L55<$U^Vtf`mMDOHyXpV9sE0A`BM6 zO%(uG%#;$oJtwYWH!aX-60GApG-V*W_<3s zJ(E+PXvqm2KgmeuWDwJ5!plHsrv3Xb-lb*ksN+P;M3Uo> z$sKIp^JNg-tL@AnrpZynExgR`q@^=WDw#cMEQUcRmuFJWbvn84p(dYWXR7SUceT`c zH_w-sM9j0sIGfBQf@w!ePoAr1SSTMug_2Cbq{`vQ9cGg7&d>hRhrj)+;{$JvU@BE| z=%hv{yGtxa`Hb>7~Wz8nfzJ9Xl+rDeN?RDF= zeYfpPGTNM6?%lTETy5}$z#shn`(OXTcZ<7BuwcPRx@%=@+eD)4nEEVvc5k~>9_Pc$ zC7A$~=p+KH&N3xnCXpnEO*po_nKf`TqAc%g^e%P8!;6N+QN>j>G6By{l37_ON>45a zT_r1wR2t;+%D%aql~n&PkGNUGdr zUBAqr43(Z#-viN@T}C2eVS$}9Vz@KWf=RFou9~n2wroNZ_@qsbxBa%y7frZagyf3G zXjW3dWMeP}rxE5f(g3?@p^TK^(lS~D5OvX|UJ!rg~5&%M3 zqNMDUQ*w8svb*=m-QhW>jFdg4Jp9dF>f2s9)ttUvN?q!QD zmWzvr7jfAHqXm}LzOAO&U~HnhS6Wt3@wzBe{p@|)x7)7WwA)SFx4oBjon4Yr%3lB< znK3^3_$tO4#?&;3W;u(*YSpw`kS28yQ+8zUyF}m`c=%vRf`Dg#<>3VYGb>CD8OcQK zdJHAwrn_9l7z}VTmNkzkZ8rU45mRT3B&)XqDv(L7_tsKK!PqKbDv9o7Dy5xuL`ngPQg{&V=9pu12YZD z1A)evK$1oRW{k!sn?4xLuxM-%?6T=En($G*OwSPau634?IaYFWU4u!z z8v|#j0>gm4U-%rHWoFMli*v<>mdwhJ3&&H7$u#5nF)}-ht>iOtm$}e$!7O7k*)>}} zGxM1kXmj^ra_nyYFq%s7Wdzgn#BO2i!>%VUpHXgZ{_~Dtn&l=+&3h3{s%FPAJv31{ z=WC!bW)k3q!ue&N3K`=m zon_`EdBR!d%+2Xp_Ia7z-t&2x!%FsKMok?b2;4|w(7@$!?OJ#1x=WK6X^iLL)pCy) z^(Bsj+T&t0y1VlcOs)_9{{QjL&-{`nkUdQRZ&J(*&wtp|+dutRKK#G`_FW^G#yYU; zQy?L+%#+>e?(TF?+2z^$RKl8ap9W`{l=_^zzHd8EIdy&RQ{VP|+ovv-v@+`_>zs1C zX}9Zb&N()5d9l2BxLRJWViRK%5>*{)&QjEdKNYa>DZfm`@Cc%geNpiJ}07ybIs0Y;LFf0`(cO$Dul|umBHLRBe zX~(3AMq88>yGbKuC}B&15D~xt#$Zv3 z<`0(fB7Sk4WnLUoMuSN?8G82(4hOQJ8o>mwlp-V$2+qb7=XwpgMbl6m3}q01?%rai%q)us_Q}@uX;W z!ZyK3{^$R*|M?|lb1NjmF^OQp@rAJ^6eDG3-%e(v$Ywu*o=_yaV`piLTUg&CyUI}$ z_4*k&%S?Eg3GT$pV1Mdz1_(&=jxaL=%|P7D?V5G5d4>^8c06Cq{McOf%xVK09_$hN z=H7)9Ogvu-rd-ed-U+L6NU2PYdWO6VFg450Dy4_ zU&@;ocEzO=`1T)u^aEf2(pzu8l0AfIBq=rx6Z6HzvTb_+ZQBFN?kPEuNN(8^AVHE$ zPp;bs(oh02k+JXu1Va$|3?REN7crO_B{C;>V=PH&=8#G*nO zB0V|G1`W9z^#%nVZFeq92qouBpGu_CBG{_2hl}uP7eC(gl2TTe-AI>q0ZlQ`jZr2F zFp{NpDl_Ghai&Bn1#1Eqfg!SZ%@lVV3xzk5C)J2kf_mc0QRiFV-K5y)p8F!v;H%0g6!cO zMrLx2)6J)FmKkGjF7g!4GWW&IWaHS^*ShzNRvp%G&QVW^-6=jBP4qNZblAc>u`}4e z!>0+Fn@NHl6)u?lB>oa-K+@NK>A$>9szSvP{V;;*3`ca_re-Gk=0-W8*u#6rjyDGZ zlJ#qCnfDJwOtFdfaH6*JF>de)Ugp>#1<#Y0@xzA9(<}4D4S*vY%2?Z<0o@wGG%YQh z#fh+{Ir?V*`_R7p{&`CMQ!3<+&N3632{|%hMs}OWTx@2K8q4pl;C{T!70SXb9QHmh=RP5GTA-Xm9Pshqg?X)CiOLZDfP+w z`nT```*<>j(5 zGXPx&BzQ^Pf<)3Kk|fC3wuv=Zr_9qx=F&q55K>crK#0*|3{VD`QtrtnnhDw6VD2cP zLdq_O3L%q*q`#1|=Pb`zKz*9oLDWPum4bm>r79Onr>nYgr?dqUfoOsmBvyu|44^cS zpuj*#77Rr`lPjk#DRb zF_T01l4rT^oMj9FkSjN)EEJzLV>KIg4rM31$)G4pi*<|i6k|R6nW@}czule2q1iMG zf2PhdcM{&=E*Aehb|;;!YFv2icP%;D_;cEMr>8c49iA2LasN+XT-&6Px3Vy^U!*6P zd@;u1S>`5GFY!E4K5t?_SZTLw62aspt5@9|_2`j`-5K8}eoQ3m=53Tj6*#WXCwrdK zS>~K^<8%-c?#0XC_FMauJ(oF_;B23r$nr6bZ43f=DXo zzDodO6LZQ;1V}JvPkrjzKJ}?tHszY#ef{_vUr=0MZ@>Drcb3c8G!NurpwUQS0rmh; ze1$pyIm?yZw{3sH=IXjNW1XfEC6jW|GdHxIRJu{2!hphhWFrZ=tCw%L{jgM!xw|`Z z&h;2oSE|C!Xb4bphv%E+nS4kzQ_omfm`VV8{S9b{>fqos=3t-QNCN`AxX;*-4Aodi zeH3S!5@}^t1E~U$3tuCo!sC$cjK=I4Ks06n&REkNlOPz(o4}B)>GLAkGK7mJyX$eA zE*DDK+w3XJjIj`8L-H33tV>@Pr<;L`iP1tZV~tTVtJF;4&JZjb1vJ4}J&hoM6pPCP zQRtgx6R`c_DC8H0l=-s^3RC!39558&fXCySB_zE z!<xJ2U!rh~qaMoGoX7gA$QXw}FD3!@k&ybe^ zrd3Uj#YlCZpk^?7Gj|vpUi)c%pNm(^OCsj^V4O{662VlMnxepN4v>a^pt9+S7VEl8 z0CQPjwYb6$&N4TLVusuwW2}90pGu0!wAlhM!id}_xNGDlQ+b(^;OYNr3+2bObS+i`Gm#gK)vRN(SB8DbfG&7^oLZ~-@2q6GqU~J<0 zYC|A|keEmaXciGb-*zBrMk43Dz1|+Z$S(xG`3E2T&<}s*l~*N*L~P9Sc$jF)@3vKT zcNeL)O{-sm%t8GCDAY;Hx$E=8%SDWaqIo5elD5g$+rE%BAW+sb zl2VqZ?1J1;0-9XP?!vGDQ(79ZQJL!o6z;S z%id>Yml2&Su#z&t*aS9ajd2m!1jb-7mQ}N0MDdUb##Lhjn?n*VEn+C+yF#_1Ofb@5 z1~+F6v<%uU)5I4GFAOOI?2phVq2XU1JY}MRCB3w>cP=db|Lnb8tSw1)CiHzPBKJN& z_owPsb-UfTjgiK{4A>qg3h({iInn$An zWWfwVLPElWF(cUzJm8TSVSBKVdR%GtnxpVKc&$;JT^{u*J-9EiD zGBPqEGh)SxFV|WjtUSJz#X?u%-~Wp9`hJUEld;*Mv$<^ z&Va|9+jo%A?7PmxH0R+(XRaI1%yta3+McNU`Ry8Ur?Jc;MxqDzBx}yWD5QrvEG#p| zyN{<1Qx@~je&dY%55qCG;!Z5q=MC{D%~||p-A+yQ_>+XxX^tk&YJcIev}&3!kly3| zkcdfGnG@Yj4>d$@m*0>3I7OpOv0FNf;eHVs+7j7;Jssi!#xhe(6Ho9mGaR0M7%zjf zshI;TRPY|wG*f9mrX3I8yKFKeXM9#XykLKf4~gO9vrpe}(O71Z{{e}MW$qs~u`?q_ zX3WSTy%IQm6&{b5nH2@>N(0WNW~OJycQ|`F#|fH;;}{Ebwg7pL6QE>{@L-zR#hmb9 z68Xt*|K>;E{bdMVXz3uzZ>41*)^JczGr{2B>H6vbCSM6Z`q`hZrB*3+CpN%|D*3jL z_%gLt#2(mnFq7$P?Mv1dGH>EHAwy&jj>kh=T7tgzOzPP^V z)~np*yvivjODPtnmeXpLQ%<+9H-o}SQkT~kH?M9}HX7q90I{Rf`f3fpt7>2S%@$vE zeDeLzE-zOvUOXk>u9VEIIrIP(fV;2Ps|~iXO;rIl+Y0j4%?4Zp=!DItM9^7=1=VOp zg9;g7;ZL;PzV5p8;j>FfeU$-~tnLCzsX!BG%?f5}{}Rz*hAoyNLbMt$gJw+~+unQk;<~_KeH zp~~e#70O`#V#KzhGZs;BSDou{A@>y>dGKaR4%LVuHX6%>$(sXEHJFC!gRK*#HmIXk zgaF7}g~w{|tsWN@!-H(|Q->+{Tx>rKvnoxG7F&lw$6AC>roqD%fR=Qi(`$tvXDqH6 z?qp=%guhtCgV~t}<8hmCf^9j}<6HVkf(A#P$+)_ht!k#H-s1~_JT#`cB>7T2nC@nM z_XXD%V=Qw5hc^6Y;?7!4Ij;jOi0@^am$MCF&QH8|emfp7W_gBQ%Go%G{ZscfmYE$M z3ZE$+=EQXDsz>ByfO%2VoY=1e2OXfD8iwNZ>@>sl?aiLe+c0T)3q`&MuqPp=n=!@V zp$8KMx%OVU1i&jS!<{JsKy{`BcS}hCKK{jj{gc1*-%QInoK2<@GAb4O6;rW4u_Uj3 ziG7S?4<^+X&vUBSC-TD zv+LE>#cGxFx=UToIh;l;<&<(lN;JECa#3!#2)+OWuv&N9jYxLuF5jeD>h^XEAXYSz zN?-6b!1urR*`NL=zSH+5=f+)_#W#`0O{~}5X49`$-FDO0T5EL%CoFY2LIiWWE$dY# zNmd%kL=vn9Gh?&7sxCs?`p2Jq4&eEdHItR<5tQzZfJ zYMG(%Rq|#U)7lnHqvUM1Ds3vJ0fM!m5<6f=7-9=H4`~0wHAzCYpsJa|l_`OMdj+gQ z05ecQfuvQhT{2g#UMWf}4j92w+~~%XOh&G=m8zVr?`!W`XTL38U6;xFHb`4#k}-B@ zN=6#jIem1sYRrq#QiOLUh_DfhDMo{8K|;njCQvK9nF&Dv6ECyn`?^8=xZ+(TWq=3= zgOIE`#%Q=u9Ucxzz_ub2#aWzH$dOE&>%~5e^=qn?G?>F3=JMva#MrEr0DSyVPJm2k zl5t=&G0{jyUS|B!(sxzZot@!?TGldrAxp$rHD)!x6FUm#WU$8bjb-j$9(^Gg+cI{i z6DhObQ+xr8%oJ~xow;|-?|hF-9qbs}G8@-Z%qljgbtcP+I6SdxdXKjWsd+HLut>+? zOY&gC{^etaJ!2VxT$WFZr5&Cf%YPJOnTI(sVZWJ+I2;fAtOtk0vkhUE8hSo)Gn_it zgNay@EI}X4$cw;Y;-X|YC$TXvM|_T^hx>VBFP2yU03ZNKL_t(|rV~?eB8R+5wBj)R z&3PGMp{QMA47s5|;9P2E%rMP^gPyYRF7J;M9scxSc#rcidM!=uVnWDi^7I!ynA#xL z__Y)kdN&D#v5e4O6y;SeJR_%mQwqw zT54at*4o$2tJh^)yw>f_ri4S&w#a2UU0hwz*j=ty>+b6Lb;>Dsxm$IyA9fr9uw-V8 zaIF{5uIr|+r2sH9%W2K^=4R{FBXj(X-tVr>>#w-)(uf~?|K-)?>hf|8033Ji0m&h@ z6oqXL+N64^uC2`M_01+F1CWfRIuI+kuu`gKNJbVnOVtHKqPau?#OF7CE%oVD2f&L9 z%^hhlcnMyn{lvr&CSt_|4YLazGZH!yH0kVZ%9DpLg$+W#F%%IHmoyTf(ToZ&G7mcub+l;1{>B8-#(!HZY5GDV0;*{}rwC%-aE z`8!3*2-4J~aR-tJ2fBmPt`^jnj;*6OdS`L2%q53)jhB(2AvkdK3MV{BonT}tRN=*P zC%G|U)D{}A*Hl2l2(*ouf{mA2`M`n75NgVhJf75qnwcskW8%D+x8EljH|P8)#cjE zp1yc?`Q-BA$z{r^>vHO{bvfmna?Y#Vbx8t@W(G1xl+<-cgI7VGm5S@)YE9zvPd~qY zetq-mmVfeT${A$b>uZ8P{^L)7?iYRr5D7DI$ICdBVj;Ei43~R_;O_F+FuS|C-D;f) z{OGfrr%x^bFe20@D^;mD0QNv)+Osw z8^=*o8ktBm3W)%WLC3@dK~Cn&6#gk-B1)8_zCA^4>sLhaey2#8K+M4sAV83ZaSDKt z?>0CS#k==#F&1E02}S6gMQ~N2!q$zH8TEisGcZG`NJ$b^p#*o(XwU(}$QZo-2 zTz=kjevf-%;BMyDF{gEF9oVzXaFl`>GA}bJLa1U##lw)^a$--x2pqe7y!Dvjw6V+- zC&;El9F}(C@>7l7f94a!9ro_CNYX4Pp6z^eB=M-R%ren)zFo|hWh`?HXF4&>3OVh6 z^e|oqSSSkEl?Lnz95drCYG%B4ynLE%#DkTnDNgkA^M}@ZoB?yWwShMsA zH~r1aSE{weF4>mt&8=52+uo~7ZaJCR^#@NRJ^%OvOV(YkyY(vNl)BXAlybJ5!fVNL z1_Z&9Su$Dpec1Y91?lG14M}&=%*>v>c-n9K)kPiyMO38rYQr9H}vqR=oa$+*8UTUFxF2&cY9D7WUJy0%} z`n~{@r5>8Ble-+@=+*+wc(5~q8h}A$Ng;DbF&pYaSizVXfz~=8Tzt&fqMU|nMzA&# zNrRXeWF@iF8z>=@0IHNJHEKrH!>U2mNplGWNCt_3)9A9;Hp*08R#lBmUJHwR7S>&o zYg?V26;~JvIb=DzD<@utV z_gB<^Eb$JKGT5JYLz5FpGR(V%DRq3Gjj>bPjHwE}n-J_}(2$eY-x3M;M#T8Q$XLXe zG31W2LFI4~JB}aVt`#~}keDW8T6~dz{^Z=K1;I9k1RdB^NE{9ELUkc+^^x#1SYUdvdqYihZ@T) zvomKH%S?`2fl-HJ+{$?P=)BCVC}39_uq$wv9;0h$!Xh;@%`jdbn2Fgld*{9IWhoM0 zZNzes0QREEoa4dt$^ZGcKKgTiNokZMZCHB?CDZm@sz`}D-TdQU{FP7s_un|QvW)p< z7Vfufdjf+^;(ivk~Ur(5^gvB#k%YJ8X|1FE$wP! zmzZ<<^s~=CczThNZTp(Dy?VXv60fgTIjm3!33arB5e6YfeNwiVEUv-c z%ny5$6--pY3CEa0};bVF_xL)oQS_O@p&dMgCnca7$Wx-y^DOx zqGZ1ZaiaxW%M%_<=OmuAi{YLyv8BMk%aqM5k$~oji-#k-v3NM>JrKI_2uefm2$dxa=E&?Fr%5RF4wW$QqIkUz`_%TW@a$EyuOUVWkfTU03=v4 zGMhEZNP@lITs^(IdUAPtv-Nu@fnO24dUgBrzwk4X;yIax&)SZ&bC;BZ6-gzztCVWS zTK)Cw&Gpq9!e(2B3$g0bt4}_^yvVnk+GX#$>|S%C#Paf% zr=X2CIFt*C09j?3lDtBRtnPqyRF%M-REu0;MnhE3+2}%Xc4pqT`gVf_Un7K}O_$AE z1BdCZ$xc}^YbHPJl6A>escp<;Mvxk&6hg3pg;5wi!6i4i!^7Y~0$CnK?EiS+og!rd z9A@PO0vR%wcx27dE{f%&6CG1X)=2)J2wU;ixlgn6qTz6nklR28}4xI6n?yUU1Uff~l zKT1v=Z{T=G?W_LLdymmE?`fLHMi{)nmid>(%!j)5)PqT9?7=jpX^ue$z2~r_9~Rz> zWdy@1D~?%d4>OiImN*l!^M;(k%iyG(UaaJiM=CV4p-IxB&CA(_F!xD(jBvm6#5XjS zIeFonotd3IX{vdgZ$Jv`lri6ZfyQLJp zoT2|q?oOAx!>g+IH=FJ2n{ZZ&jgH~kzTS9Srz+)a$s|4f=(#0JIbS`$USF)N%PHCV zat$J-!A!;oHaaE@B+Lk7&Pfv7Q%>$O2q2S0xT{-tVX3jb-I6a?7v1e`@3B4e*9?7M z1c9)`WQ~zvlLjm`Lb>EpYuDxK&g!L9xwJ~#z6#iGYf6@qk@eHhZq{8ASl!JNYwSlf zXerX|rr&O>%P%ih||16 zG8&L^RNOrQ7)&N>)Pt&0PA17+q)RP?wtxzl07G>d zay9{IJzZ6*MI>w6_FIxJSBc;l?pGLj8C-VhBJW(8MgglaQ*c0glHLKh+9MPpz#$i^ ztG|1h^UmO%B4sosGPFx^apY7;vPpPrPHM2&upW>QHZoN#P9?djSwc#%1P+}ex=<7@ zz#U$ZMh_-6YQ{1FD73CUWikd)RB@=b+J*6>DQX8Tn&Pmr81x5LP& zpB>HY)+b)>*)QlsFtf1kG%nFs?dcK{EaO9$yk-zq>^`<}h(D!{n_m zvJ9iV8FOmCqs$2ofjo=TIYj7Qiv=!a9_n+pvCN%~9dXFZEaEgTgF%e_C;!>M@hgAv z*MH-0{|9>+b}Y&6M&}s940-n6L*$tgH*(PCm!BQ(cPMi||Dp-Zmt`z--XTXPrl``6 zH`(V_7KoKc;$<*-HhYQ3O=f^yv4I&oGu18**_kohi2G;c$10vJq|GueE0QW z+MO~$+^rr7K(1t9i!4L9%q)KJ8^8D~-}|lKm`Ty8l>7Woen=u>xWK(^dU*BfUW%$m z92vO0R=KPC_VrCIby%J{AR1H7X1x61N$#?xboun^>iLtD&64Gu@~WdX?jU|(xTDO> zOtIo7L3UlHS+k^&dlev6T6LLqUGkbQuGZJjuRr+42S5DdPai#?Oo&`P%HVoyxV`QB zzFu9CL`va@MAmqzytr6x&`Y?ySgCsF*{i$9TjkB|mRxO%<=$3Z>WQ3EO{&#vvgE22 zW-v*ld!t&Q0^qZkw*c0wL_$?UxG7;Uhj9RCA!QIoAgReV0pQSO2~2Hd!U~3fB}S~3 zL?p9zXf(uvl_@&XV9a8pyORKr5))G*++l%99!(2+10)nae3!W zPE_OY)Q*BV&$gw>t|K_T{&;ho*2YFfVb+jy7?~4v%%iJ-i+b%Tyul-VI2UtftM{8Q zE%O*bGjDaYZqhTS>zwIY;yu0$NEkhsW^sH2U#Eb~G8W9-ma z=1}Y{M*KbI#5BF>X$g8F!(HUgsb%aGb3SEAp5Asb&x}dAf8vj^u}mX;mssFko+HLG z$9S1pwkZ`dJv_|tU|t4;bRD1IUSf3Rp+HO&n<+K3aCYpw)A~4UOapH;nPomydq)AFnTRxArd7n-#{n}kxQ3XBNCF(nCtzV0GuXl;Yw>W+ z%qVNoaA6jvF=oaD83{Q_vw@c(DbW(-ZEU$nf~XEA3V;gjH;a3F>1;RYypmMVvGHV-eVDiVWp5;aU65@4~b6Vc!x+CNx%Ld{$&VqHJw{(^S_)-Bq0)(SQC{95{bxRMKe)Y zU2vd^5mcRtVuBegoR|iCnaCPFB4!GAm;kj7^L+hq-&2-vX>Ffo;nK*|Y8gZ0@;r`+M8JT58 z@IG}rM)L*k&CZ;MJE*H;XYbRl9cEYUuX9FBpTlyi=SkzkM6;AC`dHv8PhXycn`r7jhJMm&fx$!8874kxx12!Xe)YN3lID6F2wW8+(`K&5UJsM>FPSpPh-bv%_OJ zTwh?O_vW%e!vFtluDG6U?&#M#u$c%hmd3)nE2cKYW6( zB{rK9k0>QeDH)lP)q(R9(7C+2ds?k>t+nfNDOIRmon+tFoN7+I-2_XLvsr$KUD!-MhG|Ox(FhA6Oh>GRSplcIE>x0i3FN8{)GgkAdd)u#+XeQ{7a*blF=mtlo*x+mY@oUED??bCNouciHWKr zkTN~2W?-TW4k1}`wavdPLD40vFe93!4-s2Dq4oZ7K>`pX6Uju15b2Srx&)WJ@uH{> z7phc&0`x*JY+Zlw>CIu6coLyFNNG<%E7hi?pF7xfl_DSpk9-NJi#F@v~c>Q|;0GSN#uH0*84H4$om^ zCYKzx>t0;Ui1~u`h_-s-tK<222p4l#qweembG~m{;9UgG0y{GfU(DA|XVNqZ_SSby z?t7e#Klsi6>N|h+uicdkkL$stGiZ)6bK{F`2-yFppaX$~H_z=I} z3FJx%^lFxOXR0&NVQ?oY0;>Gr&;BKNq4&S@U;L-}Vx3lX+_jw!Pi<+w$zAXCHm*#V7yY5AZdEBtQcxLo>@coB56h zkxK7fhgwt4Id?Vn#w}c^)k~>;UveVJoGe${YzlzSUu~|hR>|VE#F9>%zLx4a4XZw= z{RcoG!K#pS9K;n8vhBV}+_qmkjGlwun$pqb|+reuW7XyA^Kp$)tY352Z; zC7PKef*>TbB#4SAGaeldQi}rIhLYeAnFtlhBml)dc66$a1Q$#Igk&@&C$qV$R=MOx z7cBTMz_Lj&4N~6-#B*pdak^w?Fa-GsK>|H$ErE>Xy$c?Zec5beimOx=S8@N|%gz7# z``K}ua7CkkC8VVEywH7U5)J57Q2N@2PlErMNG1EHE zynJ@+>EU9n7O`6%J%$I-lH?W$J>G4jtA3Jmdu!!*(Ep-Jd1Kj z2bMTf52o1#r;TMsUgjK}BNQKzm%)PuD}eS$X4Wx_HZNxz!rUiukK$>0rtb`R%f>P` zyx3{tNr5xxUuI{A!#jDIz}w(ufP)ey5;Y!eRCzC*z(h}2q-HP?)Qjv4hH^$cimX29 zcso3_-@nIcJlKP&9abqZMv~ly?jrzRnGyg+@$NJVauDr4@>=yz{q_IY%dKy3e*3@t z&(}|`Eg6#$L#LJG9`ScElua}1a>{A_^y=dJDz8`FDyMa~TCehY)m^MoGRrBpyG~{U zwL%)axFpJ4Te^nEq6oD*1A#aEqfOYhRf zI@cPqT{$O@7`Om|0*@Y1!-{GUcm!ypTGBF;q(m~DiUtCO1TennA@FUEy`rtLEe4vg z>ykkX3TEJINF$U}Z2K(|iDcNOD7MyyyLUOY7&k-MjcoMlQDwQ9T9g?y|0DNI2rA8q z2~}=JiPdvQl3rXwUmXNBM0D~t%#tMmUR@BHZ4AO_!i<(^gpmrKi;$#vNN7TsF*Xp> z8AXY3z=h)IU0e5U^-b05uU2I1ca4;pLfZXnv3^7DjUG%&#?DYu7+L|7iM6&3m?StX z1rMp5PzpweRGVlI)7#16#NgsN)9oMuL$-l7n*1G7rqHbX;jH$vVWO!loSG+`g$G=9 zcO zF?VoM2lby8wM@_q`4_2~83Q$6JMH7nNB180hkRK0+0KIr2ZYNm|C@O*jo8sNhn6XO zL-c^!2;-N#1}D%;iPC0;1ZFqdr9YCf%$-=|WpJc~#U|`!SbipcXN?y5l*Oi;L)<*r zSf=5Tjb-*P*_A^;^K!<+_^~?-B5D8d06Si0IBwYVNj%{p(f{zvgQcR8^G9&tH2*Ki$*8I*_)Er2AF zHQN?~qcf2(=roZ%nMIfwV=_cM97_?tmlkeHiR&Qd2ueAp+`0%Tth#Ij!x;*SDnKGr zjG>$}6;XaE7gKZRvteT|B+?b!iMv1{nN@d^38*4q$#SMwMOYcH&O`|*+>{a_sKO{F zli=@001BWNkljj$-n^f`$Aj>eJeUrNhB0M!YLSwN#HqwmQ_sj@Z){*v1K`b#W$ujl zyV&HzQ!V9yms!jfXv_A6=deH(9pOP9$AhUIYR}g4ygNqfg0akj2(Tp)x}WQOW10CS z`!MqHxPyzFGVr$2bPtTCBZ=J3W|&|AFpGX|QBr@GlWfCN^M_R4bFY_ghFb2jmrSsTP%9eP1?x z-EOz9KCjzt-EO_siw{5i_#gX6%FWHq4}J);yS!LmU(gKh&%g66LxlCUyvmjm<$1kY zU#!xq%Q@E5B$)=%g)T{iyw)VtSgpETA_gLRSXB$y@iMU`Q?gjQgJITyXs<)L%U)Mz zx0dYc+11s{s~6w;5MMK_FS@5sFIVgC;-b5}T!*M|3vmuL8AEr~l1izoRo?cc%el*C zyY2g0Q?XJ#yGrq`m7FaWZ7*Nlb~!=1-Sn$2Rd)b#{pjT;e28c;k_;M0APFZ}36`3Z zb=g87?~JWT5DcEhj1fd84Q@(op`CNYks)Bl!~~e7M48ddg6JVY4$)-7_Qpk{3263L zY9A+D3`+t*W^@M$vrrBQ$io^2El3QIC?!Zzl}jMKh#&{6875Le+%P35G4{g-O_Gan z2}8xuq;w<=bWE#JjC5hAsR&`%1_!E8p$ZkK4}wP8RDZp#zxlg=h_4XdHBttEpb%$> zaV9{Jo8{=D4pPjfWXPsY^t96lVn3##Bsg4f5HN#fBtuS3V;HB3VZxXYG|^SqI+E7| zy}}p$m?M?-jCi#}oSE6w8X~Sl|->@?du6j`wwN_OJ;jN;roRoA#(_QHNhvcaJ5q zjZ1=)7BQ!>c%cglpU8(6`yK~)Lk}hgCKLU{G-fvrcQ{5~%reK}I7OU=NoPE4#bRYJ zxy5w;IlSr7%O~lN%gf+MLr#K#W=1Z{HZNxx!rYx_5s_z^+?nAa#xe#>xRsW+WGpi| z-kUjR*Ussf^H`e^Z;zLml>p2OnO=^`K*By{gNd}?T{synBNm1HhK*pE%SX#T6Qnyuosr|OReyvjK zX4Bu^%IoEe59E6K?Qebjvp?gdFWar9w7OiURoAVu*V=X6`f}~=Dn;%e{N%>~3i%qFMN~5Q^ZeViE+blXdGZW%hs2 zcU^b&S`{h;EqIVS<}8-Pn*CD(eDT@JzB3z;*MtjK|&Kgb}0 zs0i~C(Ql2oTz3Fqgc(LgCRm*mj7+nUu@uf~G^547ONj=UF>Ixpdz6_Wtf@$Vjh7+Q zV3>n8sfQGe^fr6AWCkFNXoT1R+gxHq=M`M>sS9u*fg+1HqZo-GlhMWMmYE56cy%XT zGN~4j5V4|Fsc9Ce{Xd5Z0e}n2BaE=exbu#Slhu%#juXjAH@>XTPne6{ed zkuo#RLK-L!AKtV@9P7l|nfI2mtCA~giVuP2e;U>>3ZpjBGE!nnN(m{-Bsi!GyD33H z1eXaGFFH7&i2;>HdSzEVNPA&r;=-1WJ!ldobg#VwRkTaN7YZ%iZ<4roda((+toK^Y zEMfF%8jHIB5@8ujg&b=ChdF>jjXQ^tIfssUKy#*-ED$vNl*9~&I_OO4KMUt6vWKPJ z`GgNKyJYNXhcwf4%>ZEf$$Gc0IB;T`VQI}H3GQvoKA7Km4*-OGSn}CpdN7Sdg8(c^ ziycjKz<{(2`&KhENfVd*X=53{j)=WES8|52%tYqh;ly-s>hXCQERFB+ksc)PvYulI zvrE2{Kv|;oXC6%F7|R5R*`uFGjAOYL8JtDR=IABI%wepxPW${w%eG9@@G!W{A+hpc zUIv(#Fe?OuK%Vn20EWjlPJ+h_XE5nov*~O;?B7`So}SGrA*y!%jUNBecf!m6w=nuo?d?V!Rq=dt@GuJ z5Aw=DqcIskcB`&i<&<;olI4_)UY&BeBL0E3n!8+(tCH>xQerYgs>K}Yew!U0kgCQo|{2yRD_vQoOngR;vhn@`KNxJ-dX9 z((Fi&KlO%VgB=@LRm~WXN zQSLBGV2#i-Wc4uTiN&*LE7jp5#Y7WOAObn+BDoB0#ugCagBkyEn8``SVw}TlEC6uA zr2-Y8xR>gERS}A?b=~y!kAC!M`!4;M;2k7o6dVVK-@`u_8H~Dx2U8|8l?_FVc7{qy zg4CdV$8ySP2{bS@V;K$Ky3K|~^?o%NdceU{9r}cXIU_S=YDO^}(qf;fnU;e=eL2)Q z_EnTg`jHcdtAX=ztjt;Zv21US9R;)S$mdwn&i61=uom^L6OXHLEr=Oeq@yB)QzfE5r~hT1AT2W}ri3tt9E7XfF1oHuIp^>P0^lCRM_W4t`H;I*-E)__QggEPx_kZFB$ZlA zt#Z*&O8xY+*EuD33HZSeU*&8m8HB#nQXS3YL;(7pIfqqCa49hJl+0ZzSrV9H=V=0P zsJcj4Zfl3!HaiD|3S4y&s~3P)t89oSfI#lDK^FE?fYB)Lasr$1w6LAQ;4+tt-V97P}_Ln|9mTe9b2eVPz~cRkL^HIOy)QjeI)%u)`{EMz{N;Y{7)&SKvN#&yH=!M<%QYIRMX-MP+^8Sh8$D2?Ri_h)gi7j$fd$><~8>W}+FPt#xy2sWG%U{0_ zFnyRYZ10302^94nZvY86+k?pk+xK7^`^uiQ9){(ZVLUsY8h^B+mJD0saz8MZ8S?NA zjb#>UwPY+at?Kzfe-F<-fR{NN3&qShnj?J5-4`y>@n?nmU3x3TY?@sZ7yG#u8JykO zB`2ocCHqCquHEr3M_8L#{#`MT&C4vFofa}rpH%D|YGyCP{yQ46Bm(9s_rfs>Ch!0S z;63h%pi#%)F(%ugFL*FTdJq=oaAVtsX?R^;L6h7exmQSJ6>BZ-r5{KcSFLrsskgWF z_O|xha&zN-v7FPzWq19gyMEF=d9u1%Uw-)1RyoCPz>+~AlYwBfiy=k|MpBp`P*NQw zjU>UHL|E*k+||C95X*t;jL->Ws(qn#ZH-S_O0bl2LbANdh>#wl5iX^aJSnA`)~n0w z>h4!hFRz|mefZ6T)ePMY!Lbb1h!XYgMV9bLvZtn8B}KZzSYwtdhL%OPH>B6F35pvq?c?z{r$n z#*{24%P!9*smsY3J4S~KkwFSunzk=6*>$Nif}~kCbU9hXnlWZ$4ojIJ<`7|hkt~}T z<(4ubk`UOYMN-a80jD^V3nDXVseGBfZ+v2Y_<#k{G z;M1>~nGD`NQf8Q#<)R6rCsB|cA)ygHIdxX5R9tS50BeD02qc3IxFUqiAn zq&onxS=a39z^I9u+Kd>FgIb^o|Du7Bao?qkd4ye_@f0x0HWY}7QZoGiF7pF0yXs&W zfjcqFGd(;Lr!>i^gf?3|L@W_I}%PdQ8hF3}nXpE##@|X zXK)rzae_d(HyyK7lpQaBhTRfqykuXYA0N(i&9KAbLhCN5Q8S@qL4)s7kh$k{{`WY9 z3l`MHf(H}!cex&M%!6slDa<^W#{PE>_dDj9@g|LbnUuDO6B&jQxM(bMqDqH2;jc7e zvEhqnTgnk$26r`igzt~-MXKe%pJ?HMo#W+nr0~1%xLD5HGL|`Z_HHDJiG&M73nC8L$#Bn&~-8pgJ7mLI1{E6v3&c^7$M9CcY^I#&) z<UOt4jcFybcyF6qz8oObkCGhbXu^$qGh~7a)mdQ1PI0NXQag z$*PbAFc<}pOgUMy)I3{Al2WW=1Y`-GSJhQYzDbu)E_0W^`BNW1|LEC|KKTq^Mf{nc z`_7;L3xDdv51%}JcKQ5+tBZ?O*X5K_ig>x8S1%>>G+MIU!QI_c*Lii%UCMpxx}0;{ zu&I< z6HvooCYc;mqtp--4A`RlGLdB4wuxY}6sy@}B*PG<`836)1Odh_M*(+4uo?+wbb+85 zGy0*if@AQU(h0j^P< zV6{QUH+Dv?-UH+aEpPa*33vzMiYgRWU%ap0yZ7$5#b4f*zxBI+gs&XlHBttEw41*} z)XL!w6I>BchO*#M617UMP{JJ52_Ur{xORnz?s8g!WmsZzxh6`_z{rdmV^f)YewaV-D`}FnxDHeUFq;gFJ7?Sf+6t`?(%qEHgQ7Me&F^%rs{=0Af;=c;g1SjZ1 zYA;KWoJL7+Masd0lR9RNZpdU0-!qm#ZgFx~t24 zv9@lVFIU!O0AVast%1fg9JwMfdru+f|o7-`;jz+H6ZaWp_!cwHmGKOk3O##t9dbt?N>kQ>|7z zxl60YrkTln*wScdRC9bvLtL3e62tagG>gE%BoM@p5hN*rBw+w)Bq)W;5(!HNSBp2} zE?P3ige4b1So9#~E+Hc1Br5jA&PmlliRKbKw#|mZvQwlx6;&c#*2yI=a=|j-M0J2j z=0c+6?m&=1F{bHIjA0IB{_r#;h>}ntha?oKmx|Q8Z;OAvt+%EA(O1z-2JapzyFX z_8l1+B7%PH=@7dnXqU23^1i+@E#V8s;jO>K%Df@&`ogxS@lOetMVw}2b_)Xomgty; zlFv~G_tyY{=^JS>Z;6`033lc@EIz>V>6k-(_t=tp5j*XFroK<8nd#EyUcAiSAia-A z(sV4p$Kyc)z6=khU4h*FSfo+>z=LU42(UZ$H#U|T^E|+b=?!@q9JcE?wX~1D0?#vq zi9CB1oUB$k-jURd(UTfqa7Wy&&wRrG;f2K!7y1& zpT=T_*^IMC*~HKq4i9OU@l{M8@N}A(5j252hswQhjD>mBo4m&i%@zh6e~(;DcrXD& z_?Ue(V;Pch9fuAk0jhm9a}c%4OAkbd->q*qbug1Dx3_h>RV@Hk*Vh*xd~o&Ai}kao zX}z*;omQ)~T7fytWKxnyNr_UkEkIfu`d%(VaA$Zp87NVt$1-}TzaXhpsT4_G9T;XW zg2p7Z55s^9xyueF(n%w|1tg&n27y3`0R_=>)va^x^56U2zqh(rEj+Jx5a0QSKmOLY zKY0H9@-P0&KmX*(#i~nJ*B6&ptILbk`eM~}sq1o=bI!>ug5d(9yH=~N@5{^2Zkj7i zd-AF4QkU)GV)gp<=Hg5Y0&Dh4(g9#>hHdVoma1l-a%BEza zcpPSbCPcVjGs=YMCZTWy`8rGpI}Jb+)nS+hTJ?s2GFW0{G|9PZ&ZKEmjbu`M%X5gu z#K7a~D<^Igco&zW=Mw>K;yfTVMbarJLruiqjGWK8Ac&|lv<^gZ* z5$Twt&wsJ&o@~_K@@ZW8@a}%Qh}pAhgVzz0hcEdqEv^~f<0K^g^e_K4`QvynMF|h{ zV45*6(;tVIkB8&scftV^v_CR}m)TWj{}v~VWu`c0pYoP?nZ?XA`2Lt*rcvhDD^+J1 z!bBBMIW)axW0^7cjA1e0z~E)aP@GW|2HCYh$J}K;^N73*FwZu%bD2ulJ^p1v&CF(* z!|7>eID<(qnDxFx#^en}?rB4q!w&o2aOOT}!}H|)d*&WY0I1+pr-^NhCYTR>MSv>J zOEtZ6ID*>WN~kR(9TB4kW_ z9uOfAlq#B=2*ZfM=tE53_=^|;LUq9<_flO_Elwz`dNQD->Yk+P$|MIFJ>H)NnxTy< z(dA}2L!Ps=DivP4RsQJPAJ%3Zbt5sgFI!j!w@_HSNOiC%|oRUS*Tq79^FRwD|@{9EO%iEF?(7UUY+UK0RocfyD zo>r{Yt1Fz9%m!2CfGYC{OTJ@ByDvVSx>I58KbneVRv#^*EX&0-AnPdh8U6aEzNP>ZY z0ZfS`-I1I93vIBe0e5VRoXI4y0!Aae##Y#%d(e$xphXGpmP`WOC5mzhRbjj1bRsB2 zMEvRqMx!Et!An9MBg@3djf04F-PTN`FszIrhYI9U_5Yu}caOO($?k)GYgN@g=X>4m zd#7i*XL{P6=`kKV8QT+kLhKmDIC;2$hIV8?P2NJx~}PV9MjdZr)u@qOPpyK4RTW7U41ea?5j+xPZ#&)n7L ze!X|?Rkf@3u3EL~x7S+5ty!CGo69!ab!(T~^2=Ym-0d)59`s%UWh!~UCP+Fk46_bB zA&ep_phDbOCZI$mVSrmQLfr5L7}Ru<9vRp@m}sCwHg4zz>3YqQ>OGolxF3k6rVfW`SdMhxITuz-_GFZyMJQo>$#18M{=GXb z0taJ6WHGG00Wj9@yOq!WwaNmG8E#~LMqdg#;K4M3W2R8f7>4K}19n}?<~?<|#A&y)C1V-S zZhMSnhKh}Z@4SQ=0MrY={kY8dMnlH^18K1#2N-}l8Q#)lW*H$G(+qH#pDrUfliDrw z^pnVbo~LCjb0qT^xxlVO>WbP6yUfyeB-?&j_n5fMBHOGGU|_%)e;G^W*`a-Qs6p3#&9;7Ga&<{nHiFbwaeH>=S?MiC7FAR?vPlA44W5d~Wi zxy`gfX0f(hU)koeY%a_7Rk^y%*H^aPKoFWHtXAptB%PhclQlF+PzfrUJe;p@TvLi@ zCk>?&p}9arsyLMj)QF0(TQvxL0w|C798ifrSi!1Qiv7JCPtj zOvJZ*Qb92WkphGmN-i1$MKy%=>FVx__b%?9A92B=3Bn?W9yT2 za?-4tkeZNEh|w)vLX0t{5TnKrHAspv1cz3%TFO*e2d$PF(yXFLDaA@DImeW;dmH&r zRRPfKGoO9q-re(*;%XJY=GB){iXn(fZ?59q%1FAqQ_jW6?KbCJt~dGmy8ZO$-aI*J zniNjf>E83FC0lMSpU_B>A`lhTq9P$!P$@x+u_)E(rUV!3xG;YTt0IK^mM2*lIR9XYr;p}RJ01{|Mz&yT;fuba6Mo}YB zia-z`6e%VuY68?nw757glpx|Q&LjW@+%AIRJ2%zkcsHK}m1=hb8eo({7NRw@hOKc^ zxXE?jr7u@A8NBB}nIHU*{*OQY1HY|K;WR+N4%#q-6o{%u(gY68RAU(uP%9Lp0Pevw zHiRVUrep$Dm{vD7p$IR&)nSCWQNFi-(zQV!x3jq;eg1HRp+=7kr^K!Nqv@m zpFVG-8*gd{Yv?IGyuzcuFOP}))uyLSb#m3=csQLVdKC{NG6Qqhqh@N~A`Ij-R<#!t zSx=cN?wG!jMQFz90P?a(hu|1Y#>-wfX6KHFHQ=G8eMSP)M(Ol>^=)Y~){gUCvcY!d zJAU7f@|Y~osH0#0U;oU9|Dk{WCN6&uxA9==PDwFg->Z7rvx`cMuR8TS%gpc<#j(O{ z7qtQ4#xkSRTL`&PTb7MtW)R7&@MCZp0EbOxdRhdkcF6A_-N~)P{qBKU4jIcV(wt}M zEy}ckb9pkyU-q~u9lk0v6PxKhWe>=50EO9?WlTrnGU%rDF%mz5&kSJ}y5>&G@pQO@EKNh-@qK^pnQVXJR5qdRI6JcS!Ql&1y=HA!|iovCUcpuuWh?!$)X`NO*%VEr)P1!mbB6sH3Tq73;-;P zM#3woqM%U<5Cm2-dV?SVO+;8Iin`HK0lM{aU9*@m7n+BbEv4i_)@Z*ugb;n%&uA=# zB#a1EoJs)_V3437DM7_dsSK)a)uAftQGxNHS3h|1{(BEzdmZm$I=eXex=+0F{Jpc^ z`kkLVJzF(Rny|Tp001BWNklq=T@uOG%3cAV#RAjpgK$pB+Y1M zW-O)TT-t1Aw%O#i&6{n$-n3WOZ7JMt^XW-*@A=bWma~;o*O>r>-eXN>CNVAPOM_DkMc!U1Uln&>*6!Fq7aohhK$z*@@#)L0C}zKztQOzzhKbQ6sgs z4XOaO7*YN3SZJys)y&*{rd0DTp@1enCO|q2sUU#@z~c%729$1jt3JvFBmv*bD!1le zWsEdqA&W5=+xp+aZL!O3ez3`Z`&S?A^q?n0!$*rR9lggu82~WWurgIx``c#yk?g|# zdYB^x$sCn15s*Rv0tDI7L`l&AQ)sO7JPT;uq#%$U^WK@sxd)Nq2$5l@a}3M?Z;!~l z{Z8X09Jz*Y+IQ1qDsv3a)1$>^-m2~BbjsmWCW&K^MHGvO%#m=+0U%~fGqxUva2mh} zOqT>Qe-{U!nHy=hs`IBhVmJnlIsQ}R0uCj^P#IPfhd*vF(uiU9Sa_4~(ldH{B*fG6 zV4CoE-OfgRlshoZ3||;eIkE?!@d9^Y7tb=_8hu&2D8}uLWyW+QF0+&C?cy@Kw0Ork zZZborrp7XR5*MmA=i!2}%*^uTU1Kbx6A`@+;_0({%8-5pE;A*6P*wFj2A7#6j>;UG zFs=5C2YaBI-K!YQAd_b`dId9p#%Z^wz8%^(4c%5}ct(#)M|v=!18B6=J)6{M0x_eI zEY?!h%!+L{U<)(08xQVlo2#<9&YNq?t(B}A;(C?N&eQ2xIy()`X-G+flA6v=MNzkN zBCUuhOGZ$DuHTy(f`{YnjAe`>V1Wocvah)J1_=_St~)!2nQT@HO*AlzXe`An0X|eD zs5`hA5&U@onMid`fw*&vYE}(VOAH}~kOCzKKt%{5reMaDLeAEi+h~lTY2yF)H~#Ludl#>M^rfarO@)D?2aD+dE^)b5AGwpU zm|15plRbEJTduFSZ7!Q_-fY_KHXFCO*!kJ&xpT9M=Fp0f3{lqrMT{g-2qP&d#zGI> zLZ}k(sg#6t2oZp4BjXF#;L&6hfPfCydCSE?76FVQ3E>~L0st|i1~?EC1oXM3dMZ^( zSPUS7Xn_HaPDv>qHAadF2o<47g=Pc*g+jPR8O>D(n1UX{SA?Yw9+DK0D4^Hg$O0BJ z`>)ux*rwQZwzsa@`&aEx|IN?u?^Ex2dJlp!9Aq1f(rmV)O^h-{*$D!#+Tmzj_==?f*t*QX#%L5rP_Xor2^y+}fj0@L+v!+i= z>rQYi?NfFY*aOY@-DfZKw$RMt6P`S-=}Knc>5oUuyD#zh4moM5OdiOjIXYSSEK;xT zk2BJ4@mP9#j4aRS_O$51L;{##2V)N=1JD5w+yPB6f@!!1`&h(KW;{2N0Xl;LfPKa? z<9g0$k>@dj%i!tYG6$(IM1VO&(s9gyl4E$Du(6EeGJ|BP=)+W=>_bv!%&toeSNAwE z)m+^@_kk?O;4(AL5e=UN592bxytd;RWQ?keMe%HwIR=^;Ghj3Y%%-Z{$zlN8Dz+$Qe&D4VlsJ1f@Ku|xtg5BB~fRTpMvSe7xd|j>`l)19a zb=h1qXKPytA+A>Oblsevr?XRSPQt3uCTU8VQhRl^I$OKrg_&w#hpSUSl7&!lLkXDUl$l`lx=h7By+q_=I zZCke6cC$&FZQg9#obAoGuFP1B9zn(%^5}A#99Rm{77N8nP&1lGg9%EXYwN-RA*uqw zcbf)8MFirHh(HK1gNOU=rY$ib1Oo8*zT$gk0|3n9`x+n!0d(^j0Yr+?fgdvoY6Pr! zq!<)b(8nNFL{&)>x*wAeA|l09gzC$2pEW=NBHn&_3VOr_2AaS?p%kJJh0Mm*xOGsb zEmv*1YRgaj)Gsdfs4p9O4}vlPT-b4RZ2s-Yp_zi1gq^Vr9hzaNrZUxmu5O&yt-QN_ z0*XR9_a%jU!PWcLg?DE!Gp4FK^gc=s0PY|%qpQFak*UqGzBKk?ygwR}c9fMFjjtZR z#Kn~P@GXvE`0}6`2GKvFIWAMX)J=#?$FI}uFW13)gB$=E3I z89g;(_lbdXI@wg0t1JKO8ITML62SK=G9W%T0BF_!w@W~j<^ryR$1sDbK-G`)%nTN9WT%D)y zDQy9c*=JC*2d432j=^QddB@mF&pBfr%iGy3a~PWG8OA)=nHP3djvMF7ThYF0=ouFC z^l6_56HFK?Peoxsj>hI^|I&Z|(zpLEfDov$zXBtAqk%1uLb|aGbM}>nQQ%R2 z1qA@ubzuQOW4q1P=8_AXTcl)zh#6U|%|%snVIZ?H2}u?szzm52h4kr0L<&WNY7;f8 zgb-4+Vg&x)&-_2HeCiX;$@=}Te)zRt_!WFf>BX1d_pz^k`4gY|*mHN!&d*n?brM00 z+N2O-NKsM>A`*jxG7_}z+~fO9i-^nl8skO8cfuACfrv9&QB$=*gKpEli6b(^mN>Z{*@7<#DXMnt9-=!rcKhK@9=;o|!m6x@z^g3feT$13;) zm6)v!1laSY4x=zV%_}*|H33}uE7sa2fOv%y>e%jmgZ6ZN>=>Ix8H*X(aw%42U_dWO0$3OD9zxhk}Qqbx7$v6D^ulaS~_VGJ+&rVL8lapq>j;V<$ zh8TrrA%yihgrER|qHZ-E+)_qTP!WiVhmLVjCN$ zNV8yt|K`8_BVYAZAGmYpY`sqJyLS;nHJoWu7p*3^GZaKbL{!}1rjs=llW{1)J(%L) z#MG9iDcfzEQb;kbSNZB{n_}2(^P{Wnwk_t-g6cL=@VYlCmSWj78xui701XmUL-Z++ zP!+Jo9CqNv=!RFIRdXJAlp_mmvO#l+2ON~zzn3dI*S ztS(Cq&1is8ghXKtHp~K|*02=7^(t}WWV!_n34oAXOx;2Ps6Thz0!Yh7BePj+Y_n~% zZL)1!yMNXG?|=(#%kD!u$A3MS?WA-!9jhIWt%7*Q8x@h`>9K19z;~cV7A~jy6G;FnK1E`U`MmbGicik~xj(%g#W? z{|@Q<&15|B8NDOq9!zsMh5!c0zzCgsF!dd4SNzNv#xz45IGqgql(#aL8Rg%BGj0L& z^fejZ>z%=6fce9Bnm1?pkxmNU@lKjveb*Yx3}<_sm}bl}1x*HAW}0h;%j{w<2XL8L zQM*}2w2aH3lc)m(GoDb!b~ICy8Q>PsOwWw2d|+O~0|1TF6I5yT4Y{YMkUXO))xEL1 zcRIsjgmh7|fX(+3knDm^8cShmkz2NH*=%jQvF*m%t+g#nwwyJ_uv%#oIvxNU=EELs|E&WKWTE*8zJiafGA)uQ-_~L-{y_vTyiz>vrI&!25Lzx~Z7xOt&AMsZ(zYc%w_2U7 z?*Hl+&tJG3Qu>nE%lz6;ea*N2mTx@2vwr^W`T8_9O-fBzH6f*-K~s=6dV{J@dMxgpMU5>_s-8xPfu4D7bhvj z^{Q!_&@?GU2W2Ew$0%_xFc)5RwxfX{jf7coV;SGnsT9j?-i8umNh!o_n_}FySyf|d zJ;qJWc=`i~TI_y)z`HV*}W$EksuSx}fqlR=N&R5*s!kWAo9 z-EKlrz#kjn(@i$2NHHTi4VoxcjgWF-TbPS&vu$&^YReZMZvLB}`~`dk(0dS+abkr$ z=m7;-UpMG7+hBcU8LF@kf2k531XMtQbTMTBz<{Z8J*f$*GLDhXA)PwH z9Yki#*$eSw5Sjji5@&xsm8)>)NN7Aj zjSasoOQxB|7@8ECW_9OIb9O4BfoOB*PDr8dy99`ch)nw=N`Yz>bakkU7$LA?R9~V3 z)&ca8VGkz27Xh?V4J}AmE~U+-^*x$IMAjE)9t^k?(pZSnmK=hJkP-wXlZm2gf*>$J zSc3;|uUija0+G_@+~(8Mb=huDp1%l>t^fl3z}LL;+TZz=-tyzA(u*H{;iDga`6FNZ z^4af&p+)Ca40A%7};e6+ste zMq>!#{$B=?sscuj;Pd_8_h(*tAGD^(w8`&DrVN&1BqhsoUd8D&hmEj;13T zfX;o1>@8)?JhDtqu_?CQwryKt4BObot%e}+T2l;H*X?H0wz*tgw*sUT0CdoglbVqb zig<9DVir<>>j|PHgZprS5QtAxq5_5#0D&32!bSuQm?3hUtryCokWMo zl+l5J9uhg6+CIF_^N#gpO7B5Xh6cJs1n4MC@4@77i~(a~8G;nIY^!Q~FNx;rD`Db@ zj1aVLIV}M9D5N_Y3<%KuS$6;oRT#rDefn^^bAaQtkg5{x(IKW9=i|_>G{3P{ zk39jr{o3A=1{pll%*N>xj79)JM~RDwOugC})C>joxeh`SJ=u5_X%n4z_I(vy6Be}m z9Yh1W1L8)SR`?hLnaM4d8@{g+LkF7Z`dS~tksWLoZMm(P3|z6=ZzgkOO`g$|#HIQZ z@?atbqZq*BaK9K~2V?2NuJ82&tiQ@>x7>rLK)Nhmg578=Gb(xq&gg1*U_|Vxp3?#E zp~t{w7TIb#_f_yl@*<@>dAoaKziW(Tri}YQoEez1i_3HwfGPjJ5)J`3BMCV#9}`># zok;AnOdy!?gfdR=hLh76%9%kkyUZBrDq}@rHvjI5ZtPcbZcB?OjMJ04#WOmBe)g~Z z*-w4rcLU2FOfK_!=05SWKlR_f^c%j5gwl$@a*N#3GD~L3B^S%BwOh-rBQg;2IKFXx z8k&{FRamd$x(O*kgT@HJ%n<;GfM{7XxCsn~r2ssxPD}^Ai{U7;Bf8iK-1L=YS)bP;WxKV4OW`puPSCK>>`HH{D` zga9F+lzKp-cuZ;Wcr>C)QdoN5C4tniG6p5+;bvgS1%Rm1%%2`fBa6}ChGQ_Y5XF#5 zfwX3Av9{Rdrfjoqvpw46KmV70X}O1e+0i>OET1ru19CyfoX2}G`HIL_M;=*5AS$Fv z@GeT0zA%vrhfi*y{(-BDDdW}$3kxkm6tJaBetij%>3J97m_B7l9~FKh@G+wU&^0W5Wmmj1Un6Q2&CWs=CQVL}*ru(OBA&+g!G7Xp+VdQnU)J(EsLb0b#{T zE^(ED01&efgJwZ++@EH}#H?f^d40Wo?bENdoAwv}+Aniyvvdtg* zH6Q!TU;jIJYV`3>e(almDty8u+hOhSM_tnQEvBXn?C0(>WI75Lh@TM=jgNQZAo7>6yH z0Q#0s#XN+K6b}^~AVev31umeQz9>Y*S_>+Op%`IgHUb0;AjALz3qhb5ilLB36k1!@ z7H--aP3DUaw?FZf;ke}V9t35aIKnt+)f|ch4J6aoyU5PQm(qStLYlE(HPSog`o6mG5a0BgVw6{ZK z=5Wlc{~dS;{i0|XZ?z$}s{d#-(T3;19kiv&6Z&%-5E*mKw*y-j5^qzPXLL&7B=A=5Wjn z;eIFO?HJ2=;tXNAP4pQq7|UQveSv;;;2|NyDVMj$?r4UR%(Q|Th%<1F9D>HP{1`?* zAj!w4$KW#i66+OjCV|m?cX1Z$vI9fq`}A3s-K+Bt0~k8%_yNv(-l7VGt{QKj78N9n z>h_G@DRN_(&I$%V^Y$w?#0z$#&_0KlXe*k7=ky6YGEn9Axx0b8Rk~@G%jG9td zt>U^vWRjyZYw!GIP*qW34aGtKM~^I11x!sW1*#MQhyqdR2C4{rnM!sYTS$_GzyLQG zC?1cb&AH97&0ICP&9V1gB6FdrwOgvfLLiU@K}4*Spg}bQVrHelYa*D54`c|&*Lf3H+H5VN?>0?Zubb5>t=CP{q^3#kyA)%H zK|%;_dDD3nvGZOMkq(hDvk##kshnL$`6Su8hZ!&=EO-|=46^SV#sF7}lMS%eU0u_M%;Djc<$W;|p(4^aL ziEc6j_e(MmR24HoBnUjdZvQP)l?1w7mSBL^;J$#+eV2-dp#ig%V!0p}T5*7p4#9XU z34|!f=3iaB^5%#P%?LN?DrVVO3fp34Y>jQYe? z-P=sl7uASG{Tf}a<28@PyYEw&zT~v8UH&L$`kYQr#}Ldoagr{h<0V9ol$>j#zRcNl*b?g zYNeLSKc?iq{+tY`Vc0zlTn0Ow*ePw^mZ=1pLoG|zF|!&yJ!6@t=)|-DulU6sa71qm zE;GIJZV88R8DJrCNASvFS0?oJ#Jq&ZfM$UCGwEq2fft9tV<;Xs4kt{j(bG&lct%f? ze)g~a*tdMsch5YSfErDPJF-W}vbki*CX~GO=qk+BeaTH`z~YEZ2tiW}%}UcrSg*rs z9hy~Wnz{+IsxSJ9004qw619)=OSsH0?vv3;JQqb20X?P+-OY&*h>3`Zo9I(jLr^X0 z^yKo5`=!lREN|Pg&AH8HmbWd<3Q4jUW3iTWH>D7bWKhXKyI~N~psHeKQY`1(UTrp) z*GA7JuK)lb07*naRM+<}FCSc9KiWKa<3T!E#Z}6i*0x(wp;1F%DH=pmRMqD{`2IIP z`x>4oed@P-@wMJ4^(I5`W1cieyQN@>20VEKD zhNvPEl&sb?4#4iLJfhe9zCi82#M7yefu~6YcIe2!s*%i1Mh!6#;{t&latkY zog9&An&dHMLJZZzNJN9w$iXTx^zRYuyq12=*MAro$u1X|pEWr3<7fEQ)CTP>d*B@M?kff0Y6rvDjEQV~z#zDAA zMDHcj*BFcA<{!;q@tja32AdzE!)3r5<^ zl-K!3Dvk_wLsvbH5E*vjyJx_`W81@UnEJ}h=zxOH<4k6bufw~FT%D>w%n{A(sb`iB zZP`7o$cqlo*85CNmyW0FNZR2kGQ4y;tZvVSN~v!Bi&(rt7AI zbQqE8uMQ7f$LT{;MyrCu$kh0IHTova8orNq#}sYSc8;K#o}o)5Z(}BN)XRKE6C(N1 z-~Rh<;+hjF297sZax#iy_^MjjMyIj^QrM{L_u_C7g6R(y0TjQ)+ZxM^3hz$kNMo5X z%?eqh!#ulJ_stsz%Gr4`hmB8!7dQptki9S0f56cOm)T~At*&cpb!KLMZsd-Co=Kx zyZ#pj7cpYRt{*&Vw{2;2$)&UBk+<#l_tdGz4&(f!NM|MIW?`hVf~l+D)KHW*39Vk9)G2GyWuJiB`bf;%tXyZ@`N z;|bBP`;KpX>6Q1NU93;f*C(f|W}Q+KQwk}C7*!OZbLvsm5d1*YqsfHoyX070>`on3 zP+>};8?2%ts?Z>Ss7jN%oTSHTjv@ToSHJ&bAN$b7#mT*UcbX=ioHVDW>(kSwX;QOF zO^T`e#Tfi4yN!&3s*+-0B_r|bM_vTb%{exa=W;VH1IQox{_h0vNB-FVK$5_Ao7-I8 zynl7FP5^RY&Q?-bH8B@6qnR0_o3j|J8L$eZdR9SHgcLwTg@jfudE~n!B7j*{fDEF6 zpp+r34BVTkblyy*0Mbi-)WE5T?_?R0+6uvHrqaSA}Y& z7*RZw44I9^tTkq1TPzpbv}M!Un~%2t-A_J6_qul{9lZd1*U%6C>Ho7HaTo6(+>p#l z1^hqU=Zv>WKrxnz8c=}qhN>>iQohzWiD zCv8N%HXj;5&v1lUO+Sc6oI|d0$QTZ2sq^$$jQ@Q`?-c#gU;NK*VyQ%eCVPaU%wF1o zcQ^oaQ)ykV_9@dF%_Jz75|1PgkvprP?YUi_+}EeK;oS2mhGUxsU>LK{F3qlXJj)H_ z4)d(Ed0VhZ6DH2NrdQ`po#nmF>9l9e?t3`z(8xupyURF}y8HNtS!`(n0u~B9FlUW< z7TEUH%`eI@8&0JCsKQm5^7QHCIKG5w1;z}sSzC2Jx%4Em@VhN77Ba7xE>vT&w$JEk z($D_Y9}`kX5=0EULf;dG*uMFB903dSx z@M?Q?ef8k#^18{|N4V3e)jdx{Ow=)*5Ch6Yfz1`Za1AmT%~5cN~rJA&;mULQ1h&rIdoIhS)g@1*sM< zB1DBnLYJlb7NxL0X<`$bRa&jndYw+sS0|^Rf%@vN{=jq3ojrf| ze7#O5r>m2b)ye6~eV5j&wCa9M6H|&!6W6P>T6vkPrb!YUE)s~o`sxQenIJ$XHplW` zM(GFX-}*z}wOXZBlbRF(G4kQ%Rz&{pue{L~E5$b3^49%pGb#?pnjr|_ksS)Ob7BJgiy1jMkfZC>Gm#`NS4DyDyZ7X}qL$|3w(FXGc~0zo zmwH5o!=)KbHR$E^-P2jhu3+Vo3CzAvpBR-k&b6rW_cULl?63SHE!OZIqy9PUDuR8j zM5fS;RKR7XwCy4?I&oPNz@3$d0bA++VrNwVfWU5qh5E_43wAJxbfCAlml<*$N3#bt zL1d;3ecMOrqrp+(81%HSw|KgU^ZNjsf3iMAqo$4fYQ=3kV&}NDL*y;ZWXx{+Rqz=( zy?qa+ai<`G5wj6vn;Pr7CP&?}7mdAG>QiP?bDXOuyQQ(rKvD<8MDBKz$^n5rFn5KI zOCI0x_|2Gbuywn%Sm;r8K4{@GKizLEvvBq48_USN;hog2XM22(hV&_aFOzjA=&y{w zwT=qxv&a$lGRthE6ku2S$v#&mU=Y_sg?Gy7M^0BIO`7p-ut}F_6q{E^RJt7TJ)rVw*?TF$Ld(h-U5eMgb8C(RaO}2to`&wKZcYdE1t4 zTXHGe_V4|_zxcQQ@4x=f{=}asxtIlwMsrgKh^Xt0B;neq!18%%^GhH92;Mfm{OSik z_6@I|-8ntGI9Z*nnst*J_Y6`u_UY|VLQuDYA)&&4A+PG@N&>(?o2x=aD2Q5t8@Dy@ zfd~-<4G_mqhK}{vv-NtF)+bHVgr-TUiK&V1yA-2uy%a*w z7-JVXSRkO$P`>J`UaX>cP^Wjq|BQ66u?#?n{*iz69|iD(fB44<TX zhbqiOIt|A8Nfh(Fv9GVTpMUdm+m_n&UTj(yGXb4opNaH-=A|`BA+XMODI45C#-W*Q zwzk++TQ;rTziNN-uYCsZAj!K!w!cR~83#DJ9zcRZ7uuxndkrh_g(?jcns{)TIzm8l zP`qY{Ox4L$V5qnZrUS6DD>w?RDpE(_zSr8Ucv0gG`IHtP{HJK*v>Ml2`%*WjB z$0hAtQ!Kg{vd8SLcb~GT5*C zj(NuPScpvjfqFn>MxAW})A+rEJ~uis zjm7`*!DV(CI=06Y(l>c9P4329h4P)}ed>{Ao{$sM4*vmM#_6$f8DKw~ck87|0e;<^ z+_eZx(99fs8QrnJI$@Yf^%>ikvtHUWSs(u`0s9M?@a*Z_U4_5npZYKPz5m5B&og?O z)L}8g(MBD8qQ?M!^Kbv`#VfxCA<(u~5t$4cO#l%@C^SV4K|^#`8A=yNMkJ)h?chZq z7-KO4u-G(+3<$uAs+yUocy!b*JOU^b2wj5_ph5+z5;U8UEXAyt2DO|m7t6&$D8)*f zH;*pm&bb+_7>zJ1xp?1v>vLalBmx4EK@}j2-T&fSmv27&9Y6eoU;Vr6H+n#t(&Hav2jF3LsZm_jog9=5YR)$C`1rcVi58DupQ!pb4rKKxDgE~68k{B5D~Zo zR|pCRXn>UBt1o`v$3FJK=kK1U6fZ7L*6SMIceQGoRcxBLazw@rd4dLyDicBoDk83+ zzxcts045ssm?mh80>?Tr0hQ;E|KZ>L2mg&fC!*Jzyk4cZ9$q&ooSm$;*+RD0-@3Z{ z+=&Kl65TL9YM|fOLsp9t6n_eYgDhgzcS+16^2#u6``%0>%_Iov6LXiMN)W{$Qqe{u zRjxMW!NcpTO>RpszyZ`LXkk*obY4Iw&qY&;l#&gZ%++6sxzymk+iaV*T({+o2iyPo zFa09EBI&&e%8ZHZeg*`GU|=I-85K|l1A2Y_e$lc*zz-2H^e=J<)Fx$Sw5ly}#jzJ~qh6=hqF@t7+ zIXW|GSKnV}4|4iyc6Fr6cEoglm=5$~*}3%Pr!T$h>F0j_N5A=#-(z7k=ty9`A2v_{ z8p~TSkcA~9x5$|^mt>+46^Thxk`P4$5Hv>dP%;`JKo_9HsHzaqs4DcZzJ4EoeUxA6 z0sy;q2ko>miVCY|6GTJ`NuXHOXf!B^Qi^YM?crm6Gn;m^4NY=H=Kkm3@Xtb43XA1! zyLot7+DtPmCZwqR)*t>uB$NiiS2+G`C=!BNQPmn^m}DU%;8rDN+n&GgxvTq+Tr10M zD=OSK)^4JYed4317pEuZ>t@|Fs}xhv7-I}EX^cVLG)6_eeifl|!x@N(29cmCR!^EJ zRF#y1heq*{7lXtg0D?ekU>Yb?lpzF%yhNcAMCFxN-v9jb=j(M7g1Up!YL!;&v|1;3 z9qL2LM2BNy2tgf}AxYwe7oM9x3mJdo6rjgn9v_$alYjJ|_yhm?e_b*zueK@b_4&HZ zW!qXxRtj^rTv!UtXhyO#Zvi~cZ&3A!O;EtswL!$?b5QALYa;;q-9@_Yiy8yCfT|#1 z#n&t-1prjrP5bDoeY7dA-cHsC{TSrV+F>TxJIN`OpjymI=e5-4(iUs8ZL@8&U2gN6 z54Zo*PyYhGQt7=3%8;OdGY=-xHE&LhW!N19wYR}Nm;{i;B_BF{u2(MV_^D(-uZs~7 zh=78j1YgEGTUQ2aC4v1K#+ZNcbTOr;`RZiHKxQ}P?c*|!E5V-t-AW((MYPMS0}pS)WqO5sQK27|KhWW4`$Q3$al;Qh zgxTuoAngny$4%;~o5^r!H9N;8a`q_ZW-^P2%rkm&RAV}t+l}n^U|Q+|?v^_9Y?}3# zvA`S4cy?XXhK@U@z9IINq`yhGA*$06N~5!x^l*}Z>Bz8{Wwz3SsW)v=6yy_hkowofAo{z{Jkz>dn`uC zPTWiY8s;sKP+C~aQt0s|U0g_Yae)Sn5t<}K2~k2IqJ#*d6r`qCu6kpcO2s4yt%_^u z%#{JaEodC95dk5^RHcXjB+WorRd}EwWD%N~he{PGxscS9wpW|DYRqW4eBl>AU2?g; z|8RSG^$-5Le>g-Dkq{vg!(l{U7G4#db#bW6Vh699!2 zw_XSUSqve7yuRGNdAWJ((Z)9eo67mo6yO8UyVgE*rpu^7dK(!JBC~C6)0Vc_^|n0P zl+S%dhLY*&y$i|^1lS+@sez$lG-e3WZIbQrxBz(Hb?wB}ugP>P(76U$N7Sg(RRzvE zElmMJWcbyxZc-z5HIFgPt{!F{^UTJ;zH0RJ_HmgfPMmzwd>k4>FvnmwW1a~Xvxvg% zHnjAoeWmiCURqdC;u{s*!-R;5N9-BF&cp2kD6*GF@eQ6lNEW8K=+2 zX}vUAO-<`xk(XJcddH>F80-<5A#a6R=7`KbvQFR-mH!#NgG2(DY_3$(ryfj>>?{VO z8Q_m4OA{M<^MrX3i?JtREHf)@_jH($x}wHTOnu_G@bOI#Z-c#v%K!@_@W9mZxW+O9 z;GH*?d8eG1WRN_Qbj6~5jqV99vsmTh;q5)D}nMz@Z0ueI_N;A~}2+W{LDsALYj_RKf0GO2!qX%>qpuOH$Ic+!F2cLgEZ?>QMgFg^gQ9}?BLe+zZ z03uQfJX9-zLIj8bs!Gyf1HYv35fnfaFeH)`Hw6JmmTitrOzY<4xr@AM)2eAVTilXf z{+gHX+`BkAKWSE}X_BTGQ}o@eJ(P@ZgAJ(;N)W78NrePdpdc|kA%TdhZ%_`Ow^gEw zH%~RFFr)}GRS`kcBpOu|kkmwgFT8N)&YjZ`!u#HLC&lP{Ev?q6X<}+ZOd+NiQ%EVK z7-Q7xwxl8=ATI8nqLc7d!XB?qA45C1%%A%Azx!YMp&!eIzxvvP7hgDkH_Oe?82}<06hv#RzrtKQec8NA3H(3xAAtSRx5jQQ~CJ4Ros`|hJ&yXvO)iS9nr)nsfYbCcszcPc=S$QaB}dPJsn zL>APjaI|WiIQ(iqW^u@%>(-8y8vr-hU>ym=6ZWgq26!t37zs-3wDCsr=P)|2|DPi= z9UVku`kD_-7#dZ7eMvoMpB;eA>P$`+MSY@=dd!fQ-czYdoX84rx3mhrOp%(JXyet^ zGkV%&1SuqF^PWx$4V+rHk56rytAEGi7uc^5d%0!P_d9#rRj%W4sGA(d@@FnD*^RZK zV*RDQhD(EfT+F!41Di4V)nm3~0_s4Wr0g(EPZugVzCJ$Tys$}GoIRu2)#FZQ=`)&N z<#Mi}NmUZ=$ivx(fnIP8>H8S^vimZ3^L9KtliEaF%T5Wselq7AB`(bi(yc8k>1;E; zQL#W(vc^>B(`m^n!*50+dw)mLk=YmP(AV~uDe@UTHTwC#{3GL2A%qm0kKb+&?99^A z3anJs2?7yPi2xb4{ILQ+MAR}siiol9PvZ+wE0cawOaS=y!Zw?I1OR*+CLsY96Ns3I zi0{Ev>q}7xGzN-VHuqYhu@nn2K6?HBgU`SI#b5gLH~)cug%W*{O{z#0%K|YIK)f>k z&z+=1{ioG6C@3_DDpfTG-|s0lNt>oQS*=dj>x zO;CucDn&z32?~g+hN#UdiXcXfF*HpSfdH-j^)3M(`A%C6C)!wV!O?yaMPBn z>-O@h-R5G3TrB(g!OZ%JPXwqh8UR9oKp@hK;<^aMD5bMG&`$QL!tG^L>lWHUM5WE; zt%ujY{My6oY^87_{d(~@mZCb{u1>n&=q|&Lz2lN?+Oo-Z-P$(WH&szHss{}XtH30|{4%Cpoo|u)mwPi!L&=O085(KU;R~c0i^DNJ7DLj; zm`nHbOa0YdXvXh7!(}*|NM?(KNh0Qb^sv&oIG0Qk1{n5(JgV?y2O0=DAmy9`sgip9J;xyI!zz%iqn zE_05my=Pu{9BAekbY{%Fh|!E`AKEheEz;~RHv%y?R^}N!W%?^W`(OD4F;1-W1t|sq zRx;WR+b!CRoM8sDeg<(pt-8&K80-5r)~-_AQb1ruL5E$44u4@02#QIS2!zy2!Xh18 zzdd$WLlN|YMXD}H1If5bW>#`xacCx&ob$H*%wPRmzv++uduF~&@p^XfkkoJ=E?}*Q z`1lurh{0hX6%~^jvc+K(fl(Ntgi-%DZ~_FlR~KG zO({ixfCj{<6{r&516#zM(%dmhv4X_CEKXdwD5RqWG{PiIOSXvMo8Xt;Da`FkmD|0)H3*oJ4^A zm5(4mfZ@{8OLBS3Q=y z+ov6q(QC8Iap($7!z6J0CXJV&Srth75@H8o>H@wpLTFUwjXsd`7z$dn5_ zMYtp`ZqIjdL)*V#ksW(hP;|<~lx!7c{^V!v=58{m4#|X(Ne@OFI;LIejMZ zh`h`svD&ysQYJN-_X|nt1Zrj?aFm@{Jj|$X_;;BoCOJXJU`#vxwCuz z`SY&x+pV{?mb#7KY@F|0xAEI;x7nr;zu9be;^etIXA71p?dbtnGN&H{FEgsy|KY#+ zxA*;U=;NUeA!t-R4AELgGBY7*NB{@hAOVVGADJcoC7s$*q&g&kdCgRMJ3xZs{)0mw zsOhM_Sld!c&H#W`m##(z_E@K&-jQ~#^>>bZ|ZJ7_p z%REC^f2@-yZqM;r*t0e_nZn4x2{315Qq!~zrbbC95s9d5KXwSymTej5yi6Il5Mu*4 z=5>kMlwU$^o-W*q>Rycsx9V>y9ARXVz)5t>QJLZtQ+vi0Hw`i+O+WfsK@$N87`=wxry32B!{cN^gq$CjDc(cPA;sr2epZZUnl)JD73cIr%a zn%<~WjOuqJ1&T~LG%I$ESZOwnNM4&h#WmCnrsX7=6@KKJ-e-yJbqu<|$s;G)}&47rD!$CvOIH(3{q#6GS1VFl?^GRwTV}zs$K_X&) zC@{i>Oinb&w1_&%lb8&GGEyd@`6P=2qGZjjF*Wb0--o#VNmm=cYw5D#i74Q9~ zbDckb@$L)n`x=~#_kZ{U+p}%A?VNLYMQJC_!ve~5-g&WsmT@O%=Ua%luJhh?8^76Y z&+hDe3ObXj=e_SbCju4Mb#A-c*sX(z>%4Pfg>QEo=foyAnM)9yeA@^9qZeMdv)TBr z^V?mw*><~a*KM4we%*CG&*Ji%tuKpUdoSnbyNPvL+p7z_%uzf7FC!wC`yoVqaCuP0 z;SlSgFlg zTNYJWcYGB=^U2S>(GR-s!&M*Nx*86HKDZkG(NBIBw*W97L_HfAhgAfvJ#zZVDJY&Y zDF*vwBnCp6T62Z*Jk!%k{v?>j6^HU}puB;Zph3=H+qGrc);WJ_%u||-b1Wuad&n@g zn#U`BnsJJqG&lRw#(LiL1YLTL_LQBeAvfw)mlcx;;KaF#j^W72WCt0|vzam%Q^LpS z#78DYmWi3T$z@J?8RXu=1WMh@_ChbRaLFg!z!8;j%g9X9+d^6OaOqo5VPqZ>_JS*0 zMkXEAmXR6rRm_WFd_IdzP30!DI6%L}O)&bRug_pwgGCr#?NA={_ zQf#9IOH7Zpof`9>qhc-pl5?%_ zGL2Ys2X8Qmsii84QCreyS!%<#m6>Os<-$@?wLQHkc|3iED_N#yn%<{2)ylmL?gBgle6Wf>{iR}Y~w`4i9oz}nd?bWbWHuc=e;lNjEgx-%Pn;q zpI5(j<>S-(+-Z8$?rdY)zUesDQOy1+v2sKy*`e!rnIHK6f2Z%mgNwtVk5M&5RYD^p zM3vb-B9T^Thd2n334&q4A2{ZWu=TfIw@i-Ky@-tt!H|oHmz-u))r)Fd< zWO}qEXa>cX-@G~my*z}gLpTijU_bmnx3iWiT-U2TeP9{z7Q=|Gvkb}HNNZu+&_u`B zHqt;iXPA6y)ZMa7cdKS+9|zKoLtcx;=1!bPMpGO z7hG%P8C=b{IT%?3hr-Adp2aAb63W+_TBc(XWkXeliJJ^9lrJq8NkYdbUGXyW{=-^- z+HS|MjTu_+PFv|Jmc+&uK6WscG;x#3u-!QwliZ7{57y9{$V+ZA1%;7GyIBJ?jEq&4 zQ$~ifP%`5=nNCXBZ*=`y`OC8BA^ZjDJ}R_nX5J0F}}^mNGrp;sdwD znx?T_S{+@y+)15hZ}=(BGN^c&Nmnp#&M#qlqtIfFmzm#n`?L1;w(QLGXsauDbhlM3 z^Cz|_w!AynrKgCE>5aZRLbBxj{I{61>se}(x0RV^pPI{Qdoi)vs3n($r^d{BUpfDA z>li!J-j;QKWD$#OOEXSb#F4Ws)@^ul|tJql_8!wUq+_>aH zv)R~UOv$muJLC;<-ibiO99aCO>pCyKvmn9S-KOjOX5+WJj&RO7h&b;k&WZWpq#(xt z-v8Xcx7%$tn{Kz;oS$trDR7MMI_GU8O|5cW=Pj(LbF$mDF?5n*C(HJdvrJ|y#uRH& zu1cw73XbzK;;#0?)qWU)US5RQwj#g7^1>#^Gv{bX1#DRba zoDkVlhNj$XHXQq?hY(XG*Ql9v$qoOq!p=+ynnTe0m;HW-mxu7yZQJXF$Gw*$7?hKk0QUey@=C? zl&4F+4XEuZm-^acVzs)dVO`5267gJ=>~!Lig^~f0Si{7~U|db9rDGZvCVyxe*wRxY zKFZ5v?nCRBp=>YQg=O1lF>zg>HBZ}{HeqC{zHJf5D3~P(M8pwOLL}|x_gXm_^o=ac zl##JrU}R*9e9g$D@>!+Vg>yV~i${P33pgTBhx=7x(d~sQP5SY0zm-@KSUf(FX&yC~ z5^sbiYh?oh6Bb5hDS2b*?2`*$Nyllt%qcE2E$y;;tB)(3Nhvc9cX~8mNh8J;vAnM< zVof2-Vi-?4NguCjPO`M=l{woUs;H1wrktV3%(K__#;mAGVpX@v_L4BNJyX(;5+5x! zgKXR{^tmep4W`EZ{IiDRx#k%c>riT&EBqvAgHBt!Z+45ZCAqQ>-;$rxa-byP`jk_e>oh(UTmBsUmFR}N_ufv7q# zufzmnMjTL9UlLnONW7=2#z-YpfA7El7pQUQhuFs$6sj@MTxMcO&N3EeIBg*L(GUnx znBV*=xC|69engy5oH!@mSp{rPlk2?Sc(>g+=XTHEvEVY#zvo>z87^@v>5WfI0*N`H zIB`Pfyakl;-h1c0M@exuYpHXcbKb%GZsQ^1JJ)q?ceW7$ocG>hGegDMTIMb-hZ&)ABL!iQByRTY$6fP1`>&CDW&BdDN%mM!qWyp2;frKU~`{|5QY5e z%dY{Ip0BlKt-5Pf*(lXHCtfMu2Uq=Jh?j@3AL6~s{zty**IJt6SfBdT;y?Lc{yBi6 zWI>veN{7=xl^}sEpw(ck{HUIG<4DZ_*RnGOWB)7;NIY@=&J8;C^ls9KF*SPTXwAp# zD+A;kQtvKq*H}zH;o6^49JLYGuwZGWN9M+7ib??$9V$iJIsudsWO9eeJ~G8YrqD6U z5H+aT1*xgkO*v&RtW6SIiaC|3 zso!YZEsP9j!F}h9Oe!NYGFs6qtn>hvXcuDQSyLo$@v!*RkNq{^*cwbnK!US1QXeAR zZ(TtB{A-k5PQ~Vl$qKJ>X?3(MHi=d5Q1zalqL@p8Yr$peGMckr%gfAV3|ta!o>i%Q za}_hXWpemLXPK68!LnGp+ww2V_ji@+Y3r86#v3$eAKLWFkZljmbnK0JnOVl#2&s9Q zLNRXTyp(6juSbT21m{^BVw?23AcF4@SDH(&o1BmVmw@_eTEe7 zml`ET#0aVe!b}cEQez-VfU2Scp&DZ{pRx;#s5$_u6C0jUAQDw1FH>zHY0}a1We{L| zk6HfsP1kKVYCqeNaR{*wVc(ne7XxERwxrQ9qz2<USDl`qX#@9s5fyKGawiD-I3`&ky0s!7t%XZy% zlVSmT*Le?d-aCb}cTIdJK&rNL7NJK3Do!LRWm`0TWl&pf*KUwfpcHp^D;l6^ae}+M zySo*432wpNrMSCWiWhe&E%G46<>Wmx-{k*JX0rCW?{%$9SiUf8@JsgnHz@hq&6#(r0g1E6%pZWZHT>Na_F#jQ$a1_A;~IOCS^Dt^IEAlz${a z2aIBgZ(xoa*f;&Q8=0r5v2>8jzh^5MixW>Xo`S%lYyjWw2j4c1?_Y;?P8CG2caoL|L0GxwWI!0&s*YKu+UQ(n-uDz`|uVzDf3tjuO^ zJT|CX)w}y(L~pus+C$cZhL1Yl!VR%B#&J4Sg`CAnTQefG$=WG~&hQf-Hj}<@%5+o# z>Z#1uQFr#4#{L>VlzlXQQ#fnbpL6GP;p0HVht*+~9O%X{L5zo%h6)s1%{h|fki@r& zyh@MRIAc{TpkqOloPvyX7_^f3gGG0id0k$K$)g88uv=9hte#$q9>%N2QcRX{OPyg`B_F)>gz{ z^bU6wPk2iz;y+Nf-9&#_S##*J$gKbU5)}c^O%`ThZx%u0R0|f?Niwd$hXTa0Q_RLC zN$hm^t%T!#oOX|jk;=xZUU0n&H{)>S597a8? z_tpeGHU2!nm*-oF5neXov169tU!mz9`BHAkC=)E}X}fK?_M_Np2~DZFi6O5Sfd19A z(W&;Rd;41ggfpR>?Iw+r2Cj4)zcD0DT+nV+tO0JRz7g12d(<9H`dLgD6g)q z*SD)Brj@!6pSC)i1qggRuLH6gX3}m*BT^r6Cdc?zF|hH($AfHwBYRLcCMGzx=2M9v z9x~&o86}VDk0m?L-qWj>_!uEgo3?_9vJ88Hj!27UC{r0FMM{i-Y2j&JqPfh}1|SVx z0P|O61=gCW`x}|s@6}kEL{^wqK zq#WMfVkO=QVuw(FPL5-rh>y1{ke4D^ra z-wuK~C_y8^goV{&Jdz_a;CN1?Weyu;Vq1~As`9{{KzF8}8{e&Gq>&zbW0~Pq1)>$r zsxxZy4DaGI zcoPLUn}sU#s!)IuH6ry4yP|HH;5{yUu*PTp6b_Tox?Wt2i*L~qDrF!|E^RpXBsc)p z2boW-2L`_!y-6k`Jz6?iwg*W~bKA3e@x3Fdp%8ZE1Tu3aS;Uw^N+KHbC1D%1r;j4n z0YAUBH%m42dUtP)c|(1fQnR{Xh~P$qV6qdJl0mhq!`U@>D&ZS!SUoWHg}h%q_cMS% znyyNkRN{1d4qJ2)j3{u}!_>i;3T#}6GfwSd;!c2lQWQEWafZ8G6IVcP&!Y)N2{l9c zpV=x%%Ek3RSR#hIDMSCu-v&eSA^ZC~<)XxR3#&h5Jeiqnxa&vF@Lt>z4tV0}o0 zIxr#=PDb2Qyt|eYcpC4;h|z`5z_#4S)$U`VV8vnh@zebV(tRE*N@?gd0VrkijqY6O z%UIcnePeNHjh!~!ej=_>fSc2#zy@?Pv}rFiCI zMb-`Qalq~IAflJ12Q34&;e(xh>G^5(xpa|Ly}31jl*ZZRK{%X@XZT*`M}L+l87n-(JExY zGFYO^tt){m&YW83v49jXVbq+l!6?C|q^#g)~U zja8@#90$17_QOo72kjCP#o#6;s-?^Im`uc?&FAJY_&&1-y>Mk>)JFy)t4hof@{F{I zegFe7Ikh5EhM+nD(bwIrIQsTXvf{atd`wiSOcqyhA?w+aQ&b$1=g)+pfxn{q@89$@ zLF21#>uu+~?A{}VyizHp&70hgD0fza{6X@&GjgEIjmdeCPvS%ZtHN>m1j6=x=- zky|bnm`cwnhMcJ;nWTp(GZ3@-LO=AI5FYcChQ0nNAR6xzSD6({FVPs3%-W7jFo2-% zVJa*!=qVj`;JcXrekXQ(}bn>C8Df5P6&OcYdT~Othk%)CxhWfG{50fk0;>Lm87Lux{Z|GGm3M*MD zn#%bd8sd|fGc^VT?HcYGriH#EG7UgR2f<3T?5|IZs#wvYs`Qye0Nt8$5HFn#HW06P zcGcuK!*}Wn`y-b`os0FUV`MdnM70~3oW#$_tYisKN~`lYF|hTTGwubRw7+vkJ{!st$aySj~XUOluj(yiIgSlP*(VAGg=|BvuUgA)1C(Lk3XAqast7}xi%b2*9K6& z?TAK(?9-n_ia)}2>koQn;G5M?Zz2^SCwdQJ#VHLu@2+;`$hjcv3AA5F9h^?zW7Xal z+4k_2F^qy9PoDi7e!I-gab@+-3~;D~p^Hf@am;;Ji<}_jL2H9kqnU~Qp~E_AUy6G4 z$&zOva`o>hV~RFZDS^uav$76DW}tjKiIS)K#>o8E@T1A9am27#lvtGTE!8S*IrmyD z(}e<}rs*_)GO<+HT&`cr0a_A0=lp&GEhrK|n0f>=lS%o+J0SzYM${a}+{fIXEJX{R zZm&nrjDitvG0il<)zwxH@JXalkfes>9)MaAHbNVv(8P;MGNlYtW%A2026Cb@V|+7@ z4xo^Tjxvf8CP&}9_;lh8BkKOk=@IO2#tCsZ>xuV<^6OX_^F3WH&{~z*%(4nh)e|)vGy- zNoSWleprf+kUflZlxD0M5g%7Dek{cX(v;xQARMMm1t8hL-2LMf@(9$+kMS`+QT-)S zz_BKBwB}&V82CH%+SV;S{7Nn>+dbP^J)*8BF?_j1$nL_d4t*dDn>Nvk!ht1|%3!;% zALY4b=;GU-<}kgh#kBS)eEFgGhSH-DH-)Yg+G^ow!fmFW)Avn}pvl5c*&zkb?wIef z$;V@RcsqAJ%bhVX`ma(JXTv!YXdU*;d)_(=D9C&2duPp2i4i0?sK+*~%|u41J|HAe zjpoIKrPwZD)`$&S{G$t#iG8qNv}2)l!9}ioB)Pi*m{Xd=6m*P6C9`)SW2H>PM^Zut zqkR|>Ro3xho|KwA9P6O)h#r*Ae1U(-^T;Vw!FT2noW$9L5E(?eNz7C!B6&_2LE`{3 zGH}({VD|EaIB-uo=SV(rxAHEq3@>e_ zL^^$GWE8Qi8a;&A6PZ|C2n~&j{Au-m?+@l1Smf3?zrfkMdlrUt>0bt*8lCmJ8#%Z# z$|KI`9yaz)R~4%JK*~QQJ>2A48zwKd_5NN2e8i>H)KD_o}y z`bd%Z&R4We@XB`aeo zfH^TTvSz7{M{eON`5R45ukW*ps~8V~syX(ENg!9&W2O}aDY;b7PF;C&b9i(0e2V~u zr_dX{EzCIDCH2q)5f0&zZ!nLTS%>lUKts1w*)DWM9`;7uac4^Z5z0!^r4Q-CU1F9h z+QK2&vzC$A8pT6E=Asp~$rh9)7GajyS?rYs3hYn!J&LxGoN;)3^NOHSRs+g4MNo}* z8zy5cp_A6>dd4l}}A5WrQ^gt0;B3y-HQrTGA90?9HoF_L&d^ znLbFJMRz0PN+tTSI6r}qSZ&%0es`0gQwu6?d-3=I@% zmzY0^{jc-f!4PudurZhy10q@E+{b3r83sBf@mdWikUHL}E+Pp4zLu$HoBU~s{Hpo0 zg|O|4j&Qq+;O_Z{$cO|*6y~!{z93cQJf{Y@H+_^ZNmL=^TH~W=6&$flc7^i3rNfNb zO^P@yWm_(ZN?*8|IFe8zI)D&eVwGb1X~8~DyCqV)|Gz)WVbg&-7*VFbqd3A%qpOKa z)(?LqAybCSFl*xQmQH?%tcK^1jtya!&;Sc$aV-{djtv)vaoPrStdGBOoHay>Ty?(> zSsm=WUUcW2cOZFXm!$QYPo>eFAcjJ_&4g&aoZlKUy{ykbucuS+n1_z$eIPUIC^kEk zvN|r1!+zxlQp>S#enG+~5Dw{>4><`IcJ9G4QrT;HJ>3ATlQsetHr-KH^J~NFT+9!xdEE4^#`l z{o$=JwxXNeN{E%(ZaFob23{QOTAwY#d$clF+_e>*j@dkfvJ|JOWPZiR-no*Se57FXZJcNQYlj0>+ z>_#$IqK}EDoYn4YTDC?Z@^4v|i^+EjWjCW$@Ys86kBpu1W%RZn)lVdbe(_N5 zbk$T`AzzO-^3Cm0U~OC7nLK1sOJ|Zl73~hlZOg)8bBE|%=TY1IX=XQ4b?4nqmfPM> zzItkaNgTEpi%wirat3kzK7afiaOR%_7>0$OnEv2blsH3>_~9#If?A^gbs@f#a|*4L zR_zSabof(tH<$>S311-^s*j>!>D{KT32^?tUlAGiad_G2*(i$Y4)#GR1C3#R94lOE znc-a~wQz95$;!Mhr;j536oG@3jh0T8VlmPDM?&K{A=ZUZTqIl=Evm0y0PQ*ON{M`s zLHOy)AC@545#;LW%Xbzv9Ecq7LsmzEf`<{Ct~6W7xH9{zc1h{N{+{P=-aGgI*!dY+kh!UkaSwXkp`EuE^wRvHY*8MH zu}{gqkR^0{{(I?NOKxV{*R+9I#>gj+O>=MpnbOecswL%ABnJYE z+vlTkKm#bnO*mKZHo^?HAuF1@Ft6}WEbZp-xP%11h1Ol@M#X44HnIXCRHfgKT1P68?F`7jIShG7>+=xF@%EmV>g(K!@4#R zdNk>(&0h^pF~W|^7D`FE9)fYu=qzIH^6KqmpX+J0co?#FG8%=e3iO3N4Xw5|mQTUV z5N!!2C%<+_YsHY1%1f^mYhT`9%~dWmT84j1Ls$6dLnj@|SmJorNE&-`x*i|{u|}+B znkc7Z;Hc~#s3knF_j{Q&DTZuUcSkSAKazaYKjH_U-&j z>ia~+Z7E2aaDeZLcL((0H{UiFn;yKfv9(`Z)bQVeM~HXO!@cvNlq)~wR%DxuDsC)C zFlM`}5!ocvnGrf(2&GapV9-Fpyex#PN1((+u4IO)F2_h}y24i@JLsu52~Wi~0OF5K zH9}OXR7d^Yi{UtWJe_Ygw_Nz#H|ji$!WIelci~P2=48<@8*mw^l+xibPPmayNJ~QV zp3cp5UiVhXb7Wr~%#11l>l2r8p-z`+EPq9&<%GG&jk!K6=^ zCF17DS3h6vcit6uUW6}4{Tt*i!5PUKpBU;@xH?7Lwz-=TL@)$ zx2pn~7E4Vkm<8xi2TE;oj3~ixXDai1P(Fx(a~!E8Hf_2%vW#b_hUfUwZa8&<93S#R z)#xp&sRSyZ)1TV+H`qe~fm$!`b2h>(z+_q9{=#pes2U*~d1BP`K|kp7e{-FF$EdB3 zl`LfaN9$e^uhEzhx;VFU?xrsj=K<=1+3A?ndRw&K>FE_oRE4#K=wfzI;ZRVz^3 zbBE|yt$OY5n)h)6^ZPH(4eW;Z1?BHs-tSC7f#Z&C`@H)Czoe6mNfXWnOlg*OCUdofxxeGm(RnaZ-e&EeUFbj zLlS$R3Z6pS*Cfi>&da`jKuG#6kPa=|75u{;dyg$8z|I*<;P+7!QD6Xuqy6CaLYU1J zB=!4DEb-DDw9o#QfB7(W-nQYyd*a=;EqKD}$051f!^cj&Ps&1eErWD3{2wrq1h$(M z5ULh9sb%%OOfz|si3QQ_r+E~*(`r)fBdP*gEBQ6JJS6LaxItrZ3g4*=RJJI@10Xa! zVYWf4_n+*~&^U}j#SY)3-scORg`8Z8^JUdw_B8k3cG7DLu4f%G-Tts3WPSX+*93Hvme>kV1qGa`pOPkHgQBH?Wt!)x)%ax?gHnLqG8uVt+A@ zu*QEy6{7xEJIy0n!H4s1LZ85ILWXW00^K7Oqn`Tf0G~< z*czvU4G7k>s@VKCyMBzH2uPuL91arBagpBERsRY3)JV})PAN4A(_nGRS@qR~FCNBd zE^;3Qd}*+Q=A`|`Vza{f#k>Ui#jDLCtJYotbp%vX1L}@h z1)KdsQrE#PG7^iMbGlX`nYM^p(U;~u3hLtY9<)0(45QP4R>gu*SmfFC;7A)^a(1Uz zyM?r-IF|hBVxYBCsu0N2Z*!MFIoh|Fc3vCq%<#bcdoAyXegD(^{r3%u>)AIrF7U>gXza8V1Hg-#noTVN)$S+MzVwDN&s(8K0mDyVoRBJ? zQw>w15GTcUMI}BKHXoXc{Kkw7;{<9)O}~!X-@D*&vph<_?OwV;2LrJg5LGmh9iSTV z!l3q3(io_{%YyF}`g{xl(E=@Dwi%wPd(ym&ig`4#c@SYCaY@R{Zd70S-%YO#82vKf z;_>F@h81ukiHNT?#(&bwcOnUZL(IvO)`IpFHO~x;s& zs|C`c9vk+tlR^K{XR@#=0R#68u`2=HA`{dwm99db(DLnibv00_(k`;`qDB+rL&~%O zhQc)D9>J!&3nljZdY&h-rQ#0)$NXGIW&lh*g8Q( zJF^-nZ|{xP1W4M#ry#2O_RSt<9s80h9ggNA~qt31|?#rNeN8H zrCJ`dnh;arMp`B`i{_NX<{Wwd@8{c5;!6Ss4ClD)bGd_we-Xc*%%CT#_VOl(qX;m# zcfIWSK%Q(C1%;K2kO0e}_;I^0-z(L=fZC*R}8hNDx0`LJN>a>`KyOhObmI5n1T z=9r~i8O6b1>s|%WbJ|G)*CWRd(K&;Q;hzEfysmLMySj9iefQ2Vp~92*`{=D!QtD^_ z?is8|Uk(tQ%%@2)+GlfY1Ejh}yW*KZ-m2cZWP16S6p#356#$k`=8ixf;j~psX1g|X zC};)BH>@EoL{9py>7B4>|6Hm%u@%L~++-4~saxC2(Mo&l+OJ1~fC=N55NH5FR&ySV z899N)f2yC)0n@F#aM8%6Dsa-2D^qN z$5`sAON}oXpmC(Iq#ELUm5tXWHiZ%rxv75K+%JDP6nXx;^Az>|;;Dv{1Ro(F&~h8A z#l|74Q{+VYe!|(|Sa8T<&Lp)(zGvnXJMv2b&$mi6Wm02Vr?I4GMhA+@?*8IYCSC11 zVriA47L5g1{_N1yplZm)tvOxw1qxL3o9u&LAKrE;Dxex1SrSjWDN~TJBWQ$AtAWy_ zHLrjZcIO(IM5T+Z4)U6BL-SX!x{8A>pLsw-)K`rYOPjdA)R^t*)l;w5-JGsicHam@ z9_yVS?!Y&`9jCHmgsZ+uhVkVhVom@ubifa~UAPBUYJnY2P>vq7a-Y;Tfs5I!#78WC zpLX;e0LO(7WXGZ50fCmI3DZ@hrUMKoUu*hLy{#!ny{Ta=#3d3@NkYtJ)jh$OUcR4| z@C&}-$j<0MD5)>9nxrf$wQPu8{7@kbu604%FmLu<1vi% zEkx_pEq{yVdXlSe;6HW2R5E|()g|WAwDu4Oj313fo)#(I;hkJ@glbCB0L-J-(@RSQ zW`Bq!80&cGX<|@)va%Jx6{|#TzPXuIET!vua&+m%@dIBM$G5pf5uqXB-X@6GwDgBv zoemnl=8ZCd&Sk8@1kWz=5 zQ5F}o{P-P$?2|c0O^%EkSLRgGAali-n&2Esw{KPL*s9s5OiT^6;+M@Ah_22rd*|XM z66DV7D4k0(d%_1r(ijgnZMHfBSjn0vM&qRwv6t^)$Ont^q?&WQ#Re-m`B3SKGRl2i z)u;HqG?#wb*n#i$siud5T8xCO3ZJPG&seyu45(KbQnRwA3u0$|iOiPZJsT@-fbMW6 zkW8itC~#(I8duYwD-K7|o!99%%i@$s-~W2Fm15Io6O4FLb+Z!rZ`QzSF26pSikGLJ z^jnIV4vmdUy6(Zq+f#W~zxF}V8c~4?nbQ??Z#FaN%5dku^BjWh93x-m+{CaHyJ_Pv zR7thb$BXzS#rBP81V0aJbnbO>;!#hP2pk(y8f2QBuhp2Pe8566bIKCC7AAg+r8$f} z4&C$Lb(tbp3zsiW?Sdq1?2?kX2x}qpzB06@g5!*ZH)JKTH_^=TxESF75~5uasC0f{ zq0Sh6)({UX9|b@85bpP3Xh#i?_Vo)H^tfKO8vA$l8jBJoO)FXzy#9W_qM>+1=syT} zA9~L3_CLAk&KK-=$A5A;c{IG-S{~i@4s5>4fr&MI%&oh){f$51n}KZOY{v=3ZS7ju9>h)MH0s(|p%{8ExKi^K3qGc@@ArBa+J z_Dg8hV*_sdk&x#Zj2CB$*CL8uL)X(WpGa9E08UNY_Z%_fth5&|dTSz{o47UTPhlPa z?d!)Wn|Wg0(dk0&ZaH4OZplq$EH`+_7`Dyn4$9h4=G;4@Qk zAWg#_gmS;5St7flj!{X@_LJHRY5wg+5+D=UBK&L(Lj>VNCKg8My05YehC43Q%q&uJ z&~V7i3H)>8#EMKfE}&fapmO1C?ip5^8>mDKN%-@(KP+L@0oFXdNh$K(k#YPVCK>fT z+?c+pe~YQY)}Q(J^*OG`B zcbkTOmpb@g1&_k|@Oj*&=Y~7hPt4ss^eIBZk8{Y`yJVGJ{Ouhr0~zsb@KE_+4|3uW zUw;USsAjIB0rm)Y0E!6=OWwzJkVvm4)-*q3XFK8fevm2rlz;i6ox2n5Zq9{|wZ`FB zeUnG?jR_i}nj00{k@aGk>pK-48S*tiworo(L1GD$=dO`r=neD!dC!ib{*J&n(D8jt zpLqIbL`j5$)j61uXt?18Mq*sKd}y7f79JOd!)bB-G3*QGBD>^|CcoB zgpJ=LPh0adRXC#@04gS_!Gmu4TLNt`@a1K_!GG^!>xN**)4F}*0qQL9(UlX%^^l+y zwNo`&lF?%%T3jAc%(cvp6M#;)5Wf}q67BxoxnF?kr$l!2JV;%YBxT~zMhZ=$o37A{ zH8=s{Df_(^*B`2MLTD4{Kc_+}Vqx@OW9}C$@HcyM@bk=a;KN9T5dQscOxcDui-a+h ztSZQQG_AA|Ql~HuR}Jf%t+e5z{d_z#evsa`4;5KvADZxw+L=pnf9Gn(i8>i6P#|!t zz|&PK8i;EUX&^~anD!_{BQLYM#zIX2;m9K_223W$x1|C=%Oko9k*+dNzo?LOfGP!>nF45u~z^`j5;w<2bpc(nw*vM76x~8QM4t0~ZkE zbpC(LV4~b|ehRit=OTVPJ97_tUGI#&rO60+DA05A4s;sZtPn=u=u3k4OWCYi)w)4m z7=~Q0{{>jU z5DY#L-p5b3ah23o0Y4DaWWy7Ys*{0Y2TO|OyUPWy493qN-_rOpZ`0{hhn>H*mORy) zYnU=X>DtLa^D>ywMHD;*VPG*O56ET?vr6JD%IEjN-kdQx`aL_v_tSsVzb<>PCx3D) z+?$O$g&V(CDFYrHckVv#yqxd!eA{tq-x}?WJFnl^T04h&L&Yc!PkL~gUrgMQ(h7;y z5hms^hKnxmC4LVtAH4;=*ND7q^q;@7_jy#n81N_UB$qH?nhae6jg#5b?n~#i=p}HSyrR7e>Jr;S02k!x^gAd zCrpWCzZR%*1TP&2J$bs2ei4XyxG}dP?09zLB%o%xlZt;*I#K$Wb${dKPTUJ~rzMj1A!iHYsBTV&!@`Q$*!IsrD z83U3F?V8rcD$)G4+XTMpAN$@bKCvv2wPGfsaAxy8%Rj|`?9y07r>6l~n=Q6SC-4B` zObBzFiC;baFK{l*Pky3A$xM&^$C0HQ3Tq>f{e^7TGWe@=UT<5ovMXfpC7yBFfi5{m zyzUmit^_t-@@trbYx%J%RG8y{pU&c8tTmXAk$=rbf69Ny4MAsR&QDNiWOokOodiks z^KjU6;||R>UKFl60em4&k(cZ5@0n(j*vBjqWqi`r2;#J)I#wJZON^8}>t0@|DAiPI zo*e?Utu@aDj}LLMa!b{YFZBwn8HoJT69s~?aL_qp{{}NsSBLB)FT{KfqAr1lSy7X= zPz)9QT7}(>hhO93w2k_|pU>{RbSh8G2AC?1oCse5gE0dBMs42=-uFFRbbIHE>=^yq zHjc)*%)2!B#eY)QG-e(`g^l?sf|ME-ATJT^ifsJ;mMQ#D)UcN4`27S1yEUJ7LHSSm z{v&DVe-FD{A>{F;g9E^A1n!LrF3MqHz;4CsZ&$DrP)Gc|>;1IhHEg!rox1O4o8J{3 zYD09BNwtSUP%>0uxh*Bh2%5zdpB+=ChrzwXNC1Z?11Rs^++ii4D%GR*_Z=!COw_US z=r8hqA9Sm$=XyHX`VuwJtS~h|EO(@olHDB~4z@)pG_wQ9J2P;?9VshKX8f_)Tn%xk znXLxOPh!G31T&Qm@N@W&AU9K`%Y)jc?4=Bh^E4`$JxP>rm%%W;Hc8Rf`4RF0co|#? z9`DKQh=>#_RkJBXGfH@AkQHUpM=BCg6ivw}S3d>-B{qp@d8X##f^>8io=zUY#veU| zdcE@F9~K5gR&W4|QL$saG(I3>JQ{$}3^DdZ9#76HIawF^O24-*?q4?Yp>^0xHifB0 z7CZO#iu5sA)NGyz-R7nGZp z)|BC~>os@ch_PknSfJxFD#!Av@FBwGNq5IJDxZz}>i?zZ0*Juk`i=42iCQ;Yb&VV+ z%wjauStpU_`UqF+2*jth3l!wC-*11I8X_rpTqEe;^L93V(qjm%Ppy|gG^fCbf(wp* zfC@&t?@RWE2W~hpS@BLi4bI~Q)tt_hS^?EDKrKh z@#eqJNps&)O3jReIyY1V8&va7#{AjipI8Zs@ZZTl0igZ%4lH)ASvMd%LU8VXzzMzePJidVf3Ri#Li_iJzuw)7zkGlBp5%Hf z^(f03hBKiy;YedDAAQ|pK&$P-$H}>@5;a^_U!Z_@Dpw-_SDWqbMAtHT zyRkHL_CeN!%M;-=V!us@HhMOcP}@AAkBDKl0At|rB#Ud93GucOf*Y{UQIA>TKU2y1 z9oPb=X165hnG#PsmoVXK-q0L!E%mO{++nIA2SvIc*&U^T8#no`fr)rH%Q4x}PnkP~ zEi=~+PD2wd5u5w-losU4(+;J^VyVsXV6GDZ6V}bkk>W}Z>scr+u3aJHZ(q5T9;mLB z(#n!F|B<&E)vqg>wLi*c(hrmpum#-%toM$U;P}>SI>(=NXKZhRlfOW6_l{Gutl;fa zIy;3(+11JXxs9ycxKC_s(p0B4*FrRgB>6<*Y0)-=v&E_|!D|pbx2xY+jU;=x;wO^S z$7#h*S2O$D)BXPqMFpN**l%ARqW5GvAw;8VoAyUV`>-qRlIDi-wV#$CpT0y=nH&UY z=&LP{4N&IEDEu{*h9jI61C9C+fy32GBA&s=Jnp4_7%451#IpDXSjBP2AZwiKs$7Cytca^X4PY?=B$F2MhE58B!dJI~;P zm(^rcA(Zb=CnB0{BLD4SYzF+g+1NUUZVB#(tHQ-G?tHh$SrX0 zzv=Fqbl$vhXkL5z-+m(irgaudkCH$ZGhd3Yj4!RrH8qf(oyLUS(-a*OZ-9>d1B*UH z7F)vCK!DBRM5b}T-!1lU@0*VoFW)yDXENn`q{Q*ZTb^16HV2vHKHB_o)gTL}7AIQs zJ}yxK97q1FEJb*@@cwqM->_+1W} zWqIX*k)&%6qQ+t*#}2LA+X=ju8CIHiAA0qg2Sx_@1$8^ug%~Sb0f41wLOnjV7~Bs? z)OxqZity$P3X+N6s(@@4NMUgMM_LibI3n*4*8@gd{vND1rI>{0lsWN4Y^t>Lp6msO|JKXpyaj281P>{3`iz2zc*Oo zuvzuo0B?9G@y08WF7aQE`l`Y(8)6zS&fg%yq98B8@bKW<2N(x-=l2TP`L{YgcRz;+S zfOLW)(g4^dfBwdnmz5WB04$?P@gAr@r20LTNiapw!lKQtFJI=^Unbt~?K?(kc6I43 zajmKnp}-ZyS?aqIb+p}u%C&eY>>u7yq1RvGL{Zt)!_c~RxR{Z~2r+S(|#tWez-)PkVo0P3_%K$#Xvi{n$Pi)wCT z)-3pFa=~Y(O?Ao>vZ`N=3i{3MHRF+a7@o|@OJoIqMAb30V7ojcuXwR^n)~6t*D&BT zFRYQ-B~-V=7<+(rlN~Rs9&EUNec+imCkjxBnU5&cR-j0Ke9>gGvW z>S8f~ESzN;Kz0@@NHa{zzmvS-Z6>kj*0De6d0#%~|m`C86V3doX zDYfKDM(DVL4cnmw&Fx{rZiPous}xV|+X9>2?+LK6He`0JAkWeG`xVsv=ed{l7R)<7 zFVC?{kw6?RtnY40e{a~7&fvbgw?jBVcT=kdOZ5{rh{g$p zx0NnCu4_IqJ(Xe;z}@eQNks~au%Wi%=Ky2(!|{%Rq#2I2~_5P z_M{;thc%82x4{TKDu|w|@>p2WigzKWyD#9SB}D2}I0?>@jgOrEY&h~=o${TQZBU?& z;tXSN-no^RRjp~&&2Jin27^LCWavgXAa8Lu7dMtG^QZ*8q*6J zzYpOTfnwbj>1MDSSBoGfBA+)dkyWd&O-N8CJFa;nx2tB+v-qkXeusB9RX$9dN#4kBOQHD)2d6A&8U151BfM&J3Axz>S4D;P3%(3V(7a8w(N4a}x9zVU9TUuFu_Ady4e!K7WHZEYl5Nz;TnJTJo z(yD*4t5Zm_D$bUGvc}k%m-(|}UzJ&;ILAA>Jda?r63wdh_dPFQJHM3#x!esVZoUo` zWOxy5bl=0!!X$|b)9!gnSZa}JGZvw2h!|zzDDoILTGgeaH)pQ#8M%Pm={b zwL{)y+xKs!@x5@>nyaIC9U@3H| z%C$+Zo4)5@X^Pi7iZ_w>r~6%eW$;N>8kM2~79b_E7Yck+o6WUsf=pfewdGFdQm-{x?Yw*i*S!=n9zF!;*EXz7B*F5pjv#MF34%;ATrwRz}M=pRWC9B7m4Pl|(b3)J6#& z+u1zb1mJUR^03viFsbdv8QBv4T7$LB=6>g>{myIM<-2b`u#y^4Ms#(~Oem<93XOnZ z-LvhS5>;{;O5IXIH_u@wSyDNWg1p)K>-w3A1NB=ueR77)7&jLl``}}Z2oh{b_czeAB8GFI1R+Xt<8zQ2TE^l_ls5`GVWq!r^^D$Uqt&g+)1 zT*v#sOg*y&hnd;c7cRHI!HJs7A<1mt2qJ10HK;>6RG*@%EVMxCIJ~A!rwN7dS;mC3 zGPEitmJ<@X9_#k_nw<8xFK}K+B~6NxZw>udSMQgp&b#=$At&i+RF5Cu&Kq1nNc5?_ zjhwGvmK!WwQA2z7odvB&=wPJV8#iPXMt5w+FVmG+a9W{R07lV<$=L>wG_Jn`_wj@* z0&yzKyMyX%T3K(?&rtrcA`_(ErzxN|(7T?tCzj;(TC3Om`scU2K@J9+CONhnBs|0r zQ~@cKN=imtTvNe<0dqM7@n<&~a2#tw|aAvH`;(EVtcB-$gAB~?%T@FulG0vi{yA8uNa8Yj=u5it1 z635xwxQjpOC8gm-Q7VU`C@!`+-oEIlW_S9vZN0wCJUB*r>{ep_U!JAhr2E?uhYovp zvVo=V7)vuHy{;`uu=U`y3Fo@TOs*xf96!7QwC}T+!Y3!o%Y7i%BvzSwPXJ84%Es&^ zDtODQblJ&~mRzsKg=6*6oJy3<8rqgt-#nuk!EpVL)owib?t%xy@BApYg71!q;nx72 z#CvgZS)*&o*zgJPTJoOp;E`H0nF>H+fEwH>{wlM5>g7 znfx~dJ`EClKTPkVFjJ6-mq4Rp9K#A({W(1|9zE>?!Oy$~GRg_tKdAP8G0|jrUwgmO zH}X5(-s-N%amzBQjV)Pw!zY)RK#Xd%`Et~OuX8--)psh@8 zYls6_98|M{4+VzOH?H%7Y<%_OP&j=La^skK6nqym6xT5(J!695qmkV()p>G2pWM2B zJF^wG7Tc*>I&`b)XLPK$3Vm4rDv~&N)or$SayLW?z(o#bhIjW!!9uMR{XjAZnr9Li z1kEu4Jf;AMrWO2NRWoXcoeWiJ2T$#}*eym3t*q{8sZm4_=H|pqU4JYCv!Q7dXNu{} zA5#-cWfq&pk(RE;Ky?mRaPpP1GOLu@hLdHnTOm&U0_VSqcRdau-Q~?@`;Ov6NdJ5P zOD?QD0V8f%3pO`}HhzFz)e+)I&fveF{^#h9-f z(z<}c&XsOp8CpL(ZE{8ala2J5MAs&J^Kd8LR=Z?GkdCI0}f?;xwOY9F;{QZ5tV z)S0Cp@AK*}{519Rm2RsybIwr`LH$-SZphYIqM!>LzEt%j00hi%BhuQbl*B|UyUrE^ zI8c)UFlE%ldpdp)!U4#BATo_tS5#8l-CK*Ut=v2pfy?J2MHyf<^7&T`Pm1B55VDx* zMe!)W$n}+w&qu8hKPJp~X7p1K(Y*rpkm`1?jRb7g_4cRcwC#Z32mZl73SMstp3WUz zB_2+8rZ{)G@~LO|*!aUxB7O<7!ZycKD=n2$?se2S$wRM2y)C^EkaEczdJQc3O;MU6 zk3~{JW(H#JtHk;gavgG=k8TjSsO}ngpLr|yruo0wcKJga<<_gH_^D}r zDglTpXsQiDEKS&sVolGi$6XH6X10Y&LM2Mr>Q%*Uvt|qc0z5kK?!wJhLB*qGwIdT` z7Lajf9rvv`rk#UWi)Qnz@Gs9)IcR=p(`4_ykWG}X3+j!&TZ)cG{8YG-AA0>LoD=o4 zX`Ia&qB?^gEZLElTk2$ynZe-is&{d8d?4Q3wOI4q6JsqA_mz8wGER^oTL%H8Fc`h8I! zFs>gdep`f;CV=DSgYVrnd%Js(24(|)QYp4P&`Xb@i%yn7i2~qj!F){qerizq`Bh-} zX>Z0XsmD%jnJMw+H=$I-t=RVK0BP^jaqt|Nuv1-*#e`J0g3gXqC%kK1S{JGN-%q1U z|JR=#}h{*_maQ8=Iyq=+kZ4ZJINeOS&k>s@gc<1caRy3k3N zS3PjSkYQq_W5iTbudrh_(HaC3qIeNaW_)M=+-5{C4KHu+sKA+el6tEMpu3AqGlIDZ z(24%JdMXZnj{hk2VC5R{bE(ZV@Oe%+_>Z^SPR_80c)d#U@1;hPv>z9Wu3jiq>|SdJS?0G#;SJH!Z&2{YUz@3-Wvvg1l$E*BQ^V7h zZ&^&UdlZ}%YoC+&TKf5PH;G)z>o|-z34U(#r7)z(H}a_djb6hT6u6Ra(2X{yT>a#o zdMa?)RSHmM`OVIF`|k3SSb4OsvImmQqE!D_)?qNZ)>~eYjt!%nlTY-9A8T6QEot8` zW%B}i;RH8YzzgbC=N~E95cJ5BD}3AQL|Ti;dV90^dqE*vqoXqR5CoFkQsmuYj3AQD zTAAV=RTI#|0s?*SoJE%)X^CiX!jp&wE> zkg>-bIwSk686++v1(&_p&dHB#RWRp)SPX%MC2PxXy$OSgKDn|*#Y{F+LBY_22{SYj zKT7f$ARI&5>#6PS+Uun+m5I^@^@q)uQ^C6Vh&Er1eg2(SpM`sdk$jZ>{-U-ZTRED? zfns|MlCRz9ZUoU}x}|{}r)KlrEQZLRUUht~2eBoiBNv+9-~wn-oxX^`{fX&Lz#CpB z7&2Yl5wu0xxL+I;brDZ8sSf+~@8z?P)ZK>4|$!(aIPA0*g+Ug^Au zckMPu_RAk1$AZ^`s*i67_;uxoO%Cu4Oe$DdHe41y_|3CeVaVvJs|$^H%Y_uPm1o^P zMWquvYBkDd(Lu|s=dvJjs(gIuJ(b0L%)?nTiX|*>W8@l5q;k!M>3~eX9xwp(;MKpU zbNgH2Uytj1?oU1j{rOAt#8U7Oqfhg6@Xhsk$+g2|^ET=#*MH)G2SSD*1AZL0B5<8o z(xH2P4}itrE{fJnOnyAF;nofd#6t5K}J)BP|HEYk-Nja z7g^SG&0p%6vcrb#6~aubaMM5=sH1tkpATw{)=}n=(_d&D1_trZy`U=d5_4J75P@xT zRGm4t*6cT#Va0?G4nUbTOx;K!5LfUNURuTy*4+ozGNMnh=a-fugtXWwKntqOT4y$ zJ}rR6p9?W+oy6{diDNZ#-iim(iJAp41FPeI3z7p~CO{k&nP)Q!sx*^Gfeb_=wy1iO25&n((3W z!A_QGfvb$T`^^zs$rm5f`*}0=9K!I@UX7t7Z#j|ps=gB%6`x~n=_b6+pPk&}cp*e= zcqPc+)BKY;z=&>%-~av8+BrGTAuniJi&rH#&so?6&x?lK>`_mkvYO`)M|Jf;N5k)* z6*S#X6FuU$>gpOLQJ=E3G>?V{-*qICZ8$ zhR07^&va{L;>Pzpd;7Ujhe8GNK*lu{gafYNgO+Oupx zl^MUGG8jB!oiUBR5`f5fY>17Xg$yN0-mHxx$jr&O-|NX7tisIS7d|F|Epd#H7QR_R zF{~n=T*VUB{3NY>TY#0O4P6*NHE6=jZS26}c>_S!>~}ZSxN6(T(m1pxe+^sv?U3G> zfmiWgyocORbN`=fhe!!v>-DCu=nxfDZdfQ9r^bh#XMvQr&X$i}Yxc zl-a}L32)g3UQw}*zPvJ!L5+l=IgwO~OLB<)Q&mR4N9x zpUT}7(?8q!udyyCg^m_-b(1XR8y2@(AJ{0P7o>kaI6U=(mi7Ov3g57*?*{6dT(!x# z1j!ZF@NhRNwsiA%Is1&oq*3syzd=YHm9;5 zxtWdp{niqBK^kb>3~CAF`E`FH!FiDb%3lo|BI~r~xVGg^q;66-ZUeKH8{&likf4`m zMbTOmf+z%>a8I;S@UrMps}jS3^QzJ+0DHfpuqVkB6!7d&(&qZy+=}U!xfsdwh zg+DzsK6vVu`mhy(yqZLdWC{Du zhm4toC*LnIGAlpa(bu_^Li1*fPj#G)JR20t#uPWM1q5Ww!?-2?^K3ZVhrzxCAtq5c z1wqfdX3u?nlx@CfzSAGGtJ{C#2x#QSev(N*zppralmW(wp z@!PM7k3SOO0b(aJeGif`#4SF1#uCDWeh8RS$3|9UxE%Z|w%g7toXWulhv)?})TN?8MGt6J|kJ<+BleEggxykn1%THnz#e^dy^tSe|ODbeLo{Q%n`G9TRlWKClo+t|xc9!zE7vnK$ zYvB&4h8Ly_8z&WOstp&PxtzF?)%JS#$*(DmgIc$~&Q2hW>%G43 zXIiRlK^QQ96#7eYX(I7(MWartLFOMf_dtg$M?uepwA%A`)~8i{&YTUmv`m1J3`(F4 zl*&;mQAo;2&w^GW|9m`xcpQZdsXb6dMx$XCasK?a_sU31!>XgJ(}~v^|1zA|=l5dm z<2Gk@{^0uB8Z6QE?T|%Nr>=sC>%-iLc4&2jGjmAQdI8%Lw7S6N{;2QewfpJ1ht#Z_ zr}y3qqf4@*LqiFIhL8Ib`Z%COAZy+hN5mHmg@S-tdT4;Ts;bVyXj*<6d^Se1>OT zPm}Y1fphhPV^80L4_tS$wJ*_VBOXZDl!-*2j=cWO3O}KIf z`L%eHnfpI_ld6*Vi`;J9eVA!XCWxEQqn&SG3%Kwn+c7=fVc`~2M?ik0)9%Ky@u{Bb zd$}>NTo{2FjoJL_3=@xSG9r~!rY6VJ!n||y#soSFqOu3*#VNS=@no974hR^r56a;< z-df078kxbuzpoCy@Y(wLZw4fBw|i+#kyQ)}bu)*+*ss(Q(wbLU3@srf3|ugIL*+oA zt!)&YY3^;}%mNISy0XoyQY_1lxx0NFe3ua|idIcuT)hXuk(ZD2CeC^UN= zed*B%{5eqam9#5v*K(zV^|x-h=2v+ZhJxGy>Ta*A%$N1oxLZ7HuAtxVhdro!Cj=SZmH^EA+44orZIJuee_z8WW3blRqbg^4yP@S31k4f38xiKfAx|zwb^% zz%pu$nl{1EeZFmJK!^|rd{R9gsBiJ6p7H6=4@Of=fO(+|vU!9uD1p_3FCrR4*PQ(v zr99d?u%%c=9yvL?q5nPm77sJ4#ze0Qi-Sy4JuU#hHdF;BO(Ng|+{O`cY@%pKIhcce zjm3QL!D!qlf%@%A89g)0EqapUTARPxZhA~dj2WGK?v%{kGqnm`@q6bG?2&XJNLT3fp&hUnUx4X=MX;xT8zH=hHG4l&B1dP1^vSw}+Cw<9k48A#VN+0%-v z<=T_g;m*!BP>`i$i-8)L_Pyet|HR9X56Cj2sYaKyO#x^dTUc%qzH#3$Yb6U zl)o$6Ty;d@YcMH8wV^_9Cm2qvcg?VgVdteje`jepfIs-oF&2`4^>aV>(8(FQo2FU88jz2aH*b zMSxfIPP#h6z*2smmUfaA8OeQ&0ExMP-52{qiJydtkR2C!AU?T@sA-JiJULa|I5+nm z?=Z`8*OrT~`?i!{qyOK(ZN2xdn{^B~PFK09z_$#v(_{Tl0O_GICBDtF6v3r#JA-ge z*NB8=qYMZAr#woHzf14u-X3e2O3@>{)O0nYLy?y(SJg-}HT$92L)|#pBb95G40Ljmlz^Xz*&%tF*9bmT7E-H~u zC{{SIe+A9&Q+sv+v0J zzjogAb>Dnma-zL!p@x4`GNIU;lY79_Gb81MNaM&% zdr-cWFLdczGd{zf4sfJ037BnJ3g&yC%o3r}6YzRy_`oX|{;471pT){IBO6<)v!7Sd znq*{-QhGt5fg~{YQ~tKFI-^qrsuT&n7&UzY^|9vW@+yoBtZb7_^UT9i+qWVvKKXR= z;ZjIzAz<-Siw1T{8Hyq$+Q>i@9j;~=ys2~rsd7M?xtuV=9lw*gw7hscfT%Q zk{6n4p;eX9x%n;<`{mfhc^OCeg~@cTV&?R;Tv`*p(v4?2@^)yv@74s6FdnEkppOhU z+^dRB2VFp@8(9?D)Fd}|6xjlvb{{3T_fYE1%iMmPzM21T;g{vxU6L8H8BsRvDw4}G zeXopUhh{y*U*@69Bq9!peITTF?(bjo?>e&nVjOvmc?B*Ff3u${LYDa6calM z|Dr5)A?IIBZ8kAh!)!Xycd{;|S_}2XJ@0GG8C4oKFwI}V8R+?@s)LC|jBLwHz=ju! zHTl7~6+lq z^<``EFWI2kRgwd6#ra2TK4~j5D}>NmT!2@9y&c+A&v+rJ4$H$|jA%3Xjx~M<)<8p3gwzBEALRUBC zFjRZPT=fQ#$&2HM+SWwWLC8tMhRWkoo)q?MFl$CG5M=Jz2C=qj>`m<2`X4j*amQrBwofZ_Ym^FCd>QT@dbU+s z?n8v^wd_Sm6J-a64otJ&ZUk=bK&LxN0Scx@1lt(jIhg#_!4L3h4lTzNy`9RGF1WWM}B&V<=6o%@A zrC;Ybo@G2sDjxBI7I?+Kl0lhV{GhfmR3#`G>c^u%MWLb?b3+MJcN(ev_K7~CPe+BGqXJmq$oyLdq7It;AeYPGDTZp=K7Y?F}ZlQMLMjr@!gfB+y>J* z)o}6`R|pdi|Ij?>=>35Wz?w9_JmA2>rrXVV?>|;TaBf^eb?^Hc{bDF@MuYN{>Ve}U zkCiR&d&yZMBeyKE4jHODr`GEu)yTxF${yG;8g<|vy4iiBnPLgdx}m}=1LZ_kdYwsH zHKmMIRT>$Sl9PGWoNtg```RXuYh-tvkfMk}h>|iYmDDV{3%ldc=ItQP7Y+#Tkp?m} zvnW`cIJ6Q2r4WFJ#nyLr`_w7*^!T!VzWr<822AlnW{Osqn|NgNbB*S8d_{exRgMNV zY=P?&f4Q{`F`k(m%Q2y*CdMYxh$G9U*aSZwgT?(qPYCLLerRD?CsONWt2|9mOk7;P zJf!M!A5o&t9wjA3FA*#4gR9gnsb|}Qm)^@)6ql>kAEL}QNiCb~yk#*Ez#h=ibl{er4LRzi8iWNFCpf!EebzWc)Z59gmI0eJL z{($==oE%zK+x=hl6%DJ;%#3>N6UA)vyV|ydOg(aPLl<639FARaSHjixe=4l{cy{Ey*mi*NZA^S<8 z0+s34xMi$;Ec0Zx zd9=7>wiK=IK(^}LgNWt@QnQE;qdrH@A)syP&5|!;4w0Cj&pl;cN?6%-KE0es1zuD< z|9MA3EJhw&HvtDEojw37C+R;V#Ap}__vHM)`j z?VUtPh>W-u@`BK3=Uij21#w#SHuF*|07e)_fZ7g4iw-$?B#E3c!lFOQ@8*d@j||$1 ztCG{Mg#)k3$8KB1d16zY@-#{l{fJMC{r*j%A{Qt|e1`8Q@3mnqY`LRfbk0>`OlcZm-_mF`xb zGUsG-d{kWfh3n{kPG=X=Fi+z8ZozF#$3VdRe*E~t{(vi=FuD?E8eYDIYOji4!BQ?m zT#koNi`mo%0m;Uy;*)SO8I&@Wr+If7>tP6J8~V5-UlSGQsI5B9Ul8y+D$S)_@~D$x zhFQ}K7xDuccv4%KQ0SHMf`87uel@$@=sWFumD{;4cu6ob@q%yGObE8{E!5G%CbY_&xYNwWDwZV%W7iw z?!4(rB`k05@&DNM#r@Ls^^VW=3DwUbI$BBBy{>tuSMjZ!yD2mCyj>|iV+tK!%;(t& zTOS$CRXlvWe~&L?D*;=suRFXyHTFQ)Kz?#n!9H?1QUD)6pc8k+ETLOoT~JuYzocWy1ss9fxY0{6cMdPXrfiZBFidOFGxe#8_E} z!no+9&(3~OV|S|tu?!)^A@ro=BRF?d)$8i#dr1QR$A|A7TYJ9aR?-~8B!CeNW>|F- zjt)$mLb*cjMac}T=b(zDLw7wd9Y1xRE)6h~*#1CV7RC9A8Yx_w{k0eaITXV7N0EAb z%vgD}rdWA;6$;(Qak$MgJgA*i8U?5*jtKX+rX6zN?_0StTOQen%SX2QE>KP)%ZfV4C>S@TF2Br_JopEZ@XKm^GGEFF*za0=OV2@m6LUHPiw=1f8d0AB6)?z#T08OOW@Hfi}^|<-8mFdrqpx}9EzA_Gspa_ zgn{*@3Dnf#Lt5x5)1@w)=YVVIXG=@J(Uh9nWxLgiZ36gR6uf&=_RC6s{;sIt!6I&a zk~-+9)Z&HdKc!ZVHI5s5Gv{dBDD}=4R5fR!OeQ6uEf&`|#bSTdV*<>$#TC<1AaT4*2fijAWZM1X{La z83C5Q=ZnG*Hq64ax>>3`!>oy-@nQ<2c}8tYFX^-|LjZoInNex@th8EG)<0Z;N$2u! zI>;5lgS0hJxQ7nhf4#);Ea1~@bC$ld8Ux3MV|6yhG=w{-R%|lT)*(prb|i~SQh=h!7gnc zkjGfH%Nnrz)F^}(dUq?K|AQVS!eujg!~a%m=NsCVCql3EAs|biGk{|Mo*ggwV(s+b zxwXIdbrH|Y;B4e9DsHum+3P`tneS^{@jk0e7&rq0zBTh3u>9}pndevjId{G>Rn0;6 zUFhZgFwe8D+4e0Tv(NLGo7gznnB6Kns|=vskR+)$a*pP1zwgTNbzcfG+8B1_ zR8D6_sHkdK>IUHgKthM0@~4?!o{TV^C0V>BsxHQu5YL>ZAmKau?&-eL6UD|`WT3D} z6Tb^fDs4pXMT=`4ib135gzqdVU~hMb1;rl1^u?aca%XfV=-*r_OIP3hTyXpCubsV% z!5fVU5eQ7{wqPmR$77Fu8feuCIHYvWxXpAE1Ji^g-tzl3`kLNdiXD)ej*vcku_?QB zXO=9b>|*g~s~NgptUp=LXMu(i`s!l;)lsJ=lxO%|XfN2d@S#tZEXDOuQ&>^-4};LM zga5-zl@atc;}1=X36+M(ULl1=xPEVm0N^R9j2;wdqeGb(hSpn$5V2cWqCL(dS3-!; zD?NYh>6Ai{WtuRcP>n)03@z@y&@>DYggW%Q?b?3!-;$ByQ4bP)8VGZu-c@eV&IR&o7ASw@v31+mXh zF#1C(-O}1{#F@I$IMM`>)VVV7Px}iJZ$?Ei<9y}=kk?y zM!kuJZZ)$4?dPBmf^QA5&uHAsKRh5-`?>NeFz%|PySt{XtxQ^QPjo&~1c|mrtV+EG zwfqE5Pj?CyUN1`{GEqjKY$6EX0)mIH8=vPD?$WtCw=aOR?)3+zlaDKZaIl?su{O>@ zbN~5d@U#bE&Fn1`9`z%bxId5dvZIBaKFBBHPoya)h+=Q;x#{MshUD+$cas0z?B18$ zz^i+J-p7GE6&AnK9k`h7ENxX|*h$ocLhvmK1YBF8=*;-~J;hH^QqM*&sXGt0eY-PH zN5!$vVs!)X!hDmlNd&Y*?zkOfJB`y!VNWTIAZLSe^1(28QFP0o20+ggf>iw9iRqIT zN@z`k3rT!n?x>M8tynftOknY$prd8%xRxs48cgBR@!?-!z|+B=DK_pcy0CGK$)ahtAH zNH7~_jh3g>Xu335fP%U7=h6s(tS*p8?z2i(3UcXPbo}Q1QItL3A0|TXkVK!_a7bJ& zFA$MGH#p|YfnhF?X*(Ic&kNnl%KBd0P5Ce?v6=N z*m6=Ic}*}cLhtl7X$o_iON^viL|^C+sHC0i8NO+o27KwwTd*_4H_Xt1LOa z8$k=FDJs#Zht2B_18*_#kyPbVnD9U86vad&*~y6E4p=s;RmkC(*s}diq&9+VjSKx1 zrlR+BKX&rJ{d36kG<#&-B)aCJ-d2Q7A$!tHEdt2h>Utw{WFrxr|A$grnGY5u(&O>p z_3x+iUu)>Szu(k9->kgV3$l^8J5Rk0-k!a+C)?xo{JMfSgFxD6bfuN~M9G^_+pOOHHz&ZK&fk#qqqQ>Q1 zK|u@u^m`-(o2nAsd_~P!F2q|Lx;F&Es-U6q-VR4Lvf$ZuT4v2sl#tJes;p+jOt1&9 zBm}iACoXUWgxtJ2wp1?mIZRHdIz?e_jw7T37;I^fKqgyG$wpP(1-{f9$TB5LM5?zK zc&Of0_U|!RIT?eMy&jE%P))(h#062w1An~QVNIj$o0B$8FFe29KbDuD zEG~nZ1LSXYFwy^ZO*ji zVxc|RJYFqLZh1%Zz*jnYAJZTU4{!HZxq8nHwYIIhLB5<+=T%RNX1Jyb9m-N69Y#oM z7YqHK<%rb%aWDt&udRa_OpPgcq1|6^BD}H3 z?O7;YQ5m%oU9S>;+_Tu^#x=Ovu#mZxHbIJ4b0ZdL z4h(c0uO>~{0S$ZedolSvb`a|cf3kQ6Dg9QVvunc)AE%y- za$RF9>i{-#TMMj@{Q0nf=4c z=j{l0xze*JP(&b0k6p%P7z7@tI3G$WsrsSAwhXMFgf~B_q#pVrO6dsX^lz)NV$~u+ zKJ4upARm_}ykk{1NXu&o-cS6<7>Cr3pGm%m@CR9Tgd0?Vc@v2E!z;k!TD9yh1+#ni zzxF@=^ElJ*r@Jn*Z1JyakoEXxPt?A2aY?Rpla?49F5&5FgC9VW~Fxhkgovsz#x-e3$b6YhZ4Jh#EmW^6haXR`TE=js|- zJFb_xWftFkZM@4jTz++5@4;#AyT6|bvE+Q;LcG(H&Z+V8jdn-X*}|e>?oW6-FMmWqOJkYsMYXK8e?HeV{0iVoNdN`fCz$XtqQC6=EVDh1#7C9nT=c)K&@ ziDh1j{g}XCs$p;3~^P#LmYIB}LJV zoVc0Zo0IL`-~XB`xzRU!J|C5O(YW0-38>TL7v2zcMVXOe)0R#+@>K^K?{V@=aRdL- zjmy|Vg)U8rn3;^;A!EY6@Et7Mqev} zrnk6tRf{ELJTOOw6kqj5NOh`2#f1%U&ZL(h&u9JdO=l2#h0)*I7Q?VCq9lY+gAO(c zuM{7zM2`42SM$)5{<4;;9o}NN#6=puX*B`Z>$#{*Ds#umn&%^{FIyk@=*~y1az7-1 z3kGHDEs^}PvX+3Ay8z1|?%n$Fzq~R);5CiWn$j8|u=hU-%paMrr>@5ZgIxLe5xEMy z#JW;yJ90ZBOJRuVEaSG2E%fhjIKuw*uFBB0_lL46r!SPrp~1s+B+;4^cfmaa;p&W) z)e!>u$+@7nSghx1kEgBR5xN30J|h1ObhI430)7JlHlyK_jjF0qWTdPHn4hbcm1Hgm zYq37WBJin%8esH_ZDlB*6yPY2la0+OyI}U9`#KS<7{ujrC1DjnQ!j{)&0oAAnb#A{zKk4*zu-`_0nryyokA(kpXz3|1;a<%g_ot&9_zA;6>}W2IWX z&a~l4rw}(D9=z5ZQpu-KmosD46=xxf1+N@# zC)pF(RnH>q-fUG;9YtYsuYdL)ow)RPZYrdc7>w<7xYksbC(Rq4InK=goh**ExI7X_ z-Srf7zwYIU`)H^?TC|TId&XLWGIUP5sJdr>XYbTQ021Y_ZE z-I5S0!#Z6wkf=XCh^1>%<+6(b5KlC-cQ(rULsMaTQE8i*O|;F@)F?pAMZ|clk+G;J7&1g@t&w zLzepMo1pT#DfjobtfYx7sR^et2aDwOxd)u0q~6nnyPuYz5g=hRO1$ znX9zQ=$Da>=C{E6)LE9pNXk5j0gsbi?-IBS*-<}RC+4d$?e$r{)J!gdd8}ZUGLN4&H-BPh@ z5y`ftj@&_$5OF**vV&!As7Zb>qHn&8GxN9+bX)(r9(;2fG-P(q!>8c9F@?t014mH& z)t1aFAS+gIdgeg$JmxXb11du;$()A!g-9@5@7s!yK($&qvFSpdeOMq& zy}AE{AU*u%9-UK)k(K7kuyTiqMLqtxjd z=d&#$&%k;3g=q)K21_I0VB1&cgGQ5UQO(!UL`nYg9)c;n{@%Sea{cf!efd-*ckB7i zr3&ivzrwr_vIJ6$4YU&T$*6L*9*OW{1FCg85)$x!7wNDjwkk|O{^sz}+S*#@^F@&Z zI944;33YPIG&hij^m!^J-z-NVIMgZp)4d60(*yO?dPg;PruXh7rDZo4>uqi6%HwkH z2;#Jv(;&<0i^oJyG2igb<&(TT_pXf&Z3KDqw{`zgaATu4ZbE0gSWX3SeW45~9U&wX z@WcTKy#)f=F+ka~e1bj3gY#ZCQELn%hQcD-DK08j_TOJ^7Jke-&BtV=tX|XRKLcDH=GLyGoFBpWK0A99!aR-Ei@)E@pycYKu86$%XV!- zC?=Xq_1{JSk=loC(8xx2U{$&fXj9rbJInJ^PCx3ksWO8v|=7$u*9l zUz}~6!}QjoDlj)UazXra`uF77m`Xc5T`i_cl+a36)?Y$ac=$}1!~{fq(t=3>p~=;% zrO_B_&2JE!L$IvX=d4%U@)#1Q6FLH&W|fREjZv?SHQtD+k(407eQP zhkH`uCtF0*)9($K;_-Dda|?x#%VCUjDiYFy+%?E4w3P*zRoEy#dH9f70e6laHf7jG z(u#2r=7~gKMR4p-_UmZ4ryp-_wn{#d(mbyE)Nl85>}F*9+eVvM%9ET zFbf20Md9X_EroP$o3gH)E&`Kb(&a}KImZ56JIp=j?{vFbqNOA8vhIlblt>;wZWZEx zrlm)IK$Fh6(THejtSnO9=A{~}S^K&bv(e<$YjUZftEZw=g1mxS{&(6aG6yBc#^z}N zD&rN<$hocc)fbxP2bEBm?H-17O%`hL|CVQ)5ZM_1hWmycKlPQm+^SJpRGhhST-Q)C z8@7PnU~K_z*ZILaQZ&OH47w|Em+q5hu#5a_x0m&q!lm0i6&&cgB!l+1_xbGo&E~MotnW0nvPXq zXr;>c5EUSSns2GosP_oQfHC6J`G}SWXS$_PmH{;Q*^c~!E$SFBc z^G9!Po9};jly3Wk-IT|cR6I{??ps{JRV6?Bm|mC<#G>Cp!N z=0}GR(N)IEmqY*vR0ZBZz_NlWg>z|OFxwntBOZ1lPQ!QE zz7N4cT{}5%&8v*%=YnQqLbKO&xWSAz0fNvv?CRoQJPRGRkh{8779VvOh+^JL5nzW> zB{RjzNQgvBA%Oa!EBK?l>BA4W>(emhR(YJ-;Ci{BQwY0Z0@Dn~(+qcj5XgQO#K&hXtu_PS`n~ytOu@+X&JlV10Dk+R)oXpt6bS#P z$9BY9mZcSYdR1ZqRN?z8NA2&P;Q>gKIfYd=GnQ1Bg7cRJlTLrzu~|RT%ovbV>nmYgcxA@W+>DRbx+3j zkF}Z$-knS1X*+N9elNG7V=&{Vrka5#QB`=9tg0bA_?Q)PY*eYb=C+N|e)uBJQtnX{ z%RbmTlN=_R6f2*{t2pEc=!V_YwZ39ff2yHM92#2qx#3Mts*PD=)Ga>O+c9HT=lhBs z-1;+$F^M#BWh#c1b}s;ZIMW!B;7Wjad&N<1_vskfXq9(S(uxG0^!-ou)m?M#a7Z+T zngj`(lW+!3c?vSuC#^Vq9o&J*q#jN?z7)WX0W*DZvB@zddsVgnXax63`B^8PkjV<_ zl&9)RXJK9x-Z}=eCT1Siu&_B+j=Ht+*Z^^5ia>ab7Ws8EtAcKN+9YX9&+GN`ZQ#q} zzs-G){jZXUxQ!usdNyx&7?`iB^FuOvz9ckCgUV!hDug^?+ik%o}cI3 z=9bz@qP^;AfpGlWSD~dCYDM&Gy0)@`G=Dqqp-3?AE#e{!k zhxH;H+IL0~Lb$k>r{X@5OPUXzcPDKxLoJlv{FRH7{Qs@Egx&(!Zp@x)gP$IQ?q_<- zn{WVC+r)grnoR-oJe+Le|KPB0IA5>w z%9+B){W6}1#?0U&ef6Kb^!3_`bJFcy5LtO-3{6y|r2s}*40$mCGDFpS``*oZX$4gz z23#TvVp)Eb>g=&Xy8CK|Vnu8M0`2~he&^U^wkq-uVdI=mV)|8+lrnL#O0(a$BD$yJ zj%b50{6>Ra2W^}d%#cmDx_f;vDKtWD4zuR2>Iv>H@r6%E)bF#lS|u$r0HJ; zTmGjn|ExDI9}WY1(R%kcf_{7pTGG$$d6|XW{>s6?=+rAJf@c_E{7FwMVlPD@);DKY zD#fy#H2U0C0O_hO9VXJoPzKWb0g?$hz^!MGZPdB1xjfHdqlw-bcHrLbU)~#Q%d!}L z3e@;Yf59Lefg-hMqa_KaY(Mgn3ej4hX;q_Blq{U1$N z9oFOnbw{^!4CxY(9HWtLMl(vfLunACOKNm(fFKhtq1_vWRSZIc4_=U(mPSSE|- zhT&rV8-hH4q&;`6;Wb@-4fAYITp`09Hxfyu&Yc*z^=nVe)eQCg!aF61yeKgHZd0lt zKo~0eaM3ApI-U;V<)h?@+yGKsE3G|qOr4h|5^s4mTzfIQUc($PEi1wLebAE`z+O>R z>7zZrF*cu<(2MJ-XJr7HR!g+z4&cHs8`N8>hO4uWcJ-UT$#QSqr*}+czU3|Y#mU!5{@mxsm1(r}0fq($0J&OmL z<};ahWM#Kw>eXNTz5LK<>8SZp#1Bi)N?^MOWcIEelKKKe=p*Yd`PXaq|2COzGiPyi zh3hJ7wGcafEy;$0=9;x?oggB(ni|GjgCpq-MX7ZRkInmyj&ZTELTp(M6Tp_~6~8@g z(u)qY-ijt@)}Av%;jVtUD?(?Lp`xBfIxIaDG zKy=(Tm#FLF%DseG!5&=s@psFjPxEnX{Lj|>)KeyBQrQRn>L1CNq!`d>baH5qVxn?I z?=?cLz>W+WiC~$8aln-IoxnmlIf%0{{q}AZ zYGVho6k@jzas-w;OuO!opc|0!V<2?p(yFGTGpkqzyOK%&8g?nf*KDd=ba+3VJZxQ! z7=oEIu~%3|VTBdfUC& zKQVB%{yhP8{vq;yxBK#S!DFH&va__>hnyzSK6&3dC~*xcH<|)2v4a18_f8{T1A7ec zq(_-X^u;cilnuu+tZz7@M1B#b+iAfAgFN=d3#iH6je^6k!PkH6>Q9|}$(3`*2q~>m zB{2wLgfIH>iK{gQwK%mduv?VnWy!1FxPz61YFcN1m%c2{7h$)+w!hKlGxd6ki3Bm9 zubbMD31e6#<$KO9J3kzJU<8`2^r3m^Jg+T*_NA3he?BkUw70yS5=VSU;38{aOPwoX z8+$|WQn~DeU-3D${$|SQnjUO?+IeEwjt&SP!eJq&xR11-P8a_|4sOAtZET2mj+>=c zn)vqZM5D(GxXhe8F6r1Wu&}VvEXC56i;k!}hlN8?q*^#CL{b%T0wQ{5(GTW+yJq&$ z&BCFiwSIswjBWR%%Bm!2Bn1c_(9$}md8JOj$N1xi6$so+M|5@So=xlOM=dR`@XA4EE&98$`+q`=CXT7zEK%P1YF6a+^ht`n5}#-1~J4 ztkz}GsuFry{^uv86OsspZfM! zPyU?quf!{_zHdh1`COV~(|PCBB^Sze=-;-58iT@Hl~_PZ3n@3sr zUvmlL!IXE~R80E30G$?WH6YIY!6RZvW#(=Zf&$$4T}x!I0sP^ z_$<7UMz~lm-<87xTUL|Me>y4~O~>+2&isW)4)z$`;Mkh;jy(XhktT*G5g@S7@og0191x|{I|^HEa4+uz8&k@ zm%eR77kcn&)pMW7{c@9xN1q)0<1E4BzLY<&Qa1b3(N zsp8U7$O&^A2uDr&78S`=M}F0di*Q@6fN z$W?_RoGGXMo;T+@q0UHlzdL?FbyHN+iy`I<*%u8(?uOin0)n21kJ0>*qFslV?*2!< zb?w3LU4yTW_68gRy&5E59nQZnZ$6ZttUb?!bTz^N&M+hBB=QBDssxZ?g2$%)kpD+> zSjrzaA<4!)(OlGFsDgq(+aEGWRF?g@KGNoTAV5k?PiQ{D2n)pkE|*5w&qkV`5gB+B zg{Wf;_JRhIKe_+ikM!rD$Ci23UYtn)xnp&@9e>H)?2Z>Dh+&XvczCw22oum=f84yH zAdN3PLz4X&>OO!t#DwGkPc5!d>>;*8H&b1hFAfY7vBL%s zT}a=R2{g_^v}!OMtuQ1pw$w5;Y`BAdpALSdByKQkLnO(a276bH?aR9``eY>pP((OX zHqkZXEZQYgaYA7bdQt_MnvDG)No|JoWuQi<(P85@XW&rUUU(wDI0&STFRf75e2;}o zuC?=cm?SiO%sQm{%&w_>Jjn`J_`aW?oJXHCY!>lFtv32wdUj=U?twl!ODWaaMw>r8 zD4ANg`}Da>8GC2dzh}4dex)6b39B!Qk5V_M0^rd+-?5%MGjwD)bFqfGmN^Iz%0}np z*gFc;er-+O39Zlf8Id04uzkl&4skn)TFkfc^A>#jk5n|ScX^a}IDOzSyNhhRtj*;$ zIckdikiZ{wbd|e;PD+GM8ZD<7RU}`0hutG!N+D}7v{fdv?T2wVcuk$|}P!|0RT_oV| zy#4*{p7-VyxoLa+sBd{c=N@{#c;0*@^{#FCqT2V}```ErNtjAX^5 zf=J#xUw@=}!}l#$lXx1ItBE}6-RXVwwrsd4&Hfp3pt3~dBlCoO_!Zdwl z>@X2yhpi$(SA5r9DsK;}=WG7;FF3=*>I66JqjdTJmgsvsQRs6AoxmPl0^NX(khY7c z@ieA%aYzJoo7m_(@!)c1e0HkT&xO+r5n+qBhkAQs;m^#r?|(o{{B(o>DI?S@O)NB?XGo@?n><57gCkMEVG1!px^TdB#f#_hT>M2KoKsXVH`+ z(*hvAW{j#Um}Gtb@R}(Q&iaw0gysZ1w{rQmY(she=c};JUXTuWi-22FS3P%<1^#}4 zlv+QLsvVWyi(rF{((x=7pM%nbQ+VFXE$vq+up5^dT!Y{6MYgKD!?H~#^L{D;Vq(St z#-lG)hjvQ9p8HwpWXA9PR?Uhp`ta@2r;?bY@>BdNV$xQ3R z$&S>Y1u-6 z927eG0VJo%7Mp_#KEq74iK=9gw#HkzPom<@8IZnOvY0h|@iKwRa52$OKIhpDlR)yr&Ub$Y3G>=RxT78_&}B_6Ra_-gH%`&kz|BDN&Fa= zv6q}0?(XsmCc53-((o8t_X!Arjb)ABqs#Pu7}lzeW4gTbqn~6oi*4DPpSwg_prs9mj!8i%{jN9e^wE%{ZJ~%9t zxLbV2T#lk=(6_BLgthJpLonmC=uK0k2hU@_ueiPhc8*EIR_mL-VGlltLy9)-!K@lr zeIFsKQ0iBl?F$m+!TmQ>+Vq~9Bw;?(!dPPm1uC-*knb^%_V&CVSD3)Y72=QmD@J>|0c%}r;NODSYhD8yx^mE#~}ko6%DJQ8#b6#_@ou;I_AF1Bc?|An3tsxX3Y5RP?8GOq_Wc@yeWzt;@J1dM)Pf# zIW(Q{x+1c;FENe@b zM$f$7pWLgOS!0%yL!E%q=}CXR&lg)2a^zjdG-I-)iClZSeGJ?sJ}T&bh{ln**S~-F z<`!OXbJ-laoV$85xNCVmoj>!sIz4@EZ|~o)n)FMxdEX@xpE+OJerj->m@fz9q{iP# z+0-Qr$j^6&bNy3vDWq}l=TDIWWO1O(wNSyzd`dYMs&sWvP?W*Z%)?|s@DP(1|5-yX zD8wZOYYf+Eq@(QM&%nUVn>4E~xe=envOai6VT1A@p{k@SaQPWJK3P?L2J0Nm#T#Bm z^x13&l-Q4M{bk8hxr;)0UdSw6kY6ZmD?s_Sda=cJI;}c-#0-d%SOOIn{IZ1Z&_5hzfbfYL~3c2Im0;k8TaEE0M!6*S-K zs|VD7r-5VJYT02EX{23J1L0V=si^@G9!pO|$9Kf8)|5<*uI;NIqSXX!Eb9pcKt+wc zAZ0EoZLS)26%_jEcSkj{%Pki?PjkZoVir3`CnY68CzV%>X89MY#6%{MydEBEOskIfNq?%4EyF=s z35;7=1ygn+i8J6eyS($^t+EzwUteficYIo`1NiR-uH0=~o=zW1qSCfM{%Py9sENQf zuj|{l2%YbC*91woFtsjUENQM@e_8$Q*Z3;11r<&IFf8-1(Dh#VW<{d#52=Dd*LH#=<^gjtZ>g^`$X;6xXQL&+RFK3~A34wQFz`HF#FtAE^q|4a`7wKHtxXg#%6^MyK2%`W`cMQbhg zbalnp=-Tq762((Nt04w{ANHP78z$e#_ZoL3tu7v3dK9U%Xg2oMo_4 zrdH4SWDB5pKLFC$!_N}{>1ULDYP_|g<(T0yYdCdg?=`j-Yk32MEqczj;!&WCk+oXC z(X79%q?0+xQu1JQ2UpctXGPe@H;G9Zmu2u1NIo46@Q`kn>!TOxR(qljG=@;dJve4` z9%+sES1LhbPC@gHNqcH1AXWPafco&bY|;TP94)H?&V3)^g55K>2!w?ke;Cg30uO7x z&&zSN7C2H$)y#OV*6O6P6hiKQ7XY^l_Eo*%tlMT16Gq=j*##gY`p zhLf!R)`JliVaM~^=p}+vQ~Z!vT=ED(<*8v#7Mre{SNIj^dYDlvL#X95R83FsTuqjO zC4xkry03;DiT!PHckg_AKbAk^jE;eg!#X8MK^_`mKH6a+d1Le_JK{Z2&pyulo{d z$`QD7_fEu&W|J+;{RtEQQKSh^-rdNvx}YM^G4kRH%; z5N#=L%yG&O$TJ*f$Yk`VqNVu&6DU`a)j$kbWE)02)d_$OHE7gXe&NNY8IY$Yv16asD7i2q1h z^%onv4-p({#ru};^O3LPYfe;Qu-QAnRsV3%N$3*|5-q6(kIiCdIn+d~art<~-FE(m zPsI47;87-(+FN1%=R7k9HUhmf;9_J{4GPd8t z`FI0!BV@wYIyDpscA#+Z=Jgaza=*`FWJ6*nwV+8oc0dyQh?9pOZHm#D5_zs=&i8+I zA1)WAgeQepr1PN5_5<*ls?aeeWWKYh8hvlZydue=P*I?fjl`!>lE6@tAZ2}0dW&#k z!lRsqD5<)Cc6wnw=d44UsN*gO|45D&@c|lBxbYn0W!t5?BoQ;y7{I4m3(6#LgW&L= z#la)}G!%g;CR)OLQN)64exku{9(&5WvundSXUtBUl}qEb zRLTovVh7c9&4wM}dQ`SE0Z?B=(x zl{}AHVnx41Wv68Bm~D;a-~jIg^(>yj(Nfa>&{!}EM}C6lu-mwNv!RxY91%F=rrH0) z>7WAEhPw_LEOaqTZhW!whh&BJzKC|HJXr4qm8jy9$!=Kz1qD=9QW(UFU-or(DV|T* zgGox5KnR%==}Cd%BXtNceD}5mjtAw8I-siOWS}h4>e2DQ^47 z#5RF(+{R^-;LTUOgLK4xgpt#WeH~%%Kn*VC3ZhpOn#)KfJ`~uP?gg2k zC+Cf*Y1TvF59Qdc{E|bs3v`i@-0lkfvueYY?18D-T^Z{9sOy-VMjSs?rsqll1j?Oq zm4EemLD{DL2Ai}fPm=J6o=;bOiu_+YGJISC~6z%7o+yw3Zx-^~;rH+bwi-*BL`apNLbwp2nWgw)bWCX&m+H zZ3qz)33>{h%FJ3myXxwU(d@OgAVQ7>qNG+(aziJpMt+Fdpr^baD9{(=ZY7|Ht{7cr zjg?si;FAV zt}@;WqDiL)=nO}1bt^Q8{`11e1jmnyGk&gwb3{vzv90w2Dwesp`(ZkGUH>vJSvY77 zW;zVm1ckY(+mV#~LN4Q4*G_fk;BhSu7!2YM%t(F7Z=+sri}EazINMpw%l7g}WViR( z`CYh41+GTl`34Y;U6yf0qfrW{UkK6b?^!d9(fb_i*yt?xXqb(&ca8@XFA9)0B_%%B z5#kVemf}f2#-=eboI$4Ul26c6sF%4a021+z-}#zVp7sD{XXqa`JBnxU^lfZ4?s?XP({4F`6Zh(0+FdKAsS=y&0b}Cr7(w;`ut}7=ajy%+RJr;r-O#~VNq`C zRqM)Ca=n1e?9jta{?2`jjjydlUD=ch({s#re2c zOtj){wJ=#sbOq3xJyMp38Tld`!NHZgx5=y)=O}ts!=EzkKP2*f^;C_i`Khgu-ObCi{z&rzK>=Og;LHApjXl$~A- z5#({xSNigMm5Ai@S60cPTGUp7jo6)0SYPb(a?Y-O)y^RmpKZ2%-e4qn6yt5Gu=$2d z+)e$|-ZSOF96fzqNou};>0$wp{hVIaEB60-14&%=?Ok_+Hfhh_WsZM!4gguNd}89P zO5@ydR)b!UCf$TyJ;b~L`Y=Xjd|J85+n^>27oxWKTtz6w5=@75x-i^9M-brx;Of$-%W{_o z&{7yr@zgj>s}_rWDyB$@%bxNeb5a(XL@H|Gn?0Fwsnjj7ek@%b^hq?xoY>6a-JxnBmEM#D1TktfYBFx!n`EYu#o?*5wSdF~$b+Tp&QoOXq=mD`Ys7}yDkn-!hwTfG_GHw=f=0sFBOw56F zXy~*w!eAO^iySZrSNH^?EKQJFx*6gemZ)FjGIDV-JS^o};SWS}Kn*Z?wy!rxcJ=~; zk!RSE_S|HSYlf!hCU|e*pFNv6ocF%c2|SJwL&wn3te7@ki9OCq}Dd3>;Ft)|O5-KcaVZ?7FE;?!5TuZivNRkxRgimRh6x zoja_IOf9~T(^^S+?#H;3uoJ@Ac{osR}mn6vuG^;rl4s;SqT5|sM-#WtXRGJ$GaLV?r((NPSP;*W8 zOBJ?O(NgxAvqz?Z0jsDC190}MVS3B7mF0c*Sxs{giRQZ?rfE6~{R$vyj;5gynvN>D z90uKv90!cnBsNNX9kXUmGA~+=!3!rp?;R8WncRl-8JJojI2=(DSVF@WzX|IaZi`H5LYJXu*Z)C0HuXV+p? z8kM?lLMn0F*O~{gOM9mmQXPORER<$9)0v$w=k~VOEvZcJb<25e6h>ad>?(jb>>A_yY+qA|>JsB+It^_`{X71Q%DAJ%t8H8aJo~R4Z6QJ@iOS=wU={ z+obOWuLI`P0+!m66rd`8wqm^|iZtRYBn~nq2)>%SbE2|^eOVnmEL(L;vIeP)Z>mm+ zSxl4=Ru$ZS4}CHZ`Dk|TikIann$q=B0&Ry9qUvV&#kA4N3$xh199HPRxHDKlLnWAs zj?2VlJUH*cg2V!#g`ZV@+OgM5gdQe#gD?B{DUFL~dm}&FZEC#OZlm;de#|pjMbc99 zO=NXrXc$@zWk`18Vo&M`bro_=;1O2HJEROAW>kyK_+peL7KqecaqZ8FOE}1o3N1Y@ zcFk|kf8KqQAbqD~%e)sPtR7gKnqApQ0Sf@0 zyFCA}jjsVmz!Ieaa5`JFW3X=)0%%{L&K($nD~Dkvs-j;B)t;YN^S~bzT;_2grfz(2 z+lbEI3HS#Nz2AIlvfhlH*HiM$(_hi zZ>I|dM5FIm3hNQ2f~RoF!*xdax}&MXu`z_5zB&P*hMLX2Q#eQc=?IVF}Q+i1W+ye-aZ-yQNj3 z=kQ*P)=)x{?&Yu_Ck_8 z()WVKe{hAxOIBE~&vbiWA zi9;v_Li8{VpMte9l|NiApuaI?7^7Fr&cVz--+nes7jI_v0|Kr)oMxD#`51lxvyB6q zu(`9a#%_UOx1tR^s=ax#EI#gj z@^IN(7`sk^Pc7RNCc29(UGi@uW*8%Z0OcCq01YA7_wI}UJ?CP5Y7pDDsY`3N4j+0W ztsp1$gx(E^Rlm(h>skm@y2vj4%1TK*`#qNOPV5V!wC=leM&5Gy_$f*%;r2L-l&6_imD~2TLbbrZjyc zJH)sD;{Red)Tlx$K%I(ne?~bU?tl)VA z%jbijQmO1OBLp6TV^;X#-?Bt}CBPKbiTLO8rnfQGMGVgwwpY8}JLoib`+TDPx}fUy zZHhA2B8!zlNNhutQB?ZUJ5+|4CF6@6-5_-m+W=w`TmLGC5xJXY=0*u!eQ#vI+~utC zqRNhh=3wiDqw~_oCv8>M`i9MW;ZV|n%EU0$TeWcQLZ_ag7s;;`# zeC)VQ7PHkkY|m%RK@a%)^_=hT>#va}>C(5}*w#;jHVvK;Tj6-Jq)zy+z{N=s_7G+f zyezI>uBTA6AWbS`=_NBs={^dgd+3;D+k+Z9RI+8ZF2Z#|{H#Kq zIx13rHyD>Mx=$5oaNydh4-QJjYAdSsu*6L>^(rVD|K|;xtdxtF?b96gk`jTYj2R_u z`lVNGC#Ii1SY}wq=9rIWaCe^i0}XjcPO_&yo=&5dfwdi%`~Zgf47{){7{KA2&mJ!R zmO!oZ`AT@kDZ8z*qa1GGx;-DI;`u^-q!fbeWsP0lwQ9c<^d!bU^P5 z^6A<4cqgxwxHIYoJ@!AwaaFXcddvxuM!h2WyzFk&cNH_z>$8ZFQUr(_Y3Z} z2et?Ihqr!#U9Z}&Pd0jQ8AEm&#WrF!iWt^DVoQ3=_!FgqD)LH_AEK{&aB=7Q!9cba z*8W~zYd_e7>>1rsjSvC<_X8{r4MJwbCvyNxO!n4q+)y-ExJNRDyDYe>&q{sO$^0(? z;xD`ZWPGAumRdp%chZL_*`R$(*q?^k3R2;eTtkA=yWICix?XNA@ykL;tH|8o*x+tjd!~L#W$rvZE z9nax@i;Vm8)e{TxvPoGA|L_|vLBfEHVF{dro7}W#tgbFi5v4PFy5gK_P3-Ly{E^PS zt&Q^$s5P(&Nbh-@C1rG92ZT?{d>$2qQhJuU+2YQnudkX;-Love@~H|l%$d2e!V|_+TJhvlg=;(9Tar# z<#v6qpmmbf0rA>LsIoE1=iSvRw)*A=-(A9Cq|s5Q*Tc~?sgV+JjiH;=f875qsuV(gCqE2a&rWM#j@?*N zgU&&|x^5>*z4?b2&W*QtK#&Arti! z`O9daT1)T@S~_C4ii<{yS-H$7_**UONA!&Hz5LY+qhRS}Jpq8n1WHkMC_P>Te z^Ln18547ZHU*+21pt}MS2{V9>fBf|}hhq-Qf|Xx?&Og%%PHVJbuRQQ9u~Ie5`TIYM z<873i$&fJHI_H()pV5Wkz$H7q;gseP3rF~fFjqhE70!=BTDzA`sXJXICG}FKukbCs zZ$>Li7qS9w?OSqCuPW;QyP*$aZsa9twk~GF^{R)F_(s_&t>QqZ6(wm%`U!Oeh33}k zBv>_-)Nt9*Cns+cIPt#d2+;N%Y)NW<$Hglm>tzy1&^3ZAjm847<3nMJlq7-*e*vH+ zD#iGnIS9U@o+@qNCn3;3DguFosR}k{&!q$*kRa$kEb&$wIXXRX^{^fwwfctPqx%oh zUE&{>Pme!0V_6x9v{MR#-}q0Yf}SCrWzMKqBT>V_r!efi_7qF&Gm3P$t%NAv!P+{#K5sx=7K$VPM63f4ZOkQ>eY zuCAnuV*6fV_o$J4l?O78^O<>>Wu-344b4gz<)|JDpluHVur12i4tMOs%xA|z3~>q0 zZL2;tYRkP4BA&sVc}|H&pYcKm*2hj5tej5}R4OQ`Rmxf3pOwl*-$uxK-iv&swisaT z7s6>iw7hnIYBCkOw-M@%Re6@R*cpo_i&EOnNrC1xa?VgiaDA{1-jhL{i@zL3CBAO! z`ok9|bN>U5=?)mig{b1gwg~-RLq`U}U~?}9;__)sz}V6S?WrX(%)$%dP$v(uFJ5AP z+F~9U5D-rZ|`R?LSq?r`Sq}q9H5^)aC>;v+TT4qMk4PSLvKZq;;L}` z7G7c&O!WTo2yWFXT3<>E<(aiyI?`bZ$Wt{Lmpu7i)F8?1IL#+CFgbf68#s1#xhM-; zCR$g)r8p9`98VNP#45&yktAd=k+GW;9*S@SgHQDsffM5-#c%IJmX^L9i!LQJqBO=G z-3t~{QZk|bPbVvpF_!O7MpHAyHzah3-zMAOGc>Y`(;0;(pMH+q3cX_OHY@lJ@bcf+ zDhmaS=B4nx3;yL2u#yv9o3FunG&LKo);?lqjy}bv2itg__8#6jm$b>#+ zP_KHKtL~+$yC=NOmWh{cYv2x=t-#{)22m_;5t*v_`VT`&L)(5hPx@lEGOgaXH{tF ziJac=ly~R$;DE`<)T>~n2VH7g{1#8ExVSaLI{>5 zY4av~K@)3A%oa4>-X&C3sLNwX8SyYprXFhrz?9>~x8XeA_<@(iy``gO5Z+f+62eedKHdEH+gFU9 z-fOXcIy@gnhn`eqPCV`WW>wXA%FiXAre&*QCk+%~+(jg*wHj_;@ktdi7S;a7G8JJ* z;{tHko50wt8RB8OYT4U7`lRx2bnmJ4(;Nrc`{NmD1>mJ zL45o;^|Ii$R2$V9YKh&xDs6e1Mh->JJC(q`ht4KHNFHnUf(~Q8U=62o00gLczJFfp z1$8a+!}DHbQ9_iwPoqv+4Hh6!cyD z!HIMJ*QPy8=$V_>Awoxp7WlPV!Cf?GKJ|hU z1)ch2^%|ux%bvxXq)^YgXq1;em}Hc^x2uo#2OiLoXH|Jf*>pgh^%B)XkDoy3g>Q!T zl6{YCm+J3YN^B~Ln5JbD?m)ARL;1`?Cp?>8(^4e=Nvl+{3^s!x9H1<-jX;`?u&Kwj4m-MBq`Md^svCEd;bE$s9#YS$GDByxXCoX6 z?_yO9ipiqv4!CSs`Zx{ZI>`?DzxBvGOs?XZ<5p)@FlmP%vyYMdDB|d=v?&;{dY+1; zaGh24;<%2MtXFrh@SY4Vg5``i)(?JzdC5%rR z#~6POy=DwMc+jQ2Ic8s+41B?Xr64dzOoFh|?LPe!$0`Kq*{IUuyyFBEwr47?m?+O^ zFr=j!3B<9XcJ5*-n{2u!{Ta_!>540*8C&d9lFfCT{{jfJRhd|L_+Mo+Rj49@Q4o)e zb*AFbqRXO4m66^)Bq>-@V^;~yTE(1Fs`3U?XV%47=4NZoPSK|(RLq13Ty2f}T4M!_+@}}!(B-AV% z!4Q?-MWI?8dx)6-kR=Nd^+e&?T1Fkv)k|Qbc*e#-K)PmIsgdSk>+FX{ zgJ7P$BdtB4U7Qwn{=-Jpb;iownU@OtLLoM%^7qX}8n5_3{-`tRLOP2O1P$< z&@g9N6tSx00ufV$Hnk8khrL%`uT;_4Y)dNSqI5$pWNve-XHWv`1__hyn=Db6$(h|F zHb%$^GWcJ!B@$|>r4!p2NX)CeznP4zLAsrvbgW*j-bRLYuD-cDpSZGV4Df$iOSb6p z_qbYOyq~*rVess4+JGugbo@dw&)t8Ck)_ut&;V$tjD|$m|9NHm?d_9xR0)8@P=206 z51fF@24yZ{OKUcI;0Dy2*|W{Q%R<9rrA&*bf*L;o(wj9)T%^vvG8O!6pEeOIvTfL$ zbHH1a$X+~bS>K4#@Fk*;j%l66NFPRE(7=~{AVifS&wssq%Qs_Zf@oMc+X40TjAe9e zkMjKR=g_Qq-H;7--pVG5ciz5j!As;KGomq07Ni%d9y}nkq-Nc0{ZLkhJhHY^Pfmi3 zHzwOGtv)$+t+FSR@vwf^x0nGh(DUezzWEzRAtxj{QjjKG)5o(&fctwU!poH6-&MwJ zTdwbt^5_^!YQn`Y^gLb9L!pBZKnw@IIoGnlF2*)OWG~oYqohUTssPPlLY-mKy0!{Z zn!hkEl*tPvI20g|M0ZRX>z@qcsUut>EC8{~T@iI<@0euOA4iXdD~=m@r=22bgZed# zq$osR5s`nh22~8D#_zwg1{y|jlBR)sviX#tp<`8GY$$D|ORzpt3BLqH5hsF$^^Dm1wqIk*00d1rxaw#(VIC%3q_HUH78 zKY97ctDnJSDs-pDCsm&ul1d;Efm$*T$KA*Ay`U{8taN`q^yostsb_oNNaaqB4H8I> z8lmq}e`*)2;qL7eWwTZy-ej`3+H5eqf0l)Y&C;JPPz&IcHk;-OU7mZX?EmZjO4sRY zvB28>6DlCl`C|&jGkxgRM|XOT^eu1L_%jKIz5LHmqMU7ey8x^iR_T?A2iqJWsJAf8 zIe)qSOCOKB9<|;+zo%f+4+|D-<1L%`-;-(I|K{k;HVJ^TA128>e3c!JfBN>5$1Ghn zBjLvd*ke{3Y8Wkw^{}sZn)TzdtK(Q>22wwIx;fZ9UJTFRK77cGWmBN{3jA?nX5LA>Q7uN#up zb5v6jG|x_xf7er<3-aH$kXeBs+McS+wg+qPpE29)uzgPATmH!U5jQ92cp!Iu>U2y7 zIdvVhreOctvRl$YCiyTjRv-~r>=^-&9Ywi%R8}5pR@1%9cIRjeiiXA}Ib9TWD48mh z5hznqwjwPv8#IEc6Ojnx_?=#;n?aR+42cXQ5qvJ9U8wXaR=!8|aTmG0DjvLklwf&U zcA5Wr`TZP9h~mN?hJA=yM&T8quU$m9%cr4iqxmbXmnWHl;LYWwM~A@uTgigEFR1XW zX#URix0bg!$g_8z{iMm%b?7cTs-uLc%^h%Mv6Yg6<1pO{sI88aCC*}=!RL4uig)u3 zbTr?ZkUnt+XkKUFv88x!C=hKx#FgeQR%s}}o-LHUn~-BnE3>P6Tc2tsn6FwD>1B%l zruzaD+yMz-QT$*9S588Sai?M7g_RE-?0jXqoa6_HY`6=DsM!of@Xa3a_qCg!!OZP> zEP+MB!kbQQw%$#QiuqfcLxTfMyS$~P`p-sd96}J!3T=PScGEu?2enf@EN+!iy4qSN4J%r``e^@xqn`;axrxhdOVyh-?D9xM|Ns{wD@s*je z$w?8z>iw$v%6%FfwyKt9@g${i_x)T$@wl51FjFEXjhb9WGSUe7eShU{=+`^98m(W; zBW;o<(R@zxHE#J)TEQwZ#95~v@R?n=NO18nE5_MH>(V32GZr%Ces_5|F_J%%zEpmV zJRwL%s)%pk4hd;a(5b;m$@tG1gI?uC+Up2{K-4@6=+W7vlZ9xUU?RCT1{e#=#luD@ zv;v(Hp?b(rqKC%HQ)2~^r$WI7!u_0o5hX*G>U{w@_e!jAFr+C)s{+*1o?<1)p5 z|3L>*eQ_s<64?*UW5R4qk(K6<22T9HiJR9o=zMZn}$3QlH?txEHU&zd!t)$%3Tgnce*J z3+wCihIa9f*io5ZWVU)65Q(NJo`eC#xbe-Sgt+kk4E zxIKLcF=*PQ?vx-G*Bw4b@^eyBS@Z>r4dkF8kPRlU26`{jgO!jN2Sc*wyqEue@93;*fMS~!8z=ALMBMC_cfwUF;2A8Rk84ymZ3K)yXN%`~Ww7U?ru+d!5SN`5L#TxCNIIToSajMT4ify9Re_af&o(aR^r2 z-JwWvcPr3B3luF-T#CH;e1G%)k;x>JNixsg=iakt&z^;93KFt4^R@i4QO~MDncnJ} zF5XO_)U9r*6(PYdC;TX1EsZy(I2hLL(sN(T z$u!Y2LP;c_e4o~1Tm#2PJzMo!RaIR!2o7~dVqvW)pHP|(Vr^}qGK9d@4+m`Xz{7zF zEWFC80_GuV)_>HkfjuieS9iEnN+wd70a%q@i5^3?_7!ThXf`5}wxgO~Dy3$Io7uTl~@mEzzU-=686 zbF2Sbj?XMFS0gg(Ey5Kl%nXPHVTp0ln)50MC!#Sm3KNzxg0)|l9e7jiAQx?<#KdESG zkbNDKVZY?JC1WwrrxZ)r9mr70Pzhn7rVYrkArR{xM(@x1JRgNi>G;XMP+Kv6k*HB!dVtf4t}TxGHJV1~ zurpVw>gGQay(mOwiJ|Wz%aE*`jd;7oPgmDnjDIb<3g9T_fR%xeH7|6L0A#W0Qs;=R zMNFjVl!H;2Fv8Yxd)*S+{ecsMrJn#){=r7mQIKCS%tjvcR$<5EcIl{YfeDwQ&qEq| zW*1Sx#%1M;C-^KqbDmAU$=a3nbsp2_|?4Mh|S^Lyfn{cjKXp^&U*<9`+HQ$7aGH`zg1`yZOzVY4#}a$wrN?h+;>r5Gmlc3|SqjS&Yeb6pbxUzmUE zFvA>F0CpE5rvP-u-ZFgUa@ffS|LyXh7nOVs!$D*Hb^i=`=<$kEwJ>0_=V|YUXr~t{ zdWWxCJLZ!YkLDLgmGw+o4`i0@Q9gW%R2{@G%5Zcj5aF*C_EeXc9**h^ph7W}ma&1Z zYh@;TIrF0vs`?QwT^M=H`mv}=68p!|ggc>4#j!s!gX6Kbe}-<&kr&f~7%!;F-df>U zbbU}7cu)Qu0Y2hkwf@o$bHV4R3W(TDD3&Wz;(3|YB_vGGJ;ZkV*@pC+Zq_qWwOnD*$iH>CE2AMGi@#XPuJ;{fr0 zxql5zac&*{2oq~v_i>GeE{rPbFSvxnAa}S*A2+%VQ$2=ERMN&m=ebcl(A#h0BRtq6 z6Z9#Lf?k7YKfeTRQ-v(GOKu{ie%VBCJD|kcN9O6v zCm!MXchTvnRj+j>^o;TwaC^>Ulf&Y|h);eDmXn$_Fm(V=qG7?g-P9&{TjO>2W8<;4 zWK7{TSsT9TPBP=up8v)ZvpE9c4)eX|O?fLWZ0SOxCEe8%D~FFhORV{gmA68fh~%1u zH=4t4bltx*7U>rPz$Y;?!NTJqKoiVQtZ!Wp`W<|S+q&_0ZvWYI&iAgxoLrw))jq%C z!$pT5_RHRtc_yXOvQ$ELc$t!2FA~W67?-$P4KU_D$jwvpR^T)fTQLfSo9f8onGo|U zqm*d&c-`X9Q0W}`Prsvn;s3%fy=fzH5kzeNRrUNF^MegaYL=Y`3=C7sV-P3B0wFfh z1%z66R-RRy(Dx}ud>nnZNc`2Fyd)AmjBK!P_QkOGHrS6|bA-wsh@3+JkG~$W*^N^1 zZdi>?vJ6?g(hQEo$6QO|pvFd%q+!G4;J)J}!DmXF-pOnR?(f%#kmamBH zZ_9n3!$}f$;9#8tI&t$WvNFX+g?Whid*haCzSBX3$|MUYrl6Ncrr#WWB9DcVGgIG6 zg;lPU20!COmRLvVj=d(L`+40L7)Gcdq{+Ahw|T(sP4H>YqpR6XMQhc`y2Sg(Fd*DSc{^nEBBdr*k-w5#AQc>p$la$bTleA%|9>^0HzwSvj=CUMBp1kpFs*xY{585;>0#oAMj?flKL z@yR1h>$Nzi{`l6RvroBLx_T;$$fuqQ7xHQ=_IH!Dct$)j*K&8?+>}-xURF86KH2@6 z^*rZkkLt|cTRQ$7T*K*~k>m|yVcXQA_7}pm{3v_;X!N11zBUYU;d7a=^0J#(Fn;SM zL^)wv`FrFKCc|#HB2BXJM9Y_paz&rL1!LK2Gph9kBipD*fqmK0FJbAxMgM9P;b=6G z!f>>H*UHyoFop$Kt`M*BLZgFD-qe~Mix$wV!po6{`J?g1Vv%PGLkG%OZ*`IV7Jo*Y z&GqCy5`#1BkK7K70XrV_r#vWn@9<7qVL+J|Cc=UmWnbz9p~r+#f**R-9%^0h=bv(M z49Rs2PyPTV-W+n!@MM-za}>bA&&l8gd2$qSLKNVTStxS^A|D?w#*&1Ica8;k)ZQBK z9!2JL1XjL!bELXtfok7d{xK?ErDxHc#OEbFDA`ej5n2%)J-*L4gtNeEGl5k!yU<}y z*(_@-pAZXnQM+qAKN^&WG7;-&VCNrPOeLnj+^n~xR{I=5d!jf-7rx2GW-4D{4wUq< zoz64|x+eol;)0|xpReNUK(Rr1%JLbw;~4@n>Ms$i@faMrSQbh5I<83o)ktkuZCoX9 z1wZV|jTd?_AQ0K{O}G6(F+1fQuZ<@%nps&ei4N7r?KJ*0z5YdEt!3ddXJeyw$I$=8 z^EmnA$a7P2Mq-LOqmN0)176`_AJYBpn@D1rSZd3D;PXetl{_ip&iE^=-F;;32geS* z>I^5|M680uoyh2c)n5mh&_9@Ca1j_EpJz8RI!;z>)IOCBR~ekwFRl82_Pmnt4;mK# zw@`JbGdY}jF6tPxRO>*Q{Ev_X`P>~=v!j~86r(p~Ly8!US*>o0|0U1W22dA~s7cKS ze>D^XAwShoSFLE5T8^DQQGH_*r3TiLQ>j4b*g)OQ7{l9X`%kQ7z)x1H5o({C5|dnm z4U&-ulUrC~~4a?GA$lsGwqHE&%x8y@63BO&A- z2kij$pe)(RG?9L0L27xyqXZxtdH3IUszVKwnbJOUEtRZm)i_eyy*z>X!?dk=@*iiJ zjO6llUk&Fs2RQLZNVkMlPc%#}ZIaV?c5#`2hR=Qn-N>Tcu|>Q4% zyCZ zMM_)Zrab=MLej|R+q3_ENdBG7{|6$tGI@FMJPn`TVUZeDYmmiu@7TZzvPSXF0ksU^ zwvKOj#G17-^Ts_&CTzwo7 zIPek@Bco{GXydWtcsf4|XWV%5zd(X^<1+$4oBZU(pNURm3851U zLz$?R3g(vuRvxf?l7MY3I_whIYE=!%2-VygzEloJ*AN$q(!z|X*S5+ zU_P?;8^+`zjJ+=inN1^QY5vX+mytnUurO)XLwPDHqS3DL3m!iji=hv;Q6msK2!yBR zxWdyde{S#`nXgM%@#=a-OABT?QU@+l=m3XP=%pfo6l4-i@{AWoz7-?a7l%oUiN zcB+oFz$3doNnP@d&=y*uppG__NmB>IIY&|fc3IGUtHR*R)*YjioRpch!-<-v7bMYn z(6O8A-_qK8eSM7{u+7SdoP?t+aeC@2<|xuDx-Y3DiE{;Xm{*G)TKS)+%%()XTgE=c z-&Lc*AOj~_2!upy)YCXfNWef3Tt-exjl|~41?&b+G1L1tz_%Ku3tyH?CR28nIs!5a zpR@Vn*ai%{!VGiNih_K7JvJ_*V2w)jm%l}M-CC5govrha&Ac+IR+ z#gAq?BZFsMufB=#bbBE~+{O_9uUC z^4^MDywYvwI@wnYR(sSZUoqo>ALqWD+`?x{&)`_OfadPl0lix>-a@ZgC<6=e` zL7I#dc5X0kK^ojx(ticm`by0mM@904gO(w&?M2@7gu_M9b(yoqQ<7a}tdVUooaCAi z)dtt6^NqXP0#}nw>iiw!@k#M;He7K#^bcv*2rA1@D)@3}tB1;CG0}~$d}-Uosg%d_ zL9!~P(uM{Q=rX;yrxc>UX96YK*~JY7kmGXA2^_*et4iVb$9 z*y0MY&>|ELFk(V;%Y3qAj7@BdrIx0o!ko9-fC^6+tsA9RI2Xr??aSSp^wWXEF}c@M zsjr0hXT(RlAN1Q`e55br=dD{3-B*N3=du8-DXslcIG0mcjcx+{55XC$**;MfDB$eaD_FN3)wg zDv5`EwcvlK0^#YW;CpPxLyT!?A_1J$pCwm6|Mnvg40<_J`WBmk322{clGeyfW+2Evz~MRp1;ZhqQox} z6G5f?QXuIx;XuDtN9Rkk(3>`fxz%rq|J;Os993<#%|4a09_d-u8>hIH-ft8^gB%_gqq&qH!>`~H`#nXbO+>65dEbyO}>0dvzpd#Wy zRL4~xk-8;@2KBsJ6|^@Je?3HzRg}>C;a!?FGQGsmotFGmH2Ei?ENViae~c!4xedIx z4Y0YQ$=h0^zDf1{as6rifovq`d_UgGL4?OY%>{#umBO)F2tM?F!LU+flF#;e3D)mn z5w`Xl??x9!h%6V*9hJFDuiuxv>a)A4x8>MKk4vQ#Ph+pV zG;yI~ZYKC*P=RK8(doMrlU!4#+U+X2Y5({6#ti|R<_A*F{!;WF#d16;17(YpWYRak zjJnB~E^{@|!`p+9Iyx(O zvRD`-z!I~d6(iHjTz{sD3M>i|$!?|WuP@yRr=q;4#LKHI9SxRz4ETMnk9zy~9KPru zn{{@6d-LwXlgKn^d(+D7UE4LDT4qy;>ZM|_Y0lnDoB#H$>+b8WPp{8LrXRjf=WtBN zzr!J({ZaeCnD=NaEZNQ<#<5)%d?9T2kX`ue$2SRmoUsZ5HquBEZ%}L;Q7N^zUsqrO zj|MDC=B;gUPk!h`cHlb}tqW0XCLAmRV%Y~CUXndDiy^ateUEaa9Y{jTx>En+h?hI; zB%|=fH2vIc-VU#dMXb9-JHQuy?@TX`Cbqo|8Bv$J=BBoXuF4I6u84M& zC6ui&35s|kin5yN5&vXWL;E?1%Zn?(|0m?Jz(QH=-;o|D!ckmTbN1}=+hi=tR`>Br zSjCGjdAw-y*`)l|P+qcN%8Fyi6xUfLZ#k+lpduBNF5MD+xtlJ1QGR<5LF+^R^oRBH zHrvL}d``H)UCW=&-v#a`dVWNPz6C%)9x|K(B9Y{3zEySh&x$}~UsYU7U*s4Kew9DkRu8Godap}OJwW_%0>9FUPN_5?a zcG`Avs@=h{a&={AWkQ<*Ii^MJHaIj>1@Y`3N&s8;Ely1+4*;0MuPvtmXw+g531E#O58oER@7r)vBsHr?CFyjgSO!~-=K`3bXo@I+{f`VLzA(S?h6s!%f zmnGFCis3MeHX?qd%@AQ7SeJk_+|T{;e0790<4^Vh-d;$gaO{Y_9} zj~_X}W9Dke&A&`TQT<-h$QCs}oq88(TJG*zxRTv1YYbORInB(Ui))I1AdcaT*3^2g z#z~nRD*1Aasj##|q_|z$aF>RsL}%3oY_Jm@j*%jodcc@A;??r2&;Nv|dFfUYL|>^I z;~G5n%#?BD9DxeexGFq1rB6Qa$X^;>kUJiisf3`ze`Q8c8|$a6GV$B>DRk-C=$ zESfYJI+20eX)EHxDJ!E$I zpnIZ-2)O})+>2hoUhoZ$->~Dbj{%y^DRsOHV4GC}F*>KG5EdC?5(PI^nb=rE?xl(a zP5wo`V$-Rq4fCDGY9kk<^W@|epf5c|9m5;8W=M&XSm2GUfBgwknv4W`QI?53j6$_$8|^8ccotv-L%x_$+PWj004 zO&-oHpWrV?MMBhq&XUnPPvcyTZQk)xv`rj7DG*DCuA2kTdLe|$>L-3$b$~=juL2&8 zE*?4;VDgK&v%bvRR{Nq?Dd>pBgLqX3mzSXlHPyN~!buggH*+9t9#;*}-?277_cmp^% zOw?p3T-DU9gI?1@VDo_>868t*fcz1|tpZ-clquN0T4DDma)0UhZnpP%&h66ek%R8a zs^wgf&RJ_kzgc4`l$~ZQ;Exp}=HCfh3D4AD!n!{pt~Xg|d5>gaHrn~>I!Qh#CAwlB zp>ZN4QS-*0B6R%5p7=L>ath{ed(p6>}9?@3sw{sbZclp^58_lnU8GKGE*A% zSW7B3ppL8V|BRECVo}9<$bDkwZ?TXYez$0jJF*{p+?ZL(Y#dv8+<7jITnzME2ZGcoeZxr5es#nG*iUWAbhqH|!zxpA36->HP1t&u2NV zcB(B-6B?>-mV)10%`k!XvBs*}C^FUs9->`u0B>`JaIt)i~veS}|^;6E1O8O*j{&H>E zvkm)NuGQ|6nwSG3li&*k$Z#Cpn!knYK$i6m5xwE)8AEe^d@w8fc7L98uNNr`~0HB^*K4) zeeBt|O|lIN^mbwIIcYNtVM9zf$q$882pI5lS#o@%-dTYS^aDerN=U~_HdshQ0RLj* z^cP6LU?92}10bgy4W~{Ofp|VYG2zpq4fbvL{-d^K=lrPpymdWXDLFFLMd{-gZM7U# zp_JP_1xdd(=hinUP-YsSUL_E^(+)J=YmXN6p_oBo6XwnjSg37>f` zLHn5_z=rFO(J+=2?c_x~{J_Xbq-0H1uW}DIG1eW$EQMKRF4PP6PPZD|??}`YKM0_D z9dBS@Gp93ee{uBjWpINz5)=*nr8d17mT_RnwZ*H%1a;#Z7)MlK)=Le%6o~S~n;-rv z4MmAXC@d6Ztq+PTsSNqQpM9nOGk#|7bNoHd%;!sw2rVCGy$bfCi$PX3ui(cIqtup~ z0^k%b9hOKkk<h$@Fg?q?|x*t$P^z*T0#p0Tx_MXem zVHYW>vxG@`xS9o{vheFY#Q^7)AqltS!fM0rW;nQVy{Y8a{Y7czlUT$v{>r5iq3%<0 z{^-vj+!dD^nE%TN4i4j2^&~}&RTNBQSH=5Jtr*rgh>3{nfWLkI5_?(0MP!BZdux*a zvYyC8xQe-(-@R$^4X>CHJG46W`;&6=xqqJ1AZ6zaN$P+;xxb<5szG~-nh@Ob(tvO` z?}t;aq;)ctaFqT+ppwX$^Z2o@0qYy&?JvXIia$FeqQBoZj&fqSDep(DARCo#w?8+Y zQ8dRZjbm!m{~nxm)U#6XhiQRm?{_NOG&R^T9dyXZBp$Z@4ZM_64!bD zUh@6!c~C>^Sdn8H>aQ+;jW3qCSGc~z;=F%VXT(25$@cWcLElL^uv<)J+%QMZq_WZ4 zks`*XdP<>NzqP5x=b);x`r9gR_3G;4(o*w+N-IDvMkW2=N6h$m`eE0b)*ii+qs1@& z$L7K9{`R}4YEnDwTc5nJ%1n5O7{2VKshqNY90>t{W#f>y>87+IfJ}EA1QHpgWJzU+ z$S^=eAmT#H#X3KvMHxgzAp!q<;r%`KSr05>$c#$VtoJD&;o6DLRf(Q3rDO<6;wQ^j zVcicEl>nY1u_>8fNZgL@qQP$3KsVib~k9 z5LIFp2g#2KMft?Sl(!3b^oL@W-#wpiv%aVV;>Srp7A56kB-@NBi9A9RKsm|^1y-72 zU973`+^FK-b-cFBA*c0^Q)Masc%(enTXCQF8;>@=oy>eY=n(KvHrjy~79$ToV|(8J zNa6fQGZ5a*l1HH&_WG&r6e1U75q3YdP_LHd_vaKc5(z2xaifz(&aC^Ej>y78cHXfN z1=)X`U?K|V{}xDj^c(Uc-PzZhC)Wo-@H_K=zhBM( z=a?Uf?UDNf^Cc!bFa#vCg0XhDY({!SWFNjV>6BG`N}Q!;WtFL8nd(6^%F!{35o;?( zh1+<f@ zJ=wLBN5h&Z!T{^Au~dwO;AKb?Xt04u;FLC$a1ox8%`srPcFNmo=zzBKWJPs57*5kG zG|K(slSmh7E&E&bof{P7d%E-e8N8MM)n-90wV0+IFt{vGA1xp_Fnv}DV;Kv<$QIWo zw}?~_v=`DcbVTk|Jfj*~^z%}Dax_G|ml?}tAC!jS6j{K5G}OH*y0dx6tFOhTEc4?w z9>u#1pO2HMQik)N;NOizcZGSkLNV7I0T)fi`abS}2A8Y5`C;-uAb#cQ-(sLq3!~vX?IdEG?(_*D7B3&7uyUR^#p*Z7r*SKyifpr8ib-sN;nS~ z8qKEv$sPe6Of>Y?&5l@baP(HGS1C_#Km|Ma^e^U>`N^zW&~|Olg+F?%8}iQlbf#3| zyvX*!Tc;m3z=L7M6b~yl2I>eTem0BbfHsgdmgz=Pn1j4oDkfgYkk62kB$xdnV8S!k z9(>7KP$1UY`0Wi7oF((~8qWC=Ij85&?~_-wW`WCvAP!Ht1vlX2P>5Z&nLPfcm8fN1 ztkfF^4GbU$IEn>Zi5mlfE_FKJxRg7wrG9Ymo+Y^^`&%$;@RNl)UYayb$p91nOXbHg ztpbnpC=n*e^K_sD-mTi)alpmx$>v_P^u7NJc80H{@BpJ4po)tcFbABFkRvel>Q8(PCDziI4! z>O{D4$u{fiSt4-cTgZ6T1oR{43knfhcVHeq(*6M6#Ii<@kx-JcpL0i5s5OCVSO6gN z*Asx`{?C8f*_GHZDigl7ojEJ*?<=|J+qnqBlHf=JB3V&{DvDu6~c$81(scIhjgw ztG35yczwXSS|CvX&|eNwgeqD%e(gEzm!!iRP-23pW``M-MT=Luly5F9xvA7&p4ERW zl5mT8=$a1pc?zAzz)Q8HhV!Ruq=pUiO;3N;5*`vjBdH4&6A{En$F9qEwZe9?@QjOWj2>*{_v_}6sHP6doRs|D;$OXx8yDcQ>IPc_WS z`35?Efaoc&_h7dDV|?9;GHb<>`YB5x&4l~4Co0MUnsX9Esy*uKu9=R|%!r*Pe7IhJ zmr%3x@+2nyK99Wo4Jn2!GqELMFC=%IqXDWQ0V~lrVR$A8wU%#5kXe33O=S!cJ$ys6 z0PGcVk$kZ)$Uu~^f(qgiJ$}L{DkHm4;dX~r2@hE+_Z3uo87lLXtfUXA#{fZwxGzKx zUx@+ZkpXu_l_lG2pZ%5(Ln$MAS8In|(B4w=Bt!b6mr(O$4tVE$INr8^KkhSq&P@BG^V4GBoP&Fz_ORN<^#`Mouo69T^alHAjh0!mw@4&I+Ai3L%c zu%3uV48c0al)D2T8mEXXWoXjFQGm=P;A|Pu=yrgo{c$#MJUpMiI~MwSBH(bGB`&xV}T?wI2}Lvb51_QSO>04 zl7~i{>*~r&M490QZ`#-K@j=xixjBOSNM?Vy`Y|kc0!y6~g#o?V__f3E@V`K~OgP@m z&)ocaemUs2ziQAQO&@v3E8kSFw`X%4j~F}s)^moIPpRb0;#$8r0XFu;#0moCAH>|s zicp#^#A2X$Lk3@bem8?X;zJammF=z~jHViw0!7=xBFEzYi(M8f;g_TKd(*-tEp$S+ z896eYqqkdi{hoW{VK4jesE5Lo^&ZhpiGPjCh_q`mUSm4yq4poVfMHGndKBlbGd zJGy#vz}irJyN0tMZ(bta10xlr=W@gs@a>h-vdm~}ZSY+ns?IF&FUu5IcSIAG@!f-Q z0M!9d!O>XZC&_^|ZKj@#Hoo!e-RZ+>@EY5u?V}i<^2(J!QKE1wUzos&hgYS@W zw@-NZ`W_C^zL&3VATVm9 zVypqQRy&?&Tlulb&ue`}jrsV)v4<~Yka3;AM$;8ee0y#&wENB3Hu&)c$8V^9GeIZO z24GA&I(UjCUt{H+q_J280x|$-0~)Gfr;v|tGPsz+B%1&ENLizyeKZFq36ughXPB)| zAwg!}PL!8EU(AA|dH^IyQ@Ug-d%7xZ^5+aJXkw2fFw;S06|_WN7aFGd8K85ltYKDD zD_2R>sT@6>AT^C(pJl<=tkMVrO=qv7F<|W63vbx?Ce^-Z&VJE*|9^KG^PVPkohF{| z0$r^T7;+>H`%&YWKE8e6f&9>E7m(3CW(=|qmjYB(E9z4guu6$y2L z|E~p5{*xs$_=lr|h<$)Kq>q}1#ZptU5s(;yK)mK)Cr$8^lEn()#*}LoW<3s`$?*Dt zn4@<;ay)Q4Kn3$qEp)u|_pN$_j9e5JEOZ z>rKYY0lytgCBEy7&JFsOxeS})0kQ2zs346 zmJ3F~NO+Rj4b;3-G6f9yHC7iP?`3?}HYUmll2aX%rk4y*yc4!;eB8UGCyx6X>-{ zwn-fsLypu-bUOBN{M~Lupbd%r7yEU_po>WKfp5T@@}CczeI7U1Y7kJA~iZ&;)`tPwMZP*X5l zAyAFoTxH=89(|F6PkRU2IXJt2O09?^Y{7(ojfaLuZhC?Sq3RfV6ln1NA9kZk%Q5%o zKkk-ify-DnHAyslTf28QVZ=U-4a-)YMbMe9vGkX^q)+fmG$7^OEW7j_MNiPCmY9OCl& zjhu-}?-jD8cl6z0{3)kO4!DC}W#bOSVY#mMc*C%KaI$IxX*w`_qcv;(7|1xz1VVe6 z&Y7T$X+}71Gc!8DJNpZ7xX)&0^=#(SRp(?2G7^7F@)5$!@`U`qfp3%DaB zS>3a?`3ZA{7YN#6X7)W}ylc0`TcR_`9W*~XN>(i*oV2-*?fKQJ?2H2@Y5dWHrA3lA zfrVfudZQzkP0`F%z%WA`rSIKb5#7k8o89bB^}}-c$gKN*myvOUnu61Xk=1zTXQD!7 zbvtr$ff|mHC+`JCmZPBA6lq5=<0)Ao6cPTe;+3zu=1GT@elbk`De$k ze3&V63o+HDdu)xi{1gKhL9+m3%@C;^7lZDyxSo$+dm=fZB?Ba|M|xP`nhbv1w%Uug zAu#`rjSIZ5ISgM>+1X{wCT?ycjc-0O>A1<2*^fD(FDHR+!D0pr+IItmpGZ{N_`SRU7|}+Xtdb+R$VGzo$-KAHM>HK8O;_;OlGT+}Qs!wh?TpxH0lY|U<8Ndda8h<_YNuGSQUnS3 z?}Ku|o_XNMtN^Sx(gjMs?qO@=fYw^#i0Jjtu57-Iu;2h3qmq|;} z^paO|KxY8hySP~MSR>?7Z54cpO2{l#0X_#co4Qe<#|qmE^bekjzG$a9z zMt1~jSyB&I;ec~u6MkTWN3LkTlG6z8r=aqi+WVU2ibzvmi7i(phe%v71y9MI4(3Rh zO=h#LamfH538l4Hp~igrUZ5NY?3C_b`1iMx&-ms1`kc)~ z*V=(lk}={83jkV~cs=mhug z)Hfkg<2^X!NxdHurD6Hy&vC&vYjx=9qayp%ZLm3IK1`wg0UKgo(M<@d{8N(An=b>O zagt#OZ&$ zR>xyX8|;LsBxRl_?ZK)j_oBpDer=m=Ho-jD8ih8IazQoje95Io$jUZAf=I5eDmbhO zRW(GS8!C9*|A6U7z-RIWu`^LGS-10MfBL646ca|+^ZN8u@s*4Ue{hPtNJ639qC-`M zUl2VrOJ2RjOyiRESNf)%BSUC{B5s6E+S00Dt1QEUiE*D?oZL_Oa6am|ZNG(l_EVxZ=?|NX@k$j@+d$gcBmLJml8*%msh+?~es+() zUB!H{BMQ9sB3ZCDRdLMBv4g<=lxP+^TSFj5N?+Asw5~zZ0u+9qre&4kj&!pqMMGK& zX3Aj@_gP0GV4v{p-|DGiGosT7EX25?-WWAljq{3v<(^btks#}V)=R@+OdfclR7RE1 zeekazs5)ZNq3&pF@S<YuEz)a#k9&VPmEErsS2 z5G!Vrox0{~vj3_L(S|}7Ub?ePl^=LKnOjqVayW9*1fMP(K0ucBAGbjT{z3PuQqu0x zKN&U zMX`#)1-*XnV*wDX`B#muWM66?L-c1ztN6%UA_O!m*0zSv|i=!$!q+A;5O(Er^M35^JP8 zpI}mOtTE0v^8+)c<`!_e^!LNp)+J?ga)PX6lX+n%1YOgYw}t^>{9Y_5MxNJ;7QAnn zX}mQuG~P%J>kgc|tQkuE{736yzUTC~_VHNHb8vOo2CWH01bbX19scSS(~~NjrS2hK zc=RGahMVq6nfMK3h>lcI#Q{kyMV*4JM9pwJm;F+v+AYJWKlA>(d0Fo<-!;-(km#2e&AZt_AXtMKd1@aR zb5Mzx7?MrXlqpO;8U(K-+SO;65;q!)1IzxiWOsrO6(vD|Ob}-} z!68ds1{n=Bs`Yc}9-;P2C0nhiaVBvSs9W75Q^{pJtXF-Grppii+1zt>K`C2D5_(K; z-nD9PZbEn93#|EZyQQBHL~9Hyp^eN2k03R}WcVJ&vb5m002o=rzy3Qf5P37zvft$# z*Y)>ek^jpXrYck=(i4BnwEA}B^`wt~3k%e%v~Z7Cw8+y8doFd50=v=Zon|V877&yw zzhKE7M~F#5bHr$e_{tH{Vx^k{OH(h|r(PaHw|~ynG+y14G5Gc;GX&p0BZ@m7E~>2H z6xuPg{{UfEd}ADmH=!;?j|iyk)Z+EGV`PN{{%F}NQo$qjOQHE-qs>$VdtV12(SF#5 zD9iRI5*xto4pDAa4n4k{Hh#{pvnDKb=CTRB@ze{=$uK1x!$(lcl`(u33uJV_ue;FDtz6_7%f%HL>(XuJo8>LG1N#WVy$ISqVn|d0T%TW zfnt$n1C*dE8j&89zI>rOfvM)PI>(fO&KG9aIYlmr=7-9``VdPZt<2+hg~&6*qd+HA8*XN$Ofvi$3)G3jCWh1aHtSIqWfXM^x5ZI-=_n6-^U*g z|BO2?W8HyYdTKX|;Fj0}oXF_xq+N(E+E)YYuSG0Olx5Le(k}*VUNw_S#p>88Rw5cz zbFf&fOuF>N4_!JQYI*rhMa~btSBsv!Is5i47uLarLYXzyC~m!B8&zQU5851L!UjO4 z$n#L;k{t2J9#9TnX~JG`5)PW%{O83}b09Vc_V_!>E&n9I`&4_A!2Au%ybU-%XV0xT&dPG@jaZ4;(AKcR1xg7u zyuHN!b_}&CE(c5#mAHT#2;EUfw7#!*9Sj8ugzjdzhiy7;mAWXjevaOus9w{Tg8;XT z1R!*zVaKkdKvy+BB+}1GyaeBI9!N(pgB7Mq*x<~teBJ%CQ~<@=B|}gL3ZypXZmt-0 z?L>ln62`^2r$S@fGACfte|W&jWNs9$Y%F+*O9iS?58P?tKTA(^K9nWkf-SLw3^zJ_ znwyuRylYx6E!*4LzH~N;`mc(RygX#M7V><=Z#|Z{85xO(brgt??*3hw#j;V&zGst$ zUf&PIpyLv!V5Z|Eu$&DObIY3VpEY$@b2jczD*c4m`JN`7O4Y^Q2{3ROniT3O1cqf8 z;(Uxl*kiWc@WlP>(WS-H_8fcXpDoCXH9JH>+iWY$cC0QCV?@_{&%pLs6G3<##9eSg!{(NBwoOoYiEyEwP?SM=~((^W04N6HOM8fyR80YZ0O&(zVSUF^=|2w_r2xpo&JUbZ7#as*M1u$`EZVIq0KPXt110;*v() zj0WbM-_}rmX;SB2vN!o+!`tlL+^pi5zQn+DR1kEqaR<^f5`nwhFE5_w7`bF0IW5nR zXVlcpg{|Sg_MXl|ElC-QNXWy03CcO1M%J+Ja^Ck8@GIjub?Jb1sM}>^K))^)mDfk5 zXl?Kxl4w#7lhe4=rnlKj%`j600gk!tZA$`O2S)&|^6piJc>_;Jm6e#>&z-Q32DJ}p zdzPR>twP~e(fsxLv8Y=B^wv;oy%hy>CEND@w?j&sHa#S4{D#!KjM{i)#i5YnT8R-3 zFkO1%s>thEdDSLlL>HHi(;BD05IG|+_+u7kniX%~XZI84f>T8yv88kK$J8g%sqtAL zs$oXZe$G$4UKS~Fp7pd@zHvHNo#k@;8-j32uw6T#%TtXjUnoZ>qb&;oDq~3v8iZPu zVLf7Kkf6WxCOlY(s&uSZ@n%t7F#O`?^2^w{xaP=`bBWLAnljyYS2VjZrWmQy`uehE z_;K_6oObFop!@&z^wnWebziuHC>SUpEukU|h=j~gl9CcbgLDYY&>-FQ(Jjr;EhXJ8 z(ld0oGK929=iGz8doOL3%4vHW!Vp;mqs&X&s z0rJPMw~J;e3omACWXYsOxj+|uR~Nc?>AT-Q^9S<e()g7!>oC1~!3qleCC&#A3CIwM;65g zq~lUE6H|je%YDkNy{AUG!W6Ge?DXXGN?MSN7Y9Vrj9Ov*kI#!A#=WYKr z-j=`3Tdlaz`RZ`S>63!_w$-tXHZ#N|o59KI2keRi<`S~W3@C*{6`+JU33gP=;}gZI=<21Jcaj zJ03saRk@?I%@f<%_QF-($uQ0Y*#1ICFc>225Yp#3O2Als90J>_qqsh`8vS34apYrU z_m`##!sC>@EWj0_W5w;F=ZZstdeQK|=n2>Cmw=Uno}9{pYwE3Yvti8hR)OdjF$D^J z1XJSB#imv>yj(vhC1q8-N_RHh9j@QaUx}5cRB~4r4bD6NiS);|i9NFYsujC|GB~pb zA%t<6ZwN+KAP<8=zKM1Ts{}<7#gME#%vcYpE&E81ABdYQp+5Pbh~yv!qEM=8s%M>F zXJpx2QB}H|QzzHFu&ARy=W(*^6*}<p z)TIlED&?i2Nzbxhw@oyAFDHX^9LkjhVDiia_k%!RMPW(|>~T|~KZ#5**g|I>%zYMr zxKV+If{9e8iMzPrfT$u><8PK=u*^Ggb5<{9>IZsodd#5be5-h(JFdy!*6j*PL;LMG zFs*ujy$EKle5kOOlfX!ppFc^IDF{x>9``rAU#p1>XauqOQXNaKsMQTuDz%Heop5zr zFvGLF3)Ce1{>tzDpX7e8*uJ^hPrLOC{B%GF$tf!szFKe2bl!N=^_tqTF+!|A+1(S;XhHnJP@!8y#!wUBSIXl!5D?E0X8+m)*e{JLtSxZa`}EfYeC)45U|=6Xm^w zC|H^U`Ys%`J~A;4qo2v>;uTegb|5BBt*;MpL}_NshTtj7FL3VoX}A9YM-twb-D2c$ zGcRwF0Mka=Bu7{%CP!#+glrBTZFTc@t3kf2P@~vm`klUoz~;hjjBay{&4x*+ zdlE9eeU{w<8mA3|-|v|Ttniz-dx!cd7-aEH{cUIfEh*W{N}vuAE#+gHDlW+{>z=Y{ ztU+lf9^d3rY`6N~ip}qiq$xi?i}+Wjh>g)1q$mI8YYOq5(}EcuM{01P5Z|Z|msvIPm?0hXH+F z^@UV9D_#5v+NE(#A2lv1k?uY|2Q!Qw6%AJ%;>#B&yy3M{ldlWup)M*qU)GBM`>hgM zGJDlS{c-0n(9N4P_uzXY4KqJA35vM#F_3S$& z9r0S89hW8u1!AK%LAX7V^p+*GLl(>e@WaWLZ$Yh~P#3&{#RRVd$h=jEeRH#rHcnXt zX`%}tf)T3H$7V$PF9VsY{uR!7Z^}vf;5XatH{DEv+d4MhbI=Ben)(Y2`Q;WuS1VVC zCFpUeF}*3vQ=>AF(SGTU=wqe(U;I>woy1rb)YA)fib+D8oOTmy7ne&;PqwacT`C%m z8u4#VMBGzPQ|?e=e`h^3+%7mw5K02uT4d6Y+w81^5_nBAU5pkOXvJ$*nr}CIvC76p6jvSdYVw~ zxwd~bv@dwtIJ~&H=r!b!;c&TIj|FUmKe`?Fw?#h?hE7|qCEu?CRF2=AO zFXWyz=UKDIIbDYsF$js3%C11#*g5kigQG_|pHXmMP5yH8IUKV095Va3JC8GW8OO~n zVWN^Yq?8ORR{xnG=)mwZAD%{x6pOX~!51@nHd!iT^7n+%G&>?ke;mAz*n-F&-XkWd=I`x- zp^cEqqIZj3>zr;vquT-Hz3;j(Zevh+90!sh_m6Ybm*)>t2&m^bcRxoNc;AZlND<5B ztI@ipj#~TMcDX0;GGkFrUHzC1yE?6|CaI25<;TnFP*Mhw&ynKW#JW5Yg2f~&t#2e3axm9JKF`kKwwIzF-6Swrlq>(%0j;#l=fU1-z|5SB&SNrT*c3`}O?35BAcw z?R;H5q0Fzvr~R^5pOQSESGO5Pq#Qo9N&$Ttp0t^&8^m`P^~Fc{&x3pS=eEMdUiBW7 zNN9?(@!Drqv|+>dkM6gb8TA6yU_h?qU=|6VKWj$5KYU6y!mSKxLN3)qC^ogm9Sr95@7#fgA#oaW1 zWjntI0YxTh#c@q#fTv#6tLHo0U;6V(S6zwh18Am@g#LhI4DrQRI#4nsSwbrSqy$%! z_)uUcc*t-5QX?)(nF+=1ntnI9wz=5@FanXkeXoFCT7ZW~>rx77Po&Rj#{PQbX)#f@ z6NhK0STw#%Q6xizL-SNP^L`g+4CLL%2w768YNcnug7mZ!ATN~24R1Sk>G?xy@;U}Z z2D8&Wm*r(qmR%1wzHp%Gwy#1jti%<7Hi6%zPPc5Iljlua@BWa@hhD%D|1(<2httTC0sTF*UPTF05n&I5TiVdj5#sW%yD%JhW}W`~jKBDQtI>hn5X zcoOIrs+_o8nO7R8!Wa*I93rTiWO|_wm2;~jDo}?#uM8jv|8B=-w<}tO6wPJWsUv^m z%odigsohtwrrcXilxx=P`>Igy1h=(@wc#F-WGnEclRH@s*>&$_@G?S)Id=X`G2A2acGY{M5B?UWf_nY4 zo1-qbP?dgW)%?Nl-i_b@kl6}6jHNN?Gt8F1v6{Z64W(uOP29wtd+cmZ0&H6>X;O7X zCIQyG(-G+61KNB6OL07L-12PRfVda-!}%Y_`t(thl5zN}^ZZUGi`|KI>+ca@%e=*{ zqG%xgns7lAyxGTxzm=hVt^@pjP)#xc|0h|mZ3IO;rv+|_W~Q27hw3vsN{+*Qu7ZUR zx@^x9EiC8J^@SY&0w5iNYNw(M|tZI6{L%JQL3m!@HJbRiI3V?gb zL5s4UdG}ifJ!!@pWZ84CH&-XZ3=`>w#yGcE62ydUul?lYC9R9U>bpsqJPmmr3e$FM z`mZkp;DZr%1qDZA^JJ!*70PX~7aA?aDEM6BwQhmSn1Mu-odmmT^2@4F^72g##En0b z{xn1v&m6bPQF(1Eiwe}846ZrlnhTKN00N}%dhpkgWSKaHYFiPc#vX;p)lGccv1Qv3 zy55xsw*vbUS4rJYWm<)U?q(uWa80M(YePn{ZOt+Rp2Uq~!^7l^C{uV`=}_wz9Yb89 zT-WkCsj%50xo+%yz^Mx z)<1pJ{^t>)@Y&B|*=haa;$qwDexLBW!Zp{S&PdT@`srzT^6#aA$_ypRgA}>X@Ni@h zUwi~(Mkf$;VGE;NJbK0S<^pnSk}tpc@8(U?y0aj~zDUYvqX<2 zEj=#CuTQt(l#t_U0RJ`srmLr0EZ>ZJ_!Q}^Hoo77e=7lB%gdHAa>qS`*|>Cpc~NYK zS2Pm!1XYg0$F|itv!zYvR}EuJb=f@Ok^S`!W_Jv0ctO6tJn49Rydy&lZS3Cm+^6U| zcP2c%EZJL)^f@bYQ2grA(^&SH&B&4g0ghpuE z)z0|)zlH^cgcQhl4(ALJ#b?7_A#6S7=l9yNHwM>}20}hJSE)-!V9VX%EA(*0OeS;D zmX6QX^10eJZoDsegMxTa0{R#aqbo%-5$Jdjb9AR5zVF%hpT-h18G`AbVPbEESPN7c ztz=Y%b{2_vwLbU<+|z0r|9d~?!Qh=fA_8~v)OYYay?U9fgk0Apj_dD%9@syftv-C` zM>5l`Lm{@<->vPWdbHVgZyy7EhB&DPG(Q@?P+<9yc>~g&JZjVAYOCBXpRvq<;&FKX zE&!2615KfFlOgey--P|DBS}Axp=kf%b4zAxnx;C~#dx@M9{>CxQscQp+}v(^>MYvB zmgH;>6t=(e^zn99y4dmq5nYYiy5HzmO6Rn7^}=h9m%jh-xmf=A?eId4f0Ak08{zb{ zmuu`GvIZ@*Xjkj@S5m^I{dj&{OgS-s9@*6wO^FktdhSk5pXcTy&Ni^!V^MUt1cnqV zQD}u0Z|N)pE76>CVD;;yq6QI<%UU$d>zV|aJh-$(O|%zGyihg#rG=0N=f{)b(`;+u zsqLUkxArr9J~`9NJk`AZ`sd0LN5|!^lLc`%^iN7&>-Ufr+>Hu1|1EE4N?G4O7`tYB zy?olrGj#l@#`_AOw%1F4#Ssp?w4HVQI9L1Y*H-tw@+${WZZnk`ZO@(we>be7m8Y*Q zU3XE-uQlY)DR#rYY1wJq9XcM!;3EN z#~hp}y#197I5_ZfhUaCq{xR-30|4RQ@JwE=C&frJuPPm)RuO@W^byj?j;+@S-rn^owu2auk!-v_|Qnh>^vJwnoH=HgR{FC^hJ{MS;LR!JOmz^A?j%&bTsD`8J{R z6ZIe_k)yuiTD^ZXYTfAv@kko?Yzh87Z`7gqM;Q|%Ua0VNucUI<7pgm1T7-+! zWX#$kT(SE#i1a~|Z@YeSpKv{6ddcss(*dLtGrZqEII467nPE+QcqLgf8SU$51wS6^ zvOv6!#alqsAjZ%B8#h-zN7jm_H+kENakd=5mkgloSoI$~j7jE(1C{nvj%Xq`{JEZY zcV|OTS!OMRa3XQU!#?1%3NK;fWlp4A{t6gSSif(a{3jj>)1|9e7YYAObf#k~kaPqB z$Wr773=lGJn6Qx>o#+s)gzQcQc{s(;P+fiBTT)%W_bhk#YNuwk7sqH>cfVIHEgI;y zxNcTwdYvg^9Vd&+@blxaT;9__&xisiAc>2HE6$fW&C8yjlKb)`Oy1B1Dm{@^k>t8o zd=80oNsReQ;?gJ8Q*!51<*}C&fJTTxUssp3q}RzS`VvTG$@Dh7farCur0Qn&)kKMx zE}b^ztF@*r@G`Efwhdo@#S)MAi)=QHug>=-cwXL$p%rNRUL#wFv?2nvJsB{N_wS+N zBYCcGNLCiJ44-cYptEKoa_)DGju7w=q-}EJe))~oY1_^;HjER-m?;OYJ3jQ*Q5EuZ#T5Zk*B{Wa`k5km(cUqtLIv^ zv8v_cs70{ey>tjO^xcvC+Na({+cu)a%g_isc3g%T(0j(m@B#gV^eBF}sV~_`LE>lQ zBbY%46$)YX*TCv=IY?qB*TF3i62bXqD?PBj_w2p|nEMi2@8Z*LAAd^QD^d-XmM#QZ zqr?NX5AXx-O6%~(`&+Z8q-y67tH-VwSG>&j#n4A&RWeRTDJ5VE%)Rex#c)V7sMQWh zhT__23j~g=gH=#ut-mED<;*8jfuzJR)6M+=EqLgaab8*7T)nXqeJa<79ukH&>pQob zo~7*{mSSDzSK4@D`YqET^ETK=A}k8flWv^EyR}{_ZH9Ggkj<`qz9e%lY4%)5MaZ0H zianKiur^|13LLi(L72vz5(73%Q}*D;R8t-hcRb9lQygI1RV%={V({C~Mq7EYV;mdP z<*4hc-%=ZOhoixNg?Jz+p`agoc)~hz@9Gov|UzXuQPqH2O<#y z*H3e~@w`|?nZ-pjm19tXP6n3rBqru4U~<6RQkZ*69yy)Z7tp(_xxi}`V5(xXgYDT~QT0_9wa!$|)a}_% zGI2omAgJUsP|}-kyG8DbQOl7=NkiD6UiMV-OORZ?6!Sr&*wh0*uN*4*8NS>C$91IQ z=rjLG;0B|>*G90{iXw@Q+kVSS-q>rxMNbEwXS=yXJr%I^U}<2%dA#K#zwMay>>_f=C|XUtyXo+ zJ0;Cx<*h!na}lIzeJJRIcY4dLb=&vB88q~>O(U7Cp0dW z8d1Us@isIjiW69~!A|SHQ6$f#(d3yp`4l+keJLK?yAE0MZG>SeB;pfb0tqtdY|u~1 z)=1@Dw}CY44l^k@Svl{JneM+4)#~`0TSsW=2FMlbyatzP|EtsPfEo7UYp>h5xWsZd zeTv_mje{fq?p|GRCvEJ_U1G12I3fkct8%#UR5iPlz%_DzKGG?UDQ?*$KBz`s z-RQ=mK1i}-w6zgiVl=8&0_WHQy0)O6{m`RDncmizRd6a;-P3-dYcK^^siX8IyAFmxddi#(ysCc z*=2e=S*BpXv)s}Fc;1%QYX#G}nen=AP6(mKj;*XoN)k>6TTD@gF9X!Bp8?`*w|u=( zM*hR?hq06L{Kofb=TuZf0}EjWitCBo%y)rtNG?RBXc#f}el)_JYpCzaaiEGmoY>a2 zl*{5$umPSiQ1dsrC?K2Fkr|`Bg0Ha#2@@ycu0>0$AaoF6>WEJD&vweJ`B=Nod*;dC zR4wC_8_ot9)klHJmr`+?pxSOuXT@Gf$E3VQWIRIFBu$QlihE(bYv zc9NV#hNRa~DJ)6KRDm;}AZ%}6mM7CEjjpmm!*Z$SA-STtx0dR#!M;a@Ei7DCFME~ zLnGekNOtBs>er!3ds7_26%^|aniU3T_`nP5k!VaRJDpWjIz528k)JM}_WI~p9hUWI z=hP9tnK+52d$)j*o+jYha9xDx$TR-Ee*5eD60WoTW7H zM&)vkLwMbyUyO>wlkUtF&3sj{aM9^KUS_1PNIjy)85xN%VV7UiNITG(lo8|kHK5kn zxTV~gVB3(Q!0x?$E@$XZS;YUf)zXTF*%k@qPs_css4h2EDDO9e zF$vOSKxGE_7j^8b=gH$^IWIHE=cU#1&hLTpys4=Jk|-Swbb4|@c-A0Ob>oG)&ggYk zoApGbMe^(vO%9cS@VhIy%xrGFWXf7E=T(S9cPfd1Et%M@x5!mm9zM5)%jQs(SGs_W z$_C>8bYrP_V&5}Bm%W874AwL(8cJqtt0<8l>M^}MeT4+7&yw!arS`4<;$W7c{;JP5 z{GIw~^3z%`sqDICJ4c5tT`85scd1%x{_;SMa8noz_ON&tPAejbg7KpTbMPV&TNp3$ zS4&?y2nu(h^hs1S-#BL^Pl}Dn_<(in9%hzEZ474)%hoSC>-6^3t{60t_&854haBGL zy~z~*ag|J@zsk|6#b=)J~}`?#%Sh&(MjV3`wCq@g6zPAB}Ar_-y)7C@@~` z)-Ep1#)=cw4cM6-28LQd5OntT%{!hLZ5 zx2g{w*~p6{b3a5c*`wG@htjBcKQEqNu$yo9dkt7n1+O?3L`E8`;nA0o2QqwR!P>qz zp3Ho&L!Rqx7ctOlol>=mp|s$y@GaaG3Y@#Cd=(rt0N-&YU(f5!Z>liyp zpY2yxnHGZ{3(0U{fC~iv?*$64AGwNFkWO*NH-7%rxrRzo%n)=t{|lGFC#0is>C%2A z7h)~cSUxkEJmgqnPlYw5H%lEVd~LD$(MPtQ{yRxG=O`VMIR=@8(jgJ%1PS~% zCmf?RkKYd`{qR=bSU=8+srBtV+hbPRYGk~i-_R`VSjb>8j~h?ZY~my|V6JVQJ93JW zHOc9!i;nN}&#lQ4?@K_7frXaqn|ptts;ss{Bh7?k8qtsa!y+#(S-LpaTAA9;FDwmw zUI@ulAuxGc%Tp7{-OS!W)}axh##4Xd?yyo=Tsn%ezfuoBQ>ff4AJAOgZ$B9Os?P`S zovE@pByQftrO>!BtPEyA9VoQoRjhnp7KB+6RNO7x9j6jZqPhxj5VIsm9OZW&&~3Db zFA#D4VT;jGv?8sqR*rWu>CQT%Y^LS?8qnMsBqAPL>}bkmZIG;2{v)o3bF3${oh4Xx z%8&8Ab!OC?q?`0611UHciMH`pI;%C1m}|~GGJaCY27Ua>gM$kwss0dsu zVlv?1<*aGNsbFjqrw)os)Zb2G?{bZD)^c?rcfTY=q|nm|7?$q6kYdw{(7qjaUv6TH z%k*d~g&34HB6$|s#mJKslVet3$DWz`!*UJjYKp?(3)dnx!#zY z10{t(_2kpRBLg!aQg^sNto34i^z$Khvcl#*{4J-!WfkkZ`&9xus;3^Z>-vfod`_*&GcYvzD>0PP1Tx=9Z?-SVYD4A5KyY zdT!r`CV)ztmF6`Ho|Aw64MJ#7pFa0+l__(@3X2cQ2aasp=@( zpo|8Z0rv<(O`o^}Vap+Oz!~)vs{erlHg8qn4$CbfPdO+5#tAYk?NI}38&?zqkK9V1 z7Em zJ+lJKP(+PA!vzf{@+JYJ_9j{h5BlKvT6YXWLTEAbWK%{OcE#vpTukrawpaW3(H3f9 z1UUN|$c8pvr9#&sTYG_kJY{UA+EZohxDHoxYkEK(lyXTZAXnrTW%k!4(fdS_YfKd9DCu=22Ho{sXJ}z<|=nal1xwA5kGe_CpzTjrSoUl22Tu};o zZVD)mKO@a};_{M&KUflgV1Qe@$C@4foD%2oJ+uFJQaQBe9?2#x52H+CLM1mZ^xLje z{}E&4e)`KO9N*bMM#RM`l5%t+)p6>e1fWONIyFVGE>2BT;5k%)y8kbA-+zjEiD*P4tZKg{CUcI*Kf_H~@}v=$$M^jXl*7}x!4t74=DTm)cp$1sH7V$E zB31V&3}aQ$?h4fApyY5pG#q&8J7QD>!-4re(R=ckgu2x3h5SS(6EV%WJ_i}3-`ze$VtKDF|AGeYKeBa}4`te1EDTneDj$!=_BjSo;EngK0ONF(MNTH4=I%>L&r_Q+uG$vxS6fi{=d`uz5o#(lXl2ZwV%9MJFnbx3h7+OxmbH1kSUMj7=8+v z%V52>WYmIr@rVTYL7?fDl7q+V^@Mls+>9=sseAJIgF z@B85^fwNj=7dpFn$No-tPX=tP=k=|e`_Ja+F5o%M_V8D&_E(OSS_02N6(KzeaKMfz z?`9J?YXdszs!_Gv%Q@i)(v$)`D1g+&zmq8!cG8}325Bl-|9e&6 z`~S})8u;I?9Y8JrZTafGXU6@vRq^OP{C@|XzhiOXIKCr;A7{00_sB>pNR){g{`Y?X D{1ZFK literal 0 HcmV?d00001 diff --git a/src/axom/mir/docs/sphinx/mir_algorithms.rst b/src/axom/mir/docs/sphinx/mir_algorithms.rst index 97a4c47384..000f4f431c 100644 --- a/src/axom/mir/docs/sphinx/mir_algorithms.rst +++ b/src/axom/mir/docs/sphinx/mir_algorithms.rst @@ -9,7 +9,7 @@ MIR Algorithms The MIR component contains MIR algorithms that will take a Blueprint mesh as input, perform MIR on it, and output a new Blueprint mesh with the reconstructed output. -A Blueprint mesh is contained in a ``conduit::Node`` and it follows the [https://llnl-conduit.readthedocs.io/en/latest/blueprint_mesh.html](Blueprint protocol), +A Blueprint mesh is contained in a ``conduit::Node`` and it follows the `Blueprint protocol `_, which means the node contains specific items that describe the mesh coordinates, topology, fields, and materials. ####### @@ -23,6 +23,17 @@ space where it will be used. +---------------------------------+------------------------------------------------------+ | Option | Description | +=================================+======================================================+ +| coordsetName: name | The name of the new coordset in the output mesh. If | +| | it is not provided, the output coordset will have the| +| | same name as the input coordset. | ++---------------------------------+------------------------------------------------------+ +| fields: | The fields node lets the caller provide a list of | +| - currentName: newName | field names that will be processed and added to the | +| ... | output mesh. The form is currentName:newName. If the | +| | fields node is not given, the algorithm will process | +| | all input fields. If the fields node is empty then no| +| | fields will be processed. | ++---------------------------------+------------------------------------------------------+ | matset: name | A required string argument that specifies the name | | | of the matset that will be operated on. | +---------------------------------+------------------------------------------------------+ @@ -39,12 +50,16 @@ space where it will be used. | | contributions from zone numbers in this list, if it | | | is given. | +---------------------------------+------------------------------------------------------+ +| topologyName: name | The name of the new topology in the output mesh. If | +| | it is not provided, the output topology will have the| +| | same name as the input topology. | ++---------------------------------+------------------------------------------------------+ ############### EquiZAlgorithm ############### -The [https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.osti.gov/servlets/purl/15014510&ved=2ahUKEwittMui-euIAxUzxOYEHXTWA2kQFnoECBcQAQ&usg=AOvVaw3qbX9qgwCn4qDP0iZ3Sq0J](Equi-Z algorithm) by J. Meredith +The `Equi-Z algorithm `_ by J. Meredith is a useful visualization-oriented algorithm for MIR. Whereas many MIR algorithms produce disjointed element output, Equi-Z creates output that mostly forms continuous surfaces and shapes. Continuity is achieved by averaging material volume fractions to @@ -90,12 +105,12 @@ function can be used to copy Conduit nodes from one memory space to another. :end-before: _equiz_mir_end :language: C++ -The MIR output will contain a new field called "originalElements" that indicates which +The MIR output will contain a new field called _"originalElements"_ that indicates which original zone number gave rise to the reconstructed zone. This field makes it possible to map back to the original mesh. The name of the field can be changed using options. ##################### -Example Applications +Example Application ##################### The mir_concentric_circles application generates a uniform mesh populated with circular @@ -122,16 +137,16 @@ To run the example program from the Axom build directory, follow these steps: .. figure:: figures/mir_concentric_circles.png :figwidth: 800px - :alt: Diagram showing MIR output from the mir_concentric_circles application. + :alt: Diagram showing MIR output from the _mir_concentric_circles_ application. ##################### Visualization ##################### -The [https://visit-dav.github.io/visit-website/](VisIt software) can be used to +The `VisIt software `_ can be used to view the Blueprint output from MIR algorithms. Blueprint data is saved in an HDF5 format and the top level file has a ".root" extension. Open the ".root" file in VisIt -to get started and then add a "FilledBoundary" plot of the material defined on the +to get started and then add a _FilledBoundary_ plot of the material defined on the mesh topology. Plotting the mesh lines will reveal that there is a single material per zone. If the input mesh is visualized in a similar manner, it will be evident that there are multiple materials in some of the zones, if viewing a mixed material dataset. diff --git a/src/axom/mir/docs/sphinx/mir_clipping.rst b/src/axom/mir/docs/sphinx/mir_clipping.rst index c700d7df4c..64877da334 100644 --- a/src/axom/mir/docs/sphinx/mir_clipping.rst +++ b/src/axom/mir/docs/sphinx/mir_clipping.rst @@ -49,6 +49,17 @@ algorithm copies the options node to the memory space where it will be used. | | name of the color field, which is called "color" by | | | default. | +---------------------------------+------------------------------------------------------+ +| coordsetName: name | The name of the new coordset in the output mesh. If | +| | it is not provided, the output coordset will have the| +| | same name as the input coordset. | ++---------------------------------+------------------------------------------------------+ +| fields: | The fields node lets the caller provide a list of | +| - currentName: newName | field names that will be processed and added to the | +| ... | output mesh. The form is currentName:newName. If the | +| | fields node is not given, the algorithm will process | +| | all input fields. If the fields node is empty then no| +| | fields will be processed. | ++---------------------------------+------------------------------------------------------+ | inside: number | Indicates to the clipping algorithm that it should | | | preserve zone fragments that were "inside" the clip | | | boundary. Set to 1 to enable, 0 to disable. The | @@ -67,88 +78,59 @@ algorithm copies the options node to the memory space where it will be used. | | contributions from zone numbers in this list, if it | | | is given. | +---------------------------------+------------------------------------------------------+ +| topologyName: name | The name of the new topology in the output mesh. If | +| | it is not provided, the output topology will have the| +| | same name as the input topology. | ++---------------------------------+------------------------------------------------------+ ########## ClipField ########## To use the ``ClipField`` class, one must have Blueprint data with at least one vertex-associated -field. Views for the coordset and topology are created and used to instantiate the ``ClipField`` -class, which then takes an input node for the Blueprint mesh, an input node that contains -the options, and a 3rd output node that will contain the clip output. The input mesh node -needs to contain data arrays for coordinates, mesh topology, and fields that are in the -memory space of the targeted device. Other Conduit nodes that contain strings or single numbers -that can fit within a node are safe remaining in host memory. If the mesh is not in the -desired memory space, it can be moved using ``axom::mir::utilities::blueprint::copy()``. - -.. literalinclude:: ../../ClipField.cpp - :start-after: _mir_utilities_clipfield_start - :end-before: _mir_utilities_clipfield_end - :language: C++ - -############# +field. Views for the coordset and topology are created and their types are used to instantiate +a ``ClipField`` object. This takes a Conduit node for the input Blueprint mesh, a Conduit +node that contains the options, and a 3rd output Conduit node that will contain the clipped +mesh and fields. The input mesh node needs to contain data arrays for coordinates, mesh +topology, and fields. These data must exist in the memory space of the targeted device. +Other Conduit nodes that contain strings or single numbers that can fit within a Conduit +node are safe remaining in host memory. If the mesh is not in the desired memory space, it +can be moved using ``axom::mir::utilities::blueprint::copy()``. + + .. codeblock:: cpp + + #include "axom/mir.hpp" + + // Set up views for the mesh in deviceRoot node. + auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(deviceRoot["coordsets/coords"]); + auto topologyView = axom::mir::views::make_rectilinear<3>::view(deviceRoot["topologies/Mesh"]); + + // Make a clipper. + using CoordsetView = decltype(coordsetView); + using TopologyView = decltype(topologyView); + using Clip = axom::mir::clipping::ClipField; + Clip clipper(topologyView, coordsetView); + + // Run the clip algorithm + conduit::Node options; + options["clipField"] = "data"; + options["clipValue"] = 3.5; + options["outside"] = 1; + options["inside"] = 0; + clipper.execute(deviceRoot, options, clipOutput); + + +.. figure:: figures/clipfield.png + :figwidth: 800px + :alt: Diagram showing original mesh colored by clipping field (left), original mesh colored by a radial field (middle), and the clipped mesh colored by the radial field (right). + + +^^^^^^^^^^^^^ Intersectors -############# +^^^^^^^^^^^^^ -An intersector is a class that is passed as a template argument to ``ClipField``. The intersector -determines how the ``ClipField`` algorithm will generate intersection cases, for each zone -in the mesh. The ``ClipField`` algorithm default intersector uses a field to determine clip +An intersector is a policy class that is passed as a template argument to ``ClipField``. The +intersector determines how the ``ClipField`` algorithm will generate intersection cases, for +each zone in the mesh. The ``ClipField`` algorithm default intersector uses a field to determine clip cases, resulting in isosurface behavior for the geometry intersections. Alternative intersectors can be provided to achieve other types of intersections. - -An intersector needs to provide an interface like the following: - - .. codeblock{.cpp}:: - - template - class CustomIntersector - { - public: - using ConnectivityView = axom::ArrayView; - - // Internal view - runs on device. - struct View - { - // Given a zone index and the node ids that comprise the zone, return - // the appropriate clip case. - AXOM_HOST_DEVICE - axom::IndexType determineClipCase(axom::IndexType zoneIndex, - const ConnectivityView &nodeIds) const - { - axom::IndexType clipcase = 0; - for(IndexType i = 0; i < nodeIds.size(); i++) - { - const auto id = nodeIds[i]; - const auto value = // Compute distance from node to surface. - clipcase |= (value > 0) ? (1 << i) : 0; - } - return clipcase; - } - - // Compute the weight[0,1] of a clip value along an edge (id0, id1) using the clip field and value. - AXOM_HOST_DEVICE - ClipFieldType computeWeight(axom::IndexType zoneIndex, - ConnectivityType id0, - ConnectivityType id1) const - { - return 1.; - } - }; - - // Initialize the object from options (on host). - void initialize(const conduit::Node &n_options, const conduit::Node &n_fields) - { - } - - // Determine the name of the topology on which to operate. - std::string getTopologyName(const conduit::Node &n_input, - const conduit::Node &n_options) const - { - return "mesh"; - } - - // Return a new instance of the view. - View view() const { return m_view; } - - View m_view; - }; diff --git a/src/axom/mir/docs/sphinx/mir_utilities.rst b/src/axom/mir/docs/sphinx/mir_utilities.rst index 0d68c15b0d..d9206420b1 100644 --- a/src/axom/mir/docs/sphinx/mir_utilities.rst +++ b/src/axom/mir/docs/sphinx/mir_utilities.rst @@ -10,7 +10,7 @@ MIR Blueprint Utilities The MIR component contains several useful building blocks for writing algorithms for Blueprint meshes. - * Structured as classes with an ``execute()`` method to permit them to contain state or divide their algorithm into stages in various methods. + * Structured as classes with an ``execute()`` method to permit them to divide algorithm into various methods. * Often templated on execution space and views ####################### @@ -26,13 +26,13 @@ memory space, which lets algorithms on the host side query them. For data that h to the device, their sizes and data types can still be queried using normal Conduit mechanisms such as getting metadata via the ``conduit::Node::dtype()`` method. - .. codeblock{.cpp}:: + .. codeblock:: cpp - conduit::Node hostMesh, deviceMesh, hostMesh2; - // host->device - axom::mir::utilities::blueprint::copy>(deviceMesh, hostMesh); - // device->host - axom::mir::utilities::blueprint::copy(hostMesh2, deviceMesh); + conduit::Node hostMesh, deviceMesh, hostMesh2; + // host->device + axom::mir::utilities::blueprint::copy>(deviceMesh, hostMesh); + // device->host + axom::mir::utilities::blueprint::copy(hostMesh2, deviceMesh); ############################ ConduitAllocateThroughAxom diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst index ef1f1787b8..c1b52733ec 100644 --- a/src/axom/mir/docs/sphinx/mir_views.rst +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -23,10 +23,10 @@ function to help wrap arrays stored in ``conduit::Node`` to ``axom::ArrayView``. ``make_array_view`` function, one must know the type held within the Conduit node. If that is not the case, then consider using one of the dispatch ''Node_to_ArrayView'' functions. - .. codeblock{.cpp}:: + .. codeblock:: cpp - // Make an axom::ArrayView for X coordinate components. - auto x = axom::mir::blueprint::make_array_view(n_mesh["coordsets/coords/values/x"]); + // Make an axom::ArrayView for X coordinate components. + auto x = axom::mir::blueprint::make_array_view(n_mesh["coordsets/coords/values/x"]); ---------- @@ -36,25 +36,20 @@ Coordsets Blueprint supports multiple coordset types: uniform, rectilinear, explicit. Axom provides functions to explicitly create coordset views for each of these types. - .. codeblock{.cpp}:: + .. codeblock:: cpp - // Make a 2D uniform coordset view - auto view1 = axom::mir::views::make_uniform_coordset<2>::view(n_mesh["coordsets/coords"]); - - // Make a 3D uniform coordset view - auto view2 = axom::mir::views::make_uniform_coordset<3>::view(n_mesh["coordsets/coords"]); - - // Make a 2D rectilinear coordset view with float coordinates - auto view3 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); - - // Make a 3D rectilinear coordset view with double coordinates - auto view4 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); - - // Make a 2D explicit coordset view with float coordinates - auto view5 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); - - // Make a 3D explicit coordset view with double coordinates - auto view6 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + // Make a 2D uniform coordset view + auto view1 = axom::mir::views::make_uniform_coordset<2>::view(n_mesh["coordsets/coords"]); + // Make a 3D uniform coordset view + auto view2 = axom::mir::views::make_uniform_coordset<3>::view(n_mesh["coordsets/coords"]); + // Make a 2D rectilinear coordset view with float coordinates + auto view3 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + // Make a 3D rectilinear coordset view with double coordinates + auto view4 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + // Make a 2D explicit coordset view with float coordinates + auto view5 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + // Make a 3D explicit coordset view with double coordinates + auto view6 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); ---------------- @@ -77,17 +72,17 @@ strided-structured Blueprint meshes, which are structured meshes that exist over of the overall mesh. There are helper functions for creating structured topology views from a Conduit node. - .. codeblock{.cpp}:: + .. codeblock:: cpp - conduit::Node &n_topo1 = n_mesh["topologies/mesh2d"]; - conduit::Node &n_topo2 = n_mesh["topologies/mesh3d"]; - conduit::Node &n_topo3 = n_mesh["topologies/mesh2dss"]; - // Make a 2D structured mesh view from the topology. - auto topologyView1 = axom::mir::views::make_structured<2>::view(n_topo1); - // Make a 3D structured mesh view from the topology. - auto topologyView2 = axom::mir::views::make_structured<2>::view(n_topo2); - // Make a 2D strided-structured mesh view from the topology. - auto topologyView3 = axom::mir::views::make_strided_structured<2>::view(n_topo3); + conduit::Node &n_topo1 = n_mesh["topologies/mesh2d"]; + conduit::Node &n_topo2 = n_mesh["topologies/mesh3d"]; + conduit::Node &n_topo3 = n_mesh["topologies/mesh2dss"]; + // Make a 2D structured mesh view from the topology. + auto topologyView1 = axom::mir::views::make_structured<2>::view(n_topo1); + // Make a 3D structured mesh view from the topology. + auto topologyView2 = axom::mir::views::make_structured<2>::view(n_topo2); + // Make a 2D strided-structured mesh view from the topology. + auto topologyView3 = axom::mir::views::make_strided_structured<2>::view(n_topo3); ^^^^^^^^^^^^^^^^^^^^^^^^ Unstructured Mesh Views @@ -97,13 +92,13 @@ There are 3 unstructured mesh views. The ``UnstructuredTopologySingleShapeView`` Blueprint topology that contains a single zone/shape type. The zone type is a template argument that determines the type of zone that is held within the topology. - .. codeblock{.cpp}:: + .. codeblock:: cpp - // Make a topology view for a tetrahedral mesh with int connectivity. - namespace bputils = axom::mir::utilities::blueprint; - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - const auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); - axom::mir::views::UnstructuredTopologySingleShapeView> view(connView); + // Make a topology view for a tetrahedral mesh with int connectivity. + namespace bputils = axom::mir::utilities::blueprint; + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + const auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); + axom::mir::views::UnstructuredTopologySingleShapeView> view(connView); There are multiple shape types defined in ``axom/mir/views/Shapes.hpp`` that can be used with the ``UnstructuredTopologySingleShapeView``: TriShape, QuadShape, TetShape, PyramidShape, @@ -113,39 +108,39 @@ Blueprint supports "mixed" topologies that contain multiple shape types. These t handled using the ``axom::mir::views::UnstructuredTopologyMixedShapeView``. Additional array views are needed to supply the sizes, offsets, and shapes arrays. - .. codeblock{.cpp}:: + .. codeblock:: cpp - // A shape map helps map values from the values used in the Blueprint topology to - // the shape ids used in Axom. - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - axom::Array ids, values; - auto shapeMap = axom::mir::views::buildShapeMap(n_topo); + // A shape map helps map values from the values used in the Blueprint topology to + // the shape ids used in Axom. + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + axom::Array ids, values; + auto shapeMap = axom::mir::views::buildShapeMap(n_topo); - namespace bputils = axom::mir::utilities::blueprint; - axom::mir::views::UnstructuredTopologyMixedShapeView view( - bputils::make_array_view(n_topo["elements/connectivity"), - bputils::make_array_view(n_topo["elements/sizes"), - bputils::make_array_view(n_topo["elements/offsets"), - bputils::make_array_view(n_topo["elements/shapes"), - shapeMap); + namespace bputils = axom::mir::utilities::blueprint; + axom::mir::views::UnstructuredTopologyMixedShapeView view( + bputils::make_array_view(n_topo["elements/connectivity"), + bputils::make_array_view(n_topo["elements/sizes"), + bputils::make_array_view(n_topo["elements/offsets"), + bputils::make_array_view(n_topo["elements/shapes"), + shapeMap); Once a suitable topology view type has wrapped the Blueprint topology, it can be used in device kernels to obtain zone information. - .. codeblock{.cpp}:: + .. codeblock:: cpp - topologyView = ... - axom::for_all(topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // Get the current zone. - const auto zone = topologyView.zone(zoneIndex); + topologyView = ... + axom::for_all(topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // Get the current zone. + const auto zone = topologyView.zone(zoneIndex); - // Iterate over this zone's nodes. - for(const auto &nodeId : zone.getIds()) - { - // Do something. - } - }); + // Iterate over this zone's nodes. + for(const auto &nodeId : zone.getIds()) + { + // Do something. + } + }); ---------- Matsets @@ -187,14 +182,14 @@ To generically wrap any type of datatype supported by Conduit, the ``axom::mir:: function can be used. This template function takes a variable number of ``conduit::Node`` arguments and a generic lambda function that accepts the view arguments. - .. codeblock{.cpp}:: + .. codeblock:: cpp - conduit::Node n; // Assume it contains data values - axom::mir::views::Node_to_ArrayView(n["foo"], n["bar"], [&](auto fooView, auto barView) - { - // Use fooView and barView axom::ArrayView objects to access data. - // They can have different types. - }); + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They can have different types. + }); Using ``axom::mir::views::Node_to_ArrayView`` with multiple data values can instantiate the supplied lambda many times so be careful. It is more common that when wrapping multiple @@ -202,14 +197,14 @@ nodes that they are the same type. The ``axom::mir::views::Node_to_ArrayView_sam will ensure that the lambdas get instantiated with views that wrap the Conduit nodes in array views that of the same type. - .. codeblock{.cpp}:: + .. codeblock:: cpp - conduit::Node n; // Assume it contains data values - axom::mir::views::Node_to_ArrayView_same(n["foo"], n["bar"], [&](auto fooView, auto barView) - { - // Use fooView and barView axom::ArrayView objects to access data. - // They have the same types. - }); + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView_same(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They have the same types. + }); When dealing with mesh data structures, it is common to have data that are using only integer types or only floating-point types. Axom provides functions that limit the lambda instantiation @@ -231,15 +226,14 @@ Coordsets The ``axom::mir::views::dispatch_coordset()`` function can wrap Blueprint coordsets in an appropriate view and pass it to a lambda function. - .. codeblock{.cpp}:: - - const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; - axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { - // Get the C++ type of the coordset. - using CoordsetView = decltype(CoordsetView); + .. codeblock:: cpp - // Implement algorithm using coordsetView. - }); + const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { + // Get the C++ type of the coordset. + using CoordsetView = decltype(CoordsetView); + // Implement algorithm using coordsetView. + }); ^^^^^^^^^^^ Topologies @@ -250,47 +244,47 @@ topology types through a topology view. These dispatch functions can be called f topology types such as unstructured topologies or they can be called to implement algorithms that can operate on any topology. - .. codeblock{.cpp}:: - - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - // Handle rectilinear topology type. - axom::mir::views::dispatch_rectilinear_topology(n_topo, [&](auto topologyView) { - }); - // Handle structured topology types - axom::mir::views::dispatch_structured_topology(n_topo, [&](auto topologyView) { - }); - // Handle unstructured topology types - axom::mir::views::dispatch_unstructured_topology(n_topo, [&](auto topologyView) { - }); - // Handle any topology type. - axom::mir::views::dispatch_topologies(n_topo, [&](auto topologyView) { - }); + .. codeblock:: cpp + + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + // Handle rectilinear topology type. + axom::mir::views::dispatch_rectilinear_topology(n_topo, [&](auto topologyView) { + }); + // Handle structured topology types + axom::mir::views::dispatch_structured_topology(n_topo, [&](auto topologyView) { + }); + // Handle unstructured topology types + axom::mir::views::dispatch_unstructured_topology(n_topo, [&](auto topologyView) { + }); + // Handle any topology type. + axom::mir::views::dispatch_topologies(n_topo, [&](auto topologyView) { + }); Nesting dispatch functions permits the calling code to handle both coordset views and topology views using a single lambda function for the algorithm. For portability, the algorithm should be placed in a function or class member method when instantiated from the anonymous lambda function from the dispatch functions. - .. codeblock{.cpp}:: + .. codeblock:: cpp - struct Algorithm - { - void execute(const conduit::Node &n_mesh) + struct Algorithm { - // Handle product of coordset types and topology types. - axom::mir::views::dispatch_coordset(n_mesh["coordsets/coords"], [&](auto coordsetView) + void execute(const conduit::Node &n_mesh) { - axom::mir::views::dispatch_topologies(n_mesh["topologies/mesh"], [&](auto topologyView) + // Handle product of coordset types and topology types. + axom::mir::views::dispatch_coordset(n_mesh["coordsets/coords"], [&](auto coordsetView) { - implementation(coordsetView, topologyView); + axom::mir::views::dispatch_topologies(n_mesh["topologies/mesh"], [&](auto topologyView) + { + implementation(coordsetView, topologyView); + }); }); - }); - } + } - template - void implementation(CoordsetView coordsetView, TopologyView topologyView) const - { - // Do algorithm that involves coordsetView and topologyView. - } - }; + template + void implementation(CoordsetView coordsetView, TopologyView topologyView) const + { + // Algorithm that involves coordsetView and topologyView. + } + }; diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 509139c6e1..70856ab57b 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -153,6 +153,108 @@ TEST(mir_equiz, equiz_uniform_unibuffer_hip) } #endif +#if 0 // FIXME +//------------------------------------------------------------------------------ +template +void braid3d_mat_test(const std::string &type, + const std::string &mattype, + const std::string &name) +{ + namespace bputils = axom::mir::utilities::blueprint; + + axom::StackArray dims {10, 10, 10}; + axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1, dims[2] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + + // Make views. + auto coordsetView = axom::mir::views::make_explicit_coordset::view( + deviceMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + + using ShapeType = axom::mir::views::HexShape; + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView; + TopologyView topologyView(deviceMesh["topologies/mesh"]); + + conduit::Node deviceMIRMesh; + if(mattype == "unibuffer") + { + // clang-format off + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // clang-format on + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + conduit::Node options; + options["matset"] = "mat"; + m.execute(deviceMesh, options, deviceMIRMesh); + } + + // device->host + conduit::Node hostMIRMesh; + axom::mir::utilities::blueprint::copy(hostMIRMesh, deviceMIRMesh); + +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMIRMesh, name, "hdf5"); +#endif + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostMIRMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh)); +#endif + } +} + +//------------------------------------------------------------------------------ +TEST(mir_equiz, equiz_hex_unibuffer_seq) +{ + AXOM_ANNOTATE_SCOPE("equiz_explicit_hex_seq"); + braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_equiz, equiz_hex_unibuffer_omp) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_omp"); + braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_equiz, equiz_hex_unibuffer_cuda) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_cuda"); + braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_equiz, equiz_hex_unibuffer_hip) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_hip"); + braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); +} +#endif +#endif + //------------------------------------------------------------------------------ #if defined(DEBUGGING_TEST_CASES) void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) From 5429ff403ff658761b082cf6d8c9fdcc9ce6424f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 01:55:36 -0700 Subject: [PATCH 268/290] doc edits --- src/axom/mir/docs/sphinx/index.rst | 11 +- src/axom/mir/docs/sphinx/mir_algorithms.rst | 67 ++--- src/axom/mir/docs/sphinx/mir_clipping.rst | 73 ++--- src/axom/mir/docs/sphinx/mir_utilities.rst | 44 +-- src/axom/mir/docs/sphinx/mir_views.rst | 262 +++++++++--------- .../mir/examples/mir_concentric_circles.cpp | 1 + src/conf.py | 3 +- 7 files changed, 236 insertions(+), 225 deletions(-) diff --git a/src/axom/mir/docs/sphinx/index.rst b/src/axom/mir/docs/sphinx/index.rst index 37f3fb1bef..f3cb0ac65b 100644 --- a/src/axom/mir/docs/sphinx/index.rst +++ b/src/axom/mir/docs/sphinx/index.rst @@ -4,14 +4,15 @@ .. ## SPDX-License-Identifier: (BSD-3-Clause) ======================= -Mir User Documentation +MIR User Documentation ======================= Axom's Material Interface Reconstruction (MIR) component provides algorithms for -reconstructing the interface surfaces between different materials in multimaterial -meshes. The algorithms take Blueprint meshes containing a coordset, topology, and -matset as input and they output a new Blueprint node with a new coordset, topology, -and matset that contains at most 1 material per zone. +reconstructing the interfaces between different materials in multimaterial +meshes to go from a topology that contains mixed materials to a reconstructed one +that contains zones with only one material. The algorithms take Blueprint meshes +containing a coordset, topology, and matset as input and they output a new Blueprint +node with a new coordset, topology, and matset that contains at most 1 material per zone. The MIR component also contains some useful components that can be used to develop other algorithms that process Blueprint meshes. diff --git a/src/axom/mir/docs/sphinx/mir_algorithms.rst b/src/axom/mir/docs/sphinx/mir_algorithms.rst index 000f4f431c..f8eda62aa3 100644 --- a/src/axom/mir/docs/sphinx/mir_algorithms.rst +++ b/src/axom/mir/docs/sphinx/mir_algorithms.rst @@ -23,34 +23,34 @@ space where it will be used. +---------------------------------+------------------------------------------------------+ | Option | Description | +=================================+======================================================+ -| coordsetName: name | The name of the new coordset in the output mesh. If | +|``coordsetName: name`` | The name of the new coordset in the output mesh. If | | | it is not provided, the output coordset will have the| | | same name as the input coordset. | +---------------------------------+------------------------------------------------------+ -| fields: | The fields node lets the caller provide a list of | -| - currentName: newName | field names that will be processed and added to the | -| ... | output mesh. The form is currentName:newName. If the | -| | fields node is not given, the algorithm will process | -| | all input fields. If the fields node is empty then no| -| | fields will be processed. | +|``fields:`` | The fields node lets the caller provide a list of | +| | field names that will be processed and added to the | +| | output mesh. The form is *currentName:newName*. If | +| | the *fields* node is not given, the algorithm will | +| | process all input fields. If the fields node is empty| +| | then no fields will be processed. | +---------------------------------+------------------------------------------------------+ -| matset: name | A required string argument that specifies the name | +| ``matset: name`` | A required string argument that specifies the name | | | of the matset that will be operated on. | +---------------------------------+------------------------------------------------------+ -| matsetName: name | An optional string argument that specifies the name | +| ``matsetName: name`` | An optional string argument that specifies the name | | | of the matset to create in the output. If the name | | | is not given, the output matset will have the same | | | name as the input matset. | +---------------------------------+------------------------------------------------------+ -| originalElementsField: name | The name of the field in which to store the original | +| ``originalElementsField: name`` | The name of the field in which to store the original | | | elements map. | +---------------------------------+------------------------------------------------------+ -| selectedZones: [zone list] | An optional argument that provides a list of zone ids| +| ``selectedZones: [zone list]`` | An optional argument that provides a list of zone ids| | | on which to operate. The output mesh will only have | | | contributions from zone numbers in this list, if it | | | is given. | +---------------------------------+------------------------------------------------------+ -| topologyName: name | The name of the new topology in the output mesh. If | +| ``topologyName: name`` | The name of the new topology in the output mesh. If | | | it is not provided, the output topology will have the| | | same name as the input topology. | +---------------------------------+------------------------------------------------------+ @@ -59,39 +59,39 @@ space where it will be used. EquiZAlgorithm ############### -The `Equi-Z algorithm `_ by J. Meredith +The `Equi-Z MIR algorithm `_ by J. Meredith is a useful visualization-oriented algorithm for MIR. Whereas many MIR algorithms produce disjointed element output, Equi-Z creates output that mostly forms continuous surfaces and shapes. Continuity is achieved by averaging material volume fractions to the mesh nodes for each material and then performing successive clipping for each material, using the node-averaged volume fractions to determine where clipping occurs along each edge. The basic algorithm is lookup-based so shape decomposition for a -clipped-zone can be easily determined. The cliping stage produces numerous zone fragments -that are marked with the appropriate material number and moved onto the next material +clipped-zone can be easily determined. The clipping stage produces numerous zone fragments +that are marked with the appropriate material number and moved into the next material clipping stage. This concludes when all zones are comprised of only 1 material. From, -there points are made unique and the output mesh is created with a new coordset, topology, -fields, and matset. +there points are made unique and a new output mesh is created. -Axom's implementation of Equi-Z can run on the CPU and the GPU. First, the zones of -interest are identified and they are classified as clean or mixed. The clean zones are -pulled out early into a new mesh and mixed zones are sent into the Equi-Z algorithm to -reconstruct clean zones. The two meshes are then merged together at the end in an output -Conduit node. +Axom's implementation of Equi-Z is data parallel and can run on the CPU and the GPU. +First, the zones of interest are identified and they are classified as clean or mixed. +Clean consist of a single material and are pulled out early into a new mesh while mixed +zones are sent into the Equi-Z algorithm to reconstruct zones where material interfaces +exist. The two meshes are finally merged to form a single output mesh. The mesh may +consist of multiple Blueprint shape types in an unstructured "mixed" topology. Axom's implementation supports 2D/3D zones from structured or unstructured topologies -made of Finite Element Zoo elements (e.g. triangles, quadrilaterals, tetrahedra, pyramids, -wedges, hexahedra, or topologically-compatible mixtures). The MIR logic for Equi-Z is -encapsulated in ``EquizAlgorithm``, which is a class that is templated on view objects. +made of Finite Element Zoo elements *(e.g. triangles, quadrilaterals, tetrahedra, pyramids, +wedges, hexahedra, or topologically-compatible mixtures)*. The MIR logic for Equi-Z is +encapsulated in ``axom::mir::EquizAlgorithm``, which is a class that is templated on view objects. View objects help provide an interface between the Blueprint data and the MIR algorithm. At a minimum, an execution space and three views are required to instantiate the ``axom::mir::EquiZAlgorithm`` class. The execution space determines which compute backend will be used to execute the algorithm. The Blueprint data must exist in a compatible -memory space for the execution space. The views are: _CoordsetView_, _TopologyView_, and -_MaterialView_. The CoordsetView template argument lets the algorithm access the mesh's +memory space for the execution space. The views are: *CoordsetView*, *TopologyView*, and +*MaterialView*. The *CoordsetView* template argument lets the algorithm access the mesh's coordset using concrete data types and supports queries that return points. The -TopologyView provides a set of operations that can be performed on meshes, mainly a -device-aware method for retrieving individual zones that can be used in device kernels. -The MaterialView provides an interface for matsets. +*TopologyView* provides a set of operations that can be performed on meshes, mainly a +method for retrieving individual zones that can be used in device kernels. +The *MaterialView* provides an interface for querying matsets. Once view types have been created and views have been instantiated, the ``EquiZAlgorithm`` algorithm can be instantiated and used. The ``EquiZAlgorithm`` class provides a single @@ -105,7 +105,7 @@ function can be used to copy Conduit nodes from one memory space to another. :end-before: _equiz_mir_end :language: C++ -The MIR output will contain a new field called _"originalElements"_ that indicates which +The MIR output will contain a new field called *"originalElements"* that indicates which original zone number gave rise to the reconstructed zone. This field makes it possible to map back to the original mesh. The name of the field can be changed using options. @@ -137,7 +137,8 @@ To run the example program from the Axom build directory, follow these steps: .. figure:: figures/mir_concentric_circles.png :figwidth: 800px - :alt: Diagram showing MIR output from the _mir_concentric_circles_ application. + + Diagram showing MIR output from the *mir_concentric_circles* application. ##################### Visualization @@ -146,7 +147,7 @@ Visualization The `VisIt software `_ can be used to view the Blueprint output from MIR algorithms. Blueprint data is saved in an HDF5 format and the top level file has a ".root" extension. Open the ".root" file in VisIt -to get started and then add a _FilledBoundary_ plot of the material defined on the +to get started and then add a *FilledBoundary* plot of the material defined on the mesh topology. Plotting the mesh lines will reveal that there is a single material per zone. If the input mesh is visualized in a similar manner, it will be evident that there are multiple materials in some of the zones, if viewing a mixed material dataset. diff --git a/src/axom/mir/docs/sphinx/mir_clipping.rst b/src/axom/mir/docs/sphinx/mir_clipping.rst index 64877da334..4a6f115063 100644 --- a/src/axom/mir/docs/sphinx/mir_clipping.rst +++ b/src/axom/mir/docs/sphinx/mir_clipping.rst @@ -15,70 +15,70 @@ template arguments that govern where it will execute, which coordset and topolog types it supports, and how it performs intersection. The input to the algorithm is a Blueprint mesh and when instantiated with coordset and topology views appropriate for the input data, the algorithm can operate on a wide variety of mesh types. This -includes 2D/3D structured and unstructured topologies, that can be represented using +includes 2D/3D structured and unstructured topologies that can be represented using finite elements. By default, the algorithm will clip using a field but other intersection routines can be substituted via a template argument to facilitate creation of clipping using planes, spheres, surfaces of revolution, etc. The Equi-Z algorithm uses ClipField -with an intersector that uses material volume fractions to determine the clipped geometry. +with an intersector that examines material volume fractions to determine the clipped geometry. ####### Inputs ####### Like the MIR algorithms, the clipping algorithm is designed to accept a Conduit node -containing various options that can influence how the algorithm operates. The clipping +containing various options that influence how the algorithm operates. The clipping algorithm copies the options node to the memory space where it will be used. +---------------------------------+------------------------------------------------------+ | Option | Description | +=================================+======================================================+ -| clipField: name | A required string argument that specifies the name | +| ``clipField: name`` | A required string argument that specifies the name | | | of the field that is used for clipping. At present, | | | the field must be a vertex-associated field. | +---------------------------------+------------------------------------------------------+ -| clipValue: value | An optional numeric argument that specifies the | +| ``clipValue: value`` | An optional numeric argument that specifies the | | | value in the field at which the clip boundary is | | | defined. The default is 0. | +---------------------------------+------------------------------------------------------+ -| colorField: name | If inside=1 and outside=1 then a color field is | +| ``colorField: name`` | If inside=1 and outside=1 then a color field is | | | generated so it is possible to tell apart regions of | | | the clip output that were inside or outside the clip | | | boundary. This field permits the user to change the | | | name of the color field, which is called "color" by | | | default. | +---------------------------------+------------------------------------------------------+ -| coordsetName: name | The name of the new coordset in the output mesh. If | +| ``coordsetName: name`` | The name of the new coordset in the output mesh. If | | | it is not provided, the output coordset will have the| | | same name as the input coordset. | +---------------------------------+------------------------------------------------------+ -| fields: | The fields node lets the caller provide a list of | -| - currentName: newName | field names that will be processed and added to the | -| ... | output mesh. The form is currentName:newName. If the | -| | fields node is not given, the algorithm will process | -| | all input fields. If the fields node is empty then no| -| | fields will be processed. | +|``fields:`` | The fields node lets the caller provide a list of | +| | field names that will be processed and added to the | +| | output mesh. The form is *currentName:newName*. If | +| | the *fields* node is not given, the algorithm will | +| | process all input fields. If the fields node is empty| +| | then no fields will be processed. | +---------------------------------+------------------------------------------------------+ -| inside: number | Indicates to the clipping algorithm that it should | +| ``inside: number`` | Indicates to the clipping algorithm that it should | | | preserve zone fragments that were "inside" the clip | | | boundary. Set to 1 to enable, 0 to disable. The | | | algorithm will generate these fragments by default. | +---------------------------------+------------------------------------------------------+ -| originalElementsField: name | The name of the field in which to store the original | +| ``originalElementsField: name`` | The name of the field in which to store the original | | | elements map. | +---------------------------------+------------------------------------------------------+ -| outside: number | Indicates to the clipping algorithm that it should | +| ``outside: number`` | Indicates to the clipping algorithm that it should | | | preserve zone fragments "outside" the clip boundary. | | | Set to 1 to enable, 0 to disable. These fragments are| | | not on by default. | +---------------------------------+------------------------------------------------------+ -| selectedZones: [zone list] | An optional argument that provides a list of zone ids| +| ``selectedZones: [zone list]`` | An optional argument that provides a list of zone ids| | | on which to operate. The output mesh will only have | | | contributions from zone numbers in this list, if it | | | is given. | +---------------------------------+------------------------------------------------------+ -| topologyName: name | The name of the new topology in the output mesh. If | +| ``topologyName: name`` | The name of the new topology in the output mesh. If | | | it is not provided, the output topology will have the| | | same name as the input topology. | +---------------------------------+------------------------------------------------------+ @@ -97,32 +97,33 @@ Other Conduit nodes that contain strings or single numbers that can fit within a node are safe remaining in host memory. If the mesh is not in the desired memory space, it can be moved using ``axom::mir::utilities::blueprint::copy()``. - .. codeblock:: cpp +.. code-block:: cpp - #include "axom/mir.hpp" + #include "axom/mir.hpp" - // Set up views for the mesh in deviceRoot node. - auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(deviceRoot["coordsets/coords"]); - auto topologyView = axom::mir::views::make_rectilinear<3>::view(deviceRoot["topologies/Mesh"]); + // Set up views for the mesh in deviceRoot node. + auto coordsetView = axom::mir::views::make_rectilinear_coordset::view(deviceRoot["coordsets/coords"]); + auto topologyView = axom::mir::views::make_rectilinear<3>::view(deviceRoot["topologies/Mesh"]); - // Make a clipper. - using CoordsetView = decltype(coordsetView); - using TopologyView = decltype(topologyView); - using Clip = axom::mir::clipping::ClipField; - Clip clipper(topologyView, coordsetView); + // Make a clipper. + using CoordsetView = decltype(coordsetView); + using TopologyView = decltype(topologyView); + using Clip = axom::mir::clipping::ClipField; + Clip clipper(topologyView, coordsetView); - // Run the clip algorithm - conduit::Node options; - options["clipField"] = "data"; - options["clipValue"] = 3.5; - options["outside"] = 1; - options["inside"] = 0; - clipper.execute(deviceRoot, options, clipOutput); + // Run the clip algorithm + conduit::Node options; + options["clipField"] = "data"; + options["clipValue"] = 3.5; + options["outside"] = 1; + options["inside"] = 0; + clipper.execute(deviceRoot, options, clipOutput); .. figure:: figures/clipfield.png :figwidth: 800px - :alt: Diagram showing original mesh colored by clipping field (left), original mesh colored by a radial field (middle), and the clipped mesh colored by the radial field (right). + + Diagram showing original mesh colored by clipping field (left), original mesh colored by a radial field (middle), and the clipped mesh colored by the radial field (right). ^^^^^^^^^^^^^ diff --git a/src/axom/mir/docs/sphinx/mir_utilities.rst b/src/axom/mir/docs/sphinx/mir_utilities.rst index d9206420b1..4348f25b14 100644 --- a/src/axom/mir/docs/sphinx/mir_utilities.rst +++ b/src/axom/mir/docs/sphinx/mir_utilities.rst @@ -10,7 +10,7 @@ MIR Blueprint Utilities The MIR component contains several useful building blocks for writing algorithms for Blueprint meshes. - * Structured as classes with an ``execute()`` method to permit them to divide algorithm into various methods. + * Structured as classes with an ``execute()`` method * Often templated on execution space and views ####################### @@ -19,20 +19,20 @@ Copying Blueprint Data If a ``conduit::Node`` containing Blueprint data is not on the desired memory space, it can be moved using the ``axom::mir::utilities::blueprint::copy()`` function. The ``copy`` -function moves the source ``conduit::Node`` to the destination ``conduit::Node``, making sure +function copies the source ``conduit::Node`` to the destination ``conduit::Node``, making sure to use the appropriate Axom allocator for non-string bulk arrays (e.g. arrays of ints, floats, doubles, etc.). Data small enough to fit in a ``conduit::Node`` and strings are left in the host memory space, which lets algorithms on the host side query them. For data that have been moved to the device, their sizes and data types can still be queried using normal Conduit mechanisms -such as getting metadata via the ``conduit::Node::dtype()`` method. +such as the ``conduit::Node::dtype()`` method. - .. codeblock:: cpp +.. code-block:: cpp - conduit::Node hostMesh, deviceMesh, hostMesh2; - // host->device - axom::mir::utilities::blueprint::copy>(deviceMesh, hostMesh); - // device->host - axom::mir::utilities::blueprint::copy(hostMesh2, deviceMesh); + conduit::Node hostMesh, deviceMesh, hostMesh2; + // host->device + axom::mir::utilities::blueprint::copy>(deviceMesh, hostMesh); + // device->host + axom::mir::utilities::blueprint::copy(hostMesh2, deviceMesh); ############################ ConduitAllocateThroughAxom @@ -50,7 +50,7 @@ installs an allocation routine in Conduit that can be used to allocate data thro Axom. The Conduit allocator is set on each ``conduit::Node`` before setting data into the object. -.. literalinclude:: ../../ClipField.cpp +.. literalinclude:: ../../ClipField.hpp :start-after: _mir_utilities_c2a_begin :end-before: _mir_utilities_c2a_end :language: C++ @@ -69,7 +69,7 @@ type of intersector used to determine intersections. The default intersection us based intersection method, though other intersectors could be created to perform plane or sphere intersections. -.. literalinclude:: ../../ClipField.cpp +.. literalinclude:: ../../tests/mir_clipfield.cpp :start-after: _mir_utilities_clipfield_start :end-before: _mir_utilities_clipfield_end :language: C++ @@ -78,16 +78,16 @@ or sphere intersections. CoordsetBlender ################ -The ``axom::mir::utilities::blueprint::CoordsetBlender`` class takes a "BlendGroup" and makes -a new explicit coordset where each new point corresponds to one blend group. A "BlendGroup" is +The ``axom::mir::utilities::blueprint::CoordsetBlender`` class takes a ``BlendData`` and makes +a new explicit coordset where each new point corresponds to one blend group. A "BlendData" is an object that groups several array views that describe a set of blend groups. Each blend group -is formed from a list of node ids and weight values and the new coordinate is formed by looking +is formed from a list of node ids and weight values. A new coordinate is formed by looking up the points in the blend group in the source coordset and multiplying them by their weights -and summing them together to produce the new point in the output coordset. Classes such as +and summing them together to produce the new point for the output coordset. Classes such as ``ClipField`` use ``CoordsetBlender`` to make new coordsets that contain points that were a combination of multiple points in the input coordset. -.. literalinclude:: ../../ClipField.cpp +.. literalinclude:: ../../ClipField.hpp :start-after: _mir_utilities_coordsetblender_start :end-before: _mir_utilities_coordsetblender_end :language: C++ @@ -101,7 +101,7 @@ new explicit coordset where each point corresponds to a single index from the no stored in SliceData. This class can be used to select a subset of a coordset, reorder nodes in a coordset, or repeat nodes in a coordset. -.. literalinclude:: ../../ExtractZones.cpp +.. literalinclude:: ../../ExtractZones.hpp :start-after: _mir_utilities_coordsetslicer_begin :end-before: _mir_utilities_coordsetslicer_end :language: C++ @@ -114,7 +114,7 @@ The ``axom::mir::utilities::ExtractZones`` class takes a list of selected zone i a new mesh from a source mesh that includes only the selected zones. There is a derived class ``ExtractZonesAndMatset`` that also extracts a matset, if present. -.. literalinclude:: ../../ExtractZones.cpp +.. literalinclude:: ../../ExtractZones.hpp :start-after: _mir_utilities_extractzones_begin :end-before: _mir_utilities_extractzones_end :language: C++ @@ -160,7 +160,7 @@ The ``axom::mir::utilities::blueprint::MatsetSlicer`` class is similar to the `` class except it slices matsets instead of fields. The same ``SliceData`` can be passed to MatsetSlicer to pull out and assemble a new matset data for a specific list of zones. -.. literalinclude:: ../../ExtractZones.cpp +.. literalinclude:: ../../ExtractZones.hpp :start-after: _mir_utilities_matsetslicer_begin :end-before: _mir_utilities_matsetslicer_end :language: C++ @@ -189,7 +189,7 @@ O2M (one to many) relation that relates node numbers to the zones that contain t is akin to inverting the normal mesh connectivity which is a map of zones to node ids. The O2M relation is useful for recentering data from the zones to the nodes. -.. literalinclude:: ../../tests/mir_blueprint_utilities.hpp +.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp :start-after: _mir_utilities_n2zrel_begin :end-before: _mir_utilities_n2zrel_end :language: C++ @@ -202,7 +202,7 @@ The ``axom::mir::utilities::blueprint::RecenterFields`` class uses an O2M relati field data from multiple values to an averaged value. In Axom, this is used to convert a field associated with the elements to a new field associated with the nodes. -.. literalinclude:: ../../tests/mir_blueprint_utilities.hpp +.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp :start-after: _mir_utilities_recenterfield_begin :end-before: _mir_utilities_recenterfield_end :language: C++ @@ -216,7 +216,7 @@ sorted list of unique outputs, along with a list of offsets into the original va one representative value in the original list for each unique value. This class is used to help merge points. -.. literalinclude:: ../../tests/mir_clipfield.hpp +.. literalinclude:: ../../tests/mir_clipfield.cpp :start-after: _mir_utilities_unique_begin :end-before: _mir_utilities_unique_end :language: C++ diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst index c1b52733ec..1079c73aed 100644 --- a/src/axom/mir/docs/sphinx/mir_views.rst +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -7,11 +7,11 @@ Views ****** -The MIR component provides view classes that provide lightweight, device-compatible, C++ interfaces -for Blueprint data. Blueprint data defines many object protocols and supports several data types. -Views can simplify the process of writing algorithms to support Blueprint data. Views are also -lightweight in that they can be easily copied and do not own their data. This makes them suitable -for use in device kernels. +The MIR component provides lightweight, device-compatible, view classes that add a C++ interface +for Blueprint data. Blueprint data defines several object protocols represented with arrays of +various data types. Views can simplify the process of writing algorithms to support Blueprint data. +Views do not own their data so they can be easily copied, making them suitable for use in device +kernels. ---------- ArrayView @@ -23,33 +23,33 @@ function to help wrap arrays stored in ``conduit::Node`` to ``axom::ArrayView``. ``make_array_view`` function, one must know the type held within the Conduit node. If that is not the case, then consider using one of the dispatch ''Node_to_ArrayView'' functions. - .. codeblock:: cpp +.. code-block:: cpp - // Make an axom::ArrayView for X coordinate components. - auto x = axom::mir::blueprint::make_array_view(n_mesh["coordsets/coords/values/x"]); + // Make an axom::ArrayView for X coordinate components. + auto x = axom::mir::utilities::blueprint::make_array_view(n_mesh["coordsets/coords/values/x"]); ---------- Coordsets ---------- -Blueprint supports multiple coordset types: uniform, rectilinear, explicit. Axom provides functions +Blueprint supports multiple coordset *(coordinate set)* types: uniform, rectilinear, explicit. Axom provides functions to explicitly create coordset views for each of these types. - .. codeblock:: cpp +.. code-block:: cpp - // Make a 2D uniform coordset view - auto view1 = axom::mir::views::make_uniform_coordset<2>::view(n_mesh["coordsets/coords"]); - // Make a 3D uniform coordset view - auto view2 = axom::mir::views::make_uniform_coordset<3>::view(n_mesh["coordsets/coords"]); - // Make a 2D rectilinear coordset view with float coordinates - auto view3 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); - // Make a 3D rectilinear coordset view with double coordinates - auto view4 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); - // Make a 2D explicit coordset view with float coordinates - auto view5 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); - // Make a 3D explicit coordset view with double coordinates - auto view6 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + // Make a 2D uniform coordset view + auto view1 = axom::mir::views::make_uniform_coordset<2>::view(n_mesh["coordsets/coords"]); + // Make a 3D uniform coordset view + auto view2 = axom::mir::views::make_uniform_coordset<3>::view(n_mesh["coordsets/coords"]); + // Make a 2D rectilinear coordset view with float coordinates + auto view3 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + // Make a 3D rectilinear coordset view with double coordinates + auto view4 = axom::mir::views::make_rectilinear_coordset::view(n_mesh["coordsets/coords"]); + // Make a 2D explicit coordset view with float coordinates + auto view5 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); + // Make a 3D explicit coordset view with double coordinates + auto view6 = axom::mir::views::make_explicit_coordset::view(n_mesh["coordsets/coords"]); ---------------- @@ -64,83 +64,88 @@ Axom provides topology views for structured meshes and unstructured meshes. Structured Mesh Views ^^^^^^^^^^^^^^^^^^^^^^ -The structured mesh topology view, ``StructuredTopologyView``, pertains to any of the Blueprint -topology types. The ``StructuredTopologyView`` class is a template that takes an indexing policy +The structured mesh topology view, ``axom::mir::views::StructuredTopologyView``, pertains to any of the Blueprint +structured topology types. The ``StructuredTopologyView`` class is a template that takes an indexing policy as a template argument. The indexing policy computes zone indices and converts to/from logical/global indices. The ``StridedStructuredIndexingPolicy`` class supports indexing for strided-structured Blueprint meshes, which are structured meshes that exist over a sub-window of the overall mesh. There are helper functions for creating structured topology views from a Conduit node. - .. codeblock:: cpp +.. code-block:: cpp - conduit::Node &n_topo1 = n_mesh["topologies/mesh2d"]; - conduit::Node &n_topo2 = n_mesh["topologies/mesh3d"]; - conduit::Node &n_topo3 = n_mesh["topologies/mesh2dss"]; - // Make a 2D structured mesh view from the topology. - auto topologyView1 = axom::mir::views::make_structured<2>::view(n_topo1); - // Make a 3D structured mesh view from the topology. - auto topologyView2 = axom::mir::views::make_structured<2>::view(n_topo2); - // Make a 2D strided-structured mesh view from the topology. - auto topologyView3 = axom::mir::views::make_strided_structured<2>::view(n_topo3); + conduit::Node &n_topo1 = n_mesh["topologies/mesh2d"]; + conduit::Node &n_topo2 = n_mesh["topologies/mesh3d"]; + conduit::Node &n_topo3 = n_mesh["topologies/mesh2dss"]; + // Make a 2D structured mesh view from the topology. + auto topologyView1 = axom::mir::views::make_structured<2>::view(n_topo1); + // Make a 3D structured mesh view from the topology. + auto topologyView2 = axom::mir::views::make_structured<2>::view(n_topo2); + // Make a 2D strided-structured mesh view from the topology. + auto topologyView3 = axom::mir::views::make_strided_structured<2>::view(n_topo3); ^^^^^^^^^^^^^^^^^^^^^^^^ Unstructured Mesh Views ^^^^^^^^^^^^^^^^^^^^^^^^ -There are 3 unstructured mesh views. The ``UnstructuredTopologySingleShapeView`` class wraps a +There are 3 unstructured mesh views, covering single shape meshes, mixed shape meshes, and polyhedral meshes. +The ``axom::mir::views::UnstructuredTopologySingleShapeView`` class wraps a Blueprint topology that contains a single zone/shape type. The zone type is a template argument that determines the type of zone that is held within the topology. - .. codeblock:: cpp +.. code-block:: cpp - // Make a topology view for a tetrahedral mesh with int connectivity. - namespace bputils = axom::mir::utilities::blueprint; - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - const auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); - axom::mir::views::UnstructuredTopologySingleShapeView> view(connView); + // Make a topology view for a tetrahedral mesh with int connectivity. + namespace bputils = axom::mir::utilities::blueprint; + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + const auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); + axom::mir::views::UnstructuredTopologySingleShapeView> view(connView); There are multiple shape types defined in ``axom/mir/views/Shapes.hpp`` that can be used with -the ``UnstructuredTopologySingleShapeView``: TriShape, QuadShape, TetShape, PyramidShape, -WedgeShape, and HexShape. +the ``UnstructuredTopologySingleShapeView`` class: *TriShape*, *QuadShape*, *TetShape*, *PyramidShape*, +*WedgeShape*, and *HexShape*. -Blueprint supports "mixed" topologies that contain multiple shape types. These topologies are +Blueprint supports *mixed* topologies that contain multiple shape types. These topologies are handled using the ``axom::mir::views::UnstructuredTopologyMixedShapeView``. Additional array views are needed to supply the sizes, offsets, and shapes arrays. - .. codeblock:: cpp +.. code-block:: cpp - // A shape map helps map values from the values used in the Blueprint topology to - // the shape ids used in Axom. - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - axom::Array ids, values; - auto shapeMap = axom::mir::views::buildShapeMap(n_topo); + // A shape map helps map values from the values used in the Blueprint topology to + // the shape ids used in Axom. + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array ids, values; + auto shapeMap = axom::mir::views::buildShapeMap(n_topo, ids, values, allocatorID); - namespace bputils = axom::mir::utilities::blueprint; - axom::mir::views::UnstructuredTopologyMixedShapeView view( - bputils::make_array_view(n_topo["elements/connectivity"), - bputils::make_array_view(n_topo["elements/sizes"), - bputils::make_array_view(n_topo["elements/offsets"), - bputils::make_array_view(n_topo["elements/shapes"), - shapeMap); + namespace bputils = axom::mir::utilities::blueprint; + axom::mir::views::UnstructuredTopologyMixedShapeView view( + bputils::make_array_view(n_topo["elements/connectivity"), + bputils::make_array_view(n_topo["elements/sizes"), + bputils::make_array_view(n_topo["elements/offsets"), + bputils::make_array_view(n_topo["elements/shapes"), + shapeMap); -Once a suitable topology view type has wrapped the Blueprint topology, it can be used in +The final unstructured topology view is ``axom::mir::views::UnstructuredTopologyPolyhedralView`` +and it provides a view interface to polyhedral meshes. + +Once a suitable topology view type has wrapped a Blueprint topology, it can be used in device kernels to obtain zone information. - .. codeblock:: cpp +.. code-block:: cpp - topologyView = ... - axom::for_all(topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) - { - // Get the current zone. - const auto zone = topologyView.zone(zoneIndex); + auto topologyView = ... + axom::for_all(topologyView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) + { + // Get the current zone. + const auto zone = topologyView.zone(zoneIndex); - // Iterate over this zone's nodes. - for(const auto &nodeId : zone.getIds()) - { - // Do something. - } - }); + // Iterate over this zone's nodes. + for(const auto &nodeId : zone.getIds()) + { + // Do something. + } + }); ---------- Matsets @@ -162,8 +167,8 @@ methods allow algorithms to query the list of materials for each zone. Dispatch ---------- -There are several helper functions for converting a Conduit node to a specific view type -and passing the view to a lambda for further processing. These dispatch functions take +There are several helper functions that wrap a Conduit node in a specific view type +and **dispatch** the view to a lambda for further processing. These dispatch functions take care of wrapping a Conduit node containing Blueprint data in various view types before passing the views to a user-supplied lambda function. The lambda function will typically be instantiated multiple times to handle cases when there are multiple data types and @@ -180,31 +185,32 @@ wrapping to ``axom::ArrayView`` and passing the views to a user-supplied lambda. To generically wrap any type of datatype supported by Conduit, the ``axom::mir::views::Node_to_ArrayView()`` function can be used. This template function takes a variable number of ``conduit::Node`` -arguments and a generic lambda function that accepts the view arguments. +arguments and a generic lambda function that accepts the view arguments. The lambda gets +instantiated for every supported Conduit data type. - .. codeblock:: cpp +.. code-block:: cpp - conduit::Node n; // Assume it contains data values - axom::mir::views::Node_to_ArrayView(n["foo"], n["bar"], [&](auto fooView, auto barView) - { - // Use fooView and barView axom::ArrayView objects to access data. - // They can have different types. - }); + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They can have different types. + }); Using ``axom::mir::views::Node_to_ArrayView`` with multiple data values can instantiate -the supplied lambda many times so be careful. It is more common that when wrapping multiple +the supplied lambda many times so be careful. It is more common when wrapping multiple nodes that they are the same type. The ``axom::mir::views::Node_to_ArrayView_same`` function -will ensure that the lambdas get instantiated with views that wrap the Conduit nodes in +ensures that the lambdas get instantiated with views that wrap the Conduit nodes in array views that of the same type. - .. codeblock:: cpp +.. code-block:: cpp - conduit::Node n; // Assume it contains data values - axom::mir::views::Node_to_ArrayView_same(n["foo"], n["bar"], [&](auto fooView, auto barView) - { - // Use fooView and barView axom::ArrayView objects to access data. - // They have the same types. - }); + conduit::Node n; // Assume it contains data values + axom::mir::views::Node_to_ArrayView_same(n["foo"], n["bar"], [&](auto fooView, auto barView) + { + // Use fooView and barView axom::ArrayView objects to access data. + // They have the same types. + }); When dealing with mesh data structures, it is common to have data that are using only integer types or only floating-point types. Axom provides functions that limit the lambda instantiation @@ -226,14 +232,14 @@ Coordsets The ``axom::mir::views::dispatch_coordset()`` function can wrap Blueprint coordsets in an appropriate view and pass it to a lambda function. - .. codeblock:: cpp +.. code-block:: cpp - const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; - axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { - // Get the C++ type of the coordset. - using CoordsetView = decltype(CoordsetView); - // Implement algorithm using coordsetView. - }); + const conduit::Node &n_coordset = n_mesh["coordsets/coords"]; + axom::mir::views::dispatch_coordset(n_coordset, [&](auto coordsetView) { + // Get the C++ type of the coordset. + using CoordsetView = decltype(CoordsetView); + // Implement algorithm using coordsetView. + }); ^^^^^^^^^^^ Topologies @@ -244,47 +250,47 @@ topology types through a topology view. These dispatch functions can be called f topology types such as unstructured topologies or they can be called to implement algorithms that can operate on any topology. - .. codeblock:: cpp - - const conduit::Node &n_topo = n_mesh["topologies/mesh"]; - // Handle rectilinear topology type. - axom::mir::views::dispatch_rectilinear_topology(n_topo, [&](auto topologyView) { - }); - // Handle structured topology types - axom::mir::views::dispatch_structured_topology(n_topo, [&](auto topologyView) { - }); - // Handle unstructured topology types - axom::mir::views::dispatch_unstructured_topology(n_topo, [&](auto topologyView) { - }); - // Handle any topology type. - axom::mir::views::dispatch_topologies(n_topo, [&](auto topologyView) { - }); +.. code-block:: cpp + + const conduit::Node &n_topo = n_mesh["topologies/mesh"]; + // Handle rectilinear topology type. + axom::mir::views::dispatch_rectilinear_topology(n_topo, [&](auto topologyView) { + }); + // Handle structured topology types + axom::mir::views::dispatch_structured_topology(n_topo, [&](auto topologyView) { + }); + // Handle unstructured topology types + axom::mir::views::dispatch_unstructured_topology(n_topo, [&](auto topologyView) { + }); + // Handle any topology type. + axom::mir::views::dispatch_topology(n_topo, [&](auto topologyView) { + }); Nesting dispatch functions permits the calling code to handle both coordset views and -topology views using a single lambda function for the algorithm. For portability, the +topology views using a compact amount of code. For portability, the actual algorithm should be placed in a function or class member method when instantiated from the anonymous lambda function from the dispatch functions. - .. codeblock:: cpp +.. code-block:: cpp - struct Algorithm + struct Algorithm + { + void execute(const conduit::Node &n_mesh) { - void execute(const conduit::Node &n_mesh) + // Handle product of coordset types and topology types. + axom::mir::views::dispatch_coordset(n_mesh["coordsets/coords"], [&](auto coordsetView) { - // Handle product of coordset types and topology types. - axom::mir::views::dispatch_coordset(n_mesh["coordsets/coords"], [&](auto coordsetView) + axom::mir::views::dispatch_topologies(n_mesh["topologies/mesh"], [&](auto topologyView) { - axom::mir::views::dispatch_topologies(n_mesh["topologies/mesh"], [&](auto topologyView) - { - implementation(coordsetView, topologyView); - }); + implementation(coordsetView, topologyView); }); - } + }); + } - template - void implementation(CoordsetView coordsetView, TopologyView topologyView) const - { - // Algorithm that involves coordsetView and topologyView. - } - }; + template + void implementation(CoordsetView coordsetView, TopologyView topologyView) const + { + // Algorithm that involves coordsetView and topologyView. + } + }; diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 7a0e71cfdb..27dbdd80ee 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -89,6 +89,7 @@ int runMIR(const conduit::Node &hostMesh, axom::mir::EquiZAlgorithm; MIR m(topoView, coordsetView, matsetView); conduit::Node deviceResult; + options["matset"] = "mat"; m.execute(deviceMesh, options, deviceResult); // _equiz_mir_end diff --git a/src/conf.py b/src/conf.py index f662a92b0d..2deb8e8334 100644 --- a/src/conf.py +++ b/src/conf.py @@ -57,7 +57,8 @@ 'sphinxcontrib.jquery', 'sphinx.ext.todo', 'sphinx.ext.coverage', - 'sphinx.ext.mathjax' + 'sphinx.ext.mathjax', + 'sphinx.ext.viewcode' ] # Add any paths that contain templates here, relative to this directory. From cb12815221c3dbdac444717b723233f4472c39b2 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 16:35:48 -0700 Subject: [PATCH 269/290] Ported mir_tutorial_simple --- src/axom/mir/MeshTester.cpp | 350 ++++++++++----- src/axom/mir/MeshTester.hpp | 1 + .../mir/examples/mir_concentric_circles.cpp | 3 +- src/axom/mir/examples/mir_tutorial_simple.cpp | 398 +++++++++++------- src/axom/mir/tests/mir_views.cpp | 2 +- 5 files changed, 496 insertions(+), 258 deletions(-) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index ae6f6c17df..dd2318f467 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -63,9 +63,9 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / - static_cast(gridSize - 1); + static_cast(gridSize - 1); axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / - static_cast(gridSize - 1); + static_cast(gridSize - 1); int countOverlap = 0; for(int y = 0; y < gridSize; ++y) { @@ -77,7 +77,7 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, ++countOverlap; } } - return countOverlap / static_cast(gridSize * gridSize); + return static_cast(countOverlap) / static_cast(gridSize * gridSize); } } @@ -660,20 +660,21 @@ static void addCircleMaterial(const TopoView& topoView, topoView.numberOfZones(), AXOM_LAMBDA(axom::IndexType zoneIndex) { const auto zone = deviceTopologyView.zone(zoneIndex); + // NOTE: node ordering shuffled because function takes different node ordering. auto vf = calculatePercentOverlapMonteCarlo(numSamples, center, circleRadius, + coordsetView[zone.getId(3)], coordsetView[zone.getId(0)], coordsetView[zone.getId(1)], - coordsetView[zone.getId(2)], - coordsetView[zone.getId(3)]); + coordsetView[zone.getId(2)]); greenView[zoneIndex] = vf; blueView[zoneIndex] = 1.0 - vf; }); // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; - std::vector volume_fractions; + std::vector volume_fractions; for(int i = 0; i < numElements; ++i) { int nmats = 0; @@ -989,66 +990,6 @@ void MeshTester::generateGrid(int gridSize, conduit::Node& mesh) mesh["topologies/mesh/elements/offsets"].set(offsets); } -void MeshTester::generateGrid3D(int gridSize, conduit::Node& mesh) -{ - int nx = gridSize + 1; - int ny = gridSize + 1; - int nz = gridSize + 1; - int nzones = gridSize * gridSize * gridSize; - int nnodes = nx * ny * nz; - - std::vector xc, yc, zc; - xc.reserve(nnodes); - yc.reserve(nnodes); - zc.reserve(nnodes); - for(int k = 0; k < nz; k++) - { - for(int j = 0; j < ny; j++) - { - for(int i = 0; i < nx; i++) - { - xc.push_back(i); - yc.push_back(j); - zc.push_back(k); - } - } - } - - std::vector conn, sizes, offsets; - conn.reserve(nzones * 8); - sizes.reserve(nzones); - offsets.reserve(nzones); - for(int k = 0; k < gridSize; k++) - { - for(int j = 0; j < gridSize; j++) - { - for(int i = 0; i < gridSize; i++) - { - offsets.push_back(offsets.size() * 8); - sizes.push_back(8); - conn.push_back((k * nx * ny) + (j * nx) + i); - conn.push_back((k * nx * ny) + (j * nx) + i + 1); - conn.push_back((k * nx * ny) + ((j + 1) * nx) + i + 1); - conn.push_back((k * nx * ny) + ((j + 1) * nx) + i); - conn.push_back(((k + 1) * nx * ny) + (j * nx) + i); - conn.push_back(((k + 1) * nx * ny) + (j * nx) + i + 1); - conn.push_back(((k + 1) * nx * ny) + ((j + 1) * nx) + i + 1); - conn.push_back(((k + 1) * nx * ny) + ((j + 1) * nx) + i); - } - } - } - - mesh["coordsets/coords/type"] = "explicit"; - mesh["coordsets/coords/values/x"].set(xc); - mesh["coordsets/coords/values/y"].set(yc); - mesh["coordsets/coords/values/z"].set(zc); - mesh["topologies/mesh/type"] = "unstructured"; - mesh["topologies/mesh/coordset"] = "coords"; - mesh["topologies/mesh/elements/shape"] = "hex"; - mesh["topologies/mesh/elements/connectivity"].set(conn); - mesh["topologies/mesh/elements/sizes"].set(sizes); - mesh["topologies/mesh/elements/offsets"].set(offsets); -} //-------------------------------------------------------------------------------- mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) @@ -1193,6 +1134,53 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) return testMesh; } +static void addMaterial(axom::IndexType numElements, + int numMaterials, + const std::vector> &materialVolumeFractionsData, + conduit::Node &mesh) +{ + // Figure out the material buffers from the volume fractions. + std::vector material_ids, sizes, offsets, indices; + std::vector volume_fractions; + std::vector sums(numMaterials, 0.); + for(axom::IndexType i = 0; i < numElements; ++i) + { + int nmats = 0; + offsets.push_back(indices.size()); + for(int mat = 0; mat < numMaterials; mat++) + { + if(materialVolumeFractionsData[mat][i] > 0.) + { + material_ids.push_back(mat); + volume_fractions.push_back(materialVolumeFractionsData[mat][i]); + indices.push_back(indices.size()); + nmats++; + + // Keep a total of the VFs for each material. + sums[mat] += materialVolumeFractionsData[mat][i]; + } + } + sizes.push_back(nmats); + } + + // Add the material + mesh["matsets/mat/topology"] = "mesh"; + for(int mat = 0; mat < numMaterials; mat++) + { + if(sums[mat] > 0.) + { + std::stringstream ss; + ss << "matsets/mat/material_map/mat" << mat; + mesh[ss.str()] = mat; + } + } + mesh["matsets/mat/material_ids"].set(material_ids); + mesh["matsets/mat/volume_fractions"].set(volume_fractions); + mesh["matsets/mat/sizes"].set(sizes); + mesh["matsets/mat/offsets"].set(offsets); + mesh["matsets/mat/indices"].set(indices); +} + template void addConcentricCircleMaterial(const TopoView& topoView, const CoordsetView& coordsetView, @@ -1275,47 +1263,7 @@ void addConcentricCircleMaterial(const TopoView& topoView, } }); - // Figure out the material buffers from the volume fractions. - std::vector material_ids, sizes, offsets, indices; - std::vector volume_fractions; - const axom::IndexType numElements = topoView.numberOfZones(); - std::vector sums(numMaterials, 0.); - for(axom::IndexType i = 0; i < numElements; ++i) - { - int nmats = 0; - offsets.push_back(indices.size()); - for(int mat = 0; mat < numMaterials; mat++) - { - if(materialVolumeFractionsData[mat][i] > 0.) - { - material_ids.push_back(mat); - volume_fractions.push_back(materialVolumeFractionsData[mat][i]); - indices.push_back(indices.size()); - nmats++; - - // Keep a total of the VFs for each material. - sums[mat] += materialVolumeFractionsData[mat][i]; - } - } - sizes.push_back(nmats); - } - - // Add the material - mesh["matsets/mat/topology"] = "mesh"; - for(int mat = 0; mat < numMaterials; mat++) - { - if(sums[mat] > 0.) - { - std::stringstream ss; - ss << "matsets/mat/material_map/mat" << mat; - mesh[ss.str()] = mat; - } - } - mesh["matsets/mat/material_ids"].set(material_ids); - mesh["matsets/mat/volume_fractions"].set(volume_fractions); - mesh["matsets/mat/sizes"].set(sizes); - mesh["matsets/mat/offsets"].set(offsets); - mesh["matsets/mat/indices"].set(indices); + addMaterial(topoView.numberOfZones(), numMaterials, materialVolumeFractionsData, mesh); } void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& mesh) @@ -1641,6 +1589,116 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) //-------------------------------------------------------------------------------- +void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &mesh) +{ + // Generate the mesh topology + generateGrid3D(gridSize, mesh); + + // Generate the element volume fractions with concentric spheres + int numMaterials = numSpheres + 1; + int defaultMaterialID = + numMaterials - 1; // default material is always the last index + + // Initialize the radii of the circles + std::vector sphereRadii; + axom::float64 maxRadius = + gridSize / 2.0; // Note: The choice of divisor is arbitrary + axom::float64 minRadius = + gridSize / 4.0; // Note: The choice of divisor is arbitrary + + axom::float64 radiusDelta; + if(numSpheres <= 1) + radiusDelta = (maxRadius - minRadius); + else + radiusDelta = (maxRadius - minRadius) / static_cast(numSpheres - 1); + + for(int i = 0; i < numSpheres; ++i) + { + auto rad = minRadius + (i * radiusDelta); + sphereRadii.push_back(rad * rad); + } + + // Make views to wrap the coordset/topology. + auto coordsetView = axom::mir::views::make_explicit_coordset::view(mesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + using PointType = typename CoordsetView::PointType; + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topologyView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + + // Initialize all material volume fractions to 0 + std::vector> materialVolumeFractionsData(numMaterials); + for(int i = 0; i < numMaterials; ++i) + { + materialVolumeFractionsData[i].resize(topologyView.numberOfZones(), 0.); + } + + // all spheres are centered around the same point + const auto sphereCenter = PointType::make_point(static_cast(gridSize / 2.0), + static_cast(gridSize / 2.0), + static_cast(gridSize / 2.0)); + + // Use the uniform sampling method to generate volume fractions for each material + for(int eID = 0; eID < topologyView.numberOfZones(); ++eID) + { + const auto zone = topologyView.zone(eID); + const auto v0 = coordsetView[zone.getId(0)]; + const auto v1 = coordsetView[zone.getId(1)]; + const auto v3 = coordsetView[zone.getId(3)]; + const auto v4 = coordsetView[zone.getId(4)]; + + // Run the uniform sampling to determine how much of the current cell is composed of each material + int materialCount[numMaterials]; + for(int i = 0; i < numMaterials; ++i) { materialCount[i] = 0; } + + float delta_x = + axom::utilities::abs(v1[0] - v0[0]) / static_cast(gridSize - 1); + float delta_y = + axom::utilities::abs(v3[1] - v0[1]) / static_cast(gridSize - 1); + float delta_z = + axom::utilities::abs(v4[2] - v0[2]) / static_cast(gridSize - 1); + + for(int z = 0; z < gridSize; ++z) + { + for(int y = 0; y < gridSize; ++y) + { + for(int x = 0; x < gridSize; ++x) + { + const auto samplePoint = PointType::make_point(static_cast(delta_x * x + v0[0]), + static_cast(delta_y * y + v0[1]), + static_cast(delta_z * z + v0[2])); + + bool isPointSampled = false; + for(int cID = 0; cID < numSpheres && !isPointSampled; ++cID) + { + if(primal::squared_distance(samplePoint, sphereCenter) < + sphereRadii[cID]) + { + materialCount[cID]++; + isPointSampled = true; + } + } + if(!isPointSampled) + { + // The point was not within any of the circles, so increment the count for the default material + materialCount[defaultMaterialID]++; + } + } + } + } + + // Assign the element volume fractions based on the count of the samples in each circle + const axom::float64 nzones_inv = 1. / static_cast(gridSize * gridSize * gridSize); + for(int matID = 0; matID < numMaterials; ++matID) + { + materialVolumeFractionsData[matID][eID] = materialCount[matID] * nzones_inv; + } + } + + addMaterial(topologyView.numberOfZones(), numMaterials, materialVolumeFractionsData, mesh); +} + +//-------------------------------------------------------------------------------- + mir::CellData MeshTester::generateGrid3D(int gridSize) { // Generate the topology for a uniform quad mesh with n x n elements automatically @@ -1744,6 +1802,84 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) return data; } +void MeshTester::generateGrid3D(int gridSize, conduit::Node &mesh) +{ + const int nzones = gridSize * gridSize * gridSize; + const int dims[3] = {gridSize + 1, gridSize + 1, gridSize + 1}; + const int nnodes = dims[0] * dims[1] * dims[2]; + + conduit::Node &coordset = mesh["coordsets/coords"]; + conduit::Node &topo = mesh["topologies/mesh"]; + + // Make coordset + coordset["type"] = "explicit"; + conduit::Node &n_x = coordset["values/x"]; + conduit::Node &n_y = coordset["values/y"]; + conduit::Node &n_z = coordset["values/z"]; + n_x.set(conduit::DataType::float32(nnodes)); + n_y.set(conduit::DataType::float32(nnodes)); + n_z.set(conduit::DataType::float32(nnodes)); + auto *x = static_cast(n_x.data_ptr()); + auto *y = static_cast(n_y.data_ptr()); + auto *z = static_cast(n_z.data_ptr()); + + for(int k = 0; k < dims[2]; k++) + { + for(int j = 0; j < dims[1]; j++) + { + for(int i = 0; i < dims[0]; i++) + { + *x++ = i; + *y++ = j; + *z++ = k; + } + } + } + + // Make topology + topo["type"] = "unstructured"; + topo["coordset"] = "coords"; + topo["elements/shape"] = "hex"; + conduit::Node &conn = topo["elements/connectivity"]; + conduit::Node &sizes = topo["elements/sizes"]; + conduit::Node &offsets = topo["elements/offsets"]; + conn.set(conduit::DataType::int32(8 * nzones)); + sizes.set(conduit::DataType::int32(nzones)); + offsets.set(conduit::DataType::int32(nzones)); + auto *conn_ptr = static_cast(conn.data_ptr()); + auto *sizes_ptr = static_cast(sizes.data_ptr()); + auto *offsets_ptr = static_cast(offsets.data_ptr()); + + for(int k = 0; k < gridSize; k++) + { + const int knxny = k * dims[0] * dims[1]; + const int k1nxny = (k + 1) * dims[0] * dims[1]; + for(int j = 0; j < gridSize; j++) + { + const int jnx = j * dims[0]; + const int j1nx = (j + 1) * dims[0]; + for(int i = 0; i < gridSize; i++) + { + conn_ptr[0] = knxny + jnx + i; + conn_ptr[1] = knxny + jnx + i + 1; + conn_ptr[2] = knxny + j1nx + i + 1; + conn_ptr[3] = knxny + j1nx + i; + conn_ptr[4] = k1nxny + jnx + i; + conn_ptr[5] = k1nxny + jnx + i + 1; + conn_ptr[6] = k1nxny + j1nx + i + 1; + conn_ptr[7] = k1nxny + j1nx + i; + + conn_ptr += 8; + } + } + } + for(int i = 0; i < nzones; i++) + { + sizes_ptr[i] = 8; + offsets_ptr[i] = 8 * i; + } +} + //-------------------------------------------------------------------------------- } // namespace mir diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 54cc746118..80a8475780 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -122,6 +122,7 @@ class MeshTester * \return The generated mesh. */ mir::MIRMesh initTestCaseSix(int gridSize, int numSpheres); + void initTestCaseSix(int gridSize, int numSpheres, conduit::Node &mesh); /** * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 27dbdd80ee..1d595dcbf4 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -89,7 +89,6 @@ int runMIR(const conduit::Node &hostMesh, axom::mir::EquiZAlgorithm; MIR m(topoView, coordsetView, matsetView); conduit::Node deviceResult; - options["matset"] = "mat"; m.execute(deviceMesh, options, deviceResult); // _equiz_mir_end @@ -249,7 +248,7 @@ int main(int argc, char **argv) RuntimePolicy policy {RuntimePolicy::seq}; std::stringstream pol_sstr; - pol_sstr << "Set runtime policy for intersection-based sampling method."; + pol_sstr << "Set MIR runtime policy method."; #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) pol_sstr << "\nSet to 'seq' or 0 to use the RAJA sequential policy."; #ifdef AXOM_USE_OPENMP diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/mir_tutorial_simple.cpp index e7dc7b7e9e..28193007d6 100644 --- a/src/axom/mir/examples/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/mir_tutorial_simple.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -15,116 +15,98 @@ namespace numerics = axom::numerics; namespace slam = axom::slam; namespace mir = axom::mir; namespace fs = axom::utilities::filesystem; +namespace bputils = axom::mir::utilities::blueprint; -//-------------------------------------------------------------------------------- - -enum InputStatus -{ - SUCCESS, - INVALID, - SHOWHELP -}; +using RuntimePolicy = axom::runtime_policy::Policy; +//-------------------------------------------------------------------------------- +/// Contain program options. struct Input { - int m_test_case; // valid values 1,2,3,4,5 - bool m_should_iterate; - int m_iter_count; - double m_iter_percent; - bool m_verbose; - std::string m_output_dir; - InputStatus m_status; - - Input() - : m_test_case(2) - , m_should_iterate(false) - , m_iter_count(100) - , m_iter_percent(.3) - , m_verbose(false) - , m_status(SUCCESS) + int m_test_case{1}; // valid values 1,2,3,4,5 + bool m_should_iterate{false}; + int m_iter_count{0}; + double m_iter_percent{0.}; + bool m_verbose{false}; + std::string m_output_dir{}; + RuntimePolicy m_policy {RuntimePolicy::seq}; + std::string m_annotationMode{"report"}; + axom::CLI::App m_app{}; + + /// Parse command line. + int parse(int argc, char** argv) { - m_output_dir = fs::joinPath(AXOM_BIN_DIR, "mir_examples"); - } - - Input(int argc, char** argv) : Input() - { - for(int i = 1; i < argc; /* increment i in loop */) + m_app.add_option("--test-case", m_test_case) + ->description("Select the test case."); + + m_app.add_option("--output-dir", m_output_dir) + ->description("The directory for output files"); + + m_app.add_option("--iter-count", m_iter_count) + ->description("The number of iterations for MIR"); + + m_app.add_option("--iter-percent", m_iter_percent) + ->description("The percent error for iterative MIR"); + + m_app.add_flag("--verbose", m_verbose) + ->description("Verbose output"); + +#if defined(AXOM_USE_CALIPER) + m_app.add_option("--caliper", m_annotationMode) + ->description( + "caliper annotation mode. Valid options include 'none' and 'report'. " + ) + ->capture_default_str() + ->check(axom::utilities::ValidCaliperMode); +#endif + + std::stringstream pol_sstr; + pol_sstr << "Set MIR runtime policy."; +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) + pol_sstr << "\nSet to 'seq' or 0 to use the RAJA sequential policy."; + #ifdef AXOM_USE_OPENMP + pol_sstr << "\nSet to 'omp' or 1 to use the RAJA OpenMP policy."; + #endif + #ifdef AXOM_USE_CUDA + pol_sstr << "\nSet to 'cuda' or 2 to use the RAJA CUDA policy."; + #endif + #ifdef AXOM_USE_HIP + pol_sstr << "\nSet to 'hip' or 3 to use the RAJA HIP policy."; + #endif +#endif + m_app.add_option("-p, --policy", m_policy, pol_sstr.str()) + ->capture_default_str() + ->transform( + axom::CLI::CheckedTransformer(axom::runtime_policy::s_nameToPolicy)); + + // Parse command line options. + try { - std::string arg = argv[i]; - if(arg == "--test-case") - { - m_test_case = std::stoi(argv[++i]); - } - else if(arg == "--output-dir") - { - m_output_dir = argv[++i]; - } - else if(arg == "--iter-count") - { - m_iter_count = std::stoi(argv[++i]); - m_should_iterate = true; - } - else if(arg == "--iter-percent") - { - m_iter_percent = std::stod(argv[++i]); - m_should_iterate = true; - } - else if(arg == "--verbose") - { - m_verbose = true; - } - else // help or unknown parameter - { - if(arg != "--help" && arg != "-h") - { - SLIC_WARNING("Unrecognized parameter: " << arg); - m_status = INVALID; - } - else - { - m_status = SHOWHELP; - } - return; - } - ++i; + m_app.parse(argc, argv); + } + catch (const axom::CLI::ParseError &e) + { + return m_app.exit(e); } - checkTestCase(); + int retval = 0; + checkTestCase(retval); checkOutputDir(); - checkIterationParams(); + checkIterationParams(retval); + return retval; } bool shouldIterate() const { return m_should_iterate; } int numIterations() const { return m_iter_count; } int iterPercentage() const { return m_iter_percent; } - void showhelp() - { - std::cout - << "Argument usage:" - "\n --help Show this help message." - "\n --test-case N Mesh test case. Default N = 2." - "\n Valid values {1,2,3,4,5,6}" - "\n --output-dir dir Directory for output mesh" - "\n Default is: '${AXOM_BIN_DIR}/mir_examples'" - "\n --iter-count N Number of iterations for iterative algorithm" - "\n Defaults to 100." - "\n Setting a value triggers iterative algorithm" - "\n --iter-percent D Volume diff percentage for iterative algorithm" - "\n Must be between 0 and 1. Defaults to 0.3" - "\n Setting a value triggers iterative algorithm" - "\n --verbose Increases verbosity of output" - << std::endl - << std::endl; - }; - private: - void checkTestCase() + void checkTestCase(int &retval) { if(m_test_case < 1 || m_test_case > 6) { - m_status = INVALID; - SLIC_WARNING("Invalid test case " << m_test_case); + retval = -1; + SLIC_ERROR("Invalid test case " << m_test_case); } } @@ -136,25 +118,115 @@ struct Input } } - void checkIterationParams() + void checkIterationParams(int &retval) { if(m_should_iterate) { if(m_iter_count < 1) { - m_status = INVALID; - SLIC_WARNING("Invalid iteration count " << m_iter_count); + retval = -2; + SLIC_ERROR("Invalid iteration count " << m_iter_count); } if(m_iter_percent <= 0. || m_iter_percent > 1.) { - m_status = INVALID; - SLIC_WARNING("Invalid iteration percentage " << m_iter_percent); + retval = -3; + SLIC_ERROR("Invalid iteration percentage " << m_iter_percent); } } } }; +//-------------------------------------------------------------------------------- +/// Print a Conduit node. +void printNode(const conduit::Node &n) +{ + conduit::Node options; + options["num_children_threshold"] = 10000; + options["num_elements_threshold"] = 10000; + n.to_summary_string_stream(std::cout, options); +} + +//-------------------------------------------------------------------------------- +/*! + * \brief Run MIR on the input mesh. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * + * \param hostMesh A conduit node that contains the test mesh. + * \param options A conduit node that contains the test mesh. + * \param hostResult A conduit node that will contain the MIR results. + */ +template +int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit::Node &hostResult) +{ + std::string shape = hostMesh["topologies/mesh/elements/shape"].as_string(); + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; + conduit::Node &n_topo = deviceMesh["topologies/mesh"]; + conduit::Node &n_matset = deviceMesh["matsets/mat"]; + auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); + + // Make matset view. (There's often 1 more material so add 1) + constexpr int MAXMATERIALS = 12; + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); + + // Coord/Topo views differ. + conduit::Node deviceResult; + if(shape == "tri") + { + auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topologyView(connView); + + using MIR = axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + } + else if(shape == "quad") + { + auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topologyView(connView); + + using MIR = axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + } + else if(shape == "hex") + { + auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + TopologyView topologyView(connView); + + using MIR = axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + } + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +//-------------------------------------------------------------------------------- /*! * \brief Tutorial main showing how to initialize test cases and perform mir. */ @@ -164,103 +236,133 @@ int main(int argc, char** argv) axom::slic::setLoggingMsgLevel(axom::slic::message::Info); // Parse arguments - Input params(argc, argv); - - if(params.m_status != SUCCESS) + Input params; + int retval = params.parse(argc, argv); + if(retval != 0) { - if(params.m_status == SHOWHELP) - { - params.showhelp(); - return 0; - } - else if(params.m_status == INVALID) - { - params.showhelp(); - return 1; - } + return retval; } +#if defined(AXOM_USE_CALIPER) + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper( + params.m_annotationMode); +#endif - mir::MIRMesh testMesh; + // Make the mesh + conduit::Node mesh; mir::MeshTester tester; - auto timer = axom::utilities::Timer(true); - switch(params.m_test_case) { case 1: - testMesh = tester.initTestCaseOne(); + tester.initTestCaseOne(mesh); break; case 2: - testMesh = tester.initTestCaseTwo(); + tester.initTestCaseTwo(mesh); break; case 3: - testMesh = tester.initTestCaseThree(); + tester.initTestCaseThree(mesh); break; case 4: - testMesh = tester.initTestCaseFour(); + tester.initTestCaseFour(mesh); break; case 5: - testMesh = tester.initTestCaseFive(25, 12); + { + constexpr int GRIDSIZE = 25; + constexpr int MAXMATERIALS = 12; + tester.initTestCaseFive(GRIDSIZE, MAXMATERIALS, mesh); + } break; case 6: - testMesh = tester.initTestCaseSix(15, 3); + { + constexpr int GRIDSIZE = 15; + constexpr int MAXMATERIALS = 3; + tester.initTestCaseSix(GRIDSIZE, MAXMATERIALS, mesh); + } break; } - timer.stop(); SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); - SLIC_INFO("Test mesh is " << (testMesh.isValid(true) ? "" : " NOT") - << " valid."); + // Save input mesh + std::string filepath, filename("inputMesh"); + if(params.m_output_dir.empty()) + filepath = filename; + else + filepath = axom::utilities::filesystem::joinPath(params.m_output_dir, filename); + conduit::relay::io::blueprint::save_mesh(mesh, filepath, "hdf5"); if(params.m_verbose) { SLIC_INFO("Initial mesh:"); - testMesh.print(); + printNode(mesh); } // Begin material interface reconstruction timer.start(); - mir::MIRMesh processedMesh; - mir::InterfaceReconstructor reconstructor; + // Set up options. + conduit::Node options; + options["matset"] = "mat"; + // Future options + options["iterate"] = params.shouldIterate() ? 1 : 0; + options["iterate_percentage"] = params.iterPercentage(); - if(!params.shouldIterate()) // Process once, with original Meredith algorithm + // Run MIR + conduit::Node resultMesh; + if(params.m_policy == RuntimePolicy::seq) { - reconstructor.computeReconstructedInterface(testMesh, processedMesh); + retval = runMIR(mesh, options, resultMesh); } - else // use iterative algorithm +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) + #if defined(AXOM_USE_OPENMP) + else if(params.m_policy == RuntimePolicy::omp) { - int n = params.numIterations(); - double p = params.iterPercentage(); - - reconstructor.computeReconstructedInterfaceIterative(testMesh, - n, - p, - processedMesh); + retval = runMIR(mesh, options, resultMesh); + } + #endif + #if defined(AXOM_USE_CUDA) + else if(params.m_policy == RuntimePolicy::cuda) + { + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + retval = runMIR(mesh, options, resultMesh); + } + #endif + #if defined(AXOM_USE_HIP) + else if(params.m_policy == RuntimePolicy::hip) + { + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + retval = runMIR(mesh, options, resultMesh); + } + #endif +#endif + else + { + retval = -1; + SLIC_ERROR("Unhandled policy."); } - timer.stop(); SLIC_INFO("Reconstruction time: " << timer.elapsedTimeInMilliSec() << " ms."); - // Output results - processedMesh.writeMeshToFile(params.m_output_dir, "processedMesh.vtk"); - - using VolFracs = std::vector>; - timer.start(); - VolFracs materialVolumeFractionsElement = - processedMesh.computeOriginalElementVolumeFractions(); - timer.stop(); - SLIC_INFO("Computing volumes took: " << timer.elapsedTimeInMilliSec() - << " ms."); + // Save output. + if(retval == 0) + { + std::string filepath, filename("processedMesh"); + if(params.m_output_dir.empty()) + filepath = filename; + else + filepath = axom::utilities::filesystem::joinPath(params.m_output_dir, filename); + conduit::relay::io::blueprint::save_mesh(resultMesh, filepath, "hdf5"); + } if(params.m_verbose) { SLIC_INFO("Final mesh:"); - processedMesh.print(); + printNode(resultMesh); } - return 0; + return retval; } //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index ecc468e372..9b926f236e 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -16,7 +16,7 @@ namespace bputils = axom::mir::utilities::blueprint; //------------------------------------------------------------------------------ -#define DEBUGGING_TEST_CASES +//#define DEBUGGING_TEST_CASES // Uncomment to generate baselines //#define AXOM_TESTING_GENERATE_BASELINES From bbc1e46a443cee42f087779cdb6cad91c5961a85 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:11:12 -0700 Subject: [PATCH 270/290] Fix 3D equiz test --- src/axom/mir/tests/mir_equiz.cpp | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 70856ab57b..38331e0ded 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -12,8 +12,6 @@ //------------------------------------------------------------------------------ -//#define DEBUGGING_TEST_CASES - // Uncomment to generate baselines //#define AXOM_TESTING_GENERATE_BASELINES @@ -153,7 +151,6 @@ TEST(mir_equiz, equiz_uniform_unibuffer_hip) } #endif -#if 0 // FIXME //------------------------------------------------------------------------------ template void braid3d_mat_test(const std::string &type, @@ -162,7 +159,7 @@ void braid3d_mat_test(const std::string &type, { namespace bputils = axom::mir::utilities::blueprint; - axom::StackArray dims {10, 10, 10}; + axom::StackArray dims {11, 11, 11}; axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1, dims[2] - 1}; // Create the data @@ -175,13 +172,14 @@ void braid3d_mat_test(const std::string &type, #endif // Make views. - auto coordsetView = axom::mir::views::make_explicit_coordset::view( + auto coordsetView = axom::mir::views::make_explicit_coordset::view( deviceMesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); using ShapeType = axom::mir::views::HexShape; using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView; - TopologyView topologyView(deviceMesh["topologies/mesh"]); + auto connView = bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]); + TopologyView topologyView(connView); conduit::Node deviceMIRMesh; if(mattype == "unibuffer") @@ -227,14 +225,14 @@ void braid3d_mat_test(const std::string &type, TEST(mir_equiz, equiz_hex_unibuffer_seq) { AXOM_ANNOTATE_SCOPE("equiz_explicit_hex_seq"); - braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); } #if defined(AXOM_USE_OPENMP) TEST(mir_equiz, equiz_hex_unibuffer_omp) { AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_omp"); - braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); } #endif @@ -242,7 +240,7 @@ TEST(mir_equiz, equiz_hex_unibuffer_omp) TEST(mir_equiz, equiz_hex_unibuffer_cuda) { AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_cuda"); - braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); } #endif @@ -250,13 +248,11 @@ TEST(mir_equiz, equiz_hex_unibuffer_cuda) TEST(mir_equiz, equiz_hex_unibuffer_hip) { AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_hip"); - braid3d_mat_test("hex", "unibuffer", "equiz_hex_unibuffer"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); } #endif -#endif //------------------------------------------------------------------------------ -#if defined(DEBUGGING_TEST_CASES) void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; @@ -264,7 +260,7 @@ void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int while(1) ; } -#endif + //------------------------------------------------------------------------------ int main(int argc, char *argv[]) @@ -278,7 +274,7 @@ int main(int argc, char *argv[]) app.add_option("--handler", handler) ->description("Install a custom error handler that loops forever."); #if defined(AXOM_USE_CALIPER) - std::string annotationMode("report"); + std::string annotationMode("none"); app.add_option("--caliper", annotationMode) ->description( "caliper annotation mode. Valid options include 'none' and 'report'. " @@ -295,12 +291,11 @@ int main(int argc, char *argv[]) #endif axom::slic::SimpleLogger logger; // create & initialize test logger, -#if defined(DEBUGGING_TEST_CASES) if(handler) { conduit::utils::set_error_handler(conduit_debug_err_handler); } -#endif + result = RUN_ALL_TESTS(); return result; } From 73f130762143b15f8d47d7d6c74154ea12a0880c Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:11:28 -0700 Subject: [PATCH 271/290] Added ph topo view example in docs --- src/axom/mir/docs/sphinx/mir_views.rst | 6 ++++++ src/axom/mir/views/dispatch_unstructured_topology.hpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst index 1079c73aed..217b4f69f4 100644 --- a/src/axom/mir/docs/sphinx/mir_views.rst +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -129,6 +129,12 @@ views are needed to supply the sizes, offsets, and shapes arrays. The final unstructured topology view is ``axom::mir::views::UnstructuredTopologyPolyhedralView`` and it provides a view interface to polyhedral meshes. +.. literalinclude:: ../../tests/views/dispatch_unstructured_topology.hpp + :start-after: _mir_views_ph_topoview_begin + :end-before: _mir_views_ph_topoview_end + :language: C++ + + Once a suitable topology view type has wrapped a Blueprint topology, it can be used in device kernels to obtain zone information. diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 066dd8cf83..62fe3a3ae1 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -74,6 +74,7 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, const std::string shape = topo["elements/shape"].as_string(); if(shape == "polyhedral") { + // _mir_views_ph_topoview_begin auto seConnView = bputils::make_array_view(topo["subelements/connectivity"]); auto seSizesView = @@ -92,6 +93,7 @@ void typed_dispatch_unstructured_polyhedral_topology(const conduit::Node &topo, connView, sizesView, offsetsView); + // _mir_views_ph_topoview_end func(shape, ugView); } } From fd268aec660802292a89ee74c143ba35fa9d130f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:11:47 -0700 Subject: [PATCH 272/290] small fix --- src/axom/mir/examples/mir_concentric_circles.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/mir_concentric_circles.cpp index 1d595dcbf4..c93d405809 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/mir_concentric_circles.cpp @@ -25,8 +25,8 @@ void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int SLIC_ERROR( axom::fmt::format("Error from Conduit: s1={}, s2={}, i1={}", s1, s2, i1)); // This is on purpose. - //while(1) - // ; + while(1) + ; } //-------------------------------------------------------------------------------- From 40660eab9418c8e6656fdc8d63fb198adfdd55fd Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:22:26 -0700 Subject: [PATCH 273/290] Move some files --- src/axom/mir/CMakeLists.txt | 28 ++++++++++++------- src/axom/mir/{ => reference}/CellClipper.cpp | 0 src/axom/mir/{ => reference}/CellClipper.hpp | 0 src/axom/mir/{ => reference}/CellData.cpp | 0 src/axom/mir/{ => reference}/CellData.hpp | 0 .../mir/{ => reference}/CellGenerator.cpp | 0 .../mir/{ => reference}/CellGenerator.hpp | 0 .../InterfaceReconstructor.cpp | 0 .../InterfaceReconstructor.hpp | 0 src/axom/mir/{ => reference}/MIRMesh.cpp | 0 src/axom/mir/{ => reference}/MIRMesh.hpp | 0 .../mir/{ => reference}/ZooClippingTables.cpp | 0 .../mir/{ => reference}/ZooClippingTables.hpp | 0 src/axom/mir/utilities.cpp | 15 ---------- 14 files changed, 18 insertions(+), 25 deletions(-) rename src/axom/mir/{ => reference}/CellClipper.cpp (100%) rename src/axom/mir/{ => reference}/CellClipper.hpp (100%) rename src/axom/mir/{ => reference}/CellData.cpp (100%) rename src/axom/mir/{ => reference}/CellData.hpp (100%) rename src/axom/mir/{ => reference}/CellGenerator.cpp (100%) rename src/axom/mir/{ => reference}/CellGenerator.hpp (100%) rename src/axom/mir/{ => reference}/InterfaceReconstructor.cpp (100%) rename src/axom/mir/{ => reference}/InterfaceReconstructor.hpp (100%) rename src/axom/mir/{ => reference}/MIRMesh.cpp (100%) rename src/axom/mir/{ => reference}/MIRMesh.hpp (100%) rename src/axom/mir/{ => reference}/ZooClippingTables.cpp (100%) rename src/axom/mir/{ => reference}/ZooClippingTables.hpp (100%) delete mode 100644 src/axom/mir/utilities.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index d344fc9010..1604f63365 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -16,20 +16,34 @@ axom_component_requires(NAME MIR #------------------------------------------------------------------------------ # Specify all headers/sources #------------------------------------------------------------------------------ -set(mir_headers + +# A serial reference implementation (remove eventually) +set(mir_reference_headers MIRMesh.hpp MIRMeshTypes.hpp ZooClippingTables.hpp InterfaceReconstructor.hpp CellData.hpp - MeshTester.hpp CellClipper.hpp MIRUtilities.hpp CellGenerator.hpp + ) +set(mir_reference_sources + MIRMesh.cpp + InterfaceReconstructor.cpp + ZooClippingTables.cpp + CellData.cpp + CellClipper.cpp + CellGenerator.cpp + ) + + +set(mir_headers + ${mir_reference_headers} + MeshTester.hpp blueprint_utilities.hpp ClipField.hpp - BlendGroupBuilder.hpp CoordsetBlender.hpp CoordsetSlicer.hpp @@ -76,13 +90,8 @@ set(mir_headers ) set(mir_sources - MIRMesh.cpp - InterfaceReconstructor.cpp - ZooClippingTables.cpp - CellData.cpp + ${mir_reference_sources} MeshTester.cpp - CellClipper.cpp - CellGenerator.cpp blueprint_utilities.cpp MIRAlgorithm.cpp @@ -94,7 +103,6 @@ set(mir_sources clipping/ClipCasesWdg.cpp views/UnstructuredTopologyMixedShapeView.cpp views/MaterialView.cpp - utilities.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/CellClipper.cpp b/src/axom/mir/reference/CellClipper.cpp similarity index 100% rename from src/axom/mir/CellClipper.cpp rename to src/axom/mir/reference/CellClipper.cpp diff --git a/src/axom/mir/CellClipper.hpp b/src/axom/mir/reference/CellClipper.hpp similarity index 100% rename from src/axom/mir/CellClipper.hpp rename to src/axom/mir/reference/CellClipper.hpp diff --git a/src/axom/mir/CellData.cpp b/src/axom/mir/reference/CellData.cpp similarity index 100% rename from src/axom/mir/CellData.cpp rename to src/axom/mir/reference/CellData.cpp diff --git a/src/axom/mir/CellData.hpp b/src/axom/mir/reference/CellData.hpp similarity index 100% rename from src/axom/mir/CellData.hpp rename to src/axom/mir/reference/CellData.hpp diff --git a/src/axom/mir/CellGenerator.cpp b/src/axom/mir/reference/CellGenerator.cpp similarity index 100% rename from src/axom/mir/CellGenerator.cpp rename to src/axom/mir/reference/CellGenerator.cpp diff --git a/src/axom/mir/CellGenerator.hpp b/src/axom/mir/reference/CellGenerator.hpp similarity index 100% rename from src/axom/mir/CellGenerator.hpp rename to src/axom/mir/reference/CellGenerator.hpp diff --git a/src/axom/mir/InterfaceReconstructor.cpp b/src/axom/mir/reference/InterfaceReconstructor.cpp similarity index 100% rename from src/axom/mir/InterfaceReconstructor.cpp rename to src/axom/mir/reference/InterfaceReconstructor.cpp diff --git a/src/axom/mir/InterfaceReconstructor.hpp b/src/axom/mir/reference/InterfaceReconstructor.hpp similarity index 100% rename from src/axom/mir/InterfaceReconstructor.hpp rename to src/axom/mir/reference/InterfaceReconstructor.hpp diff --git a/src/axom/mir/MIRMesh.cpp b/src/axom/mir/reference/MIRMesh.cpp similarity index 100% rename from src/axom/mir/MIRMesh.cpp rename to src/axom/mir/reference/MIRMesh.cpp diff --git a/src/axom/mir/MIRMesh.hpp b/src/axom/mir/reference/MIRMesh.hpp similarity index 100% rename from src/axom/mir/MIRMesh.hpp rename to src/axom/mir/reference/MIRMesh.hpp diff --git a/src/axom/mir/ZooClippingTables.cpp b/src/axom/mir/reference/ZooClippingTables.cpp similarity index 100% rename from src/axom/mir/ZooClippingTables.cpp rename to src/axom/mir/reference/ZooClippingTables.cpp diff --git a/src/axom/mir/ZooClippingTables.hpp b/src/axom/mir/reference/ZooClippingTables.hpp similarity index 100% rename from src/axom/mir/ZooClippingTables.hpp rename to src/axom/mir/reference/ZooClippingTables.hpp diff --git a/src/axom/mir/utilities.cpp b/src/axom/mir/utilities.cpp deleted file mode 100644 index 76bf002546..0000000000 --- a/src/axom/mir/utilities.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level LICENSE file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#include "axom/mir/utilities.hpp" - -namespace axom -{ -namespace mir -{ -namespace utilities -{ } // end namespace utilities -} // end namespace mir -} // end namespace axom From ac04c0de69e10b128bc685fca5862b8fe438bcbf Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:50:30 -0700 Subject: [PATCH 274/290] Moved reference files --- src/axom/mir/CMakeLists.txt | 28 ++--- src/axom/mir/MeshTester.cpp | 2 +- src/axom/mir/MeshTester.hpp | 6 +- src/axom/mir/reference/CellClipper.cpp | 6 +- src/axom/mir/reference/CellClipper.hpp | 15 ++- src/axom/mir/reference/CellData.cpp | 4 +- src/axom/mir/reference/CellData.hpp | 4 +- src/axom/mir/reference/CellGenerator.cpp | 4 +- src/axom/mir/reference/CellGenerator.hpp | 18 +-- .../mir/reference/InterfaceReconstructor.cpp | 4 +- .../mir/reference/InterfaceReconstructor.hpp | 16 +-- src/axom/mir/reference/MIRMesh.cpp | 2 +- src/axom/mir/reference/MIRMesh.hpp | 4 +- src/axom/mir/{ => reference}/MIRMeshTypes.hpp | 2 +- src/axom/mir/{ => reference}/MIRUtilities.hpp | 8 +- src/axom/mir/reference/README.md | 3 + src/axom/mir/reference/ZooClippingTables.cpp | 4 +- src/axom/mir/reference/ZooClippingTables.hpp | 2 +- src/axom/mir/tests/mir_utilities.cpp | 114 ------------------ 19 files changed, 67 insertions(+), 179 deletions(-) rename src/axom/mir/{ => reference}/MIRMeshTypes.hpp (96%) rename src/axom/mir/{ => reference}/MIRUtilities.hpp (98%) create mode 100644 src/axom/mir/reference/README.md delete mode 100644 src/axom/mir/tests/mir_utilities.cpp diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 1604f63365..68777dc712 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -19,22 +19,22 @@ axom_component_requires(NAME MIR # A serial reference implementation (remove eventually) set(mir_reference_headers - MIRMesh.hpp - MIRMeshTypes.hpp - ZooClippingTables.hpp - InterfaceReconstructor.hpp - CellData.hpp - CellClipper.hpp - MIRUtilities.hpp - CellGenerator.hpp + reference/MIRMesh.hpp + reference/MIRMeshTypes.hpp + reference/ZooClippingTables.hpp + reference/InterfaceReconstructor.hpp + reference/CellData.hpp + reference/CellClipper.hpp + reference/MIRUtilities.hpp + reference/CellGenerator.hpp ) set(mir_reference_sources - MIRMesh.cpp - InterfaceReconstructor.cpp - ZooClippingTables.cpp - CellData.cpp - CellClipper.cpp - CellGenerator.cpp + reference/MIRMesh.cpp + reference/InterfaceReconstructor.cpp + reference/ZooClippingTables.cpp + reference/CellData.cpp + reference/CellClipper.cpp + reference/CellGenerator.cpp ) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index dd2318f467..9f9cee15c7 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 80a8475780..589cf00111 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -16,8 +16,8 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions -#include "MIRMesh.hpp" -#include "MIRUtilities.hpp" +#include "axom/mir/reference/MIRMesh.hpp" +#include "axom/mir/reference/MIRUtilities.hpp" #include #include diff --git a/src/axom/mir/reference/CellClipper.cpp b/src/axom/mir/reference/CellClipper.cpp index 0fc83071ba..bf2d567e0e 100644 --- a/src/axom/mir/reference/CellClipper.cpp +++ b/src/axom/mir/reference/CellClipper.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) -#include "CellClipper.hpp" +#include "axom/mir/reference/CellClipper.hpp" namespace axom { @@ -176,4 +176,4 @@ const std::vector>& CellClipper::getClipTable( //-------------------------------------------------------------------------------- } // namespace mir -} // namespace axom \ No newline at end of file +} // namespace axom diff --git a/src/axom/mir/reference/CellClipper.hpp b/src/axom/mir/reference/CellClipper.hpp index 26b0519cf7..d4c4d5529f 100644 --- a/src/axom/mir/reference/CellClipper.hpp +++ b/src/axom/mir/reference/CellClipper.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -16,12 +16,11 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions -#include "MIRMesh.hpp" -#include "MIRUtilities.hpp" -#include "MIRMeshTypes.hpp" -#include "CellData.hpp" -#include "ZooClippingTables.hpp" -#include "MIRUtilities.hpp" +#include "axom/mir/reference/MIRMesh.hpp" +#include "axom/mir/reference/MIRUtilities.hpp" +#include "axom/mir/reference/MIRMeshTypes.hpp" +#include "axom/mir/reference/CellData.hpp" +#include "axom/mir/reference/ZooClippingTables.hpp" //-------------------------------------------------------------------------------- @@ -114,4 +113,4 @@ class CellClipper } // namespace mir } // namespace axom -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/reference/CellData.cpp b/src/axom/mir/reference/CellData.cpp index 524e7dc9c7..c092731901 100644 --- a/src/axom/mir/reference/CellData.cpp +++ b/src/axom/mir/reference/CellData.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) -#include "CellData.hpp" +#include "axom/mir/reference/CellData.hpp" namespace axom { diff --git a/src/axom/mir/reference/CellData.hpp b/src/axom/mir/reference/CellData.hpp index 168a66bde6..e1d2e25f51 100644 --- a/src/axom/mir/reference/CellData.hpp +++ b/src/axom/mir/reference/CellData.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -16,7 +16,7 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" -#include "MIRMeshTypes.hpp" +#include "axom/mir/reference/MIRMeshTypes.hpp" namespace numerics = axom::numerics; namespace slam = axom::slam; diff --git a/src/axom/mir/reference/CellGenerator.cpp b/src/axom/mir/reference/CellGenerator.cpp index a18dbdc9e6..196fb25034 100644 --- a/src/axom/mir/reference/CellGenerator.cpp +++ b/src/axom/mir/reference/CellGenerator.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) -#include "CellGenerator.hpp" +#include "axom/mir/reference/CellGenerator.hpp" namespace axom { diff --git a/src/axom/mir/reference/CellGenerator.hpp b/src/axom/mir/reference/CellGenerator.hpp index cabf4840ef..f50a7a12ff 100644 --- a/src/axom/mir/reference/CellGenerator.hpp +++ b/src/axom/mir/reference/CellGenerator.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -16,13 +16,13 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions -#include "MIRMesh.hpp" -#include "MIRUtilities.hpp" -#include "MIRMeshTypes.hpp" -#include "CellData.hpp" -#include "ZooClippingTables.hpp" -#include "MIRUtilities.hpp" -#include "CellClipper.hpp" +#include "axom/mir/reference/MIRMesh.hpp" +#include "axom/mir/reference/MIRUtilities.hpp" +#include "axom/mir/reference/MIRMeshTypes.hpp" +#include "axom/mir/reference/CellData.hpp" +#include "axom/mir/reference/ZooClippingTables.hpp" +#include "axom/mir/reference/MIRUtilities.hpp" +#include "axom/mir/reference/CellClipper.hpp" //-------------------------------------------------------------------------------- @@ -127,4 +127,4 @@ class CellGenerator } // namespace mir } // namespace axom -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/reference/InterfaceReconstructor.cpp b/src/axom/mir/reference/InterfaceReconstructor.cpp index 2774dad523..793d5a590a 100644 --- a/src/axom/mir/reference/InterfaceReconstructor.cpp +++ b/src/axom/mir/reference/InterfaceReconstructor.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) -#include "InterfaceReconstructor.hpp" +#include "axom/mir/reference/InterfaceReconstructor.hpp" namespace axom { diff --git a/src/axom/mir/reference/InterfaceReconstructor.hpp b/src/axom/mir/reference/InterfaceReconstructor.hpp index ec9af1530a..58a6026a25 100644 --- a/src/axom/mir/reference/InterfaceReconstructor.hpp +++ b/src/axom/mir/reference/InterfaceReconstructor.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -16,12 +16,12 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" -#include "MIRMesh.hpp" -#include "CellData.hpp" -#include "ZooClippingTables.hpp" -#include "MIRUtilities.hpp" -#include "CellClipper.hpp" -#include "CellGenerator.hpp" +#include "axom/mir/reference/MIRMesh.hpp" +#include "axom/mir/reference/CellData.hpp" +#include "axom/mir/reference/ZooClippingTables.hpp" +#include "axom/mir/reference/MIRUtilities.hpp" +#include "axom/mir/reference/CellClipper.hpp" +#include "axom/mir/reference/CellGenerator.hpp" #include @@ -106,4 +106,4 @@ class InterfaceReconstructor }; } // namespace mir } // namespace axom -#endif \ No newline at end of file +#endif diff --git a/src/axom/mir/reference/MIRMesh.cpp b/src/axom/mir/reference/MIRMesh.cpp index a71116466a..954513a4a9 100644 --- a/src/axom/mir/reference/MIRMesh.cpp +++ b/src/axom/mir/reference/MIRMesh.cpp @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -#include "MIRMesh.hpp" +#include "axom/mir/reference/MIRMesh.hpp" #include "axom/core.hpp" #include "axom/primal.hpp" diff --git a/src/axom/mir/reference/MIRMesh.hpp b/src/axom/mir/reference/MIRMesh.hpp index 851f8de8e5..1401e916f6 100644 --- a/src/axom/mir/reference/MIRMesh.hpp +++ b/src/axom/mir/reference/MIRMesh.hpp @@ -16,8 +16,8 @@ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions -#include "MIRMeshTypes.hpp" -#include "CellData.hpp" +#include "axom/mir/reference/MIRMeshTypes.hpp" +#include "axom/mir/reference/CellData.hpp" // C/C++ includes #include // for definition of M_PI, exp() diff --git a/src/axom/mir/MIRMeshTypes.hpp b/src/axom/mir/reference/MIRMeshTypes.hpp similarity index 96% rename from src/axom/mir/MIRMeshTypes.hpp rename to src/axom/mir/reference/MIRMeshTypes.hpp index 34259da7cf..6529aaaece 100644 --- a/src/axom/mir/MIRMeshTypes.hpp +++ b/src/axom/mir/reference/MIRMeshTypes.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) diff --git a/src/axom/mir/MIRUtilities.hpp b/src/axom/mir/reference/MIRUtilities.hpp similarity index 98% rename from src/axom/mir/MIRUtilities.hpp rename to src/axom/mir/reference/MIRUtilities.hpp index aee6db156f..0268b1de24 100644 --- a/src/axom/mir/MIRUtilities.hpp +++ b/src/axom/mir/reference/MIRUtilities.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -11,10 +11,10 @@ * */ -#ifndef __MIR_UTILITIES_H__ -#define __MIR_UTILITIES_H__ +#ifndef __MIR_UTILITIES_HPP__ +#define __MIR_UTILITIES_HPP__ -#include "ZooClippingTables.hpp" +#include "axom/mir/reference/ZooClippingTables.hpp" //-------------------------------------------------------------------------------- diff --git a/src/axom/mir/reference/README.md b/src/axom/mir/reference/README.md new file mode 100644 index 0000000000..7c5137ec5c --- /dev/null +++ b/src/axom/mir/reference/README.md @@ -0,0 +1,3 @@ +This is a deprecated serial reference implementation that was developed as +a summer project. The plan is to remove it once the newer CPU/GPU implementation +supports the iteration scheme in EquiZAlgorithm. diff --git a/src/axom/mir/reference/ZooClippingTables.cpp b/src/axom/mir/reference/ZooClippingTables.cpp index e1cd5c3c3d..08d0145413 100644 --- a/src/axom/mir/reference/ZooClippingTables.cpp +++ b/src/axom/mir/reference/ZooClippingTables.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) @@ -788,4 +788,4 @@ const std::vector> hexahedronClipTableVec = { {8, 0, 1, 2, 3, 4, 5, 6, 7, -1}}; } // namespace mir -} // namespace axom \ No newline at end of file +} // namespace axom diff --git a/src/axom/mir/reference/ZooClippingTables.hpp b/src/axom/mir/reference/ZooClippingTables.hpp index 3a555d96d8..187146f158 100644 --- a/src/axom/mir/reference/ZooClippingTables.hpp +++ b/src/axom/mir/reference/ZooClippingTables.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and // other Axom Project Developers. See the top-level COPYRIGHT file for details. // // SPDX-License-Identifier: (BSD-3-Clause) diff --git a/src/axom/mir/tests/mir_utilities.cpp b/src/axom/mir/tests/mir_utilities.cpp deleted file mode 100644 index 03d94b707b..0000000000 --- a/src/axom/mir/tests/mir_utilities.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and -// other Axom Project Developers. See the top-level COPYRIGHT file for details. -// -// SPDX-License-Identifier: (BSD-3-Clause) - -#ifndef MIR_UTILITIES_TEST_H_ -#define MIR_UTILITIES_TEST_H_ - -#include "gtest/gtest.h" - -#include "axom/slic.hpp" -#include "axom/mir.hpp" - -namespace mir = axom::mir; - -//---------------------------------------------------------------------- - -TEST(mir_shape_tests, shape_dimesionality) -{ - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Triangle), false); - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Quad), false); - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Tetrahedron), - true); - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Pyramid), true); - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Triangular_Prism), - true); - EXPECT_EQ(mir::utilities::isShapeThreeDimensional(mir::Shape::Hexahedron), - true); -} - -//---------------------------------------------------------------------- - -TEST(mir_shape_tests, check_shape_central_vertex) -{ - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangle, 6), false); - - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Quad, 8), false); - - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 9), false); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 10), true); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Tetrahedron, 11), false); - - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 12), false); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 13), true); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Pyramid, 14), false); - - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 14), - false); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 15), - true); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Triangular_Prism, 16), - false); - - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 19), false); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 20), true); - EXPECT_EQ(mir::utilities::isCenterVertex(mir::Shape::Hexahedron, 21), false); -} - -//---------------------------------------------------------------------- - -TEST(mir_shape_tests, determine_central_vertex) -{ - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Triangle), -1); - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Quad), -1); - - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Tetrahedron), 10); - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Pyramid), 13); - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Triangular_Prism), 15); - EXPECT_EQ(mir::utilities::getCenterVertex(mir::Shape::Hexahedron), 20); -} - -//---------------------------------------------------------------------- - -TEST(mir_compute_averages, float_value) -{ - std::vector values = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; - - axom::float64 average = mir::utilities::computeAverageFloat(values); - - EXPECT_DOUBLE_EQ(average, 2.5); -} - -//---------------------------------------------------------------------- - -TEST(mir_compute_averages, point_value) -{ - std::vector points = {mir::Point2::make_point(0.0, 0.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 0.0), - mir::Point2::make_point(0.0, 1.0, 0.0), - mir::Point2::make_point(0.0, 0.0, 1.0), - mir::Point2::make_point(1.0, 1.0, 0.0), - mir::Point2::make_point(1.0, 0.0, 1.0), - mir::Point2::make_point(0.0, 1.0, 1.0), - mir::Point2::make_point(1.0, 1.0, 1.0)}; - - mir::Point2 centroid = mir::utilities::computeAveragePoint(points); - - EXPECT_DOUBLE_EQ(centroid[0], 0.5); - EXPECT_DOUBLE_EQ(centroid[1], 0.5); - EXPECT_DOUBLE_EQ(centroid[2], 0.5); -} - -int main(int argc, char *argv[]) -{ - int result = 0; - ::testing::InitGoogleTest(&argc, argv); - - axom::slic::SimpleLogger logger; // create & initialize test logger, - - result = RUN_ALL_TESTS(); - return result; -} - -#endif // MIR_UTILITIES_TEST_H_ From 54037c30834ef570405decf8c07cfd7fd0478f45 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 17:52:50 -0700 Subject: [PATCH 275/290] Moved files --- src/axom/mir/{ => clipping}/BlendGroupBuilder.hpp | 0 src/axom/mir/{ => clipping}/ClipField.hpp | 0 src/axom/mir/{ => clipping}/ClipOptions.hpp | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/axom/mir/{ => clipping}/BlendGroupBuilder.hpp (100%) rename src/axom/mir/{ => clipping}/ClipField.hpp (100%) rename src/axom/mir/{ => clipping}/ClipOptions.hpp (100%) diff --git a/src/axom/mir/BlendGroupBuilder.hpp b/src/axom/mir/clipping/BlendGroupBuilder.hpp similarity index 100% rename from src/axom/mir/BlendGroupBuilder.hpp rename to src/axom/mir/clipping/BlendGroupBuilder.hpp diff --git a/src/axom/mir/ClipField.hpp b/src/axom/mir/clipping/ClipField.hpp similarity index 100% rename from src/axom/mir/ClipField.hpp rename to src/axom/mir/clipping/ClipField.hpp diff --git a/src/axom/mir/ClipOptions.hpp b/src/axom/mir/clipping/ClipOptions.hpp similarity index 100% rename from src/axom/mir/ClipOptions.hpp rename to src/axom/mir/clipping/ClipOptions.hpp From d7ef2898c13409c6207c0b23593e80425dfea4f3 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 18:05:35 -0700 Subject: [PATCH 276/290] Moved files to clipping --- src/axom/mir/CMakeLists.txt | 6 +++--- src/axom/mir/EquiZAlgorithm.hpp | 10 ++++------ src/axom/mir/clipping/ClipField.hpp | 4 ++-- src/axom/mir/tests/mir_blueprint_utilities.cpp | 1 - 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 68777dc712..3939ca36b1 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -43,8 +43,8 @@ set(mir_headers MeshTester.hpp blueprint_utilities.hpp - ClipField.hpp - BlendGroupBuilder.hpp + clipping/ClipField.hpp + clipping/BlendGroupBuilder.hpp CoordsetBlender.hpp CoordsetSlicer.hpp EquiZAlgorithm.hpp @@ -61,7 +61,7 @@ set(mir_headers SelectedZones.hpp MIROptions.hpp Options.hpp - ClipOptions.hpp + clipping/ClipOptions.hpp ZoneListBuilder.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 42bb09a43f..b1e7c4d605 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -103,6 +103,7 @@ class MaterialIntersector for(IndexType i = 0; i < n; i++) { const auto nid = nodeIdsView[i]; +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) assert(nid >= 0 && nid < m_matvfViews[0].size()); #else @@ -110,6 +111,7 @@ class MaterialIntersector axom::fmt::format("Node id {} is not in range [0, {}).", nid, m_matvfViews[0].size())); +#endif #endif // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; @@ -138,7 +140,7 @@ class MaterialIntersector if(zoneMatID != NULL_MATERIAL) backgroundIndex = matNumberToIndex(zoneMatID); // Determine the matvf view index for the current material. - +#if defined(AXOM_DEBUG) #if defined(AXOM_DEVICE_CODE) assert(id0 >= 0 && id0 < m_matvfViews[0].size()); assert(id1 >= 0 && id1 < m_matvfViews[0].size()); @@ -152,7 +154,7 @@ class MaterialIntersector id1, m_matvfViews[0].size())); #endif - +#endif // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; // clang-format off @@ -1298,10 +1300,6 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mesh[n_newCoordset.path()].set_external(n_newCoordset); mesh[n_newFields.path()].set_external(n_newFields); - // print - //printNode(mesh); - //std::cout.flush(); - // save std::stringstream ss; ss << "debug_equiz_output_iter." << iter; diff --git a/src/axom/mir/clipping/ClipField.hpp b/src/axom/mir/clipping/ClipField.hpp index a9d683b3c7..64ac7bf900 100644 --- a/src/axom/mir/clipping/ClipField.hpp +++ b/src/axom/mir/clipping/ClipField.hpp @@ -15,8 +15,8 @@ #include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/views/view_traits.hpp" -#include "axom/mir/ClipOptions.hpp" -#include "axom/mir/BlendGroupBuilder.hpp" +#include "axom/mir/clipping/ClipOptions.hpp" +#include "axom/mir/clipping/BlendGroupBuilder.hpp" #include "axom/mir/utilities.hpp" #include "axom/mir/SelectedZones.hpp" #include "axom/slic.hpp" diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index c2188ec8e5..0ce8422024 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -8,7 +8,6 @@ #include "axom/core.hpp" #include "axom/slic.hpp" #include "axom/mir.hpp" -//#include "axom/mir/blueprint_utilities.hpp" #include "axom/mir/tests/mir_testing_helpers.hpp" #include "axom/mir/tests/mir_testing_data_helpers.hpp" From 7d27fed96005d949bca87babcfac36da5301f322 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 18:38:54 -0700 Subject: [PATCH 277/290] Moved files --- data | 2 +- src/axom/mir/CMakeLists.txt | 47 +++++++++---------- src/axom/mir/EquiZAlgorithm.hpp | 12 ++--- src/axom/mir/clipping/BlendGroupBuilder.hpp | 3 +- src/axom/mir/clipping/ClipField.hpp | 17 ++++--- src/axom/mir/clipping/README.md | 38 +++++++++++++++ .../mir/{ => utilities}/CoordsetBlender.hpp | 4 +- .../mir/{ => utilities}/CoordsetSlicer.hpp | 0 src/axom/mir/{ => utilities}/ExtractZones.hpp | 8 ++-- src/axom/mir/{ => utilities}/FieldBlender.hpp | 28 +---------- src/axom/mir/{ => utilities}/FieldSlicer.hpp | 2 +- .../mir/{ => utilities}/MakeUnstructured.hpp | 4 +- src/axom/mir/{ => utilities}/MatsetSlicer.hpp | 4 +- src/axom/mir/{ => utilities}/MergeMeshes.hpp | 0 .../NodeToZoneRelationBuilder.hpp | 6 +-- .../mir/{ => utilities}/RecenterField.hpp | 2 +- .../mir/{ => utilities}/SelectedZones.hpp | 0 .../mir/{ => utilities}/ZoneListBuilder.hpp | 4 +- .../{ => utilities}/blueprint_utilities.cpp | 0 .../{ => utilities}/blueprint_utilities.hpp | 26 ++++++++++ src/axom/mir/{ => utilities}/utilities.hpp | 0 .../UnstructuredTopologyMixedShapeView.hpp | 2 +- .../views/dispatch_structured_topology.hpp | 4 +- .../views/dispatch_unstructured_topology.hpp | 2 +- 24 files changed, 128 insertions(+), 87 deletions(-) create mode 100644 src/axom/mir/clipping/README.md rename src/axom/mir/{ => utilities}/CoordsetBlender.hpp (97%) rename src/axom/mir/{ => utilities}/CoordsetSlicer.hpp (100%) rename src/axom/mir/{ => utilities}/ExtractZones.hpp (99%) rename src/axom/mir/{ => utilities}/FieldBlender.hpp (84%) rename src/axom/mir/{ => utilities}/FieldSlicer.hpp (98%) rename src/axom/mir/{ => utilities}/MakeUnstructured.hpp (98%) rename src/axom/mir/{ => utilities}/MatsetSlicer.hpp (99%) rename src/axom/mir/{ => utilities}/MergeMeshes.hpp (100%) rename src/axom/mir/{ => utilities}/NodeToZoneRelationBuilder.hpp (99%) rename src/axom/mir/{ => utilities}/RecenterField.hpp (99%) rename src/axom/mir/{ => utilities}/SelectedZones.hpp (100%) rename src/axom/mir/{ => utilities}/ZoneListBuilder.hpp (99%) rename src/axom/mir/{ => utilities}/blueprint_utilities.cpp (100%) rename src/axom/mir/{ => utilities}/blueprint_utilities.hpp (91%) rename src/axom/mir/{ => utilities}/utilities.hpp (100%) diff --git a/data b/data index 12a6d6569b..0980446664 160000 --- a/data +++ b/data @@ -1 +1 @@ -Subproject commit 12a6d6569b7a2369e6429eaae6aaa2cd473b00df +Subproject commit 09804466644e6e137ff20e999227956088335f31 diff --git a/src/axom/mir/CMakeLists.txt b/src/axom/mir/CMakeLists.txt index 3939ca36b1..565fc9a2e1 100644 --- a/src/axom/mir/CMakeLists.txt +++ b/src/axom/mir/CMakeLists.txt @@ -42,27 +42,29 @@ set(mir_headers ${mir_reference_headers} MeshTester.hpp - blueprint_utilities.hpp - clipping/ClipField.hpp clipping/BlendGroupBuilder.hpp - CoordsetBlender.hpp - CoordsetSlicer.hpp + clipping/ClipCases.h + clipping/ClipField.hpp + clipping/ClipOptions.hpp + clipping/ClipTableManager.hpp EquiZAlgorithm.hpp - ExtractZones.hpp - FieldBlender.hpp - FieldSlicer.hpp - MakeUnstructured.hpp - MatsetSlicer.hpp - MergeMeshes.hpp MIRAlgorithm.hpp - RecenterField.hpp - utilities.hpp - NodeToZoneRelationBuilder.hpp - SelectedZones.hpp MIROptions.hpp Options.hpp - clipping/ClipOptions.hpp - ZoneListBuilder.hpp + utilities/blueprint_utilities.hpp + utilities/CoordsetBlender.hpp + utilities/CoordsetSlicer.hpp + utilities/ExtractZones.hpp + utilities/FieldBlender.hpp + utilities/FieldSlicer.hpp + utilities/MakeUnstructured.hpp + utilities/MatsetSlicer.hpp + utilities/MergeMeshes.hpp + utilities/NodeToZoneRelationBuilder.hpp + utilities/RecenterField.hpp + utilities/SelectedZones.hpp + utilities/utilities.hpp + utilities/ZoneListBuilder.hpp views/dispatch_coordset.hpp views/dispatch_material.hpp views/dispatch_rectilinear_topology.hpp @@ -80,29 +82,26 @@ set(mir_headers views/StructuredIndexing.hpp views/StructuredTopologyView.hpp views/UniformCoordsetView.hpp - views/UnstructuredTopologyPolyhedralView.hpp views/UnstructuredTopologyMixedShapeView.hpp + views/UnstructuredTopologyPolyhedralView.hpp views/UnstructuredTopologySingleShapeView.hpp views/view_traits.hpp - - clipping/ClipCases.h - clipping/ClipTableManager.hpp ) set(mir_sources ${mir_reference_sources} - MeshTester.cpp - blueprint_utilities.cpp - MIRAlgorithm.cpp clipping/ClipCasesHex.cpp clipping/ClipCasesPyr.cpp clipping/ClipCasesQua.cpp clipping/ClipCasesTet.cpp clipping/ClipCasesTri.cpp clipping/ClipCasesWdg.cpp - views/UnstructuredTopologyMixedShapeView.cpp + MeshTester.cpp + MIRAlgorithm.cpp + utilities/blueprint_utilities.cpp views/MaterialView.cpp + views/UnstructuredTopologyMixedShapeView.cpp ) #------------------------------------------------------------------------------ diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index b1e7c4d605..43e9c2cb62 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -11,14 +11,14 @@ #include "axom/slic.hpp" // Include these directly for now. +#include "axom/mir/MIRAlgorithm.hpp" +#include "axom/mir/utilities/ExtractZones.hpp" +#include "axom/mir/utilities/MergeMeshes.hpp" +#include "axom/mir/utilities/NodeToZoneRelationBuilder.hpp" +#include "axom/mir/utilities/RecenterField.hpp" +#include "axom/mir/utilities/ZoneListBuilder.hpp" #include "axom/mir/views/dispatch_coordset.hpp" #include "axom/mir/views/MaterialView.hpp" -#include "axom/mir/MIRAlgorithm.hpp" -#include "axom/mir/RecenterField.hpp" -#include "axom/mir/NodeToZoneRelationBuilder.hpp" -#include "axom/mir/ZoneListBuilder.hpp" -#include "axom/mir/ExtractZones.hpp" -#include "axom/mir/MergeMeshes.hpp" #include diff --git a/src/axom/mir/clipping/BlendGroupBuilder.hpp b/src/axom/mir/clipping/BlendGroupBuilder.hpp index 42bf6b6403..2d850c2f08 100644 --- a/src/axom/mir/clipping/BlendGroupBuilder.hpp +++ b/src/axom/mir/clipping/BlendGroupBuilder.hpp @@ -6,7 +6,8 @@ #define AXOM_MIR_BLEND_GROUP_BUILDER_HPP_ #include "axom/core.hpp" -#include "axom/mir/utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" // RAJA #if defined(AXOM_USE_RAJA) diff --git a/src/axom/mir/clipping/ClipField.hpp b/src/axom/mir/clipping/ClipField.hpp index 64ac7bf900..7fb83e0e4e 100644 --- a/src/axom/mir/clipping/ClipField.hpp +++ b/src/axom/mir/clipping/ClipField.hpp @@ -6,19 +6,18 @@ #define AXOM_MIR_CLIP_FIELD_HPP_ #include "axom/core.hpp" +#include "axom/mir/clipping/BlendGroupBuilder.hpp" #include "axom/mir/clipping/ClipCases.h" +#include "axom/mir/clipping/ClipOptions.hpp" #include "axom/mir/clipping/ClipTableManager.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" +#include "axom/mir/utilities/CoordsetBlender.hpp" +#include "axom/mir/utilities/FieldBlender.hpp" +#include "axom/mir/utilities/FieldSlicer.hpp" +#include "axom/mir/utilities/SelectedZones.hpp" +#include "axom/mir/utilities/utilities.hpp" #include "axom/mir/views/Shapes.hpp" -#include "axom/mir/FieldBlender.hpp" -#include "axom/mir/CoordsetBlender.hpp" -#include "axom/mir/FieldSlicer.hpp" -#include "axom/mir/blueprint_utilities.hpp" -#include "axom/mir/utilities.hpp" #include "axom/mir/views/view_traits.hpp" -#include "axom/mir/clipping/ClipOptions.hpp" -#include "axom/mir/clipping/BlendGroupBuilder.hpp" -#include "axom/mir/utilities.hpp" -#include "axom/mir/SelectedZones.hpp" #include "axom/slic.hpp" #include diff --git a/src/axom/mir/clipping/README.md b/src/axom/mir/clipping/README.md new file mode 100644 index 0000000000..9c2fdc83c2 --- /dev/null +++ b/src/axom/mir/clipping/README.md @@ -0,0 +1,38 @@ +============== +VisIt License +============== + +The clipping tables have been borrowed from the VisIt software. The VisIt license +appears below: + +``` +BSD 3-Clause License + +Copyright (c) 2000 - 2024, Lawrence Livermore National Security, LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +``` diff --git a/src/axom/mir/CoordsetBlender.hpp b/src/axom/mir/utilities/CoordsetBlender.hpp similarity index 97% rename from src/axom/mir/CoordsetBlender.hpp rename to src/axom/mir/utilities/CoordsetBlender.hpp index 233fef8ff8..fc513f4b58 100644 --- a/src/axom/mir/CoordsetBlender.hpp +++ b/src/axom/mir/utilities/CoordsetBlender.hpp @@ -6,8 +6,8 @@ #define AXOM_MIR_COORDSET_BLENDER_HPP_ #include "axom/core.hpp" -#include "axom/mir/FieldBlender.hpp" // for BlendData -#include "axom/mir/blueprint_utilities.hpp" // for cpp2conduit +#include "axom/mir/utilities/FieldBlender.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include "axom/primal/geometry/Point.hpp" #include "axom/primal/geometry/Vector.hpp" #include "axom/slic.hpp" diff --git a/src/axom/mir/CoordsetSlicer.hpp b/src/axom/mir/utilities/CoordsetSlicer.hpp similarity index 100% rename from src/axom/mir/CoordsetSlicer.hpp rename to src/axom/mir/utilities/CoordsetSlicer.hpp diff --git a/src/axom/mir/ExtractZones.hpp b/src/axom/mir/utilities/ExtractZones.hpp similarity index 99% rename from src/axom/mir/ExtractZones.hpp rename to src/axom/mir/utilities/ExtractZones.hpp index e46045acc0..45ab139c96 100644 --- a/src/axom/mir/ExtractZones.hpp +++ b/src/axom/mir/utilities/ExtractZones.hpp @@ -6,9 +6,11 @@ #ifndef AXOM_MIR_EXTRACT_ZONES_HPP #define AXOM_MIR_EXTRACT_ZONES_HPP -#include -#include -#include // Needed to get MatsetSlicer +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/mir/utilities/CoordsetBlender.hpp" +#include "axom/mir/utilities/CoordsetSlicer.hpp" +#include "axom/mir/utilities/MatsetSlicer.hpp" // RAJA #if defined(AXOM_USE_RAJA) diff --git a/src/axom/mir/FieldBlender.hpp b/src/axom/mir/utilities/FieldBlender.hpp similarity index 84% rename from src/axom/mir/FieldBlender.hpp rename to src/axom/mir/utilities/FieldBlender.hpp index 947dcc899e..04818f1d54 100644 --- a/src/axom/mir/FieldBlender.hpp +++ b/src/axom/mir/utilities/FieldBlender.hpp @@ -7,8 +7,8 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" -#include "axom/mir/utilities.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include @@ -20,30 +20,6 @@ namespace utilities { namespace blueprint { -/*! - * \brief This class contains views of blend data. Blend data lets is make new - * nodal fields and coordsets. The field data are sampled using m_originalIdsView - * which is a compact list of the original node ids that we want to preserve - * without any blending. This stream is followed by a second stream of data - * made using the field and the blend groups. Each blend group has - * m_blendGroupSizesView[i] elements, starts at m_blendGroupStartView[i] and - * uses values from the m_blendIdsView, m_blendCoeffView members to blend the - * data values. - * - */ -struct BlendData -{ - axom::ArrayView - m_originalIdsView; // Contains indices of original node ids to be preserved. - - axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. - - axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. - axom::ArrayView - m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. - axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups - axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. -}; /*! * \brief This policy can be used with FieldBlender to select all blend groups. diff --git a/src/axom/mir/FieldSlicer.hpp b/src/axom/mir/utilities/FieldSlicer.hpp similarity index 98% rename from src/axom/mir/FieldSlicer.hpp rename to src/axom/mir/utilities/FieldSlicer.hpp index f72afdf6a6..a63c12f426 100644 --- a/src/axom/mir/FieldSlicer.hpp +++ b/src/axom/mir/utilities/FieldSlicer.hpp @@ -7,7 +7,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include diff --git a/src/axom/mir/MakeUnstructured.hpp b/src/axom/mir/utilities/MakeUnstructured.hpp similarity index 98% rename from src/axom/mir/MakeUnstructured.hpp rename to src/axom/mir/utilities/MakeUnstructured.hpp index 44bd4917a0..b618119d23 100644 --- a/src/axom/mir/MakeUnstructured.hpp +++ b/src/axom/mir/utilities/MakeUnstructured.hpp @@ -7,8 +7,8 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" -#include "axom/mir/utilities.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include "axom/mir/views/dispatch_structured_topology.hpp" #include diff --git a/src/axom/mir/MatsetSlicer.hpp b/src/axom/mir/utilities/MatsetSlicer.hpp similarity index 99% rename from src/axom/mir/MatsetSlicer.hpp rename to src/axom/mir/utilities/MatsetSlicer.hpp index cc2eb4c4ed..61e8de5e56 100644 --- a/src/axom/mir/MatsetSlicer.hpp +++ b/src/axom/mir/utilities/MatsetSlicer.hpp @@ -6,8 +6,8 @@ #ifndef AXOM_MIR_MATSET_SLICER_HPP #define AXOM_MIR_MATSET_SLICER_HPP -#include -#include +#include "axom/core.hpp" +#include "axom/mir.hpp" #include diff --git a/src/axom/mir/MergeMeshes.hpp b/src/axom/mir/utilities/MergeMeshes.hpp similarity index 100% rename from src/axom/mir/MergeMeshes.hpp rename to src/axom/mir/utilities/MergeMeshes.hpp diff --git a/src/axom/mir/NodeToZoneRelationBuilder.hpp b/src/axom/mir/utilities/NodeToZoneRelationBuilder.hpp similarity index 99% rename from src/axom/mir/NodeToZoneRelationBuilder.hpp rename to src/axom/mir/utilities/NodeToZoneRelationBuilder.hpp index 9cae68a2fd..de594e3d10 100644 --- a/src/axom/mir/NodeToZoneRelationBuilder.hpp +++ b/src/axom/mir/utilities/NodeToZoneRelationBuilder.hpp @@ -7,10 +7,10 @@ #define AXOM_MIR_NODE_TO_ZONE_RELATION_BUILDER_HPP_ #include "axom/core.hpp" -#include "axom/mir/utilities.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include "axom/mir/views/dispatch_unstructured_topology.hpp" -#include "axom/mir/MakeUnstructured.hpp" +#include "axom/mir/utilities/MakeUnstructured.hpp" #include #include diff --git a/src/axom/mir/RecenterField.hpp b/src/axom/mir/utilities/RecenterField.hpp similarity index 99% rename from src/axom/mir/RecenterField.hpp rename to src/axom/mir/utilities/RecenterField.hpp index 7ffd2a8d69..e75408c61e 100644 --- a/src/axom/mir/RecenterField.hpp +++ b/src/axom/mir/utilities/RecenterField.hpp @@ -8,7 +8,7 @@ #include "axom/core.hpp" #include "axom/mir/views/NodeArrayView.hpp" -#include "axom/mir/utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" #include #include diff --git a/src/axom/mir/SelectedZones.hpp b/src/axom/mir/utilities/SelectedZones.hpp similarity index 100% rename from src/axom/mir/SelectedZones.hpp rename to src/axom/mir/utilities/SelectedZones.hpp diff --git a/src/axom/mir/ZoneListBuilder.hpp b/src/axom/mir/utilities/ZoneListBuilder.hpp similarity index 99% rename from src/axom/mir/ZoneListBuilder.hpp rename to src/axom/mir/utilities/ZoneListBuilder.hpp index 8f877adb9e..faaa478c32 100644 --- a/src/axom/mir/ZoneListBuilder.hpp +++ b/src/axom/mir/utilities/ZoneListBuilder.hpp @@ -6,8 +6,8 @@ #ifndef AXOM_MIR_ZONELIST_BUILDER_HPP #define AXOM_MIR_ZONELIST_BUILDER_HPP -#include -#include +#include "axom/core.hpp" +#include "axom/mir.hpp" #include diff --git a/src/axom/mir/blueprint_utilities.cpp b/src/axom/mir/utilities/blueprint_utilities.cpp similarity index 100% rename from src/axom/mir/blueprint_utilities.cpp rename to src/axom/mir/utilities/blueprint_utilities.cpp diff --git a/src/axom/mir/blueprint_utilities.hpp b/src/axom/mir/utilities/blueprint_utilities.hpp similarity index 91% rename from src/axom/mir/blueprint_utilities.hpp rename to src/axom/mir/utilities/blueprint_utilities.hpp index c5129d4075..28d32171d9 100644 --- a/src/axom/mir/blueprint_utilities.hpp +++ b/src/axom/mir/utilities/blueprint_utilities.hpp @@ -158,6 +158,32 @@ inline axom::ArrayView make_array_view(const conduit::Node &n) } /// @} +//------------------------------------------------------------------------------ +/*! + * \brief This class contains views of blend data. Blend data lets is make new + * nodal fields and coordsets. The field data are sampled using m_originalIdsView + * which is a compact list of the original node ids that we want to preserve + * without any blending. This stream is followed by a second stream of data + * made using the field and the blend groups. Each blend group has + * m_blendGroupSizesView[i] elements, starts at m_blendGroupStartView[i] and + * uses values from the m_blendIdsView, m_blendCoeffView members to blend the + * data values. + * + */ +struct BlendData +{ + axom::ArrayView + m_originalIdsView; // Contains indices of original node ids to be preserved. + + axom::ArrayView m_selectedIndicesView; // Contains indices of the selected blend groups. + + axom::ArrayView m_blendGroupSizesView; // The number of ids/weights in each blend group. + axom::ArrayView + m_blendGroupStartView; // The starting offset for a blend group in the ids/weights. + axom::ArrayView m_blendIdsView; // Contains ids that make up the blend groups + axom::ArrayView m_blendCoeffView; // Contains the weights that make up the blend groups. +}; + //------------------------------------------------------------------------------ /*! * \brief This class registers a Conduit allocator that can make Conduit allocate diff --git a/src/axom/mir/utilities.hpp b/src/axom/mir/utilities/utilities.hpp similarity index 100% rename from src/axom/mir/utilities.hpp rename to src/axom/mir/utilities/utilities.hpp diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp index 0e08f1d591..29be91d0a8 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.hpp @@ -9,7 +9,7 @@ #include "axom/core.hpp" #include "axom/slic.hpp" #include "axom/mir/views/Shapes.hpp" -#include "axom/mir/utilities.hpp" +#include "axom/mir/utilities/utilities.hpp" namespace axom { diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index aae7bd17b2..e0eae13265 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -6,14 +6,14 @@ #ifndef AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ #define AXOM_MIR_DISPATCH_STRUCTURED_TOPOLOGY_HPP_ -#include +#include "axom/core.hpp" #include "axom/mir/views/StructuredTopologyView.hpp" #include "axom/mir/views/StructuredIndexing.hpp" #include "axom/mir/views/StridedStructuredIndexing.hpp" #include "axom/mir/views/dispatch_utilities.hpp" #include "axom/mir/views/dispatch_uniform_topology.hpp" #include "axom/mir/views/dispatch_rectilinear_topology.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 62fe3a3ae1..6d4748344d 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -12,7 +12,7 @@ #include "axom/mir/views/UnstructuredTopologyMixedShapeView.hpp" #include "axom/mir/views/NodeArrayView.hpp" #include "axom/mir/views/Shapes.hpp" -#include "axom/mir/blueprint_utilities.hpp" +#include "axom/mir/utilities/blueprint_utilities.hpp" #include From 9a0aa0a563885c207c4ea2e53e84185024965ca5 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 19:11:15 -0700 Subject: [PATCH 278/290] Split template instantiation up in concentric_circles example --- src/axom/mir/MeshTester.cpp | 9 ++- src/axom/mir/docs/sphinx/mir_algorithms.rst | 2 +- src/axom/mir/examples/CMakeLists.txt | 28 +------ .../concentric_circles/CMakeLists.txt | 19 +++++ .../mir_concentric_circles.cpp | 73 ++--------------- .../examples/concentric_circles/runMIR.hpp | 80 +++++++++++++++++++ .../concentric_circles/runMIR_cuda.cpp | 20 +++++ .../concentric_circles/runMIR_hip.cpp | 19 +++++ .../concentric_circles/runMIR_omp.cpp | 18 +++++ .../concentric_circles/runMIR_seq.cpp | 10 +++ .../examples/tutorial_simple/CMakeLists.txt | 18 +++++ .../mir_tutorial_simple.cpp | 0 12 files changed, 198 insertions(+), 98 deletions(-) create mode 100644 src/axom/mir/examples/concentric_circles/CMakeLists.txt rename src/axom/mir/examples/{ => concentric_circles}/mir_concentric_circles.cpp (72%) create mode 100644 src/axom/mir/examples/concentric_circles/runMIR.hpp create mode 100644 src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp create mode 100644 src/axom/mir/examples/concentric_circles/runMIR_hip.cpp create mode 100644 src/axom/mir/examples/concentric_circles/runMIR_omp.cpp create mode 100644 src/axom/mir/examples/concentric_circles/runMIR_seq.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/CMakeLists.txt rename src/axom/mir/examples/{ => tutorial_simple}/mir_tutorial_simple.cpp (100%) diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 9f9cee15c7..3ce584e330 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -62,14 +62,15 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, else { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much + const int numSamples = std::min(gridSize, 20); axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / - static_cast(gridSize - 1); + static_cast(numSamples - 1); axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / - static_cast(gridSize - 1); + static_cast(numSamples - 1); int countOverlap = 0; - for(int y = 0; y < gridSize; ++y) + for(int y = 0; y < numSamples; ++y) { - for(int x = 0; x < gridSize; ++x) + for(int x = 0; x < numSamples; ++x) { PointType samplePoint = PointType::make_point(delta_x * x + quadP1[0], delta_y * y + quadP1[1]); diff --git a/src/axom/mir/docs/sphinx/mir_algorithms.rst b/src/axom/mir/docs/sphinx/mir_algorithms.rst index f8eda62aa3..c004ef6619 100644 --- a/src/axom/mir/docs/sphinx/mir_algorithms.rst +++ b/src/axom/mir/docs/sphinx/mir_algorithms.rst @@ -100,7 +100,7 @@ the output mesh. The output mesh will exist in the same memory space as the inpu which again, must be compatible with the selected execution space. The ``axom::mir::utilities::blueprint::copy()`` function can be used to copy Conduit nodes from one memory space to another. -.. literalinclude:: ../../examples/mir_concentric_circles.cpp +.. literalinclude:: ../../examples/concentric_circles/runMIR.hpp :start-after: _equiz_mir_start :end-before: _equiz_mir_end :language: C++ diff --git a/src/axom/mir/examples/CMakeLists.txt b/src/axom/mir/examples/CMakeLists.txt index 6e20afe1ac..41c4a9c100 100644 --- a/src/axom/mir/examples/CMakeLists.txt +++ b/src/axom/mir/examples/CMakeLists.txt @@ -1,29 +1,7 @@ -# Copyright (c) 2017-2019, Lawrence Livermore National Security, LLC and +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and # other Axom Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (BSD-3-Clause) -set( mir_examples - mir_tutorial_simple.cpp - mir_concentric_circles.cpp - ) - -set( mir_example_dependencies - core - slic - mir - ) - -foreach( example ${mir_examples} ) - - get_filename_component( example_name ${example} NAME_WE ) - - axom_add_executable( - NAME ${example_name}_ex - SOURCES ${example} - OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} - DEPENDS_ON ${mir_example_dependencies} - FOLDER axom/mir/examples - ) - -endforeach() +add_subdirectory(concentric_circles) +add_subdirectory(tutorial_simple) diff --git a/src/axom/mir/examples/concentric_circles/CMakeLists.txt b/src/axom/mir/examples/concentric_circles/CMakeLists.txt new file mode 100644 index 0000000000..4e314cd976 --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/CMakeLists.txt @@ -0,0 +1,19 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) + +set( mir_example_dependencies + core + slic + mir + ) + +# Speed up compilation by separating out the execspaces into different runMIR_xxx.cpp files. +axom_add_executable( + NAME mir_concentric_circles_ex + SOURCES mir_concentric_circles.cpp runMIR_seq.cpp runMIR_omp.cpp runMIR_cuda.cpp runMIR_hip.cpp + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_example_dependencies} + FOLDER axom/mir/examples + ) diff --git a/src/axom/mir/examples/mir_concentric_circles.cpp b/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp similarity index 72% rename from src/axom/mir/examples/mir_concentric_circles.cpp rename to src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp index c93d405809..46163103d8 100644 --- a/src/axom/mir/examples/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp @@ -7,6 +7,7 @@ #include "axom/core.hpp" // for axom macros #include "axom/slic.hpp" #include "axom/mir.hpp" // for Mir classes & functions +#include "runMIR.hpp" #include #include @@ -38,68 +39,6 @@ void printNode(const conduit::Node &n) n.to_summary_string_stream(std::cout, options); } -//-------------------------------------------------------------------------------- -template -int runMIR(const conduit::Node &hostMesh, - const conduit::Node &options, - conduit::Node &hostResult) -{ - using namespace axom::mir::views; - SLIC_INFO(axom::fmt::format("Using policy {}", - axom::execution_space::name())); - - // Check materials. - constexpr int MAXMATERIALS = 20; - auto materialInfo = materials(hostMesh["matsets/mat"]); - if(materialInfo.size() >= MAXMATERIALS) - { - SLIC_WARNING( - axom::fmt::format("To use more than {} materials, recompile with " - "larger MAXMATERIALS value.", - MAXMATERIALS)); - return -4; - } - - // host->device - conduit::Node deviceMesh; - bputils::copy(deviceMesh, hostMesh); - - AXOM_ANNOTATE_BEGIN("runMIR"); - // _equiz_mir_start - // Make views (we know beforehand which types to make) - using CoordsetView = ExplicitCoordsetView; - CoordsetView coordsetView( - bputils::make_array_view(deviceMesh["coordsets/coords/values/x"]), - bputils::make_array_view(deviceMesh["coordsets/coords/values/y"])); - - using TopoView = UnstructuredTopologySingleShapeView>; - TopoView topoView(bputils::make_array_view( - deviceMesh["topologies/mesh/elements/connectivity"])); - - using MatsetView = UnibufferMaterialView; - MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat/indices"])); - - using MIR = - axom::mir::EquiZAlgorithm; - MIR m(topoView, coordsetView, matsetView); - conduit::Node deviceResult; - m.execute(deviceMesh, options, deviceResult); - // _equiz_mir_end - - AXOM_ANNOTATE_END("runMIR"); - - // device->host - bputils::copy(hostResult, deviceResult); - - return 0; -} - //-------------------------------------------------------------------------------- int runMIR(RuntimePolicy policy, int gridSize, @@ -132,13 +71,13 @@ int runMIR(RuntimePolicy policy, int retval = 0; if(policy == RuntimePolicy::seq) { - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_seq(mesh, options, resultMesh); } #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) #if defined(AXOM_USE_OPENMP) else if(policy == RuntimePolicy::omp) { - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_omp(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_CUDA) @@ -146,15 +85,13 @@ int runMIR(RuntimePolicy policy, { constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_cuda(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_HIP) else if(policy == RuntimePolicy::hip) { - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_hip(mesh, options, resultMesh); } #endif #endif diff --git a/src/axom/mir/examples/concentric_circles/runMIR.hpp b/src/axom/mir/examples/concentric_circles/runMIR.hpp new file mode 100644 index 0000000000..4a1ca83567 --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/runMIR.hpp @@ -0,0 +1,80 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_EXAMPLES_CONCENTRIC_CIRCLES_RUNMIR_HPP +#define AXOM_MIR_EXAMPLES_CONCENTRIC_CIRCLES_RUNMIR_HPP +#include "axom/config.hpp" +#include "axom/core.hpp" // for axom macros +#include "axom/slic.hpp" +#include "axom/mir.hpp" // for Mir classes & functions + +template +int runMIR(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) +{ + namespace bputils = axom::mir::utilities::blueprint; + using namespace axom::mir::views; + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); + + // Check materials. + constexpr int MAXMATERIALS = 20; + auto materialInfo = materials(hostMesh["matsets/mat"]); + if(materialInfo.size() >= MAXMATERIALS) + { + SLIC_WARNING( + axom::fmt::format("To use more than {} materials, recompile with " + "larger MAXMATERIALS value.", + MAXMATERIALS)); + return -4; + } + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + AXOM_ANNOTATE_BEGIN("runMIR"); + // _equiz_mir_start + // Make views (we know beforehand which types to make) + using CoordsetView = ExplicitCoordsetView; + CoordsetView coordsetView( + bputils::make_array_view(deviceMesh["coordsets/coords/values/x"]), + bputils::make_array_view(deviceMesh["coordsets/coords/values/y"])); + + using TopoView = UnstructuredTopologySingleShapeView>; + TopoView topoView(bputils::make_array_view( + deviceMesh["topologies/mesh/elements/connectivity"])); + + using MatsetView = UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topoView, coordsetView, matsetView); + conduit::Node deviceResult; + m.execute(deviceMesh, options, deviceResult); + // _equiz_mir_end + + AXOM_ANNOTATE_END("runMIR"); + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +// Prototypes. +int runMIR_seq(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); +int runMIR_omp(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); +int runMIR_cuda(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); +int runMIR_hip(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); + +#endif diff --git a/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp b/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp new file mode 100644 index 0000000000..2729366231 --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +int runMIR_cuda(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +{ + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + return runMIR(mesh, options, result); +} +#else +int runMIR_cuda(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif + diff --git a/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp new file mode 100644 index 0000000000..6e66e62391 --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +int runMIR_hip(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +{ + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + retval = runMIR(mesh, options, result); +} +#else +int runMIR_hip(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp b/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp new file mode 100644 index 0000000000..d3a300ef44 --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_OPENMP) +int runMIR_omp(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +{ + return runMIR(mesh, options, result); +} +#else +int runMIR_omp(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif + diff --git a/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp b/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp new file mode 100644 index 0000000000..64c57aa30d --- /dev/null +++ b/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp @@ -0,0 +1,10 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +int runMIR_seq(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +{ + return runMIR(mesh, options, result); +} diff --git a/src/axom/mir/examples/tutorial_simple/CMakeLists.txt b/src/axom/mir/examples/tutorial_simple/CMakeLists.txt new file mode 100644 index 0000000000..213eb4d9cd --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +# other Axom Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (BSD-3-Clause) + +set( mir_example_dependencies + core + slic + mir + ) + +axom_add_executable( + NAME mir_tutorial_simple_ex + SOURCES mir_tutorial_simple.cpp + OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} + DEPENDS_ON ${mir_example_dependencies} + FOLDER axom/mir/examples + ) diff --git a/src/axom/mir/examples/mir_tutorial_simple.cpp b/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp similarity index 100% rename from src/axom/mir/examples/mir_tutorial_simple.cpp rename to src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp From f637473417fd8d0f55f3a39cec29e81eca6a269f Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 19:13:27 -0700 Subject: [PATCH 279/290] make style --- src/axom/mir/EquiZAlgorithm.hpp | 14 +- src/axom/mir/MeshTester.cpp | 91 +++++--- src/axom/mir/clipping/ClipField.hpp | 209 +++++++++++------- .../examples/concentric_circles/runMIR.hpp | 16 +- .../concentric_circles/runMIR_cuda.cpp | 9 +- .../concentric_circles/runMIR_hip.cpp | 8 +- .../concentric_circles/runMIR_omp.cpp | 12 +- .../concentric_circles/runMIR_seq.cpp | 4 +- .../tutorial_simple/mir_tutorial_simple.cpp | 112 +++++----- .../mir/tests/mir_blueprint_utilities.cpp | 38 ++-- src/axom/mir/tests/mir_clipfield.cpp | 7 +- src/axom/mir/tests/mir_equiz.cpp | 10 +- src/axom/mir/tests/mir_views.cpp | 2 +- src/axom/mir/utilities/FieldBlender.hpp | 1 - src/axom/mir/utilities/MakeUnstructured.hpp | 1 - src/axom/mir/views/NodeArrayView.hpp | 78 +++---- .../UnstructuredTopologyMixedShapeView.cpp | 1 - .../views/dispatch_structured_topology.hpp | 1 - src/axom/mir/views/dispatch_utilities.hpp | 1 - 19 files changed, 360 insertions(+), 255 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index 43e9c2cb62..ef909bfc7b 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -104,14 +104,14 @@ class MaterialIntersector { const auto nid = nodeIdsView[i]; #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(nid >= 0 && nid < m_matvfViews[0].size()); -#else + #else SLIC_ASSERT_MSG(nid >= 0 && nid < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", nid, m_matvfViews[0].size())); -#endif + #endif #endif // clang-format off MaterialVF vf1 = (backgroundIndex != INVALID_INDEX) ? m_matvfViews[backgroundIndex][nid] : NULL_MATERIAL_VF; @@ -141,10 +141,10 @@ class MaterialIntersector backgroundIndex = matNumberToIndex(zoneMatID); // Determine the matvf view index for the current material. #if defined(AXOM_DEBUG) -#if defined(AXOM_DEVICE_CODE) + #if defined(AXOM_DEVICE_CODE) assert(id0 >= 0 && id0 < m_matvfViews[0].size()); assert(id1 >= 0 && id1 < m_matvfViews[0].size()); -#else + #else SLIC_ASSERT_MSG(id0 >= 0 && id0 < m_matvfViews[0].size(), axom::fmt::format("Node id {} is not in range [0, {}).", id0, @@ -153,7 +153,7 @@ class MaterialIntersector axom::fmt::format("Node id {} is not in range [0, {}).", id1, m_matvfViews[0].size())); -#endif + #endif #endif // Get the volume fractions for mat1, mat2 at the edge endpoints id0, id1. MaterialVF vf1[2], vf2[2]; @@ -480,7 +480,7 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm mmOpts["topology"] = n_topo.name(); bputils::MergeMeshes mm; mm.execute(inputs, mmOpts, n_merged); - // _mir_utilities_mergemeshes_end + // _mir_utilities_mergemeshes_end #if defined(AXOM_EQUIZ_DEBUG) std::cout << "--- clean ---\n"; diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 3ce584e330..e128a6d692 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -78,7 +78,8 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, ++countOverlap; } } - return static_cast(countOverlap) / static_cast(gridSize * gridSize); + return static_cast(countOverlap) / + static_cast(gridSize * gridSize); } } @@ -1135,10 +1136,11 @@ mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) return testMesh; } -static void addMaterial(axom::IndexType numElements, - int numMaterials, - const std::vector> &materialVolumeFractionsData, - conduit::Node &mesh) +static void addMaterial( + axom::IndexType numElements, + int numMaterials, + const std::vector>& materialVolumeFractionsData, + conduit::Node& mesh) { // Figure out the material buffers from the volume fractions. std::vector material_ids, sizes, offsets, indices; @@ -1264,7 +1266,10 @@ void addConcentricCircleMaterial(const TopoView& topoView, } }); - addMaterial(topoView.numberOfZones(), numMaterials, materialVolumeFractionsData, mesh); + addMaterial(topoView.numberOfZones(), + numMaterials, + materialVolumeFractionsData, + mesh); } void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& mesh) @@ -1590,7 +1595,7 @@ mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) //-------------------------------------------------------------------------------- -void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &mesh) +void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& mesh) { // Generate the mesh topology generateGrid3D(gridSize, mesh); @@ -1611,7 +1616,8 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &me if(numSpheres <= 1) radiusDelta = (maxRadius - minRadius); else - radiusDelta = (maxRadius - minRadius) / static_cast(numSpheres - 1); + radiusDelta = + (maxRadius - minRadius) / static_cast(numSpheres - 1); for(int i = 0; i < numSpheres; ++i) { @@ -1620,23 +1626,28 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &me } // Make views to wrap the coordset/topology. - auto coordsetView = axom::mir::views::make_explicit_coordset::view(mesh["coordsets/coords"]); + auto coordsetView = axom::mir::views::make_explicit_coordset::view( + mesh["coordsets/coords"]); using CoordsetView = decltype(coordsetView); using PointType = typename CoordsetView::PointType; - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; - TopologyView topologyView(bputils::make_array_view(mesh["topologies/mesh/elements/connectivity"])); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::HexShape>; + TopologyView topologyView(bputils::make_array_view( + mesh["topologies/mesh/elements/connectivity"])); // Initialize all material volume fractions to 0 - std::vector> materialVolumeFractionsData(numMaterials); + std::vector> materialVolumeFractionsData( + numMaterials); for(int i = 0; i < numMaterials; ++i) { materialVolumeFractionsData[i].resize(topologyView.numberOfZones(), 0.); } // all spheres are centered around the same point - const auto sphereCenter = PointType::make_point(static_cast(gridSize / 2.0), - static_cast(gridSize / 2.0), - static_cast(gridSize / 2.0)); + const auto sphereCenter = + PointType::make_point(static_cast(gridSize / 2.0), + static_cast(gridSize / 2.0), + static_cast(gridSize / 2.0)); // Use the uniform sampling method to generate volume fractions for each material for(int eID = 0; eID < topologyView.numberOfZones(); ++eID) @@ -1649,7 +1660,10 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &me // Run the uniform sampling to determine how much of the current cell is composed of each material int materialCount[numMaterials]; - for(int i = 0; i < numMaterials; ++i) { materialCount[i] = 0; } + for(int i = 0; i < numMaterials; ++i) + { + materialCount[i] = 0; + } float delta_x = axom::utilities::abs(v1[0] - v0[0]) / static_cast(gridSize - 1); @@ -1664,9 +1678,10 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &me { for(int x = 0; x < gridSize; ++x) { - const auto samplePoint = PointType::make_point(static_cast(delta_x * x + v0[0]), - static_cast(delta_y * y + v0[1]), - static_cast(delta_z * z + v0[2])); + const auto samplePoint = + PointType::make_point(static_cast(delta_x * x + v0[0]), + static_cast(delta_y * y + v0[1]), + static_cast(delta_z * z + v0[2])); bool isPointSampled = false; for(int cID = 0; cID < numSpheres && !isPointSampled; ++cID) @@ -1688,14 +1703,18 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node &me } // Assign the element volume fractions based on the count of the samples in each circle - const axom::float64 nzones_inv = 1. / static_cast(gridSize * gridSize * gridSize); + const axom::float64 nzones_inv = + 1. / static_cast(gridSize * gridSize * gridSize); for(int matID = 0; matID < numMaterials; ++matID) { materialVolumeFractionsData[matID][eID] = materialCount[matID] * nzones_inv; } } - addMaterial(topologyView.numberOfZones(), numMaterials, materialVolumeFractionsData, mesh); + addMaterial(topologyView.numberOfZones(), + numMaterials, + materialVolumeFractionsData, + mesh); } //-------------------------------------------------------------------------------- @@ -1803,26 +1822,26 @@ mir::CellData MeshTester::generateGrid3D(int gridSize) return data; } -void MeshTester::generateGrid3D(int gridSize, conduit::Node &mesh) +void MeshTester::generateGrid3D(int gridSize, conduit::Node& mesh) { const int nzones = gridSize * gridSize * gridSize; const int dims[3] = {gridSize + 1, gridSize + 1, gridSize + 1}; const int nnodes = dims[0] * dims[1] * dims[2]; - conduit::Node &coordset = mesh["coordsets/coords"]; - conduit::Node &topo = mesh["topologies/mesh"]; + conduit::Node& coordset = mesh["coordsets/coords"]; + conduit::Node& topo = mesh["topologies/mesh"]; // Make coordset coordset["type"] = "explicit"; - conduit::Node &n_x = coordset["values/x"]; - conduit::Node &n_y = coordset["values/y"]; - conduit::Node &n_z = coordset["values/z"]; + conduit::Node& n_x = coordset["values/x"]; + conduit::Node& n_y = coordset["values/y"]; + conduit::Node& n_z = coordset["values/z"]; n_x.set(conduit::DataType::float32(nnodes)); n_y.set(conduit::DataType::float32(nnodes)); n_z.set(conduit::DataType::float32(nnodes)); - auto *x = static_cast(n_x.data_ptr()); - auto *y = static_cast(n_y.data_ptr()); - auto *z = static_cast(n_z.data_ptr()); + auto* x = static_cast(n_x.data_ptr()); + auto* y = static_cast(n_y.data_ptr()); + auto* z = static_cast(n_z.data_ptr()); for(int k = 0; k < dims[2]; k++) { @@ -1841,15 +1860,15 @@ void MeshTester::generateGrid3D(int gridSize, conduit::Node &mesh) topo["type"] = "unstructured"; topo["coordset"] = "coords"; topo["elements/shape"] = "hex"; - conduit::Node &conn = topo["elements/connectivity"]; - conduit::Node &sizes = topo["elements/sizes"]; - conduit::Node &offsets = topo["elements/offsets"]; + conduit::Node& conn = topo["elements/connectivity"]; + conduit::Node& sizes = topo["elements/sizes"]; + conduit::Node& offsets = topo["elements/offsets"]; conn.set(conduit::DataType::int32(8 * nzones)); sizes.set(conduit::DataType::int32(nzones)); offsets.set(conduit::DataType::int32(nzones)); - auto *conn_ptr = static_cast(conn.data_ptr()); - auto *sizes_ptr = static_cast(sizes.data_ptr()); - auto *offsets_ptr = static_cast(offsets.data_ptr()); + auto* conn_ptr = static_cast(conn.data_ptr()); + auto* sizes_ptr = static_cast(sizes.data_ptr()); + auto* offsets_ptr = static_cast(offsets.data_ptr()); for(int k = 0; k < gridSize; k++) { diff --git a/src/axom/mir/clipping/ClipField.hpp b/src/axom/mir/clipping/ClipField.hpp index 7fb83e0e4e..0c0445ed03 100644 --- a/src/axom/mir/clipping/ClipField.hpp +++ b/src/axom/mir/clipping/ClipField.hpp @@ -268,12 +268,12 @@ struct DegenerateHandler */ AXOM_HOST_DEVICE static bool setSize(axom::ArrayView AXOM_UNUSED_PARAM(fragmentsView), - axom::ArrayView AXOM_UNUSED_PARAM(connView), - axom::ArrayView sizesView, - axom::IndexType AXOM_UNUSED_PARAM(szIndex), - int nIdsThisFragment, - int sizeIndex, - int &AXOM_UNUSED_PARAM(outputIndex)) + axom::ArrayView AXOM_UNUSED_PARAM(connView), + axom::ArrayView sizesView, + axom::IndexType AXOM_UNUSED_PARAM(szIndex), + int nIdsThisFragment, + int sizeIndex, + int &AXOM_UNUSED_PARAM(outputIndex)) { sizesView[sizeIndex] = nIdsThisFragment; return false; @@ -293,17 +293,17 @@ struct DegenerateHandler * \param[inout] shapesView The view that wraps shapes (can change on output). * \param[inout] colorView The view that wraps colors (can change on output). */ - static void filterZeroSizes(FragmentData &AXOM_UNUSED_PARAM(fragmentData), - conduit::Node &AXOM_UNUSED_PARAM(n_sizes), - conduit::Node &AXOM_UNUSED_PARAM(n_offsets), - conduit::Node &AXOM_UNUSED_PARAM(n_shapes), - conduit::Node &AXOM_UNUSED_PARAM(n_color), - axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), - axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), - axom::ArrayView &AXOM_UNUSED_PARAM(shapesView), - axom::ArrayView &AXOM_UNUSED_PARAM(colorView)) - { - } + static void filterZeroSizes( + FragmentData &AXOM_UNUSED_PARAM(fragmentData), + conduit::Node &AXOM_UNUSED_PARAM(n_sizes), + conduit::Node &AXOM_UNUSED_PARAM(n_offsets), + conduit::Node &AXOM_UNUSED_PARAM(n_shapes), + conduit::Node &AXOM_UNUSED_PARAM(n_color), + axom::ArrayView &AXOM_UNUSED_PARAM(sizesView), + axom::ArrayView &AXOM_UNUSED_PARAM(offsetsView), + axom::ArrayView &AXOM_UNUSED_PARAM(shapesView), + axom::ArrayView &AXOM_UNUSED_PARAM(colorView)) + { } /*! * \brief Turns degenerate quads into triangles in-place. @@ -314,11 +314,12 @@ struct DegenerateHandler * \param offsetsView A view that contains the offsets. * \param shapesView A view that contains the shapes. */ - static BitSet quadtri(BitSet shapesUsed, - axom::ArrayView AXOM_UNUSED_PARAM(connView), - axom::ArrayView AXOM_UNUSED_PARAM(sizesView), - axom::ArrayView AXOM_UNUSED_PARAM(offsetsView), - axom::ArrayView AXOM_UNUSED_PARAM(shapesView)) + static BitSet quadtri( + BitSet shapesUsed, + axom::ArrayView AXOM_UNUSED_PARAM(connView), + axom::ArrayView AXOM_UNUSED_PARAM(sizesView), + axom::ArrayView AXOM_UNUSED_PARAM(offsetsView), + axom::ArrayView AXOM_UNUSED_PARAM(shapesView)) { return shapesUsed; } @@ -347,18 +348,19 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> */ AXOM_HOST_DEVICE static bool setSize(axom::ArrayView fragmentsView, - axom::ArrayView connView, - axom::ArrayView sizesView, - axom::IndexType szIndex, - int nIdsThisFragment, - int sizeIndex, - int &outputIndex) + axom::ArrayView connView, + axom::ArrayView sizesView, + axom::IndexType szIndex, + int nIdsThisFragment, + int sizeIndex, + int &outputIndex) { const int connStart = outputIndex - nIdsThisFragment; // Check for degenerate - const int nUniqueIds = internal::unique_count(connView.data() + connStart, - nIdsThisFragment); + const int nUniqueIds = + internal::unique_count(connView.data() + connStart, + nIdsThisFragment); const bool thisFragmentDegenerate = nUniqueIds < (nIdsThisFragment - 1); // Rewind the outputIndex so we don't emit it in the connectivity. @@ -410,13 +412,10 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> // Use sizesView to make a mask that has 1's where size > 0. axom::IndexType nz = fragmentData.m_finalNumZones; - axom::Array mask(nz, - nz, - axom::execution_space::allocatorID()); - axom::Array maskOffsets( - nz, - nz, - axom::execution_space::allocatorID()); + axom::Array mask(nz, nz, axom::execution_space::allocatorID()); + axom::Array maskOffsets(nz, + nz, + axom::execution_space::allocatorID()); auto maskView = mask.view(); auto maskOffsetsView = maskOffsets.view(); RAJA::ReduceSum mask_reduce(0); @@ -435,19 +434,28 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> // Filter sizes, shapes, color using the mask sizesView = - filter>(n_sizes, sizesView, filteredZoneCount, maskView, maskOffsetsView); - offsetsView = filter>(n_offsets, - offsetsView, - filteredZoneCount, - maskView, - maskOffsetsView); + filter>(n_sizes, + sizesView, + filteredZoneCount, + maskView, + maskOffsetsView); + offsetsView = + filter>(n_offsets, + offsetsView, + filteredZoneCount, + maskView, + maskOffsetsView); shapesView = - filter>(n_shapes, shapesView, filteredZoneCount, maskView, maskOffsetsView); + filter>(n_shapes, + shapesView, + filteredZoneCount, + maskView, + maskOffsetsView); colorView = filter>(n_color, - colorView, - filteredZoneCount, - maskView, - maskOffsetsView); + colorView, + filteredZoneCount, + maskView, + maskOffsetsView); // Record the filtered size. fragmentData.m_finalNumZones = filteredZoneCount; @@ -486,7 +494,10 @@ struct DegenerateHandler<2, ExecSpace, ConnectivityType> int next = (current + 1) % 4; ConnectivityType curNode = connView[offset + current]; ConnectivityType nextNode = connView[offset + next]; - if(curNode != nextNode) { pts[npts++] = curNode; } + if(curNode != nextNode) + { + pts[npts++] = curNode; + } } if(npts == 3) @@ -535,10 +546,11 @@ struct StridedStructuredFields * \param n_field The field being sliced. * \param n_newField The node that will contain the new field. */ - static bool sliceElementField(const TopologyView &AXOM_UNUSED_PARAM(topologyView), - const axom::mir::utilities::blueprint::SliceData &AXOM_UNUSED_PARAM(slice), - const conduit::Node &AXOM_UNUSED_PARAM(n_field), - conduit::Node &AXOM_UNUSED_PARAM(n_newField)) + static bool sliceElementField( + const TopologyView &AXOM_UNUSED_PARAM(topologyView), + const axom::mir::utilities::blueprint::SliceData &AXOM_UNUSED_PARAM(slice), + const conduit::Node &AXOM_UNUSED_PARAM(n_field), + conduit::Node &AXOM_UNUSED_PARAM(n_newField)) { return false; } @@ -551,10 +563,11 @@ struct StridedStructuredFields * \param n_field The field being sliced. * \param n_newField The node that will contain the new field. */ - static bool blendVertexField(const TopologyView &AXOM_UNUSED_PARAM(topologyView), - const axom::mir::utilities::blueprint::BlendData &AXOM_UNUSED_PARAM(blend), - const conduit::Node &AXOM_UNUSED_PARAM(n_field), - conduit::Node &AXOM_UNUSED_PARAM(n_newField)) + static bool blendVertexField( + const TopologyView &AXOM_UNUSED_PARAM(topologyView), + const axom::mir::utilities::blueprint::BlendData &AXOM_UNUSED_PARAM(blend), + const conduit::Node &AXOM_UNUSED_PARAM(n_field), + conduit::Node &AXOM_UNUSED_PARAM(n_newField)) { return false; } @@ -581,10 +594,11 @@ struct StridedStructuredFields * \param n_field The field being sliced. * \param n_newField The node that will contain the new field. */ - static bool sliceElementField(const TopologyView &topologyView, - const axom::mir::utilities::blueprint::SliceData &slice, - const conduit::Node &n_field, - conduit::Node &n_newField) + static bool sliceElementField( + const TopologyView &topologyView, + const axom::mir::utilities::blueprint::SliceData &slice, + const conduit::Node &n_field, + conduit::Node &n_newField) { bool handled = false; if(n_field.has_path("offsets") && n_field.has_path("strides")) @@ -632,10 +646,8 @@ struct StridedStructuredFields // If the topo and field offsets/strides are different then we need to go through // SSVertexFieldIndexing. Otherwise, we can let the normal case further below // handle the field. - if(indexing.m_topoIndexing.m_offsets != - indexing.m_fieldIndexing.m_offsets || - indexing.m_topoIndexing.m_strides != - indexing.m_fieldIndexing.m_strides) + if(indexing.m_topoIndexing.m_offsets != indexing.m_fieldIndexing.m_offsets || + indexing.m_topoIndexing.m_strides != indexing.m_fieldIndexing.m_strides) { // Blend the field. axom::mir::utilities::blueprint::FieldBlender< @@ -1449,9 +1461,9 @@ class ClipField << "------------------------ computeSizes ------------------------" << std::endl; internal::printHost("fragmentData.m_fragmentsView", - fragmentData.m_fragmentsView); + fragmentData.m_fragmentsView); internal::printHost("fragmentData.m_fragmentsSizeView", - fragmentData.m_fragmentsSizeView); + fragmentData.m_fragmentsSizeView); internal::printHost("blendGroupsView", blendGroupsView); internal::printHost("blendGroupsLenView", blendGroupsLenView); internal::printHost("zoneData.m_pointsUsedView", zoneData.m_pointsUsedView); @@ -1512,9 +1524,9 @@ class ClipField "------------------------" << std::endl; internal::printHost("fragmentData.m_fragmentOffsetsView", - fragmentData.m_fragmentOffsetsView); + fragmentData.m_fragmentOffsetsView); internal::printHost("fragmentData.m_fragmentSizeOffsetsView", - fragmentData.m_fragmentSizeOffsetsView); + fragmentData.m_fragmentSizeOffsetsView); std::cout << "-------------------------------------------------------------" "-----------" << std::endl; @@ -1578,7 +1590,7 @@ class ClipField internal::printHost("nodeData.m_nodeUsedView", nodeData.m_nodeUsedView); internal::printHost("nodeData.m_originalIdsView", nodeData.m_originalIdsView); internal::printHost("nodeData.m_oldNodeToNewNodeView", - nodeData.m_oldNodeToNewNodeView); + nodeData.m_oldNodeToNewNodeView); std::cout << "-------------------------------------------------------------" "-----------" << std::endl; @@ -1936,9 +1948,16 @@ class ClipField const auto nIdsThisFragment = fragmentSize - 2; #if defined(AXOM_CLIP_FILTER_DEGENERATES) // Set the output zone size, checking to see whether it is degenerate. - degenerates |= internal::DegenerateHandler::setSize( - fragmentData.m_fragmentsView, connView, sizesView, szIndex, nIdsThisFragment, - sizeIndex, outputIndex); + degenerates |= internal::DegenerateHandler< + TopologyView::dimension(), + ExecSpace, + ConnectivityType>::setSize(fragmentData.m_fragmentsView, + connView, + sizesView, + szIndex, + nIdsThisFragment, + sizeIndex, + outputIndex); #else sizesView[sizeIndex] = nIdsThisFragment; #endif @@ -1980,9 +1999,17 @@ class ClipField // Filter out shapes that were marked as zero-size, adjusting connectivity and other arrays. if(degenerates_reduce.get()) { - internal::DegenerateHandler::filterZeroSizes(fragmentData, - n_sizes, n_offsets, n_shapes, n_color_values, - sizesView, offsetsView, shapesView, colorView); + internal::DegenerateHandler::filterZeroSizes(fragmentData, + n_sizes, + n_offsets, + n_shapes, + n_color_values, + sizesView, + offsetsView, + shapesView, + colorView); } #endif @@ -2015,7 +2042,14 @@ class ClipField #if defined(AXOM_CLIP_FILTER_DEGENERATES) // Handle some quad->tri degeneracies, depending on dimension. - shapesUsed = internal::DegenerateHandler::quadtri(shapesUsed, connView, sizesView, offsetsView, shapesView); + shapesUsed = + internal::DegenerateHandler::quadtri(shapesUsed, + connView, + sizesView, + offsetsView, + shapesView); #endif // Add shape information to the connectivity. @@ -2097,7 +2131,8 @@ class ClipField conduit::Node &n_out_fields) const { AXOM_ANNOTATE_SCOPE("makeFields"); - constexpr bool ss = axom::mir::views::view_traits::supports_strided_structured(); + constexpr bool ss = + axom::mir::views::view_traits::supports_strided_structured(); for(auto it = fieldMap.begin(); it != fieldMap.end(); it++) { @@ -2105,8 +2140,13 @@ class ClipField const std::string association = n_field["association"].as_string(); if(association == "element") { - // Conditionally support strided-structured. - bool handled = internal::StridedStructuredFields::sliceElementField(m_topologyView, slice, n_field, n_out_fields[it->second]); + // Conditionally support strided-structured. + bool handled = + internal::StridedStructuredFields::sliceElementField( + m_topologyView, + slice, + n_field, + n_out_fields[it->second]); if(!handled) { @@ -2118,8 +2158,13 @@ class ClipField } else if(association == "vertex") { - // Conditionally support strided-structured. - bool handled = internal::StridedStructuredFields::blendVertexField(m_topologyView, blend, n_field, n_out_fields[it->second]); + // Conditionally support strided-structured. + bool handled = + internal::StridedStructuredFields::blendVertexField( + m_topologyView, + blend, + n_field, + n_out_fields[it->second]); if(!handled) { diff --git a/src/axom/mir/examples/concentric_circles/runMIR.hpp b/src/axom/mir/examples/concentric_circles/runMIR.hpp index 4a1ca83567..7526a86bfe 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR.hpp +++ b/src/axom/mir/examples/concentric_circles/runMIR.hpp @@ -72,9 +72,17 @@ int runMIR(const conduit::Node &hostMesh, } // Prototypes. -int runMIR_seq(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); -int runMIR_omp(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); -int runMIR_cuda(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); -int runMIR_hip(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result); +int runMIR_seq(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_omp(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_cuda(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_hip(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); #endif diff --git a/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp b/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp index 2729366231..3db6cc827f 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp +++ b/src/axom/mir/examples/concentric_circles/runMIR_cuda.cpp @@ -5,16 +5,19 @@ #include "runMIR.hpp" #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) -int runMIR_cuda(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +int runMIR_cuda(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; return runMIR(mesh, options, result); } #else -int runMIR_cuda(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +int runMIR_cuda(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) { return 0; } #endif - diff --git a/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp index 6e66e62391..f8c25c52dc 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp +++ b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp @@ -5,14 +5,18 @@ #include "runMIR.hpp" #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) -int runMIR_hip(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +int runMIR_hip(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { constexpr int HIP_BLOCK_SIZE = 64; using hip_exec = axom::HIP_EXEC; retval = runMIR(mesh, options, result); } #else -int runMIR_hip(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +int runMIR_hip(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) { return 0; } diff --git a/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp b/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp index d3a300ef44..5b96df37bb 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp +++ b/src/axom/mir/examples/concentric_circles/runMIR_omp.cpp @@ -4,15 +4,19 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "runMIR.hpp" -#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_OPENMP) -int runMIR_omp(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && \ + defined(AXOM_USE_OPENMP) +int runMIR_omp(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { return runMIR(mesh, options, result); } #else -int runMIR_omp(const conduit::Node &AXOM_UNUSED_PARAM(mesh), const conduit::Node &AXOM_UNUSED_PARAM(options), conduit::Node &AXOM_UNUSED_PARAM(result)) +int runMIR_omp(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) { return 0; } #endif - diff --git a/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp b/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp index 64c57aa30d..eebdb27f17 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp +++ b/src/axom/mir/examples/concentric_circles/runMIR_seq.cpp @@ -4,7 +4,9 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "runMIR.hpp" -int runMIR_seq(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) +int runMIR_seq(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { return runMIR(mesh, options, result); } diff --git a/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp b/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp index 28193007d6..7d168f1fbd 100644 --- a/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp @@ -23,18 +23,18 @@ using RuntimePolicy = axom::runtime_policy::Policy; /// Contain program options. struct Input { - int m_test_case{1}; // valid values 1,2,3,4,5 - bool m_should_iterate{false}; - int m_iter_count{0}; - double m_iter_percent{0.}; - bool m_verbose{false}; - std::string m_output_dir{}; + int m_test_case {1}; // valid values 1,2,3,4,5 + bool m_should_iterate {false}; + int m_iter_count {0}; + double m_iter_percent {0.}; + bool m_verbose {false}; + std::string m_output_dir {}; RuntimePolicy m_policy {RuntimePolicy::seq}; - std::string m_annotationMode{"report"}; - axom::CLI::App m_app{}; + std::string m_annotationMode {"report"}; + axom::CLI::App m_app {}; /// Parse command line. - int parse(int argc, char** argv) + int parse(int argc, char **argv) { m_app.add_option("--test-case", m_test_case) ->description("Select the test case."); @@ -48,14 +48,12 @@ struct Input m_app.add_option("--iter-percent", m_iter_percent) ->description("The percent error for iterative MIR"); - m_app.add_flag("--verbose", m_verbose) - ->description("Verbose output"); + m_app.add_flag("--verbose", m_verbose)->description("Verbose output"); #if defined(AXOM_USE_CALIPER) m_app.add_option("--caliper", m_annotationMode) ->description( - "caliper annotation mode. Valid options include 'none' and 'report'. " - ) + "caliper annotation mode. Valid options include 'none' and 'report'. ") ->capture_default_str() ->check(axom::utilities::ValidCaliperMode); #endif @@ -84,7 +82,7 @@ struct Input { m_app.parse(argc, argv); } - catch (const axom::CLI::ParseError &e) + catch(const axom::CLI::ParseError &e) { return m_app.exit(e); } @@ -158,7 +156,9 @@ void printNode(const conduit::Node &n) * \param hostResult A conduit node that will contain the MIR results. */ template -int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit::Node &hostResult) +int runMIR(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) { std::string shape = hostMesh["topologies/mesh/elements/shape"].as_string(); SLIC_INFO(axom::fmt::format("Using policy {}", @@ -171,53 +171,63 @@ int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit: conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; conduit::Node &n_topo = deviceMesh["topologies/mesh"]; conduit::Node &n_matset = deviceMesh["matsets/mat"]; - auto connView = bputils::make_array_view(n_topo["elements/connectivity"]); + auto connView = + bputils::make_array_view(n_topo["elements/connectivity"]); // Make matset view. (There's often 1 more material so add 1) constexpr int MAXMATERIALS = 12; - using MatsetView = axom::mir::views::UnibufferMaterialView; + using MatsetView = + axom::mir::views::UnibufferMaterialView; MatsetView matsetView; - matsetView.set( - bputils::make_array_view(n_matset["material_ids"]), - bputils::make_array_view(n_matset["volume_fractions"]), - bputils::make_array_view(n_matset["sizes"]), - bputils::make_array_view(n_matset["offsets"]), - bputils::make_array_view(n_matset["indices"])); + matsetView.set(bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); // Coord/Topo views differ. conduit::Node deviceResult; if(shape == "tri") { - auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::TriShape>; TopologyView topologyView(connView); - using MIR = axom::mir::EquiZAlgorithm; + using MIR = + axom::mir::EquiZAlgorithm; MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); + m.execute(deviceMesh, options, deviceResult); } else if(shape == "quad") { - auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; TopologyView topologyView(connView); - using MIR = axom::mir::EquiZAlgorithm; + using MIR = + axom::mir::EquiZAlgorithm; MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); + m.execute(deviceMesh, options, deviceResult); } else if(shape == "hex") { - auto coordsetView = axom::mir::views::make_explicit_coordset::view(n_coordset); + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView>; + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::HexShape>; TopologyView topologyView(connView); - using MIR = axom::mir::EquiZAlgorithm; + using MIR = + axom::mir::EquiZAlgorithm; MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); + m.execute(deviceMesh, options, deviceResult); } // device->host @@ -230,7 +240,7 @@ int runMIR(const conduit::Node &hostMesh, const conduit::Node &options, conduit: /*! * \brief Tutorial main showing how to initialize test cases and perform mir. */ -int main(int argc, char** argv) +int main(int argc, char **argv) { axom::slic::SimpleLogger logger; // create & initialize test logger axom::slic::setLoggingMsgLevel(axom::slic::message::Info); @@ -266,19 +276,19 @@ int main(int argc, char** argv) tester.initTestCaseFour(mesh); break; case 5: - { - constexpr int GRIDSIZE = 25; - constexpr int MAXMATERIALS = 12; - tester.initTestCaseFive(GRIDSIZE, MAXMATERIALS, mesh); - } - break; + { + constexpr int GRIDSIZE = 25; + constexpr int MAXMATERIALS = 12; + tester.initTestCaseFive(GRIDSIZE, MAXMATERIALS, mesh); + } + break; case 6: - { - constexpr int GRIDSIZE = 15; - constexpr int MAXMATERIALS = 3; - tester.initTestCaseSix(GRIDSIZE, MAXMATERIALS, mesh); - } - break; + { + constexpr int GRIDSIZE = 15; + constexpr int MAXMATERIALS = 3; + tester.initTestCaseSix(GRIDSIZE, MAXMATERIALS, mesh); + } + break; } timer.stop(); SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); @@ -288,7 +298,8 @@ int main(int argc, char** argv) if(params.m_output_dir.empty()) filepath = filename; else - filepath = axom::utilities::filesystem::joinPath(params.m_output_dir, filename); + filepath = + axom::utilities::filesystem::joinPath(params.m_output_dir, filename); conduit::relay::io::blueprint::save_mesh(mesh, filepath, "hdf5"); if(params.m_verbose) @@ -352,7 +363,8 @@ int main(int argc, char** argv) if(params.m_output_dir.empty()) filepath = filename; else - filepath = axom::utilities::filesystem::joinPath(params.m_output_dir, filename); + filepath = + axom::utilities::filesystem::joinPath(params.m_output_dir, filename); conduit::relay::io::blueprint::save_mesh(resultMesh, filepath, "hdf5"); } diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 0ce8422024..6cf5a262a1 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -1550,8 +1550,11 @@ struct test_fieldslicer bputils::copy(deviceMesh, hostMesh); // _mir_utilities_fieldslicer_begin - std::vector indices{0,1,2,7,8,9}; - axom::Array sliceIndices(indices.size(), indices.size(), axom::execution_space::allocatorID()); + std::vector indices {0, 1, 2, 7, 8, 9}; + axom::Array sliceIndices( + indices.size(), + indices.size(), + axom::execution_space::allocatorID()); axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); bputils::SliceData slice; @@ -1567,26 +1570,36 @@ struct test_fieldslicer conduit::Node hostSlicedMesh; bputils::copy(hostSlicedMesh, slicedMesh); - std::vector resultX{0., 1., 2., 7., 8., 9.}; - std::vector resultY{0., 10., 20., 70., 80., 90.}; + std::vector resultX {0., 1., 2., 7., 8., 9.}; + std::vector resultY {0., 10., 20., 70., 80., 90.}; EXPECT_EQ(hostSlicedMesh["fields/scalar/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedMesh["fields/scalar/association"].as_string(), "element"); - EXPECT_EQ(hostSlicedMesh["fields/scalar/values"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedMesh["fields/scalar/association"].as_string(), + "element"); + EXPECT_EQ(hostSlicedMesh["fields/scalar/values"].dtype().number_of_elements(), + indices.size()); for(size_t i = 0; i < indices.size(); i++) { - const auto acc = hostSlicedMesh["fields/scalar/values"].as_double_accessor(); + const auto acc = + hostSlicedMesh["fields/scalar/values"].as_double_accessor(); EXPECT_EQ(acc[i], resultX[i]); } EXPECT_EQ(hostSlicedMesh["fields/vector/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedMesh["fields/vector/association"].as_string(), "element"); - EXPECT_EQ(hostSlicedMesh["fields/vector/values/x"].dtype().number_of_elements(), indices.size()); - EXPECT_EQ(hostSlicedMesh["fields/vector/values/y"].dtype().number_of_elements(), indices.size()); + EXPECT_EQ(hostSlicedMesh["fields/vector/association"].as_string(), + "element"); + EXPECT_EQ( + hostSlicedMesh["fields/vector/values/x"].dtype().number_of_elements(), + indices.size()); + EXPECT_EQ( + hostSlicedMesh["fields/vector/values/y"].dtype().number_of_elements(), + indices.size()); for(size_t i = 0; i < indices.size(); i++) { - const auto x = hostSlicedMesh["fields/vector/values/x"].as_double_accessor(); - const auto y = hostSlicedMesh["fields/vector/values/y"].as_double_accessor(); + const auto x = + hostSlicedMesh["fields/vector/values/x"].as_double_accessor(); + const auto y = + hostSlicedMesh["fields/vector/values/y"].as_double_accessor(); EXPECT_EQ(x[i], resultX[i]); EXPECT_EQ(y[i], resultY[i]); } @@ -1634,7 +1647,6 @@ TEST(mir_blueprint_utilities, fieldslicer_hip) } #endif - //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index e009a86969..0cf0689c71 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -610,11 +610,8 @@ void braid2d_clip_test(const std::string &type, const std::string &name) // Make the shape map. int allocatorID = axom::execution_space::allocatorID(); axom::Array values, ids; - auto shapeMap = axom::mir::views::buildShapeMap( - n_device_topo, - values, - ids, - allocatorID); + auto shapeMap = + axom::mir::views::buildShapeMap(n_device_topo, values, ids, allocatorID); using MixedTopoView = axom::mir::views::UnstructuredTopologyMixedShapeView; diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz.cpp index 38331e0ded..427dae6845 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz.cpp @@ -160,7 +160,9 @@ void braid3d_mat_test(const std::string &type, namespace bputils = axom::mir::utilities::blueprint; axom::StackArray dims {11, 11, 11}; - axom::StackArray zoneDims {dims[0] - 1, dims[1] - 1, dims[2] - 1}; + axom::StackArray zoneDims {dims[0] - 1, + dims[1] - 1, + dims[2] - 1}; // Create the data conduit::Node hostMesh, deviceMesh; @@ -177,8 +179,10 @@ void braid3d_mat_test(const std::string &type, using CoordsetView = decltype(coordsetView); using ShapeType = axom::mir::views::HexShape; - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView; - auto connView = bputils::make_array_view(deviceMesh["topologies/mesh/elements/connectivity"]); + using TopologyView = + axom::mir::views::UnstructuredTopologySingleShapeView; + auto connView = bputils::make_array_view( + deviceMesh["topologies/mesh/elements/connectivity"]); TopologyView topologyView(connView); conduit::Node deviceMIRMesh; diff --git a/src/axom/mir/tests/mir_views.cpp b/src/axom/mir/tests/mir_views.cpp index 9b926f236e..1d619b81e0 100644 --- a/src/axom/mir/tests/mir_views.cpp +++ b/src/axom/mir/tests/mir_views.cpp @@ -366,7 +366,7 @@ struct test_braid2d_mat #else const std::string &AXOM_UNUSED_PARAM(name) #endif - ) + ) { namespace bputils = axom::mir::utilities::blueprint; const int allocatorID = axom::execution_space::allocatorID(); diff --git a/src/axom/mir/utilities/FieldBlender.hpp b/src/axom/mir/utilities/FieldBlender.hpp index 04818f1d54..e44c4a5966 100644 --- a/src/axom/mir/utilities/FieldBlender.hpp +++ b/src/axom/mir/utilities/FieldBlender.hpp @@ -20,7 +20,6 @@ namespace utilities { namespace blueprint { - /*! * \brief This policy can be used with FieldBlender to select all blend groups. */ diff --git a/src/axom/mir/utilities/MakeUnstructured.hpp b/src/axom/mir/utilities/MakeUnstructured.hpp index b618119d23..39117d6b59 100644 --- a/src/axom/mir/utilities/MakeUnstructured.hpp +++ b/src/axom/mir/utilities/MakeUnstructured.hpp @@ -21,7 +21,6 @@ namespace utilities { namespace blueprint { - /** * \accelerated * \brief Make an unstructured representation of a structured topology. diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 954795e0f0..83b6fd22a2 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -617,13 +617,13 @@ void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) } template -void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &...views) +void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &... views) { func(views...); } template -void Node_to_ArrayView_internal(const conduit::Node &first, Args &&...args) +void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -631,7 +631,7 @@ void Node_to_ArrayView_internal(const conduit::Node &first, Args &&...args) } template -void Node_to_ArrayView_internal(conduit::Node &first, Args &&...args) +void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -644,7 +644,7 @@ void Node_to_ArrayView_internal(conduit::Node &first, Args &&...args) template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_int8_ptr()), @@ -654,13 +654,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int8( template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_int16_ptr()), @@ -670,13 +670,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int16( template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_int32_ptr()), @@ -686,13 +686,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int32( template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_int64_ptr()), @@ -702,13 +702,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int64( template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_uint8_ptr()), @@ -718,13 +718,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint8( template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_uint16_ptr()), @@ -734,13 +734,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint16( template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_uint32_ptr()), @@ -750,13 +750,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint32( template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_uint64_ptr()), @@ -766,13 +766,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint64( template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_float32_ptr()), @@ -782,13 +782,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_float32( template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&func, - Args &&...args) + Args &&... args) { func(axom::ArrayView( const_cast(args.as_float64_ptr()), @@ -798,14 +798,14 @@ std::enable_if_t Node_to_ArrayView_same_internal_float64( template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&...AXOM_UNUSED_PARAM(args)) + Args &&... AXOM_UNUSED_PARAM(args)) { } template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit::Node &first, - Args &&...args) + Args &&... args) { if(first.dtype().is_int8()) { @@ -868,7 +868,7 @@ template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node &first, - Args &&...args) + Args &&... args) { if(first.dtype().is_int8()) { @@ -929,13 +929,13 @@ void Node_to_ArrayView_same_internal(FuncType &&func, /// Reorder args template -void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&...args) +void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&... args) { Node_to_ArrayView_same_internal(args..., first); } template -void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&...args) +void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) { Node_to_ArrayView_same_internal(args..., first); } @@ -961,13 +961,13 @@ void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&...args) * */ template -void Node_to_ArrayView(const conduit::Node &first, Args &&...args) +void Node_to_ArrayView(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } template -void Node_to_ArrayView(conduit::Node &first, Args &&...args) +void Node_to_ArrayView(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } @@ -988,7 +988,7 @@ void Node_to_ArrayView(conduit::Node &first, Args &&...args) * */ template -void Node_to_ArrayView_same(const conduit::Node &first, Args &&...args) +void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -996,7 +996,7 @@ void Node_to_ArrayView_same(const conduit::Node &first, Args &&...args) } template -void Node_to_ArrayView_same(conduit::Node &first, Args &&...args) +void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -1008,7 +1008,7 @@ void Node_to_ArrayView_same(conduit::Node &first, Args &&...args) //------------------------------------------------------------------------------ template -void IndexNode_to_ArrayView(const conduit::Node &first, Args &&...args) +void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal( first, @@ -1017,7 +1017,7 @@ void IndexNode_to_ArrayView(const conduit::Node &first, Args &&...args) } template -void IndexNode_to_ArrayView(conduit::Node &first, Args &&...args) +void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal( first, @@ -1026,7 +1026,7 @@ void IndexNode_to_ArrayView(conduit::Node &first, Args &&...args) } template -void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) +void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal( first, @@ -1035,7 +1035,7 @@ void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) } template -void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&...args) +void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal( first, @@ -1047,7 +1047,7 @@ void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&...args) // Float Node to ArrayView. Handle float types. //------------------------------------------------------------------------------ template -void FloatNode_to_ArrayView(const conduit::Node &first, Args &&...args) +void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal( first, @@ -1056,7 +1056,7 @@ void FloatNode_to_ArrayView(const conduit::Node &first, Args &&...args) } template -void FloatNode_to_ArrayView(conduit::Node &first, Args &&...args) +void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_internal( first, @@ -1065,7 +1065,7 @@ void FloatNode_to_ArrayView(conduit::Node &first, Args &&...args) } template -void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) +void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal( first, @@ -1074,7 +1074,7 @@ void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) } template -void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&...args) +void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&... args) { detail::Node_to_ArrayView_same_internal( first, diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp index 4ca6c83f62..422fe86022 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp @@ -11,7 +11,6 @@ namespace mir { namespace views { - ShapeMap buildShapeMap(const conduit::Node &n_topo, axom::Array &values, axom::Array &ids, diff --git a/src/axom/mir/views/dispatch_structured_topology.hpp b/src/axom/mir/views/dispatch_structured_topology.hpp index e0eae13265..aa04d1b438 100644 --- a/src/axom/mir/views/dispatch_structured_topology.hpp +++ b/src/axom/mir/views/dispatch_structured_topology.hpp @@ -25,7 +25,6 @@ namespace mir { namespace views { - /*! * \brief Base template for strided structured topology creation */ diff --git a/src/axom/mir/views/dispatch_utilities.hpp b/src/axom/mir/views/dispatch_utilities.hpp index b180da4d6e..cd52abc592 100644 --- a/src/axom/mir/views/dispatch_utilities.hpp +++ b/src/axom/mir/views/dispatch_utilities.hpp @@ -12,7 +12,6 @@ namespace mir { namespace views { - #if __cplusplus >= 201703L // C++17 and later. template From e03b4814b5566dc838b077582d75af76075ff869 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Wed, 2 Oct 2024 19:16:30 -0700 Subject: [PATCH 280/290] fix --- src/axom/mir/examples/concentric_circles/runMIR_hip.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp index f8c25c52dc..b191a3615c 100644 --- a/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp +++ b/src/axom/mir/examples/concentric_circles/runMIR_hip.cpp @@ -4,14 +4,14 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "runMIR.hpp" -#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_HIP) int runMIR_hip(const conduit::Node &mesh, const conduit::Node &options, conduit::Node &result) { constexpr int HIP_BLOCK_SIZE = 64; using hip_exec = axom::HIP_EXEC; - retval = runMIR(mesh, options, result); + return runMIR(mesh, options, result); } #else int runMIR_hip(const conduit::Node &AXOM_UNUSED_PARAM(mesh), From 2f82aa374993105508ff59d2cf8d2c206a825254 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 10:52:43 -0700 Subject: [PATCH 281/290] Break up compilation into more files --- .../concentric_circles/CMakeLists.txt | 2 +- .../examples/tutorial_simple/CMakeLists.txt | 25 ++- .../tutorial_simple/mir_tutorial_simple.cpp | 105 +-------- .../mir/examples/tutorial_simple/runMIR.hpp | 209 ++++++++++++++++++ .../examples/tutorial_simple/runMIR_cuda.cpp | 40 ++++ .../tutorial_simple/runMIR_cuda_hex.cpp | 23 ++ .../tutorial_simple/runMIR_cuda_quad.cpp | 23 ++ .../tutorial_simple/runMIR_cuda_tri.cpp | 23 ++ .../examples/tutorial_simple/runMIR_hip.cpp | 40 ++++ .../tutorial_simple/runMIR_hip_hex.cpp | 23 ++ .../tutorial_simple/runMIR_hip_quad.cpp | 23 ++ .../tutorial_simple/runMIR_hip_tri.cpp | 23 ++ .../examples/tutorial_simple/runMIR_omp.cpp | 42 ++++ .../tutorial_simple/runMIR_omp_hex.cpp | 21 ++ .../tutorial_simple/runMIR_omp_quad.cpp | 21 ++ .../tutorial_simple/runMIR_omp_tri.cpp | 21 ++ .../examples/tutorial_simple/runMIR_seq.cpp | 31 +++ .../tutorial_simple/runMIR_seq_hex.cpp | 12 + .../tutorial_simple/runMIR_seq_quad.cpp | 12 + .../tutorial_simple/runMIR_seq_tri.cpp | 12 + 20 files changed, 629 insertions(+), 102 deletions(-) create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR.hpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_cuda.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_hip.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_hip_hex.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_hip_quad.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_hip_tri.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_omp.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_omp_hex.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_omp_quad.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_omp_tri.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_seq.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_seq_hex.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_seq_quad.cpp create mode 100644 src/axom/mir/examples/tutorial_simple/runMIR_seq_tri.cpp diff --git a/src/axom/mir/examples/concentric_circles/CMakeLists.txt b/src/axom/mir/examples/concentric_circles/CMakeLists.txt index 4e314cd976..d9aeec91eb 100644 --- a/src/axom/mir/examples/concentric_circles/CMakeLists.txt +++ b/src/axom/mir/examples/concentric_circles/CMakeLists.txt @@ -11,7 +11,7 @@ set( mir_example_dependencies # Speed up compilation by separating out the execspaces into different runMIR_xxx.cpp files. axom_add_executable( - NAME mir_concentric_circles_ex + NAME mir_concentric_circles SOURCES mir_concentric_circles.cpp runMIR_seq.cpp runMIR_omp.cpp runMIR_cuda.cpp runMIR_hip.cpp OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} DEPENDS_ON ${mir_example_dependencies} diff --git a/src/axom/mir/examples/tutorial_simple/CMakeLists.txt b/src/axom/mir/examples/tutorial_simple/CMakeLists.txt index 213eb4d9cd..c958fcbafc 100644 --- a/src/axom/mir/examples/tutorial_simple/CMakeLists.txt +++ b/src/axom/mir/examples/tutorial_simple/CMakeLists.txt @@ -9,9 +9,30 @@ set( mir_example_dependencies mir ) +# Break the different MIR cases into multiple files to speed up compilation. +set(mir_tutorial_simple_sources + mir_tutorial_simple.cpp + runMIR_seq.cpp + runMIR_seq_tri.cpp + runMIR_seq_quad.cpp + runMIR_seq_hex.cpp + runMIR_omp.cpp + runMIR_omp_tri.cpp + runMIR_omp_quad.cpp + runMIR_omp_hex.cpp + runMIR_cuda.cpp + runMIR_cuda_tri.cpp + runMIR_cuda_quad.cpp + runMIR_cuda_hex.cpp + runMIR_hip.cpp + runMIR_hip_tri.cpp + runMIR_hip_quad.cpp + runMIR_hip_hex.cpp + ) + axom_add_executable( - NAME mir_tutorial_simple_ex - SOURCES mir_tutorial_simple.cpp + NAME mir_tutorial_simple + SOURCES ${mir_tutorial_simple_sources} OUTPUT_DIR ${EXAMPLE_OUTPUT_DIRECTORY} DEPENDS_ON ${mir_example_dependencies} FOLDER axom/mir/examples diff --git a/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp b/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp index 7d168f1fbd..9c386c1074 100644 --- a/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp +++ b/src/axom/mir/examples/tutorial_simple/mir_tutorial_simple.cpp @@ -8,6 +8,8 @@ #include "axom/slam.hpp" #include "axom/mir.hpp" +#include "runMIR.hpp" + #include // namespace aliases @@ -145,97 +147,6 @@ void printNode(const conduit::Node &n) n.to_summary_string_stream(std::cout, options); } -//-------------------------------------------------------------------------------- -/*! - * \brief Run MIR on the input mesh. - * - * \tparam ExecSpace The execution space where the algorithm will run. - * - * \param hostMesh A conduit node that contains the test mesh. - * \param options A conduit node that contains the test mesh. - * \param hostResult A conduit node that will contain the MIR results. - */ -template -int runMIR(const conduit::Node &hostMesh, - const conduit::Node &options, - conduit::Node &hostResult) -{ - std::string shape = hostMesh["topologies/mesh/elements/shape"].as_string(); - SLIC_INFO(axom::fmt::format("Using policy {}", - axom::execution_space::name())); - - // host->device - conduit::Node deviceMesh; - bputils::copy(deviceMesh, hostMesh); - - conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; - conduit::Node &n_topo = deviceMesh["topologies/mesh"]; - conduit::Node &n_matset = deviceMesh["matsets/mat"]; - auto connView = - bputils::make_array_view(n_topo["elements/connectivity"]); - - // Make matset view. (There's often 1 more material so add 1) - constexpr int MAXMATERIALS = 12; - using MatsetView = - axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set(bputils::make_array_view(n_matset["material_ids"]), - bputils::make_array_view(n_matset["volume_fractions"]), - bputils::make_array_view(n_matset["sizes"]), - bputils::make_array_view(n_matset["offsets"]), - bputils::make_array_view(n_matset["indices"])); - - // Coord/Topo views differ. - conduit::Node deviceResult; - if(shape == "tri") - { - auto coordsetView = - axom::mir::views::make_explicit_coordset::view(n_coordset); - using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< - axom::mir::views::TriShape>; - TopologyView topologyView(connView); - - using MIR = - axom::mir::EquiZAlgorithm; - MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); - } - else if(shape == "quad") - { - auto coordsetView = - axom::mir::views::make_explicit_coordset::view(n_coordset); - using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< - axom::mir::views::QuadShape>; - TopologyView topologyView(connView); - - using MIR = - axom::mir::EquiZAlgorithm; - MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); - } - else if(shape == "hex") - { - auto coordsetView = - axom::mir::views::make_explicit_coordset::view(n_coordset); - using CoordsetView = decltype(coordsetView); - using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< - axom::mir::views::HexShape>; - TopologyView topologyView(connView); - - using MIR = - axom::mir::EquiZAlgorithm; - MIR m(topologyView, coordsetView, matsetView); - m.execute(deviceMesh, options, deviceResult); - } - - // device->host - bputils::copy(hostResult, deviceResult); - - return 0; -} - //-------------------------------------------------------------------------------- /*! * \brief Tutorial main showing how to initialize test cases and perform mir. @@ -322,29 +233,25 @@ int main(int argc, char **argv) conduit::Node resultMesh; if(params.m_policy == RuntimePolicy::seq) { - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_seq(mesh, options, resultMesh); } #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) #if defined(AXOM_USE_OPENMP) else if(params.m_policy == RuntimePolicy::omp) { - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_omp(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_CUDA) else if(params.m_policy == RuntimePolicy::cuda) { - constexpr int CUDA_BLOCK_SIZE = 256; - using cuda_exec = axom::CUDA_EXEC; - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_cuda(mesh, options, resultMesh); } #endif #if defined(AXOM_USE_HIP) else if(params.m_policy == RuntimePolicy::hip) { - constexpr int HIP_BLOCK_SIZE = 64; - using hip_exec = axom::HIP_EXEC; - retval = runMIR(mesh, options, resultMesh); + retval = runMIR_hip(mesh, options, resultMesh); } #endif #endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR.hpp b/src/axom/mir/examples/tutorial_simple/runMIR.hpp new file mode 100644 index 0000000000..bf41b68c47 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR.hpp @@ -0,0 +1,209 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#ifndef AXOM_MIR_EXAMPLES_TUTORIAL_SIMPLE_RUNMIR_HPP +#define AXOM_MIR_EXAMPLES_TUTORIAL_SIMPLE_RUNMIR_HPP +#include "axom/config.hpp" +#include "axom/core.hpp" // for axom macros +#include "axom/slic.hpp" +#include "axom/mir.hpp" // for Mir classes & functions + +#include + +//-------------------------------------------------------------------------------- +/*! + * \brief Run MIR on the tri input mesh. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * + * \param hostMesh A conduit node that contains the test mesh. + * \param options A conduit node that contains the test mesh. + * \param hostResult A conduit node that will contain the MIR results. + */ +template +int runMIR_tri(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) +{ + namespace bputils = axom::mir::utilities::blueprint; + std::string shape = hostMesh["topologies/mesh/elements/shape"].as_string(); + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; + conduit::Node &n_topo = deviceMesh["topologies/mesh"]; + conduit::Node &n_matset = deviceMesh["matsets/mat"]; + auto connView = + bputils::make_array_view(n_topo["elements/connectivity"]); + + // Make matset view. (There's often 1 more material so add 1) + constexpr int MAXMATERIALS = 12; + using MatsetView = + axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); + + // Make Coord/Topo views. + conduit::Node deviceResult; + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::TriShape>; + TopologyView topologyView(connView); + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +//-------------------------------------------------------------------------------- +/*! + * \brief Run MIR on the quad input mesh. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * + * \param hostMesh A conduit node that contains the test mesh. + * \param options A conduit node that contains the test mesh. + * \param hostResult A conduit node that will contain the MIR results. + */ +template +int runMIR_quad(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) +{ + namespace bputils = axom::mir::utilities::blueprint; + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; + conduit::Node &n_topo = deviceMesh["topologies/mesh"]; + conduit::Node &n_matset = deviceMesh["matsets/mat"]; + auto connView = + bputils::make_array_view(n_topo["elements/connectivity"]); + + // Make matset view. (There's often 1 more material so add 1) + constexpr int MAXMATERIALS = 12; + using MatsetView = + axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); + + // Make Coord/Topo views. + conduit::Node deviceResult; + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::QuadShape>; + TopologyView topologyView(connView); + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +//-------------------------------------------------------------------------------- +/*! + * \brief Run MIR on the hex input mesh. + * + * \tparam ExecSpace The execution space where the algorithm will run. + * + * \param hostMesh A conduit node that contains the test mesh. + * \param options A conduit node that contains the test mesh. + * \param hostResult A conduit node that will contain the MIR results. + */ +template +int runMIR_hex(const conduit::Node &hostMesh, + const conduit::Node &options, + conduit::Node &hostResult) +{ + namespace bputils = axom::mir::utilities::blueprint; + SLIC_INFO(axom::fmt::format("Using policy {}", + axom::execution_space::name())); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + conduit::Node &n_coordset = deviceMesh["coordsets/coords"]; + conduit::Node &n_topo = deviceMesh["topologies/mesh"]; + conduit::Node &n_matset = deviceMesh["matsets/mat"]; + auto connView = + bputils::make_array_view(n_topo["elements/connectivity"]); + + // Make matset view. (There's often 1 more material so add 1) + constexpr int MAXMATERIALS = 12; + using MatsetView = + axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(n_matset["material_ids"]), + bputils::make_array_view(n_matset["volume_fractions"]), + bputils::make_array_view(n_matset["sizes"]), + bputils::make_array_view(n_matset["offsets"]), + bputils::make_array_view(n_matset["indices"])); + + // Make Coord/Topo views. + conduit::Node deviceResult; + auto coordsetView = + axom::mir::views::make_explicit_coordset::view(n_coordset); + using CoordsetView = decltype(coordsetView); + using TopologyView = axom::mir::views::UnstructuredTopologySingleShapeView< + axom::mir::views::HexShape>; + TopologyView topologyView(connView); + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + m.execute(deviceMesh, options, deviceResult); + + // device->host + bputils::copy(hostResult, deviceResult); + + return 0; +} + +// Prototypes. +int runMIR_seq(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_omp(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_cuda(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_hip(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); + +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda.cpp new file mode 100644 index 0000000000..c2b4ab8ff2 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +// Prototypes +int runMIR_cuda_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_cuda_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_cuda_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); + +int runMIR_cuda(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + std::string shape = mesh["topologies/mesh/elements/shape"].as_string(); + int retval = 0; + if(shape == "tri") + retval = runMIR_cuda_tri(mesh, options, result); + else if(shape == "quad") + retval = runMIR_cuda_quad(mesh, options, result); + else if(shape == "hex") + retval = runMIR_cuda_hex(mesh, options, result); + return retval; +} +#else +int runMIR_cuda(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp new file mode 100644 index 0000000000..1e34e52cc1 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +int runMIR_omp_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + return runMIR_hex(mesh, options, result); +} +#else +int runMIR_cuda_hex(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp new file mode 100644 index 0000000000..9dedeab256 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +int runMIR_omp_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + return runMIR_quad(mesh, options, result); +} +#else +int runMIR_cuda_quad(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp new file mode 100644 index 0000000000..cfa10bb643 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) +int runMIR_omp_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int CUDA_BLOCK_SIZE = 256; + using cuda_exec = axom::CUDA_EXEC; + return runMIR_tri(mesh, options, result); +} +#else +int runMIR_cuda_tri(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_hip.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_hip.cpp new file mode 100644 index 0000000000..32a25e6389 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_hip.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_HIP) +// Prototypes +int runMIR_hip_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_hip_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_hip_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); + +int runMIR_hip(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + std::string shape = mesh["topologies/mesh/elements/shape"].as_string(); + int retval = 0; + if(shape == "tri") + retval = runMIR_hip_tri(mesh, options, result); + else if(shape == "quad") + retval = runMIR_hip_quad(mesh, options, result); + else if(shape == "hex") + retval = runMIR_hip_hex(mesh, options, result); + return retval; +} +#else +int runMIR_hip(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_hip_hex.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_hip_hex.cpp new file mode 100644 index 0000000000..5b349be5f6 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_hip_hex.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_HIP) +int runMIR_hip_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + return runMIR_hex(mesh, options, result); +} +#else +int runMIR_hip_hex(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_hip_quad.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_hip_quad.cpp new file mode 100644 index 0000000000..9e20f66aef --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_hip_quad.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_HIP) +int runMIR_hip_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + return runMIR_quad(mesh, options, result); +} +#else +int runMIR_hip_quad(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_hip_tri.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_hip_tri.cpp new file mode 100644 index 0000000000..38735d8385 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_hip_tri.cpp @@ -0,0 +1,23 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_HIP) +int runMIR_hip_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + constexpr int HIP_BLOCK_SIZE = 64; + using hip_exec = axom::HIP_EXEC; + return runMIR_tri(mesh, options, result); +} +#else +int runMIR_hip_tri(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_omp.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_omp.cpp new file mode 100644 index 0000000000..302a991e71 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_omp.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && \ + defined(AXOM_USE_OPENMP) + +// Prototypes +int runMIR_omp_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_omp_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_omp_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); + +int runMIR_omp(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + std::string shape = mesh["topologies/mesh/elements/shape"].as_string(); + int retval = 0; + if(shape == "tri") + retval = runMIR_omp_tri(mesh, options, result); + else if(shape == "quad") + retval = runMIR_omp_quad(mesh, options, result); + else if(shape == "hex") + retval = runMIR_omp_hex(mesh, options, result); + return retval; +} +#else +int runMIR_omp(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_omp_hex.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_omp_hex.cpp new file mode 100644 index 0000000000..4199634327 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_omp_hex.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_OMP) +int runMIR_omp_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_hex(mesh, options, result); +} +#else +int runMIR_omp_hex(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_omp_quad.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_omp_quad.cpp new file mode 100644 index 0000000000..807ea42414 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_omp_quad.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_OMP) +int runMIR_omp_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_quad(mesh, options, result); +} +#else +int runMIR_omp_quad(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_omp_tri.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_omp_tri.cpp new file mode 100644 index 0000000000..c3af746eba --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_omp_tri.cpp @@ -0,0 +1,21 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +#if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_OMP) +int runMIR_omp_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_tri(mesh, options, result); +} +#else +int runMIR_omp_tri(const conduit::Node &AXOM_UNUSED_PARAM(mesh), + const conduit::Node &AXOM_UNUSED_PARAM(options), + conduit::Node &AXOM_UNUSED_PARAM(result)) +{ + return 0; +} +#endif diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_seq.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_seq.cpp new file mode 100644 index 0000000000..8fec795f25 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_seq.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +// Prototypes +int runMIR_seq_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_seq_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); +int runMIR_seq_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result); + +int runMIR_seq(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + std::string shape = mesh["topologies/mesh/elements/shape"].as_string(); + int retval = 0; + if(shape == "tri") + retval = runMIR_seq_tri(mesh, options, result); + else if(shape == "quad") + retval = runMIR_seq_quad(mesh, options, result); + else if(shape == "hex") + retval = runMIR_seq_hex(mesh, options, result); + return retval; +} diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_seq_hex.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_seq_hex.cpp new file mode 100644 index 0000000000..a3bb0d53e9 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_seq_hex.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +int runMIR_seq_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_hex(mesh, options, result); +} diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_seq_quad.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_seq_quad.cpp new file mode 100644 index 0000000000..0bef896d62 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_seq_quad.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +int runMIR_seq_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_quad(mesh, options, result); +} diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_seq_tri.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_seq_tri.cpp new file mode 100644 index 0000000000..75dc064860 --- /dev/null +++ b/src/axom/mir/examples/tutorial_simple/runMIR_seq_tri.cpp @@ -0,0 +1,12 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) +#include "runMIR.hpp" + +int runMIR_seq_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) +{ + return runMIR_tri(mesh, options, result); +} From 5e780a16544ee38e0311451df19c89a7b6e572a0 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 11:12:32 -0700 Subject: [PATCH 282/290] Split tests into more files --- .../tutorial_simple/runMIR_cuda_hex.cpp | 6 +- .../tutorial_simple/runMIR_cuda_quad.cpp | 6 +- .../tutorial_simple/runMIR_cuda_tri.cpp | 6 +- src/axom/mir/tests/CMakeLists.txt | 6 +- .../mir/tests/mir_blueprint_utilities.cpp | 946 ------------------ .../tests/{mir_equiz.cpp => mir_equiz2d.cpp} | 105 -- src/axom/mir/tests/mir_equiz3d.cpp | 179 ++++ src/axom/mir/tests/mir_mergemeshes.cpp | 196 ++++ .../mir/tests/mir_node_to_zone_relation.cpp | 330 ++++++ src/axom/mir/tests/mir_slicers.cpp | 485 +++++++++ src/axom/mir/tests/mir_testing_helpers.hpp | 28 + 11 files changed, 1232 insertions(+), 1061 deletions(-) rename src/axom/mir/tests/{mir_equiz.cpp => mir_equiz2d.cpp} (63%) create mode 100644 src/axom/mir/tests/mir_equiz3d.cpp create mode 100644 src/axom/mir/tests/mir_mergemeshes.cpp create mode 100644 src/axom/mir/tests/mir_node_to_zone_relation.cpp create mode 100644 src/axom/mir/tests/mir_slicers.cpp diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp index 1e34e52cc1..6fcdbf8e49 100644 --- a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_hex.cpp @@ -5,9 +5,9 @@ #include "runMIR.hpp" #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) -int runMIR_omp_hex(const conduit::Node &mesh, - const conduit::Node &options, - conduit::Node &result) +int runMIR_cuda_hex(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp index 9dedeab256..757e8a095c 100644 --- a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_quad.cpp @@ -5,9 +5,9 @@ #include "runMIR.hpp" #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) -int runMIR_omp_quad(const conduit::Node &mesh, - const conduit::Node &options, - conduit::Node &result) +int runMIR_cuda_quad(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; diff --git a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp index cfa10bb643..8cd4749465 100644 --- a/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp +++ b/src/axom/mir/examples/tutorial_simple/runMIR_cuda_tri.cpp @@ -5,9 +5,9 @@ #include "runMIR.hpp" #if defined(AXOM_USE_RAJA) && defined(AXOM_USE_UMPIRE) && defined(AXOM_USE_CUDA) -int runMIR_omp_tri(const conduit::Node &mesh, - const conduit::Node &options, - conduit::Node &result) +int runMIR_cuda_tri(const conduit::Node &mesh, + const conduit::Node &options, + conduit::Node &result) { constexpr int CUDA_BLOCK_SIZE = 256; using cuda_exec = axom::CUDA_EXEC; diff --git a/src/axom/mir/tests/CMakeLists.txt b/src/axom/mir/tests/CMakeLists.txt index b4e1b9897c..5c6c3f0f6a 100644 --- a/src/axom/mir/tests/CMakeLists.txt +++ b/src/axom/mir/tests/CMakeLists.txt @@ -16,7 +16,11 @@ set(gtest_mir_tests mir_blueprint_utilities.cpp mir_views_indexing.cpp mir_views.cpp - mir_equiz.cpp + mir_equiz2d.cpp + mir_equiz3d.cpp + mir_slicers.cpp + mir_mergemeshes.cpp + mir_node_to_zone_relation.cpp ) set(mir_tests_depends_on diff --git a/src/axom/mir/tests/mir_blueprint_utilities.cpp b/src/axom/mir/tests/mir_blueprint_utilities.cpp index 6cf5a262a1..c9d6e2cb20 100644 --- a/src/axom/mir/tests/mir_blueprint_utilities.cpp +++ b/src/axom/mir/tests/mir_blueprint_utilities.cpp @@ -216,305 +216,6 @@ TEST(mir_blueprint_utilities, make_unstructured_hip) } #endif -//------------------------------------------------------------------------------ -template -struct test_node_to_zone_relation_builder -{ - static void test(const conduit::Node &hostMesh) - { - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - // _mir_utilities_n2zrel_begin - const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceCoordset, deviceRelation); - // _mir_utilities_n2zrel_end - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); - - // Expected answers - // clang-format off - const int zones[] = { - 0, - 0, 1, - 1, 2, - 2, - 0, 3, // NOTE: these are sorted here - 0, 1, 3, 4, - 1, 2, 4, 5, - 2, 5, - 3, - 3, 4, - 4, 5, - 5 - }; - const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; - const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; - // clang-format on - - // Compare answers. - compareRelation( - hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); - } - - static void compareRelation(const conduit::Node &hostRelation, - const axom::ArrayView &zones, - const axom::ArrayView &sizes, - const axom::ArrayView &offsets) - { - const auto zonesView = - bputils::make_array_view(hostRelation["zones"]); - const auto sizesView = - bputils::make_array_view(hostRelation["sizes"]); - const auto offsetsView = - bputils::make_array_view(hostRelation["offsets"]); - EXPECT_EQ(sizesView.size(), sizes.size()); - EXPECT_EQ(offsetsView.size(), offsets.size()); - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - EXPECT_EQ(sizes[i], sizesView[i]); - EXPECT_EQ(offsets[i], offsetsView[i]); - } - for(axom::IndexType i = 0; i < sizesView.size(); i++) - { - // Sort the result so we can compare to the expected answer. - IndexT *begin = zonesView.data() + offsetsView[i]; - IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; - std::sort(begin, end); - - for(int j = 0; j < sizesView[i]; j++) - { - EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); - } - } - } -}; - -TEST(mir_blueprint_utilities, n2zrel_unstructured_seq) -{ - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, n2zrel_unstructured_omp) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, n2zrel_unstructured_cuda) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, n2zrel_unstructured_hip) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("quads", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif - -TEST(mir_blueprint_utilities, n2zrel_rectilinear_seq) -{ - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("rectilinear", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, n2zrel_rectilinear_omp) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("rectilinear", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, n2zrel_rectilinear_cuda) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("rectilinear", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, n2zrel_rectilinear_hip) -{ - conduit::Node mesh; - axom::StackArray dims {{4, 3}}; - axom::mir::testing::data::braid("rectilinear", dims, mesh); - test_node_to_zone_relation_builder::test(mesh); -} -#endif - -template -struct test_node_to_zone_relation_builder_polyhedral - : public test_node_to_zone_relation_builder -{ - using SuperClass = test_node_to_zone_relation_builder; - - static void test(const conduit::Node &hostMesh) - { - // host -> device - conduit::Node deviceMesh; - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); - const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; - const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; - - // Run the algorithm on the device - conduit::Node deviceRelation; - axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; - n2z.execute(deviceTopo, deviceCoordset, deviceRelation); - - // device -> host - conduit::Node hostRelation; - axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); - - // Expected answers - // clang-format off - const int zones[] = { - /*node 0*/ 0, - /*node 1*/ 0, 1, - /*node 2*/ 1, - /*node 3*/ 0, 2, - /*node 4*/ 0, 1, 2, 3, - /*node 5*/ 1, 3, - /*node 6*/ 2, - /*node 7*/ 2, 3, - /*node 8*/ 3, - - /*node 9*/ 0, 4, - /*node 10*/ 0, 1, 4, 5, - /*node 11*/ 1, 5, - /*node 12*/ 0, 2, 4, 6, - /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, - /*node 14*/ 1, 3, 5, 7, - /*node 15*/ 2, 6, - /*node 16*/ 2, 3, 6, 7, - /*node 17*/ 3, 7, - - /*node 18*/ 4, - /*node 19*/ 4, 5, - /*node 20*/ 5, - /*node 21*/ 4, 6, - /*node 22*/ 4, 5, 6, 7, - /*node 23*/ 5, 7, - /*node 24*/ 6, - /*node 25*/ 6, 7, - /*node 26*/ 7 - }; - const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, - 2, 4, 2, 4, 8, 4, 2, 4, 2, - 1, 2, 1, 2, 4, 2, 1, 2, 1 - }; - const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, - 16, 18, 22, 24, 28, 36, 40, 42, 46, - 48, 49, 51, 52, 54, 58, 60, 61, 63}; - // clang-format on - - // Compare answers. - SuperClass::compareRelation( - hostRelation, - axom::ArrayView(zones, sizeof(zones) / sizeof(int)), - axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), - axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); - } - - static void create(conduit::Node &mesh) - { - conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); - // Make sure all the types are the same. - conduit::blueprint::mesh::utils::convert( - mesh, - conduit::DataType::int32(), - std::vector {{"topologies/mesh/elements/connectivity", - "topologies/mesh/elements/sizes", - "topologies/mesh/elements/offsets", - "topologies/mesh/subelements/connectivity", - "topologies/mesh/subelements/sizes", - "topologies/mesh/subelements/offsets"}}); - } -}; - -TEST(mir_blueprint_utilities, n2zrel_polyhedral_seq) -{ - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create( - mesh); - test_node_to_zone_relation_builder_polyhedral::test( - mesh); -} -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, n2zrel_polyhedral_omp) -{ - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create( - mesh); - test_node_to_zone_relation_builder_polyhedral::test( - mesh); -} -#endif -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, n2zrel_polyhedral_cuda) -{ - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create( - mesh); - test_node_to_zone_relation_builder_polyhedral::test( - mesh); -} -#endif -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, n2zrel_polyhedral_hip) -{ - conduit::Node mesh; - test_node_to_zone_relation_builder_polyhedral::create( - mesh); - test_node_to_zone_relation_builder_polyhedral::test( - mesh); -} -#endif - //------------------------------------------------------------------------------ template struct test_recenter_field @@ -616,377 +317,6 @@ TEST(mir_blueprint_utilities, recenterfield_hip) } #endif -//------------------------------------------------------------------------------ -template -bool compare_views(const Container1 &a, const Container2 &b) -{ - bool eq = a.size() == b.size(); - for(axom::IndexType i = 0; i < a.size() && eq; i++) - { - eq &= a[i] == b[i]; - } - if(!eq) - { - std::cout << "a={"; - for(axom::IndexType i = 0; i < a.size(); i++) - { - if(i > 0) std::cout << ", "; - std::cout << a[i]; - } - std::cout << "}" << std::endl; - std::cout << "b={"; - for(axom::IndexType i = 0; i < a.size(); i++) - { - if(i > 0) std::cout << ", "; - std::cout << b[i]; - } - std::cout << "}" << std::endl; - } - return eq; -} - -template -struct test_matset_slice -{ - static void test() - { - conduit::Node hostMatset; - create(hostMatset); - - // host->device - conduit::Node deviceMatset; - bputils::copy(deviceMatset, hostMatset); - - axom::Array ids {{1, 3, 5}}; - axom::Array selectedZones( - 3, - 3, - axom::execution_space::allocatorID()); - axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); - - using MatsetView = - axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set( - bputils::make_array_view(deviceMatset["material_ids"]), - bputils::make_array_view( - deviceMatset["volume_fractions"]), - bputils::make_array_view(deviceMatset["sizes"]), - bputils::make_array_view(deviceMatset["offsets"]), - bputils::make_array_view(deviceMatset["indices"])); - - // Slice it. - bputils::MatsetSlicer slicer(matsetView); - conduit::Node newDeviceMatset; - bputils::SliceData slice; - slice.m_indicesView = selectedZones.view(); - slicer.execute(slice, deviceMatset, newDeviceMatset); - - // device->host - conduit::Node newHostMatset; - bputils::copy(newHostMatset, newDeviceMatset); - - // Expected answers. - const axom::Array sizes {{2, 1, 2}}; - const axom::Array offsets {{0, 2, 3}}; - const axom::Array indices {{0, 1, 2, 3, 4}}; - const axom::Array material_ids {{1, 2, 2, 2, 3}}; - const axom::Array volume_fractions { - {0.5, 0.5, 1.0, 0.8, 0.2}}; - - EXPECT_EQ(conduit::DataType::INT64_ID, - newHostMatset["material_ids"].dtype().id()); - EXPECT_EQ(conduit::DataType::FLOAT64_ID, - newHostMatset["volume_fractions"].dtype().id()); - - EXPECT_TRUE(compare_views( - sizes.view(), - bputils::make_array_view(newHostMatset["sizes"]))); - EXPECT_TRUE(compare_views( - offsets.view(), - bputils::make_array_view(newHostMatset["offsets"]))); - EXPECT_TRUE(compare_views( - indices.view(), - bputils::make_array_view(newHostMatset["indices"]))); - EXPECT_TRUE(compare_views( - material_ids.view(), - bputils::make_array_view(newHostMatset["material_ids"]))); - EXPECT_TRUE(compare_views(volume_fractions.view(), - bputils::make_array_view( - newHostMatset["volume_fractions"]))); - } - - static void create(conduit::Node &matset) - { - /* - 8-------9------10------11 - | 2/1 | 1/0.1 | 2/0.8 | - | | 2/0.5 | 3/0.2 | - | | 3/0.4 | | - 4-------5-------6-------7 - | | 1/0.5 | 1/0.2 | - | 1/1 | 2/0.5 | 2/0.8 | - | | | | - 0-------1-------2-------3 - */ - const char *yaml = R"xx( -topology: mesh -material_map: - a: 1 - b: 2 - c: 3 -material_ids: [1, 1,2, 1,2, 2, 1,2,3, 2,3] -volume_fractions: [1., 0.5,0.5, 0.2,0.8, 1., 0.1,0.5,0.4, 0.8,0.2] -indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -sizes: [1, 2, 2, 1, 3, 2] -offsets: [0, 1, 3, 5, 6, 9] -)xx"; - - matset.parse(yaml); - } -}; - -TEST(mir_blueprint_utilities, matsetslice_seq) -{ - test_matset_slice::test(); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, matsetslice_omp) -{ - test_matset_slice::test(); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, matsetslice_cuda) -{ - test_matset_slice::test(); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, matsetslice_hip) -{ - test_matset_slice::test(); -} -#endif - -//------------------------------------------------------------------------------ -template -void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) -{ - axom::Array ids {{0, 1, 2, 4, 5, 6}}; - - const auto nnodes = ids.size(); - axom::Array selectedNodes( - nnodes, - nnodes, - axom::execution_space::allocatorID()); - axom::copy(selectedNodes.data(), ids.data(), nnodes * sizeof(axom::IndexType)); - - bputils::SliceData slice; - slice.m_indicesView = selectedNodes.view(); - - // host->device - conduit::Node deviceCoordset; - bputils::copy(deviceCoordset, hostCoordset); - - // Make a view. - auto coordsetView = makeView(deviceCoordset); - using CoordsetView = decltype(coordsetView); - - // Pull out selected nodes - bputils::CoordsetSlicer slicer(coordsetView); - conduit::Node newDeviceCoordset; - slicer.execute(slice, deviceCoordset, newDeviceCoordset); - - // device->host - conduit::Node newHostCoordset; - bputils::copy(newHostCoordset, newDeviceCoordset); - - // We get an explicit coordset out of the slicer. - const axom::Array x {{0., 1., 2., 0., 1., 2.}}; - const axom::Array y {{0., 0., 0., 1., 1., 1.}}; - EXPECT_TRUE(compare_views( - x.view(), - bputils::make_array_view(newHostCoordset["values/x"]))); - EXPECT_TRUE(compare_views( - y.view(), - bputils::make_array_view(newHostCoordset["values/y"]))); -} - -//------------------------------------------------------------------------------ -template -struct coordsetslicer_explicit -{ - static void test() - { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( -type: explicit -values: - x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] - y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2.] -)xx"; - - conduit::Node coordset; - coordset.parse(yaml); - - auto makeView = [](const conduit::Node &deviceCoordset) { - return axom::mir::views::make_explicit_coordset::view( - deviceCoordset); - }; - - test_coordsetslicer(coordset, makeView); - } -}; - -TEST(mir_blueprint_utilities, coordsetslicer_explicit_seq) -{ - coordsetslicer_explicit::test(); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, coordsetslicer_explicit_omp) -{ - coordsetslicer_explicit::test(); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, coordsetslicer_explicit_cuda) -{ - coordsetslicer_explicit::test(); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, coordsetslicer_explicit_hip) -{ - coordsetslicer_explicit::test(); -} -#endif - -//------------------------------------------------------------------------------ -template -struct coordsetslicer_rectilinear -{ - static void test() - { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( -type: rectilinear -values: - x: [0., 1., 2., 3.] - y: [0., 1., 2.] -)xx"; - - conduit::Node coordset; - coordset.parse(yaml); - - auto makeView = [](const conduit::Node &deviceCoordset) { - return axom::mir::views::make_rectilinear_coordset::view( - deviceCoordset); - }; - test_coordsetslicer(coordset, makeView); - } -}; - -TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_seq) -{ - coordsetslicer_rectilinear::test(); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_omp) -{ - coordsetslicer_rectilinear::test(); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_cuda) -{ - coordsetslicer_rectilinear::test(); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, coordsetslicer_rectilinear_hip) -{ - coordsetslicer_rectilinear::test(); -} -#endif - -//------------------------------------------------------------------------------ -template -struct coordsetslicer_uniform -{ - static void test() - { - /* - 8---9--10--11 - | | | | - 4---5---6---7 - | | | | - 0---1---2---3 - */ - const char *yaml = R"xx( -type: uniform -dims: - i: 4 - j: 3 -)xx"; - - conduit::Node coordset; - coordset.parse(yaml); - - auto makeView = [](const conduit::Node &deviceCoordset) { - return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); - }; - test_coordsetslicer(coordset, makeView); - } -}; - -TEST(mir_blueprint_utilities, coordsetslicer_uniform_seq) -{ - coordsetslicer_uniform::test(); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, coordsetslicer_uniform_omp) -{ - coordsetslicer_uniform::test(); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, coordsetslicer_uniform_cuda) -{ - coordsetslicer_uniform::test(); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, coordsetslicer_uniform_hip) -{ - coordsetslicer_uniform::test(); -} -#endif - //------------------------------------------------------------------------------ template struct test_extractzones @@ -1371,282 +701,6 @@ TEST(mir_blueprint_utilities, zonelistbuilder_hip) } #endif -//------------------------------------------------------------------------------ -template -struct test_mergemeshes -{ - static void test() - { - conduit::Node hostMesh; - create(hostMesh); - - // host->device - conduit::Node deviceMesh; - bputils::copy(deviceMesh, hostMesh); - - // Set up inputs. - std::vector inputs(2); - inputs[0].m_input = deviceMesh.fetch_ptr("domain0000"); - - inputs[1].m_input = deviceMesh.fetch_ptr("domain0001"); - // The node names for input 1 in the final merged mesh. - const axom::IndexType nodeMap[] = {1, 2, 5, 6, 9, 10, 13, 14, 16, 17}; - // The 2 nodes in input 1 that do not appear in input 0 - const axom::IndexType nodeSlice[] = {8, 9}; - const int allocatorID = axom::execution_space::allocatorID(); - axom::Array deviceNodeMap(10, 10, allocatorID); - axom::Array deviceNodeSlice(2, 2, allocatorID); - axom::copy(deviceNodeMap.data(), nodeMap, 10 * sizeof(axom::IndexType)); - axom::copy(deviceNodeSlice.data(), nodeSlice, 2 * sizeof(axom::IndexType)); - inputs[1].m_nodeMapView = deviceNodeMap.view(); - inputs[1].m_nodeSliceView = deviceNodeSlice.view(); - - // Execute - conduit::Node opts, deviceResult; - opts["topology"] = "mesh"; - bputils::MergeMeshes mm; - mm.execute(inputs, opts, deviceResult); - - // device->host - conduit::Node hostResult; - bputils::copy(hostResult, deviceResult); - - //printNode(hostResult); - //conduit::relay::io::blueprint::save_mesh(hostResult, "mergemeshes", "hdf5"); - - constexpr double tolerance = 1.e-7; - conduit::Node expectedResult, info; - result(expectedResult); - bool success = compareConduit(expectedResult, hostResult, tolerance, info); - if(!success) - { - info.print(); - } - EXPECT_TRUE(success); - } - - static void create(conduit::Node &mesh) - { - const char *yaml = R"xx( -domain0000: - coordsets: - coords: - type: explicit - values: - x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] - y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.] - topologies: - mesh: - type: unstructured - coordset: coords - elements: - shape: quad - connectivity: [0,1,5,4, 4,5,9,8, 8,9,13,12, 2,3,7,6, 6,7,11,10, 10,11,15,14] - sizes: [4,4,4, 4,4,4] - offsets: [0,4,8,12,16,20] - fields: - nodal: - topology: mesh - association: vertex - values: [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0] - zonal: - topology: mesh - association: element - values: [0,1,2, 3,4,5] -domain0001: - coordsets: - coords: - type: explicit - values: - x: [1., 2., 1., 2., 1., 2., 1., 2., 1.5, 1.5] - y: [0., 0., 1., 1., 2., 2., 3., 3., 0.5, 1.5] - topologies: - mesh: - type: unstructured - coordset: coords - elements: - shape: mixed - shape_map: - quad: 3 - tri: 2 - connectivity: [0,8,2, 0,1,8, 1,3,8, 8,3,2, 2,9,4, 2,3,9, 3,5,9, 5,4,9, 4,5,7,6] - sizes: [3,3,3,3, 3,3,3,3, 4] - offsets: [0,3,6,9, 12,15,18,21, 24] - shapes: [2,2,2,2, 2,2,2,2, 3] - fields: - nodal: - topology: mesh - association: vertex - values: [1,1,1,1,1,1,1,1, 2,2] - zonal: - topology: mesh - association: element - values: [0,1,2,3, 4,5,6,7, 8] -)xx"; - mesh.parse(yaml); - } - - static void result(conduit::Node &mesh) - { - const char *yaml = R"xx( -coordsets: - coords: - type: "explicit" - values: - x: [0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 1.5, 1.5] - y: [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 0.5, 1.5] -topologies: - mesh: - type: "unstructured" - coordset: "coords" - elements: - connectivity: [0, 1, 5, 4, 4, 5, 9, 8, 8, 9, 13, 12, 2, 3, 7, 6, 6, 7, 11, 10, 10, 11, 15, 14, 1, 16, 5, 1, 2, 16, 2, 6, 16, 16, 6, 5, 5, 17, 9, 5, 6, 17, 6, 10, 17, 10, 9, 17, 9, 10, 14, 13] - sizes: [4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4] - offsets: [0, 4, 8, 12, 16, 20, 24, 27, 30, 33, 36, 39, 42, 45, 48] - shape: "mixed" - shape_map: - quad: 3 - tri: 2 - shapes: [3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3] -)xx"; - mesh.parse(yaml); - } -}; - -TEST(mir_blueprint_utilities, mergemeshes_seq) -{ - test_mergemeshes::test(); -} -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, mergemeshes_omp) -{ - test_mergemeshes::test(); -} -#endif -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, mergemeshes_cuda) -{ - test_mergemeshes::test(); -} -#endif -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, mergemeshes_hip) -{ - test_mergemeshes::test(); -} -#endif - -//------------------------------------------------------------------------------ -template -struct test_fieldslicer -{ - static void test() - { - conduit::Node hostMesh; - create(hostMesh); - - // host->device - conduit::Node deviceMesh; - bputils::copy(deviceMesh, hostMesh); - - // _mir_utilities_fieldslicer_begin - std::vector indices {0, 1, 2, 7, 8, 9}; - axom::Array sliceIndices( - indices.size(), - indices.size(), - axom::execution_space::allocatorID()); - axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); - - bputils::SliceData slice; - slice.m_indicesView = sliceIndices.view(); - - conduit::Node slicedMesh; - bputils::FieldSlicer fs; - fs.execute(slice, deviceMesh["fields/scalar"], slicedMesh["fields/scalar"]); - fs.execute(slice, deviceMesh["fields/vector"], slicedMesh["fields/vector"]); - // _mir_utilities_fieldslicer_end - - // device->host - conduit::Node hostSlicedMesh; - bputils::copy(hostSlicedMesh, slicedMesh); - - std::vector resultX {0., 1., 2., 7., 8., 9.}; - std::vector resultY {0., 10., 20., 70., 80., 90.}; - - EXPECT_EQ(hostSlicedMesh["fields/scalar/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedMesh["fields/scalar/association"].as_string(), - "element"); - EXPECT_EQ(hostSlicedMesh["fields/scalar/values"].dtype().number_of_elements(), - indices.size()); - for(size_t i = 0; i < indices.size(); i++) - { - const auto acc = - hostSlicedMesh["fields/scalar/values"].as_double_accessor(); - EXPECT_EQ(acc[i], resultX[i]); - } - - EXPECT_EQ(hostSlicedMesh["fields/vector/topology"].as_string(), "mesh"); - EXPECT_EQ(hostSlicedMesh["fields/vector/association"].as_string(), - "element"); - EXPECT_EQ( - hostSlicedMesh["fields/vector/values/x"].dtype().number_of_elements(), - indices.size()); - EXPECT_EQ( - hostSlicedMesh["fields/vector/values/y"].dtype().number_of_elements(), - indices.size()); - for(size_t i = 0; i < indices.size(); i++) - { - const auto x = - hostSlicedMesh["fields/vector/values/x"].as_double_accessor(); - const auto y = - hostSlicedMesh["fields/vector/values/y"].as_double_accessor(); - EXPECT_EQ(x[i], resultX[i]); - EXPECT_EQ(y[i], resultY[i]); - } - } - - static void create(conduit::Node &fields) - { - const char *yaml = R"xx( -fields: - scalar: - topology: mesh - association: element - values: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] - vector: - topology: mesh - association: element - values: - x: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] - y: [0., 10., 20., 30., 40., 50., 60., 70., 80., 90.] -)xx"; - fields.parse(yaml); - } -}; - -TEST(mir_blueprint_utilities, fieldslicer_seq) -{ - test_fieldslicer::test(); -} -#if defined(AXOM_USE_OPENMP) -TEST(mir_blueprint_utilities, fieldslicer_omp) -{ - test_fieldslicer::test(); -} -#endif -#if defined(AXOM_USE_CUDA) -TEST(mir_blueprint_utilities, fieldslicer_cuda) -{ - test_fieldslicer::test(); -} -#endif -#if defined(AXOM_USE_HIP) -TEST(mir_blueprint_utilities, fieldslicer_hip) -{ - test_fieldslicer::test(); -} -#endif - //------------------------------------------------------------------------------ int main(int argc, char *argv[]) { diff --git a/src/axom/mir/tests/mir_equiz.cpp b/src/axom/mir/tests/mir_equiz2d.cpp similarity index 63% rename from src/axom/mir/tests/mir_equiz.cpp rename to src/axom/mir/tests/mir_equiz2d.cpp index 427dae6845..5191d7f8de 100644 --- a/src/axom/mir/tests/mir_equiz.cpp +++ b/src/axom/mir/tests/mir_equiz2d.cpp @@ -151,111 +151,6 @@ TEST(mir_equiz, equiz_uniform_unibuffer_hip) } #endif -//------------------------------------------------------------------------------ -template -void braid3d_mat_test(const std::string &type, - const std::string &mattype, - const std::string &name) -{ - namespace bputils = axom::mir::utilities::blueprint; - - axom::StackArray dims {11, 11, 11}; - axom::StackArray zoneDims {dims[0] - 1, - dims[1] - 1, - dims[2] - 1}; - - // Create the data - conduit::Node hostMesh, deviceMesh; - axom::mir::testing::data::braid(type, dims, hostMesh); - axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); - axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); -#if defined(AXOM_TESTING_SAVE_VISUALIZATION) - conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); -#endif - - // Make views. - auto coordsetView = axom::mir::views::make_explicit_coordset::view( - deviceMesh["coordsets/coords"]); - using CoordsetView = decltype(coordsetView); - - using ShapeType = axom::mir::views::HexShape; - using TopologyView = - axom::mir::views::UnstructuredTopologySingleShapeView; - auto connView = bputils::make_array_view( - deviceMesh["topologies/mesh/elements/connectivity"]); - TopologyView topologyView(connView); - - conduit::Node deviceMIRMesh; - if(mattype == "unibuffer") - { - // clang-format off - using MatsetView = axom::mir::views::UnibufferMaterialView; - MatsetView matsetView; - matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), - bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), - bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), - bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), - bputils::make_array_view(deviceMesh["matsets/mat/indices"])); - // clang-format on - - using MIR = - axom::mir::EquiZAlgorithm; - MIR m(topologyView, coordsetView, matsetView); - conduit::Node options; - options["matset"] = "mat"; - m.execute(deviceMesh, options, deviceMIRMesh); - } - - // device->host - conduit::Node hostMIRMesh; - axom::mir::utilities::blueprint::copy(hostMIRMesh, deviceMIRMesh); - -#if defined(AXOM_TESTING_SAVE_VISUALIZATION) - conduit::relay::io::blueprint::save_mesh(hostMIRMesh, name, "hdf5"); -#endif - // Handle baseline comparison. - { - std::string baselineName(yamlRoot(name)); - const auto paths = baselinePaths(); -#if defined(AXOM_TESTING_GENERATE_BASELINES) - saveBaseline(paths, baselineName, hostMIRMesh); -#else - EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh)); -#endif - } -} - -//------------------------------------------------------------------------------ -TEST(mir_equiz, equiz_hex_unibuffer_seq) -{ - AXOM_ANNOTATE_SCOPE("equiz_explicit_hex_seq"); - braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); -} - -#if defined(AXOM_USE_OPENMP) -TEST(mir_equiz, equiz_hex_unibuffer_omp) -{ - AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_omp"); - braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); -} -#endif - -#if defined(AXOM_USE_CUDA) -TEST(mir_equiz, equiz_hex_unibuffer_cuda) -{ - AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_cuda"); - braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); -} -#endif - -#if defined(AXOM_USE_HIP) -TEST(mir_equiz, equiz_hex_unibuffer_hip) -{ - AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_hip"); - braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); -} -#endif - //------------------------------------------------------------------------------ void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) { diff --git a/src/axom/mir/tests/mir_equiz3d.cpp b/src/axom/mir/tests/mir_equiz3d.cpp new file mode 100644 index 0000000000..9dfc57f9d1 --- /dev/null +++ b/src/axom/mir/tests/mir_equiz3d.cpp @@ -0,0 +1,179 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/mir.hpp" +#include "axom/primal.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +//------------------------------------------------------------------------------ + +// Uncomment to generate baselines +//#define AXOM_TESTING_GENERATE_BASELINES + +// Uncomment to save visualization files for debugging (when making baselines) +//#define AXOM_TESTING_SAVE_VISUALIZATION + +#include "axom/mir/tests/mir_testing_helpers.hpp" + +std::string baselineDirectory() +{ + return pjoin(pjoin(pjoin(dataDirectory(), "mir"), "regression"), "mir_equiz"); +} + +//------------------------------------------------------------------------------ +template +void braid3d_mat_test(const std::string &type, + const std::string &mattype, + const std::string &name) +{ + namespace bputils = axom::mir::utilities::blueprint; + + axom::StackArray dims {11, 11, 11}; + axom::StackArray zoneDims {dims[0] - 1, + dims[1] - 1, + dims[2] - 1}; + + // Create the data + conduit::Node hostMesh, deviceMesh; + axom::mir::testing::data::braid(type, dims, hostMesh); + axom::mir::testing::data::make_matset(mattype, "mesh", zoneDims, hostMesh); + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMesh, name + "_orig", "hdf5"); +#endif + + // Make views. + auto coordsetView = axom::mir::views::make_explicit_coordset::view( + deviceMesh["coordsets/coords"]); + using CoordsetView = decltype(coordsetView); + + using ShapeType = axom::mir::views::HexShape; + using TopologyView = + axom::mir::views::UnstructuredTopologySingleShapeView; + auto connView = bputils::make_array_view( + deviceMesh["topologies/mesh/elements/connectivity"]); + TopologyView topologyView(connView); + + conduit::Node deviceMIRMesh; + if(mattype == "unibuffer") + { + // clang-format off + using MatsetView = axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set(bputils::make_array_view(deviceMesh["matsets/mat/material_ids"]), + bputils::make_array_view(deviceMesh["matsets/mat/volume_fractions"]), + bputils::make_array_view(deviceMesh["matsets/mat/sizes"]), + bputils::make_array_view(deviceMesh["matsets/mat/offsets"]), + bputils::make_array_view(deviceMesh["matsets/mat/indices"])); + // clang-format on + + using MIR = + axom::mir::EquiZAlgorithm; + MIR m(topologyView, coordsetView, matsetView); + conduit::Node options; + options["matset"] = "mat"; + m.execute(deviceMesh, options, deviceMIRMesh); + } + + // device->host + conduit::Node hostMIRMesh; + axom::mir::utilities::blueprint::copy(hostMIRMesh, deviceMIRMesh); + +#if defined(AXOM_TESTING_SAVE_VISUALIZATION) + conduit::relay::io::blueprint::save_mesh(hostMIRMesh, name, "hdf5"); +#endif + // Handle baseline comparison. + { + std::string baselineName(yamlRoot(name)); + const auto paths = baselinePaths(); +#if defined(AXOM_TESTING_GENERATE_BASELINES) + saveBaseline(paths, baselineName, hostMIRMesh); +#else + EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh)); +#endif + } +} + +//------------------------------------------------------------------------------ +TEST(mir_equiz, equiz_hex_unibuffer_seq) +{ + AXOM_ANNOTATE_SCOPE("equiz_explicit_hex_seq"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_equiz, equiz_hex_unibuffer_omp) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_omp"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_equiz, equiz_hex_unibuffer_cuda) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_cuda"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_equiz, equiz_hex_unibuffer_hip) +{ + AXOM_ANNOTATE_SCOPE("equiz_hex_unibuffer_hip"); + braid3d_mat_test("hexs", "unibuffer", "equiz_hex_unibuffer"); +} +#endif + +//------------------------------------------------------------------------------ +void conduit_debug_err_handler(const std::string &s1, const std::string &s2, int i1) +{ + std::cout << "s1=" << s1 << ", s2=" << s2 << ", i1=" << i1 << std::endl; + // This is on purpose. + while(1) + ; +} + +//------------------------------------------------------------------------------ + +int main(int argc, char *argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + // Define command line options. + bool handler = true; + axom::CLI::App app; + app.add_option("--handler", handler) + ->description("Install a custom error handler that loops forever."); +#if defined(AXOM_USE_CALIPER) + std::string annotationMode("none"); + app.add_option("--caliper", annotationMode) + ->description( + "caliper annotation mode. Valid options include 'none' and 'report'. " + "Use 'help' to see full list.") + ->capture_default_str() + ->check(axom::utilities::ValidCaliperMode); +#endif + // Parse command line options. + app.parse(argc, argv); + +#if defined(AXOM_USE_CALIPER) + axom::utilities::raii::AnnotationsWrapper annotations_raii_wrapper( + annotationMode); +#endif + + axom::slic::SimpleLogger logger; // create & initialize test logger, + if(handler) + { + conduit::utils::set_error_handler(conduit_debug_err_handler); + } + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_mergemeshes.cpp b/src/axom/mir/tests/mir_mergemeshes.cpp new file mode 100644 index 0000000000..f45530afac --- /dev/null +++ b/src/axom/mir/tests/mir_mergemeshes.cpp @@ -0,0 +1,196 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +#include "axom/mir/tests/mir_testing_helpers.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +#include +#include + +namespace mir = axom::mir; +namespace bputils = axom::mir::utilities::blueprint; + +//------------------------------------------------------------------------------ +template +struct test_mergemeshes +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // Set up inputs. + std::vector inputs(2); + inputs[0].m_input = deviceMesh.fetch_ptr("domain0000"); + + inputs[1].m_input = deviceMesh.fetch_ptr("domain0001"); + // The node names for input 1 in the final merged mesh. + const axom::IndexType nodeMap[] = {1, 2, 5, 6, 9, 10, 13, 14, 16, 17}; + // The 2 nodes in input 1 that do not appear in input 0 + const axom::IndexType nodeSlice[] = {8, 9}; + const int allocatorID = axom::execution_space::allocatorID(); + axom::Array deviceNodeMap(10, 10, allocatorID); + axom::Array deviceNodeSlice(2, 2, allocatorID); + axom::copy(deviceNodeMap.data(), nodeMap, 10 * sizeof(axom::IndexType)); + axom::copy(deviceNodeSlice.data(), nodeSlice, 2 * sizeof(axom::IndexType)); + inputs[1].m_nodeMapView = deviceNodeMap.view(); + inputs[1].m_nodeSliceView = deviceNodeSlice.view(); + + // Execute + conduit::Node opts, deviceResult; + opts["topology"] = "mesh"; + bputils::MergeMeshes mm; + mm.execute(inputs, opts, deviceResult); + + // device->host + conduit::Node hostResult; + bputils::copy(hostResult, deviceResult); + + //printNode(hostResult); + //conduit::relay::io::blueprint::save_mesh(hostResult, "mergemeshes", "hdf5"); + + constexpr double tolerance = 1.e-7; + conduit::Node expectedResult, info; + result(expectedResult); + bool success = compareConduit(expectedResult, hostResult, tolerance, info); + if(!success) + { + info.print(); + } + EXPECT_TRUE(success); + } + + static void create(conduit::Node &mesh) + { + const char *yaml = R"xx( +domain0000: + coordsets: + coords: + type: explicit + values: + x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] + y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.] + topologies: + mesh: + type: unstructured + coordset: coords + elements: + shape: quad + connectivity: [0,1,5,4, 4,5,9,8, 8,9,13,12, 2,3,7,6, 6,7,11,10, 10,11,15,14] + sizes: [4,4,4, 4,4,4] + offsets: [0,4,8,12,16,20] + fields: + nodal: + topology: mesh + association: vertex + values: [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0] + zonal: + topology: mesh + association: element + values: [0,1,2, 3,4,5] +domain0001: + coordsets: + coords: + type: explicit + values: + x: [1., 2., 1., 2., 1., 2., 1., 2., 1.5, 1.5] + y: [0., 0., 1., 1., 2., 2., 3., 3., 0.5, 1.5] + topologies: + mesh: + type: unstructured + coordset: coords + elements: + shape: mixed + shape_map: + quad: 3 + tri: 2 + connectivity: [0,8,2, 0,1,8, 1,3,8, 8,3,2, 2,9,4, 2,3,9, 3,5,9, 5,4,9, 4,5,7,6] + sizes: [3,3,3,3, 3,3,3,3, 4] + offsets: [0,3,6,9, 12,15,18,21, 24] + shapes: [2,2,2,2, 2,2,2,2, 3] + fields: + nodal: + topology: mesh + association: vertex + values: [1,1,1,1,1,1,1,1, 2,2] + zonal: + topology: mesh + association: element + values: [0,1,2,3, 4,5,6,7, 8] +)xx"; + mesh.parse(yaml); + } + + static void result(conduit::Node &mesh) + { + const char *yaml = R"xx( +coordsets: + coords: + type: "explicit" + values: + x: [0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 1.5, 1.5] + y: [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 0.5, 1.5] +topologies: + mesh: + type: "unstructured" + coordset: "coords" + elements: + connectivity: [0, 1, 5, 4, 4, 5, 9, 8, 8, 9, 13, 12, 2, 3, 7, 6, 6, 7, 11, 10, 10, 11, 15, 14, 1, 16, 5, 1, 2, 16, 2, 6, 16, 16, 6, 5, 5, 17, 9, 5, 6, 17, 6, 10, 17, 10, 9, 17, 9, 10, 14, 13] + sizes: [4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4] + offsets: [0, 4, 8, 12, 16, 20, 24, 27, 30, 33, 36, 39, 42, 45, 48] + shape: "mixed" + shape_map: + quad: 3 + tri: 2 + shapes: [3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 3] +)xx"; + mesh.parse(yaml); + } +}; + +TEST(mir_mergemeshes, mergemeshes_seq) +{ + test_mergemeshes::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_mergemeshes, mergemeshes_omp) +{ + test_mergemeshes::test(); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_mergemeshes, mergemeshes_cuda) +{ + test_mergemeshes::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_mergemeshes, mergemeshes_hip) +{ + test_mergemeshes::test(); +} +#endif + +//------------------------------------------------------------------------------ +int main(int argc, char *argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_node_to_zone_relation.cpp b/src/axom/mir/tests/mir_node_to_zone_relation.cpp new file mode 100644 index 0000000000..c9fda27e66 --- /dev/null +++ b/src/axom/mir/tests/mir_node_to_zone_relation.cpp @@ -0,0 +1,330 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +#include "axom/mir/tests/mir_testing_helpers.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +#include +#include + +namespace mir = axom::mir; +namespace bputils = axom::mir::utilities::blueprint; + +//------------------------------------------------------------------------------ +template +struct test_node_to_zone_relation_builder +{ + static void test(const conduit::Node &hostMesh) + { + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + // _mir_utilities_n2zrel_begin + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + // _mir_utilities_n2zrel_end + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); + + // Expected answers + // clang-format off + const int zones[] = { + 0, + 0, 1, + 1, 2, + 2, + 0, 3, // NOTE: these are sorted here + 0, 1, 3, 4, + 1, 2, 4, 5, + 2, 5, + 3, + 3, 4, + 4, 5, + 5 + }; + const int sizes[] = {1, 2, 2, 1, 2, 4, 4, 2, 1, 2, 2, 1}; + const int offsets[] = {0, 1, 3, 5, 6, 8, 12, 16, 18, 19, 21, 23}; + // clang-format on + + // Compare answers. + compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); + } + + static void compareRelation(const conduit::Node &hostRelation, + const axom::ArrayView &zones, + const axom::ArrayView &sizes, + const axom::ArrayView &offsets) + { + const auto zonesView = + bputils::make_array_view(hostRelation["zones"]); + const auto sizesView = + bputils::make_array_view(hostRelation["sizes"]); + const auto offsetsView = + bputils::make_array_view(hostRelation["offsets"]); + EXPECT_EQ(sizesView.size(), sizes.size()); + EXPECT_EQ(offsetsView.size(), offsets.size()); + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + EXPECT_EQ(sizes[i], sizesView[i]); + EXPECT_EQ(offsets[i], offsetsView[i]); + } + for(axom::IndexType i = 0; i < sizesView.size(); i++) + { + // Sort the result so we can compare to the expected answer. + IndexT *begin = zonesView.data() + offsetsView[i]; + IndexT *end = zonesView.data() + offsetsView[i] + sizesView[i]; + std::sort(begin, end); + + for(int j = 0; j < sizesView[i]; j++) + { + EXPECT_EQ(zones[offsets[i] + j], zonesView[offsetsView[i] + j]); + } + } + } +}; + +TEST(mir_node_to_zone_relation, n2zrel_unstructured_seq) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_node_to_zone_relation, n2zrel_unstructured_omp) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_node_to_zone_relation, n2zrel_unstructured_cuda) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_node_to_zone_relation, n2zrel_unstructured_hip) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("quads", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif + +TEST(mir_node_to_zone_relation, n2zrel_rectilinear_seq) +{ + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_node_to_zone_relation, n2zrel_rectilinear_omp) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_node_to_zone_relation, n2zrel_rectilinear_cuda) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_node_to_zone_relation, n2zrel_rectilinear_hip) +{ + conduit::Node mesh; + axom::StackArray dims {{4, 3}}; + axom::mir::testing::data::braid("rectilinear", dims, mesh); + test_node_to_zone_relation_builder::test(mesh); +} +#endif + +template +struct test_node_to_zone_relation_builder_polyhedral + : public test_node_to_zone_relation_builder +{ + using SuperClass = test_node_to_zone_relation_builder; + + static void test(const conduit::Node &hostMesh) + { + // host -> device + conduit::Node deviceMesh; + axom::mir::utilities::blueprint::copy(deviceMesh, hostMesh); + const conduit::Node &deviceTopo = deviceMesh["topologies/mesh"]; + const conduit::Node &deviceCoordset = deviceMesh["coordsets/coords"]; + + // Run the algorithm on the device + conduit::Node deviceRelation; + axom::mir::utilities::blueprint::NodeToZoneRelationBuilder n2z; + n2z.execute(deviceTopo, deviceCoordset, deviceRelation); + + // device -> host + conduit::Node hostRelation; + axom::mir::utilities::blueprint::copy(hostRelation, deviceRelation); + + // Expected answers + // clang-format off + const int zones[] = { + /*node 0*/ 0, + /*node 1*/ 0, 1, + /*node 2*/ 1, + /*node 3*/ 0, 2, + /*node 4*/ 0, 1, 2, 3, + /*node 5*/ 1, 3, + /*node 6*/ 2, + /*node 7*/ 2, 3, + /*node 8*/ 3, + + /*node 9*/ 0, 4, + /*node 10*/ 0, 1, 4, 5, + /*node 11*/ 1, 5, + /*node 12*/ 0, 2, 4, 6, + /*node 13*/ 0, 1, 2, 3, 4, 5, 6, 7, + /*node 14*/ 1, 3, 5, 7, + /*node 15*/ 2, 6, + /*node 16*/ 2, 3, 6, 7, + /*node 17*/ 3, 7, + + /*node 18*/ 4, + /*node 19*/ 4, 5, + /*node 20*/ 5, + /*node 21*/ 4, 6, + /*node 22*/ 4, 5, 6, 7, + /*node 23*/ 5, 7, + /*node 24*/ 6, + /*node 25*/ 6, 7, + /*node 26*/ 7 + }; + const int sizes[] = {1, 2, 1, 2, 4, 2, 1, 2, 1, + 2, 4, 2, 4, 8, 4, 2, 4, 2, + 1, 2, 1, 2, 4, 2, 1, 2, 1 + }; + const int offsets[] = {0, 1, 3, 4, 6, 10, 12, 13, 15, + 16, 18, 22, 24, 28, 36, 40, 42, 46, + 48, 49, 51, 52, 54, 58, 60, 61, 63}; + // clang-format on + + // Compare answers. + SuperClass::compareRelation( + hostRelation, + axom::ArrayView(zones, sizeof(zones) / sizeof(int)), + axom::ArrayView(sizes, sizeof(sizes) / sizeof(int)), + axom::ArrayView(offsets, sizeof(offsets) / sizeof(int))); + } + + static void create(conduit::Node &mesh) + { + conduit::blueprint::mesh::examples::basic("polyhedra", 3, 3, 3, mesh); + // Make sure all the types are the same. + conduit::blueprint::mesh::utils::convert( + mesh, + conduit::DataType::int32(), + std::vector {{"topologies/mesh/elements/connectivity", + "topologies/mesh/elements/sizes", + "topologies/mesh/elements/offsets", + "topologies/mesh/subelements/connectivity", + "topologies/mesh/subelements/sizes", + "topologies/mesh/subelements/offsets"}}); + } +}; + +TEST(mir_node_to_zone_relation, n2zrel_polyhedral_seq) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_node_to_zone_relation, n2zrel_polyhedral_omp) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_node_to_zone_relation, n2zrel_polyhedral_cuda) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_node_to_zone_relation, n2zrel_polyhedral_hip) +{ + conduit::Node mesh; + test_node_to_zone_relation_builder_polyhedral::create( + mesh); + test_node_to_zone_relation_builder_polyhedral::test( + mesh); +} +#endif + +//------------------------------------------------------------------------------ +int main(int argc, char *argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_slicers.cpp b/src/axom/mir/tests/mir_slicers.cpp new file mode 100644 index 0000000000..ef2396aff8 --- /dev/null +++ b/src/axom/mir/tests/mir_slicers.cpp @@ -0,0 +1,485 @@ +// Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and +// other Axom Project Developers. See the top-level COPYRIGHT file for details. +// +// SPDX-License-Identifier: (BSD-3-Clause) + +#include "gtest/gtest.h" + +#include "axom/core.hpp" +#include "axom/slic.hpp" +#include "axom/mir.hpp" + +#include "axom/mir/tests/mir_testing_helpers.hpp" +#include "axom/mir/tests/mir_testing_data_helpers.hpp" + +#include +#include + +namespace mir = axom::mir; +namespace bputils = axom::mir::utilities::blueprint; + +//------------------------------------------------------------------------------ +template +struct test_matset_slice +{ + static void test() + { + conduit::Node hostMatset; + create(hostMatset); + + // host->device + conduit::Node deviceMatset; + bputils::copy(deviceMatset, hostMatset); + + axom::Array ids {{1, 3, 5}}; + axom::Array selectedZones( + 3, + 3, + axom::execution_space::allocatorID()); + axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); + + using MatsetView = + axom::mir::views::UnibufferMaterialView; + MatsetView matsetView; + matsetView.set( + bputils::make_array_view(deviceMatset["material_ids"]), + bputils::make_array_view( + deviceMatset["volume_fractions"]), + bputils::make_array_view(deviceMatset["sizes"]), + bputils::make_array_view(deviceMatset["offsets"]), + bputils::make_array_view(deviceMatset["indices"])); + + // Slice it. + bputils::MatsetSlicer slicer(matsetView); + conduit::Node newDeviceMatset; + bputils::SliceData slice; + slice.m_indicesView = selectedZones.view(); + slicer.execute(slice, deviceMatset, newDeviceMatset); + + // device->host + conduit::Node newHostMatset; + bputils::copy(newHostMatset, newDeviceMatset); + + // Expected answers. + const axom::Array sizes {{2, 1, 2}}; + const axom::Array offsets {{0, 2, 3}}; + const axom::Array indices {{0, 1, 2, 3, 4}}; + const axom::Array material_ids {{1, 2, 2, 2, 3}}; + const axom::Array volume_fractions { + {0.5, 0.5, 1.0, 0.8, 0.2}}; + + EXPECT_EQ(conduit::DataType::INT64_ID, + newHostMatset["material_ids"].dtype().id()); + EXPECT_EQ(conduit::DataType::FLOAT64_ID, + newHostMatset["volume_fractions"].dtype().id()); + + EXPECT_TRUE(compare_views( + sizes.view(), + bputils::make_array_view(newHostMatset["sizes"]))); + EXPECT_TRUE(compare_views( + offsets.view(), + bputils::make_array_view(newHostMatset["offsets"]))); + EXPECT_TRUE(compare_views( + indices.view(), + bputils::make_array_view(newHostMatset["indices"]))); + EXPECT_TRUE(compare_views( + material_ids.view(), + bputils::make_array_view(newHostMatset["material_ids"]))); + EXPECT_TRUE(compare_views(volume_fractions.view(), + bputils::make_array_view( + newHostMatset["volume_fractions"]))); + } + + static void create(conduit::Node &matset) + { + /* + 8-------9------10------11 + | 2/1 | 1/0.1 | 2/0.8 | + | | 2/0.5 | 3/0.2 | + | | 3/0.4 | | + 4-------5-------6-------7 + | | 1/0.5 | 1/0.2 | + | 1/1 | 2/0.5 | 2/0.8 | + | | | | + 0-------1-------2-------3 + */ + const char *yaml = R"xx( +topology: mesh +material_map: + a: 1 + b: 2 + c: 3 +material_ids: [1, 1,2, 1,2, 2, 1,2,3, 2,3] +volume_fractions: [1., 0.5,0.5, 0.2,0.8, 1., 0.1,0.5,0.4, 0.8,0.2] +indices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +sizes: [1, 2, 2, 1, 3, 2] +offsets: [0, 1, 3, 5, 6, 9] +)xx"; + + matset.parse(yaml); + } +}; + +TEST(mir_slicers, matsetslice_seq) +{ + test_matset_slice::test(); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_slicers, matsetslice_omp) +{ + test_matset_slice::test(); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_slicers, matsetslice_cuda) +{ + test_matset_slice::test(); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_slicers, matsetslice_hip) +{ + test_matset_slice::test(); +} +#endif + +//------------------------------------------------------------------------------ +template +void test_coordsetslicer(const conduit::Node &hostCoordset, Func &&makeView) +{ + axom::Array ids {{0, 1, 2, 4, 5, 6}}; + + const auto nnodes = ids.size(); + axom::Array selectedNodes( + nnodes, + nnodes, + axom::execution_space::allocatorID()); + axom::copy(selectedNodes.data(), ids.data(), nnodes * sizeof(axom::IndexType)); + + bputils::SliceData slice; + slice.m_indicesView = selectedNodes.view(); + + // host->device + conduit::Node deviceCoordset; + bputils::copy(deviceCoordset, hostCoordset); + + // Make a view. + auto coordsetView = makeView(deviceCoordset); + using CoordsetView = decltype(coordsetView); + + // Pull out selected nodes + bputils::CoordsetSlicer slicer(coordsetView); + conduit::Node newDeviceCoordset; + slicer.execute(slice, deviceCoordset, newDeviceCoordset); + + // device->host + conduit::Node newHostCoordset; + bputils::copy(newHostCoordset, newDeviceCoordset); + + // We get an explicit coordset out of the slicer. + const axom::Array x {{0., 1., 2., 0., 1., 2.}}; + const axom::Array y {{0., 0., 0., 1., 1., 1.}}; + EXPECT_TRUE(compare_views( + x.view(), + bputils::make_array_view(newHostCoordset["values/x"]))); + EXPECT_TRUE(compare_views( + y.view(), + bputils::make_array_view(newHostCoordset["values/y"]))); +} + +//------------------------------------------------------------------------------ +template +struct coordsetslicer_explicit +{ + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: explicit +values: + x: [0., 1., 2., 3., 0., 1., 2., 3., 0., 1., 2., 3.] + y: [0., 0., 0., 0., 1., 1., 1., 1., 2., 2., 2., 2.] +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) { + return axom::mir::views::make_explicit_coordset::view( + deviceCoordset); + }; + + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_slicers, coordsetslicer_explicit_seq) +{ + coordsetslicer_explicit::test(); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_slicers, coordsetslicer_explicit_omp) +{ + coordsetslicer_explicit::test(); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_slicers, coordsetslicer_explicit_cuda) +{ + coordsetslicer_explicit::test(); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_slicers, coordsetslicer_explicit_hip) +{ + coordsetslicer_explicit::test(); +} +#endif + +//------------------------------------------------------------------------------ +template +struct coordsetslicer_rectilinear +{ + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: rectilinear +values: + x: [0., 1., 2., 3.] + y: [0., 1., 2.] +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) { + return axom::mir::views::make_rectilinear_coordset::view( + deviceCoordset); + }; + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_slicers, coordsetslicer_rectilinear_seq) +{ + coordsetslicer_rectilinear::test(); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_slicers, coordsetslicer_rectilinear_omp) +{ + coordsetslicer_rectilinear::test(); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_slicers, coordsetslicer_rectilinear_cuda) +{ + coordsetslicer_rectilinear::test(); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_slicers, coordsetslicer_rectilinear_hip) +{ + coordsetslicer_rectilinear::test(); +} +#endif + +//------------------------------------------------------------------------------ +template +struct coordsetslicer_uniform +{ + static void test() + { + /* + 8---9--10--11 + | | | | + 4---5---6---7 + | | | | + 0---1---2---3 + */ + const char *yaml = R"xx( +type: uniform +dims: + i: 4 + j: 3 +)xx"; + + conduit::Node coordset; + coordset.parse(yaml); + + auto makeView = [](const conduit::Node &deviceCoordset) { + return axom::mir::views::make_uniform_coordset<2>::view(deviceCoordset); + }; + test_coordsetslicer(coordset, makeView); + } +}; + +TEST(mir_slicers, coordsetslicer_uniform_seq) +{ + coordsetslicer_uniform::test(); +} + +#if defined(AXOM_USE_OPENMP) +TEST(mir_slicers, coordsetslicer_uniform_omp) +{ + coordsetslicer_uniform::test(); +} +#endif + +#if defined(AXOM_USE_CUDA) +TEST(mir_slicers, coordsetslicer_uniform_cuda) +{ + coordsetslicer_uniform::test(); +} +#endif + +#if defined(AXOM_USE_HIP) +TEST(mir_slicers, coordsetslicer_uniform_hip) +{ + coordsetslicer_uniform::test(); +} +#endif + +//------------------------------------------------------------------------------ +template +struct test_fieldslicer +{ + static void test() + { + conduit::Node hostMesh; + create(hostMesh); + + // host->device + conduit::Node deviceMesh; + bputils::copy(deviceMesh, hostMesh); + + // _mir_utilities_fieldslicer_begin + std::vector indices {0, 1, 2, 7, 8, 9}; + axom::Array sliceIndices( + indices.size(), + indices.size(), + axom::execution_space::allocatorID()); + axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); + + bputils::SliceData slice; + slice.m_indicesView = sliceIndices.view(); + + conduit::Node slicedMesh; + bputils::FieldSlicer fs; + fs.execute(slice, deviceMesh["fields/scalar"], slicedMesh["fields/scalar"]); + fs.execute(slice, deviceMesh["fields/vector"], slicedMesh["fields/vector"]); + // _mir_utilities_fieldslicer_end + + // device->host + conduit::Node hostSlicedMesh; + bputils::copy(hostSlicedMesh, slicedMesh); + + std::vector resultX {0., 1., 2., 7., 8., 9.}; + std::vector resultY {0., 10., 20., 70., 80., 90.}; + + EXPECT_EQ(hostSlicedMesh["fields/scalar/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedMesh["fields/scalar/association"].as_string(), + "element"); + EXPECT_EQ(hostSlicedMesh["fields/scalar/values"].dtype().number_of_elements(), + indices.size()); + for(size_t i = 0; i < indices.size(); i++) + { + const auto acc = + hostSlicedMesh["fields/scalar/values"].as_double_accessor(); + EXPECT_EQ(acc[i], resultX[i]); + } + + EXPECT_EQ(hostSlicedMesh["fields/vector/topology"].as_string(), "mesh"); + EXPECT_EQ(hostSlicedMesh["fields/vector/association"].as_string(), + "element"); + EXPECT_EQ( + hostSlicedMesh["fields/vector/values/x"].dtype().number_of_elements(), + indices.size()); + EXPECT_EQ( + hostSlicedMesh["fields/vector/values/y"].dtype().number_of_elements(), + indices.size()); + for(size_t i = 0; i < indices.size(); i++) + { + const auto x = + hostSlicedMesh["fields/vector/values/x"].as_double_accessor(); + const auto y = + hostSlicedMesh["fields/vector/values/y"].as_double_accessor(); + EXPECT_EQ(x[i], resultX[i]); + EXPECT_EQ(y[i], resultY[i]); + } + } + + static void create(conduit::Node &fields) + { + const char *yaml = R"xx( +fields: + scalar: + topology: mesh + association: element + values: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] + vector: + topology: mesh + association: element + values: + x: [0., 1., 2., 3., 4., 5., 6., 7., 8., 9.] + y: [0., 10., 20., 30., 40., 50., 60., 70., 80., 90.] +)xx"; + fields.parse(yaml); + } +}; + +TEST(mir_slicers, fieldslicer_seq) +{ + test_fieldslicer::test(); +} +#if defined(AXOM_USE_OPENMP) +TEST(mir_slicers, fieldslicer_omp) +{ + test_fieldslicer::test(); +} +#endif +#if defined(AXOM_USE_CUDA) +TEST(mir_slicers, fieldslicer_cuda) +{ + test_fieldslicer::test(); +} +#endif +#if defined(AXOM_USE_HIP) +TEST(mir_slicers, fieldslicer_hip) +{ + test_fieldslicer::test(); +} +#endif + +//------------------------------------------------------------------------------ +int main(int argc, char *argv[]) +{ + int result = 0; + ::testing::InitGoogleTest(&argc, argv); + + axom::slic::SimpleLogger logger; // create & initialize test logger, + + result = RUN_ALL_TESTS(); + return result; +} diff --git a/src/axom/mir/tests/mir_testing_helpers.hpp b/src/axom/mir/tests/mir_testing_helpers.hpp index bed150fbc6..8e5fb75dbc 100644 --- a/src/axom/mir/tests/mir_testing_helpers.hpp +++ b/src/axom/mir/tests/mir_testing_helpers.hpp @@ -266,4 +266,32 @@ bool compareBaseline(const std::vector &baselinePaths, return success; } +//------------------------------------------------------------------------------ +template +bool compare_views(const Container1 &a, const Container2 &b) +{ + bool eq = a.size() == b.size(); + for(axom::IndexType i = 0; i < a.size() && eq; i++) + { + eq &= a[i] == b[i]; + } + if(!eq) + { + std::cout << "a={"; + for(axom::IndexType i = 0; i < a.size(); i++) + { + if(i > 0) std::cout << ", "; + std::cout << a[i]; + } + std::cout << "}" << std::endl; + std::cout << "b={"; + for(axom::IndexType i = 0; i < a.size(); i++) + { + if(i > 0) std::cout << ", "; + std::cout << b[i]; + } + std::cout << "}" << std::endl; + } + return eq; +} #endif From 9f9bbdcd49b0cc3c7a42fac3b66feaaa79ab4738 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 12:47:25 -0700 Subject: [PATCH 283/290] Removed reference mesh generation routines from MeshTester --- src/axom/mir/MeshTester.cpp | 1104 +---------------- src/axom/mir/MeshTester.hpp | 243 ++-- src/axom/mir/doxygen_mainpage.md | 46 + .../mir_concentric_circles.cpp | 49 +- 4 files changed, 208 insertions(+), 1234 deletions(-) create mode 100644 src/axom/mir/doxygen_mainpage.md diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index e128a6d692..76daf5a546 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -4,7 +4,7 @@ // SPDX-License-Identifier: (BSD-3-Clause) #include "MeshTester.hpp" -#include +#include "axom/mir.hpp" namespace numerics = axom::numerics; namespace slam = axom::slam; @@ -15,19 +15,19 @@ namespace axom namespace mir { //-------------------------------------------------------------------------------- -/** - * \brief Calculates the percent overlap between the given circle and quad. - * - * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. - * \param circleCenter The center point of the circle. - * \param circleRadius The radius of the circle. - * \param quadP0 The upper left vertex of the quad. - * \param quadP1 The lower left vertex of the quad. - * \param quadP2 The lower right vertex of the quad. - * \param quadP3 The upper right vertex of the quad. - * - * /return The percent value overlap of the circle and the quad between [0, 1]. - */ +/*! + * \brief Calculates the percent overlap between the given circle and quad. + * + * \param gridSize The size of the uniform grid which will be sampled over to check for overlap. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * /return The percent value overlap of the circle and the quad between [0, 1]. + */ template static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, @@ -63,17 +63,17 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, { // Some of the quad overlaps the circle, so run the Monte Carlo sampling to determine how much const int numSamples = std::min(gridSize, 20); - axom::float64 delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / - static_cast(numSamples - 1); - axom::float64 delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / - static_cast(numSamples - 1); + float delta_x = axom::utilities::abs(quadP2[0] - quadP1[0]) / + static_cast(numSamples - 1); + float delta_y = axom::utilities::abs(quadP0[1] - quadP1[1]) / + static_cast(numSamples - 1); int countOverlap = 0; for(int y = 0; y < numSamples; ++y) { for(int x = 0; x < numSamples; ++x) { - PointType samplePoint = PointType::make_point(delta_x * x + quadP1[0], - delta_y * y + quadP1[1]); + PointType samplePoint({delta_x * x + quadP1[0], + delta_y * y + quadP1[1]}); if(primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } @@ -83,99 +83,6 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, } } -//-------------------------------------------------------------------------------- - -MIRMesh MeshTester::initTestCaseOne() -{ - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; - - int numElements = 9; - int numVertices = 16; - mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems(numElements); // Construct an element set with 9 elements - - // Create the mesh connectivity information - topoData.m_evInds = { - 0, 4, 5, 1, // elem 0, card 4, start 0 - 1, 5, 6, 2, // elem 1, card 4, start 4 - 2, 6, 7, 3, // elem 2, card 4, start 8 - 4, 8, 9, 5, // elem 3, card 4, start 12 - 5, 9, 10, 6, // elem 4, card 4, start 16 - 6, 10, 11, 7, // elem 5, card 4, start 20 - 8, 12, 13, 9, // elem 6, card 4, start 24 - 9, 13, 14, 10, // elem 7, card 4, start 28 - 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; - topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0, 1, // vert 1, card 2, start 1 - 1, 2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0, 3, // vert 4, card 2, start 6 - 0, 1, 3, 4, // vert 5, card 4, start 8 - 1, 2, 4, 5, // vert 6, card 4, start 12 - 2, 5, // vert 7, card 2, start 16 - 3, 6, // vert 8, card 2, start 18 - 3, 4, 6, 7, // vert 9, card 4, start 20 - 4, 5, 7, 8, // vert 10, card 4, start 24 - 5, 8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6, 7, // vert 13, card 2, start 31 - 7, 8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData - .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; - - int numMaterials = 2; - enum - { - GREEN = 0, - BLUE = 1 - }; - - volFracs.resize(numMaterials); - - volFracs[GREEN] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - volFracs[BLUE] = {0.0, 0.0, 0.0, 0.0, 0.5, 0.8, 0.8, 1.0, 1.0}; - - mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), - mir::Point2::make_point(1.0, 3.0), - mir::Point2::make_point(2.0, 3.0), - mir::Point2::make_point(3.0, 3.0), - - mir::Point2::make_point(0.0, 2.0), - mir::Point2::make_point(1.0, 2.0), - mir::Point2::make_point(2.0, 2.0), - mir::Point2::make_point(3.0, 2.0), - - mir::Point2::make_point(0.0, 1.0), - mir::Point2::make_point(1.0, 1.0), - mir::Point2::make_point(2.0, 1.0), - mir::Point2::make_point(3.0, 1.0), - - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0), - mir::Point2::make_point(2.0, 0.0), - mir::Point2::make_point(3.0, 0.0)}; - - mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = - {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves - mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); - - return testMesh; -} - //-------------------------------------------------------------------------------- void MeshTester::mesh3x3(conduit::Node& mesh) { @@ -247,100 +154,6 @@ void MeshTester::initTestCaseOne(conduit::Node& mesh) // clang-format on } -//-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initTestCaseTwo() -{ - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; - - int numElements = 9; - int numVertices = 16; - mir::VertSet verts(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems(numElements); // Construct an element set with 9 elements - - // Create the mesh connectivity information - topoData.m_evInds = { - 0, 4, 5, 1, // elem 0, card 4, start 0 - 1, 5, 6, 2, // elem 1, card 4, start 4 - 2, 6, 7, 3, // elem 2, card 4, start 8 - 4, 8, 9, 5, // elem 3, card 4, start 12 - 5, 9, 10, 6, // elem 4, card 4, start 16 - 6, 10, 11, 7, // elem 5, card 4, start 20 - 8, 12, 13, 9, // elem 6, card 4, start 24 - 9, 13, 14, 10, // elem 7, card 4, start 28 - 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; - topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0, 1, // vert 1, card 2, start 1 - 1, 2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0, 3, // vert 4, card 2, start 6 - 0, 1, 3, 4, // vert 5, card 4, start 8 - 1, 2, 4, 5, // vert 6, card 4, start 12 - 2, 5, // vert 7, card 2, start 16 - 3, 6, // vert 8, card 2, start 18 - 3, 4, 6, 7, // vert 9, card 4, start 20 - 4, 5, 7, 8, // vert 10, card 4, start 24 - 5, 8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6, 7, // vert 13, card 2, start 31 - 7, 8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData - .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; - - int numMaterials = 3; - enum - { - BLUE = 0, - RED = 1, - ORANGE = 2 - }; - - volFracs.resize(numMaterials); - volFracs[BLUE] = {1.0, 1.0, 1.0, 1.0, 0.5, 0.2, 0.2, 0.0, 0.0}; - volFracs[RED] = {0.0, 0.0, 0.0, 0.0, 0.3, 0.8, 0.0, 0.3, 1.0}; - volFracs[ORANGE] = {0.0, 0.0, 0.0, 0.0, 0.2, 0.0, 0.8, 0.7, 0.0}; - - mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), - mir::Point2::make_point(1.0, 3.0), - mir::Point2::make_point(2.0, 3.0), - mir::Point2::make_point(3.0, 3.0), - - mir::Point2::make_point(0.0, 2.0), - mir::Point2::make_point(1.0, 2.0), - mir::Point2::make_point(2.0, 2.0), - mir::Point2::make_point(3.0, 2.0), - - mir::Point2::make_point(0.0, 1.0), - mir::Point2::make_point(1.0, 1.0), - mir::Point2::make_point(2.0, 1.0), - mir::Point2::make_point(3.0, 1.0), - - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0), - mir::Point2::make_point(2.0, 0.0), - mir::Point2::make_point(3.0, 0.0)}; - - mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = - {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves - mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); - - return testMesh; -} - //-------------------------------------------------------------------------------- void MeshTester::initTestCaseTwo(conduit::Node& mesh) { @@ -389,83 +202,6 @@ void MeshTester::initTestCaseTwo(conduit::Node& mesh) } //-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initTestCaseThree() -{ - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; - - int numElements = 4; - int numVertices = - 6; // OR create a middle triangle with all of one material, and then a ring of triangles around it that are full of the other material - - mir::VertSet verts = mir::VertSet(numVertices); - mir::ElemSet elems = mir::ElemSet(numElements); - - // Create the mesh connectivity information - topoData.m_evInds = { - 0, - 1, - 2, // elem 0, card 3, start 0 - 1, - 3, - 4, // elem 1, card 3, start 3 - 1, - 4, - 2, // elem 2, card 3, start 6 - 2, - 4, - 5 // elem 3, card 3, start 9, end 12 - }; - - topoData.m_evBegins = {0, 3, 6, 9, 12}; - topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0, - 1, - 2, // vert 1, card 3, start 1 - 0, - 2, - 3, // vert 2, card 3, start 4 - 1, // vert 3, card 1, start 7 - 1, - 2, - 3, // vert 4, card 3, start 8 - 3 // vert 5, card 1, start 11, end 12 - }; - topoData.m_veBegins = {0, 1, 4, 7, 8, 11, 12}; - - int numMaterials = 2; - enum - { - BLUE = 0, - RED = 1, - }; - - volFracs.resize(numMaterials); - volFracs[BLUE] = {0.0, 0.5, 0.8, 0.5}; - volFracs[RED] = {1.0, 0.5, 0.2, 0.5}; - - mapData.m_vertexPositions = {mir::Point2::make_point(1.0, 2.0), - mir::Point2::make_point(0.5, 1.0), - mir::Point2::make_point(1.5, 1.0), - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0), - mir::Point2::make_point(2.0, 0.0)}; - - mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = {0, 1, 2, 3}; // For the base mesh, the parents are always themselves - mapData.m_shapeTypes = Vec(numElements, mir::Shape::Triangle); - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); - - return testMesh; -} - void MeshTester::initTestCaseThree(conduit::Node& mesh) { // clang-format off @@ -516,129 +252,11 @@ void MeshTester::initTestCaseThree(conduit::Node& mesh) } //-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initTestCaseFour() -{ - mir::CellTopologyData topoData; - mir::CellMapData mapData; - mir::CellData cellData; - VolumeFractions volFracs; - - int numElements = 9; - int numVertices = 16; - mir::VertSet verts = - mir::VertSet(numVertices); // Construct a vertex set with 16 vertices - mir::ElemSet elems = - mir::ElemSet(numElements); // Construct an element set with 9 elements - - // Create the mesh connectivity information - topoData.m_evInds = { - 0, 4, 5, 1, // elem 0, card 4, start 0 - 1, 5, 6, 2, // elem 1, card 4, start 4 - 2, 6, 7, 3, // elem 2, card 4, start 8 - 4, 8, 9, 5, // elem 3, card 4, start 12 - 5, 9, 10, 6, // elem 4, card 4, start 16 - 6, 10, 11, 7, // elem 5, card 4, start 20 - 8, 12, 13, 9, // elem 6, card 4, start 24 - 9, 13, 14, 10, // elem 7, card 4, start 28 - 10, 14, 15, 11 // elem 8, card 4, start 32, end 36 - }; - - topoData.m_evBegins = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36}; - topoData.m_veInds = { - 0, // vert 0, card 1, start 0 - 0, 1, // vert 1, card 2, start 1 - 1, 2, // vert 2, card 2, start 3 - 2, // vert 3, card 1, start 5 - 0, 3, // vert 4, card 2, start 6 - 0, 1, 3, 4, // vert 5, card 4, start 8 - 1, 2, 4, 5, // vert 6, card 4, start 12 - 2, 5, // vert 7, card 2, start 16 - 3, 6, // vert 8, card 2, start 18 - 3, 4, 6, 7, // vert 9, card 4, start 20 - 4, 5, 7, 8, // vert 10, card 4, start 24 - 5, 8, // vert 11, card 2, start 28 - 6, // vert 12, card 1, start 30 - 6, 7, // vert 13, card 2, start 31 - 7, 8, // vert 14, card 2, start 33 - 8, // vert 15, card 1, start 35, end 36 - }; - topoData - .m_veBegins = {0, 1, 3, 5, 6, 8, 12, 16, 18, 20, 24, 28, 30, 31, 33, 35, 36}; - - mapData.m_vertexPositions = {mir::Point2::make_point(0.0, 3.0), - mir::Point2::make_point(1.0, 3.0), - mir::Point2::make_point(2.0, 3.0), - mir::Point2::make_point(3.0, 3.0), - - mir::Point2::make_point(0.0, 2.0), - mir::Point2::make_point(1.0, 2.0), - mir::Point2::make_point(2.0, 2.0), - mir::Point2::make_point(3.0, 2.0), - - mir::Point2::make_point(0.0, 1.0), - mir::Point2::make_point(1.0, 1.0), - mir::Point2::make_point(2.0, 1.0), - mir::Point2::make_point(3.0, 1.0), - - mir::Point2::make_point(0.0, 0.0), - mir::Point2::make_point(1.0, 0.0), - mir::Point2::make_point(2.0, 0.0), - mir::Point2::make_point(3.0, 0.0)}; - - int numMaterials = 2; - enum - { - GREEN = 0, - BLUE = 1 - }; - - volFracs.resize(numMaterials); - - auto& greenVolumeFractions = volFracs[GREEN]; - auto& blueVolumeFractions = volFracs[BLUE]; - const auto& points = mapData.m_vertexPositions; - const auto& evInds = topoData.m_evInds; - - greenVolumeFractions.resize(numElements); - blueVolumeFractions.resize(numElements); - - // Generate the element volume fractions for the circle - auto circleCenter = mir::Point2::make_point(1.5, 1.5); - axom::float64 circleRadius = 1.25; - int gridSize = 1000; - for(int i = 0; i < numElements; ++i) - { - auto vf = calculatePercentOverlapMonteCarlo(gridSize, - circleCenter, - circleRadius, - points[evInds[i * 4 + 0]], - points[evInds[i * 4 + 1]], - points[evInds[i * 4 + 2]], - points[evInds[i * 4 + 3]]); - greenVolumeFractions[i] = vf; - blueVolumeFractions[i] = 1.0 - vf; - } - - mapData.m_elementDominantMaterials = Vec(numElements, NULL_MAT); - mapData.m_elementParents = - {0, 1, 2, 3, 4, 5, 6, 7, 8}; // For the base mesh, the parents are always themselves - mapData.m_shapeTypes = Vec(numElements, mir::Shape::Quad); - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, elems, numMaterials, topoData, mapData, volFracs); - - return testMesh; -} - -//-------------------------------------------------------------------------------- - template static void addCircleMaterial(const TopoView& topoView, const CoordsetView& coordsetView, conduit::Node& mesh, - const mir::Point2& circleCenter, + const MeshTester::Point2& circleCenter, axom::float64 circleRadius, int numSamples) { @@ -724,7 +342,7 @@ void MeshTester::initTestCaseFour(conduit::Node& mesh) mesh["topologies/mesh/elements/connectivity"])); // Add material - const auto circleCenter = mir::Point2::make_point(1.5, 1.5); + const Point2 circleCenter({1.5, 1.5}); const axom::float64 circleRadius = 1.25; const int numSamples = 100; addCircleMaterial(topoView, @@ -735,75 +353,9 @@ void MeshTester::initTestCaseFour(conduit::Node& mesh) numSamples); } -//-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::createUniformGridTestCaseMesh( - int gridSize, - const mir::Point2& circleCenter, - axom::float64 circleRadius) -{ - // Generate the mesh topology - mir::CellData cellData = generateGrid(gridSize); - - mir::VertSet verts = - mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = - mir::ElemSet(cellData.m_numElems); // Construct the element set - - int numMaterials = 2; - enum - { - GREEN = 0, - BLUE = 1 - }; - - VolumeFractions volFracs; - volFracs.resize(numMaterials); - volFracs[GREEN].resize(cellData.m_numElems); - volFracs[BLUE].resize(cellData.m_numElems); - - // Generate the element volume fractions for the circle - const int numMonteCarloSamples = 100; - auto& pos = cellData.m_mapData.m_vertexPositions; - const auto& evInds = cellData.m_topology.m_evInds; - for(int i = 0; i < cellData.m_numElems; ++i) - { - auto vf = calculatePercentOverlapMonteCarlo(numMonteCarloSamples, - circleCenter, - circleRadius, - pos[evInds[i * 4 + 0]], - pos[evInds[i * 4 + 1]], - pos[evInds[i * 4 + 2]], - pos[evInds[i * 4 + 3]]); - volFracs[GREEN][i] = vf; - volFracs[BLUE][i] = 1.0 - vf; - } - - cellData.m_mapData.m_elementDominantMaterials = - Vec(cellData.m_numVerts, NULL_MAT); - cellData.m_mapData.m_shapeTypes = - Vec(cellData.m_numVerts, mir::Shape::Quad); - cellData.m_mapData.m_elementParents.resize(cellData.m_numVerts); - for(auto i : elems.positions()) - { - cellData.m_mapData.m_elementParents[i] = i; - } - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, - elems, - numMaterials, - cellData.m_topology, - cellData.m_mapData, - volFracs); - - return testMesh; -} - //-------------------------------------------------------------------------------- void MeshTester::createUniformGridTestCaseMesh(int gridSize, - const mir::Point2& circleCenter, + const MeshTester::Point2& circleCenter, axom::float64 circleRadius, conduit::Node& mesh) { @@ -831,120 +383,6 @@ void MeshTester::createUniformGridTestCaseMesh(int gridSize, } //-------------------------------------------------------------------------------- - -mir::CellData MeshTester::generateGrid(int gridSize) -{ - // Generate the topology for a uniform quad mesh with n x n elements automatically - int numElements = gridSize * gridSize; - int numVertices = (gridSize + 1) * (gridSize + 1); - - mir::CellData data; - - data.m_numVerts = numVertices; - data.m_numElems = numElements; - - // Generate the evInds - auto& evInds = data.m_topology.m_evInds; - for(int eID = 0; eID < numElements; ++eID) - { - int row = eID / gridSize; // note the integer division - int vertsPerRow = gridSize + 1; - int elemsPerRow = gridSize; - - evInds.push_back((eID % elemsPerRow) + row * vertsPerRow + 0); - evInds.push_back((eID % elemsPerRow) + (row + 1) * vertsPerRow + 0); - evInds.push_back((eID % elemsPerRow) + (row + 1) * vertsPerRow + 1); - evInds.push_back((eID % elemsPerRow) + row * vertsPerRow + 1); - } - - // Generate the evBegins - auto& evBegins = data.m_topology.m_evBegins; - evBegins.push_back(0); - for(int i = 0; i < numElements; ++i) - { - evBegins.push_back((i + 1) * 4); - } - - // Generate the veInds - auto& veInds = data.m_topology.m_veInds; - auto& veBegins = data.m_topology.m_veBegins; - std::map> veInds_data; - for(int evInd_itr = 0; evInd_itr < numElements * 4; ++evInd_itr) - { - int currentElementID = evInd_itr / 4; // note the integer division - veInds_data[evInds[evInd_itr]].push_back(currentElementID); - } - - for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) - { - // Sort the vector - std::sort(itr->second.begin(), itr->second.end()); - - // Add the elements associated with the current vertex to veInds - for(unsigned long i = 0; i < itr->second.size(); ++i) - veInds.push_back(itr->second[i]); - } - - // Generate the veBegins - veBegins.push_back(0); - int currentIndexCount = 0; - for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) - { - currentIndexCount += itr->second.size(); - veBegins.push_back(currentIndexCount); - } - - // Generate the vertex positions - auto& points = data.m_mapData.m_vertexPositions; - for(int y = gridSize; y > -1; --y) - { - for(int x = 0; x < gridSize + 1; ++x) - { - points.push_back(mir::Point2::make_point(x, y)); - } - } - - // // Print out the results - // printf("evInds: { "); - // for (int i = 0; i < evInds.size(); i++) - // { - // printf("%d ", evInds[i]); - // if ((i+1) % 4 == 0 && i != 0) - // printf("\n"); - // } - // printf("}\n"); - - // printf("evBegins: { "); - // for (int i = 0; i < evBegins.size(); i++) - // { - // printf("%d ", evBegins[i]); - // } - // printf("}\n"); - - // printf("veInds: { "); - // for (int i = 0; i < veInds.size(); i++) - // { - // printf("%d ", veInds[i]); - // } - // printf("}\n"); - - // printf("veBegins: { "); - // for (int i = 0; i < veBegins.size(); i++) - // { - // printf("%d ", veBegins[i]); - // } - // printf("}\n"); - - // printf("points: { "); - // for (int i = 0; i < numVertices; ++i) - // { - // printf("{%.2f, %.2f} ", points[i][0], points[i][1]); - // } - // printf("}\n"); - - return data; -} - void MeshTester::generateGrid(int gridSize, conduit::Node& mesh) { int nx = gridSize + 1; @@ -993,149 +431,6 @@ void MeshTester::generateGrid(int gridSize, conduit::Node& mesh) } //-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initTestCaseFive(int gridSize, int numCircles) -{ - // Generate the mesh topology - mir::CellData cellData = generateGrid(gridSize); - - mir::VertSet verts = - mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = - mir::ElemSet(cellData.m_numElems); // Construct the element set - - // Generate the element volume fractions with concentric circles - int numMaterials = numCircles + 1; - int defaultMaterialID = - numMaterials - 1; // default material is always the last index - - mir::Point2 circleCenter = mir::Point2::make_point( - gridSize / 2.0, - gridSize / 2.0); // all circles are centered around the same point - - // Initialize the radii of the circles - std::vector circleRadii; - axom::float64 maxRadius = - gridSize / 2.4; // Note: The choice of divisor is arbitrary - axom::float64 minRadius = - gridSize / 8; // Note: The choice of divisor is arbitrary - - axom::float64 radiusDelta; - if(numCircles <= 1) - radiusDelta = (maxRadius - minRadius); - else - radiusDelta = (maxRadius - minRadius) / (double)(numCircles - 1); - - for(int i = 0; i < numCircles; ++i) - { - circleRadii.push_back(minRadius + (i * radiusDelta)); - } - - // Initialize all material volume fractions to 0 - std::vector> materialVolumeFractionsData; - for(int i = 0; i < numMaterials; ++i) - { - std::vector tempVec; - tempVec.resize(cellData.m_numElems); - materialVolumeFractionsData.push_back(tempVec); - } - - // Use the uniform sampling method to generate volume fractions for each material - // Note: Assumes that the cell is a parallelogram. This could be modified via biliear interpolation - for(int eID = 0; eID < cellData.m_numElems; ++eID) - { - mir::Point2& v0 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 0]]; - mir::Point2& v1 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 1]]; - mir::Point2& v2 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 2]]; - //mir::Point2& v3 = cellData.m_mapData.m_vertexPositions[cellData.m_topology.m_evInds[eID * 4 + 3]]; - - // Run the uniform sampling to determine how much of the current cell is composed of each material - int materialCount[numMaterials]; - for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - - for(int matID = 0; matID < numMaterials; ++matID) - { - materialVolumeFractionsData[matID][eID] = - materialCount[matID] / (double)(gridSize * gridSize); - } - - axom::float64 delta_x = - axom::utilities::abs(v2[0] - v1[1]) / (double)(gridSize - 1); - axom::float64 delta_y = - axom::utilities::abs(v0[1] - v1[1]) / (double)(gridSize - 1); - - for(int y = 0; y < gridSize; ++y) - { - for(int x = 0; x < gridSize; ++x) - { - mir::Point2 samplePoint = - mir::Point2::make_point(delta_x * x + v1[0], delta_y * y + v1[1]); - bool isPointSampled = false; - for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) - { - const auto r = circleRadii[cID]; - if(primal::squared_distance(samplePoint, circleCenter) < r * r) - { - materialCount[cID]++; - isPointSampled = true; - } - } - if(!isPointSampled) - { - // The point was not within any of the circles, so increment the count for the default material - materialCount[defaultMaterialID]++; - } - } - } - - // Assign the element volume fractions based on the count of the samples in each circle - for(int matID = 0; matID < numMaterials; ++matID) - { - materialVolumeFractionsData[matID][eID] = - materialCount[matID] / (double)(gridSize * gridSize); - } - } - - std::vector elementParents; // For the base mesh, the parents are always themselves - std::vector elementDominantMaterials; - std::vector elementShapeTypes; - for(int i = 0; i < cellData.m_numElems; ++i) - { - elementParents.push_back(i); - elementDominantMaterials.push_back(NULL_MAT); - elementShapeTypes.push_back(mir::Shape::Quad); - } - - CellTopologyData topology; - topology.m_evInds = cellData.m_topology.m_evInds; - topology.m_evBegins = cellData.m_topology.m_evBegins; - topology.m_veInds = cellData.m_topology.m_veInds; - topology.m_veBegins = cellData.m_topology.m_veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = elementDominantMaterials; - mapData.m_elementParents = elementParents; - mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; - mapData.m_shapeTypes = elementShapeTypes; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, - elems, - numMaterials, - topology, - mapData, - materialVolumeFractionsData); - - return testMesh; -} - static void addMaterial( axom::IndexType numElements, int numMaterials, @@ -1184,10 +479,11 @@ static void addMaterial( mesh["matsets/mat/indices"].set(indices); } +//-------------------------------------------------------------------------------- template void addConcentricCircleMaterial(const TopoView& topoView, const CoordsetView& coordsetView, - const mir::Point2& circleCenter, + const MeshTester::Point2& circleCenter, std::vector& circleRadii, int numSamples, conduit::Node& mesh) @@ -1229,17 +525,17 @@ void addConcentricCircleMaterial(const TopoView& topoView, int materialCount[numMaterials]; for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - axom::float64 delta_x = - axom::utilities::abs(v1[0] - v0[0]) / (axom::float64)(numSamples - 1); - axom::float64 delta_y = - axom::utilities::abs(v2[1] - v1[1]) / (axom::float64)(numSamples - 1); + float delta_x = + axom::utilities::abs(v1[0] - v0[0]) / (float)(numSamples - 1); + float delta_y = + axom::utilities::abs(v2[1] - v1[1]) / (float)(numSamples - 1); for(int y = 0; y < numSamples; ++y) { for(int x = 0; x < numSamples; ++x) { - mir::Point2 samplePoint = - mir::Point2::make_point(delta_x * x + v0[0], delta_y * y + v0[1]); + MeshTester::Point2 samplePoint({static_cast(delta_x * x + v0[0]), + static_cast(delta_y * y + v0[1])}); bool isPointSampled = false; for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) { @@ -1272,14 +568,15 @@ void addConcentricCircleMaterial(const TopoView& topoView, mesh); } +//-------------------------------------------------------------------------------- void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& mesh) { // Generate the mesh topology generateGrid(gridSize, mesh); - mir::Point2 circleCenter = mir::Point2::make_point( - gridSize / 2.0, - gridSize / 2.0); // all circles are centered around the same point + Point2 circleCenter({ + gridSize / 2.f, + gridSize / 2.f}); // all circles are centered around the same point // Initialize the radii of the circles std::vector circleRadii; @@ -1320,12 +617,12 @@ void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& m //-------------------------------------------------------------------------------- -int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, +int MeshTester::circleQuadCornersOverlaps(const MeshTester::Point2& circleCenter, axom::float64 circleRadius, - const mir::Point2& quadP0, - const mir::Point2& quadP1, - const mir::Point2& quadP2, - const mir::Point2& quadP3) + const MeshTester::Point2& quadP0, + const MeshTester::Point2& quadP1, + const MeshTester::Point2& quadP2, + const MeshTester::Point2& quadP3) { // Check if any of the quad's corners are within the circle auto d0Sq = primal::squared_distance(quadP0, circleCenter); @@ -1345,51 +642,6 @@ int MeshTester::circleQuadCornersOverlaps(const mir::Point2& circleCenter, } //-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initQuadClippingTestMesh() -{ - // Generate the mesh topology - int gridSize = 3; - mir::CellData cellData = generateGrid(gridSize); - - mir::VertSet verts = - mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = - mir::ElemSet(cellData.m_numElems); // Construct the element set - - int numMaterials = 2; - - std::vector> elementVF; - elementVF.resize(numMaterials); - elementVF[0] = {1.0, 1.0, 1.0, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0}; - elementVF[1] = {0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 1.0, 1.0, 1.0}; - - std::vector elementParents; - std::vector elementDominantMaterials; - std::vector elementShapeTypes; - for(int i = 0; i < cellData.m_numElems; ++i) - { - elementParents.push_back(i); - elementDominantMaterials.push_back(NULL_MAT); - elementShapeTypes.push_back(mir::Shape::Quad); - } - - cellData.m_mapData.m_elementDominantMaterials = elementDominantMaterials; - cellData.m_mapData.m_elementParents = elementParents; - cellData.m_mapData.m_shapeTypes = elementShapeTypes; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, - elems, - numMaterials, - cellData.m_topology, - cellData.m_mapData, - elementVF); - - return testMesh; -} - void MeshTester::initQuadClippingTestMesh(conduit::Node& mesh) { // Generate the mesh topology @@ -1433,168 +685,8 @@ void MeshTester::initQuadClippingTestMesh(conduit::Node& mesh) }}); // clang-format on } -//-------------------------------------------------------------------------------- - -mir::MIRMesh MeshTester::initTestCaseSix(int gridSize, int numSpheres) -{ - // Generate the mesh topology - mir::CellData cellData = generateGrid3D(gridSize); - - mir::VertSet verts = - mir::VertSet(cellData.m_numVerts); // Construct the vertex set - mir::ElemSet elems = - mir::ElemSet(cellData.m_numElems); // Construct the element set - - // // Generate the element volume fractions with concentric spheres - int numMaterials = numSpheres + 1; - int defaultMaterialID = - numMaterials - 1; // default material is always the last index - - mir::Point2 sphereCenter = mir::Point2::make_point( - gridSize / 2.0, - gridSize / 2.0, - gridSize / 2.0); // all spheres are centered around the same point - - // Initialize the radii of the circles - std::vector sphereRadii; - axom::float64 maxRadius = - gridSize / 2.0; // Note: The choice of divisor is arbitrary - axom::float64 minRadius = - gridSize / 4.0; // Note: The choice of divisor is arbitrary - - axom::float64 radiusDelta; - if(numSpheres <= 1) - radiusDelta = (maxRadius - minRadius); - else - radiusDelta = (maxRadius - minRadius) / (double)(numSpheres - 1); - - for(int i = 0; i < numSpheres; ++i) - { - auto rad = minRadius + (i * radiusDelta); - sphereRadii.push_back(rad * rad); - } - - // Initialize all material volume fractions to 0 - std::vector> materialVolumeFractionsData; - for(int i = 0; i < numMaterials; ++i) - { - std::vector tempVec; - tempVec.resize(cellData.m_numElems, 0); - materialVolumeFractionsData.push_back(tempVec); - } - - // Use the uniform sampling method to generate volume fractions for each material - for(int eID = 0; eID < cellData.m_numElems; ++eID) - { - mir::Point2 v0 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 0]]; - mir::Point2 v1 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 1]]; - mir::Point2 v2 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 2]]; - //mir::Point2 v3 = - // cellData.m_mapData - // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 3]]; - - //mir::Point2 v4 = - // cellData.m_mapData - // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 4]]; - mir::Point2 v5 = - cellData.m_mapData - .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 5]]; - //mir::Point2 v6 = - // cellData.m_mapData - // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 6]]; - //mir::Point2 v7 = - // cellData.m_mapData - // .m_vertexPositions[cellData.m_topology.m_evInds[eID * 8 + 7]]; - - // Run the uniform sampling to determine how much of the current cell is composed of each material - int materialCount[numMaterials]; - for(int i = 0; i < numMaterials; ++i) materialCount[i] = 0; - - axom::float64 delta_x = - axom::utilities::abs(v2[0] - v1[0]) / (double)(gridSize - 1); - axom::float64 delta_y = - axom::utilities::abs(v0[1] - v1[1]) / (double)(gridSize - 1); - axom::float64 delta_z = - axom::utilities::abs(v5[2] - v1[2]) / (double)(gridSize - 1); - - for(int z = 0; z < gridSize; ++z) - { - for(int y = 0; y < gridSize; ++y) - { - for(int x = 0; x < gridSize; ++x) - { - mir::Point2 samplePoint = mir::Point2::make_point(delta_x * x + v1[0], - delta_y * y + v1[1], - delta_z * z + v1[2]); - bool isPointSampled = false; - for(int cID = 0; cID < numSpheres && !isPointSampled; ++cID) - { - if(primal::squared_distance(samplePoint, sphereCenter) < - sphereRadii[cID]) - { - materialCount[cID]++; - isPointSampled = true; - } - } - if(!isPointSampled) - { - // The point was not within any of the circles, so increment the count for the default material - materialCount[defaultMaterialID]++; - } - } - } - } - - // Assign the element volume fractions based on the count of the samples in each circle - for(int matID = 0; matID < numMaterials; ++matID) - { - materialVolumeFractionsData[matID][eID] = - materialCount[matID] / (double)(gridSize * gridSize * gridSize); - } - } - - std::vector elementParents; // For the base mesh, the parents are always themselves - std::vector elementDominantMaterials; - std::vector elementShapeTypes; - for(int i = 0; i < cellData.m_numElems; ++i) - { - elementParents.push_back(i); - elementDominantMaterials.push_back(NULL_MAT); - elementShapeTypes.push_back(mir::Shape::Hexahedron); - } - - CellTopologyData topology; - topology.m_evInds = cellData.m_topology.m_evInds; - topology.m_evBegins = cellData.m_topology.m_evBegins; - topology.m_veInds = cellData.m_topology.m_veInds; - topology.m_veBegins = cellData.m_topology.m_veBegins; - - CellMapData mapData; - mapData.m_elementDominantMaterials = elementDominantMaterials; - mapData.m_elementParents = elementParents; - mapData.m_vertexPositions = cellData.m_mapData.m_vertexPositions; - mapData.m_shapeTypes = elementShapeTypes; - - // Build the mesh - mir::MIRMesh testMesh; - testMesh.initializeMesh(verts, - elems, - numMaterials, - topology, - mapData, - materialVolumeFractionsData); - - return testMesh; -} //-------------------------------------------------------------------------------- - void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& mesh) { // Generate the mesh topology @@ -1644,10 +736,9 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& me } // all spheres are centered around the same point + const float c = static_cast(gridSize / 2.0); const auto sphereCenter = - PointType::make_point(static_cast(gridSize / 2.0), - static_cast(gridSize / 2.0), - static_cast(gridSize / 2.0)); + PointType({c, c, c}); // Use the uniform sampling method to generate volume fractions for each material for(int eID = 0; eID < topologyView.numberOfZones(); ++eID) @@ -1678,10 +769,9 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& me { for(int x = 0; x < gridSize; ++x) { - const auto samplePoint = - PointType::make_point(static_cast(delta_x * x + v0[0]), - static_cast(delta_y * y + v0[1]), - static_cast(delta_z * z + v0[2])); + const PointType samplePoint({static_cast(delta_x * x + v0[0]), + static_cast(delta_y * y + v0[1]), + static_cast(delta_z * z + v0[2])}); bool isPointSampled = false; for(int cID = 0; cID < numSpheres && !isPointSampled; ++cID) @@ -1718,110 +808,6 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& me } //-------------------------------------------------------------------------------- - -mir::CellData MeshTester::generateGrid3D(int gridSize) -{ - // Generate the topology for a uniform quad mesh with n x n elements automatically - int numElements = gridSize * gridSize * gridSize; - int numVertices = (gridSize + 1) * (gridSize + 1) * (gridSize + 1); - - // Generate the evInds - std::vector evInds; - for(int eID = 0; eID < numElements; ++eID) - { - int vertsPerLayer = (gridSize + 1) * (gridSize + 1); - int elemsPerLayer = gridSize * gridSize; - int layer = eID / (gridSize * gridSize); // note the integer division - - int vertsPerRow = gridSize + 1; - int elemsPerRow = gridSize; - int row = (eID % elemsPerLayer) / gridSize; // note the integer division - - // front face of the grid cube - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + - 0 + (vertsPerLayer * layer)); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + - (vertsPerRow * (row + 1)) + 0 + (vertsPerLayer * layer)); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + - (vertsPerRow * (row + 1)) + 1 + (vertsPerLayer * layer)); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + - 1 + (vertsPerLayer * layer)); - - // back face of the grid cube - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + - 0 + (vertsPerLayer * (layer + 1))); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + - (vertsPerRow * (row + 1)) + 0 + - (vertsPerLayer * (layer + 1))); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + - (vertsPerRow * (row + 1)) + 1 + - (vertsPerLayer * (layer + 1))); - evInds.push_back((eID % elemsPerLayer % elemsPerRow) + (vertsPerRow * row) + - 1 + (vertsPerLayer * (layer + 1))); - } - - // Generate the evBegins - std::vector evBegins; - evBegins.push_back(0); - for(int i = 0; i < numElements; ++i) - { - evBegins.push_back((i + 1) * 8); - } - - // Generate the veInds - std::map> veInds_data; - std::vector veInds; - for(int evInd_itr = 0; evInd_itr < numElements * 8; ++evInd_itr) - { - int currentElementID = evInd_itr / 8; // note the integer division - veInds_data[evInds[evInd_itr]].push_back(currentElementID); - } - - for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) - { - // Sort the vector - std::sort(itr->second.begin(), itr->second.end()); - - // Add the elements associated with the current vertex to veInds - for(unsigned long i = 0; i < itr->second.size(); ++i) - veInds.push_back(itr->second[i]); - } - - // Generate the veBegins - std::vector veBegins; - veBegins.push_back(0); - int currentIndexCount = 0; - for(auto itr = veInds_data.begin(); itr != veInds_data.end(); itr++) - { - currentIndexCount += itr->second.size(); - veBegins.push_back(currentIndexCount); - } - - // Generate the vertex positions - std::vector points; - for(int z = 0; z < gridSize + 1; ++z) - { - for(int y = gridSize; y > -1; --y) - { - for(int x = 0; x < gridSize + 1; ++x) - { - points.push_back(mir::Point2::make_point(x, y, z)); - } - } - } - - mir::CellData data; - data.m_numVerts = numVertices; - data.m_numElems = numElements; - data.m_topology.m_evInds = evInds; - data.m_topology.m_evBegins = evBegins; - data.m_topology.m_veInds = veInds; - data.m_topology.m_veBegins = veBegins; - data.m_mapData.m_vertexPositions = points; - - return data; -} - void MeshTester::generateGrid3D(int gridSize, conduit::Node& mesh) { const int nzones = gridSize * gridSize * gridSize; diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 589cf00111..8ded4c69db 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -3,21 +3,19 @@ // // SPDX-License-Identifier: (BSD-3-Clause) -/** +/*! * \file MeshTester.hpp * * \brief Contains the specification for the MeshTester class. * */ -#ifndef __MESH_TESTER_H__ -#define __MESH_TESTER_H__ +#ifndef __AXOM_MIR_MESH_TESTER_HPP__ +#define __AXOM_MIR_MESH_TESTER_HPP__ #include "axom/core.hpp" // for axom macros #include "axom/slam.hpp" // unified header for slam classes and functions - -#include "axom/mir/reference/MIRMesh.hpp" -#include "axom/mir/reference/MIRUtilities.hpp" +#include "axom/primal.hpp" #include #include @@ -28,171 +26,160 @@ namespace axom { namespace mir { -/** - * \class MeshTester - * - * \brief A class used to generate MIRMeshs with specific properties so - * that the reconstruction output can be validated visually. - * - */ +/*! + * \class MeshTester + * + * \brief A class used to generate MIRMeshs with specific properties so + * that the reconstruction output can be validated visually. + * + */ class MeshTester { public: template using Vec = std::vector; - using IndexVec = Vec; + using IndexVec = Vec; using VolFracVec = Vec; using VolumeFractions = Vec; - + using Point2 = axom::primal::Point; public: - /** - * \brief Default constructor. - */ + /*! + * \brief Default constructor. + */ MeshTester() = default; - /** - * \brief Default destructor. - */ + /*! + * \brief Default destructor. + */ ~MeshTester() = default; public: - /** - * \brief Initializes an MIRMesh based on the example from Meredith 2004 paper. - * - * \note The mesh is a 3x3 uniform grid of quads with 2 materials. - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseOne(); + /*! + * \brief Initializes an MIRMesh based on the example from Meredith 2004 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 2 materials. + * + * \return The generated mesh. + */ void initTestCaseOne(conduit::Node &mesh); - /** - * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. - * - * \note The mesh is a 3x3 uniform grid of quads with 3 materials. - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseTwo(); + /*! + * \brief Initializes an MIRMesh based on the example from Meredith and Childs 2010 paper. + * + * \note The mesh is a 3x3 uniform grid of quads with 3 materials. + * + * \return The generated mesh. + */ void initTestCaseTwo(conduit::Node &mesh); - /** - * \brief Initializes an MIRMesh used for testing triangle clipping cases. - * - * \note The mesh is a set of four triangles with 2 materials - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseThree(); + /*! + * \brief Initializes an MIRMesh used for testing triangle clipping cases. + * + * \note The mesh is a set of four triangles with 2 materials + * + * \return The generated mesh. + */ void initTestCaseThree(conduit::Node &mesh); - /** - * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. - * - * \note The mesh is a 3x3 uniform grid with 2 materials and has a single circle in the center composed - * of one material and is surrounded by a second material. - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseFour(); + /*! + * \brief Intializes a mesh used for testing a single circle of one materials surrounded by another. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and has a single circle in the center composed + * of one material and is surrounded by a second material. + * + * \return The generated mesh. + */ void initTestCaseFour(conduit::Node &mesh); - /** - * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform 2D grid. - * - * \param gridSize The number of elements in the width and the height of the uniform grid. - * \param numCircles The number of concentric circles that are centered in the grid. - * - * \note Each circle is composed of a different material. - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseFive(int gridSize, int numCircles); + /*! + * \brief Initializes a mesh to be used for testing a set of concentric circles centered in a uniform 2D grid. + * + * \param gridSize The number of elements in the width and the height of the uniform grid. + * \param numCircles The number of concentric circles that are centered in the grid. + * + * \note Each circle is composed of a different material. + * + * \return The generated mesh. + */ void initTestCaseFive(int gridSize, int numCircles, conduit::Node &mesh); - /** - * \brief Initializes a mesh to be used for testing a set of concentric spheres centered in a uniform 3D grid. - * - * \param gridSize The number of elements in the width and the height of the uniform grid. - * \param numSpheres The number of concentric spheres that are centered in the grid. - * - * \note Each sphere is composed of a different material. - * - * \return The generated mesh. - */ - mir::MIRMesh initTestCaseSix(int gridSize, int numSpheres); + /*! + * \brief Initializes a mesh to be used for testing a set of concentric spheres centered in a uniform 3D grid. + * + * \param gridSize The number of elements in the width and the height of the uniform grid. + * \param numSpheres The number of concentric spheres that are centered in the grid. + * + * \note Each sphere is composed of a different material. + * + * \return The generated mesh. + */ void initTestCaseSix(int gridSize, int numSpheres, conduit::Node &mesh); - /** - * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. - * - * \param gridSize The number of elements in the width and height of the uniform grid. - * \param circleCenter The center point of the circle. - * \param circleRadius The radius of the circle. - * - * \return The generated mesh. - */ - mir::MIRMesh createUniformGridTestCaseMesh(int gridSize, - const mir::Point2 &circleCenter, - axom::float64 circleRadius); + /*! + * \brief Initializes a mesh composed of a uniform grid with a circle of material in it. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * + * \return The generated mesh. + */ void createUniformGridTestCaseMesh(int gridSize, - const mir::Point2 &circleCenter, + const Point2 &circleCenter, axom::float64 circleRadius, conduit::Node &mesh); - /** - * \brief Initializes a mesh to be used for validating the results of quad clipping. - * - * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such - * that the mesh would be split horizontally through the middle. - * - * \return The generated mesh. - */ - mir::MIRMesh initQuadClippingTestMesh(); + /*! + * \brief Initializes a mesh to be used for validating the results of quad clipping. + * + * \note The mesh is a 3x3 uniform grid with 2 materials and element volume fraction such + * that the mesh would be split horizontally through the middle. + * + * \return The generated mesh. + */ void initQuadClippingTestMesh(conduit::Node &mesh); private: - /** + /*! * \brief make a 3x3 mesh of quads. * \param mesh A conduit node that will contain the new mesh. */ void mesh3x3(conduit::Node &mesh); - /** - * \brief Generates a 2D uniform grid of n x n elements. - * - * \param gridSize The number of elements in the width and height of the uniform grid. - */ - mir::CellData generateGrid(int gridSize); + /*! + * \brief Generates a 2D uniform grid of n x n elements. + * + * \param gridSize The number of elements in the width and height of the uniform grid. + */ void generateGrid(int gridSize, conduit::Node &mesh); - /** - * \brief Generates a 3D uniform grid of n x n x n elements. - * - * \param gridSize The number of elements in the width, height, and depth of the uniform grid. - */ - mir::CellData generateGrid3D(int gridSize); + /*! + * \brief Generates a 3D uniform grid of n x n x n elements. + * + * \param gridSize The number of elements in the width, height, and depth of the uniform grid. + */ void generateGrid3D(int gridSize, conduit::Node &mesh); - /** - * \brief Calculates the number of corners of the quad that are within the circle. - * - * \param circleCenter The center point of the circle. - * \param circleRadius The radius of the circle. - * \param quadP0 The upper left vertex of the quad. - * \param quadP1 The lower left vertex of the quad. - * \param quadP2 The lower right vertex of the quad. - * \param quadP3 The upper right vertex of the quad. - * - * \return The number of corners of the quad that are within the circle. - */ - int circleQuadCornersOverlaps(const mir::Point2 &circleCenter, + /*! + * \brief Calculates the number of corners of the quad that are within the circle. + * + * \param circleCenter The center point of the circle. + * \param circleRadius The radius of the circle. + * \param quadP0 The upper left vertex of the quad. + * \param quadP1 The lower left vertex of the quad. + * \param quadP2 The lower right vertex of the quad. + * \param quadP3 The upper right vertex of the quad. + * + * \return The number of corners of the quad that are within the circle. + */ + int circleQuadCornersOverlaps(const Point2 &circleCenter, axom::float64 circleRadius, - const mir::Point2 &quadP0, - const mir::Point2 &quadP1, - const mir::Point2 &quadP2, - const mir::Point2 &quadP3); + const Point2 &quadP0, + const Point2 &quadP1, + const Point2 &quadP2, + const Point2 &quadP3); }; + } // namespace mir } // namespace axom diff --git a/src/axom/mir/doxygen_mainpage.md b/src/axom/mir/doxygen_mainpage.md new file mode 100644 index 0000000000..a1bfc98ec9 --- /dev/null +++ b/src/axom/mir/doxygen_mainpage.md @@ -0,0 +1,46 @@ +MIR {#mirtop} +============= + +Axom's [Mir](@ref axom::mir), (M)aterial (I)interface (R)econstruction component +provides classes that can process Blueprint meshes with mixed materials into new +Blueprint meshes such that there is a single material per zone. In addition, the +MIR component provides useful building blocks for developing algorithms using +Blueprint meshes. There are views that simplify dealing with Conduit data and +utility algorithms for processing and constructing meshes. + +# Design goals {#goals} + +This component's algorithms are mainly delivered as classes that are templated on +an execution space, allowing them to operate on a variety of computing backends. +The algorithms take Conduit nodes (containing Blueprint data) as input and they +output new Blueprint data in an output Conduit node. Where possible, algorithms +have been broken out into classes to promote reuse. + +# Views {#views} + +Blueprint defines protocols for representing various mesh and data constructs in +a hierarchical form inside Conduit nodes. There are objects defined for coordinate +sets (coordsets), mesh topologies, mesh fields, material sets (matsets), and there +are various flavors of each type of object. This can make it difficult to write +algorithms against Blueprint data since the data live in Conduit nodes with different +names and they may have different formats. Conduit can also use multiple data types +for any of the data arrays that represent objects. Views were developed to simplify +some of these challenges by providing common templated interfaces that let different +types of objects be accessed in a uniform way. Templating helps to deal with the +data types. The views also provide dispatch functions that can wrap a Conduit node +in a suitable view type and then dispatch that view to a generic user-provided lambda, +enabling algorithms to be instantiated for multiple data types with a compact amount +of code. + +# MIR {#mir} + +The MIR component provides an implementation of the Equi-Z MIR algorithm, though +additional algorithms are planned. + +# Utilities {#utilities} + +The MIR component provides algorithms for performing useful mesh operations such as +extracting sub-meshes, merging meshes, clipping meshes, and creating useful relations. +These building blocks can be reused to ease the process of writing additional algorithms +that operate on Blueprint meshes. + diff --git a/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp b/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp index 46163103d8..4af11f6b37 100644 --- a/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp +++ b/src/axom/mir/examples/concentric_circles/mir_concentric_circles.cpp @@ -113,41 +113,6 @@ int runMIR(RuntimePolicy policy, return retval; } -//-------------------------------------------------------------------------------- -int runMIROld(int gridSize, int numCircles, const std::string &outputFilePath) -{ - // Initialize a mesh for testing MIR - auto timer = axom::utilities::Timer(true); - mir::MeshTester tester; - mir::MIRMesh mesh; - { - AXOM_ANNOTATE_SCOPE("generate"); - mesh = tester.initTestCaseFive(gridSize, numCircles); - } - timer.stop(); - SLIC_INFO("Mesh init time: " << timer.elapsedTimeInMilliSec() << " ms."); - - // Begin material interface reconstruction - timer.start(); - mir::MIRMesh outputMesh; - { - AXOM_ANNOTATE_SCOPE("runMIR"); - mir::InterfaceReconstructor m; - m.computeReconstructedInterface(mesh, outputMesh); - } - timer.stop(); - SLIC_INFO("Material interface reconstruction time: " - << timer.elapsedTimeInMilliSec() << " ms."); - - // Output results - { - AXOM_ANNOTATE_SCOPE("save_output"); - outputMesh.writeMeshToFile(".", outputFilePath + ".vtk"); - } - - return 0; -} - //-------------------------------------------------------------------------------- int main(int argc, char **argv) { @@ -155,7 +120,7 @@ int main(int argc, char **argv) axom::slic::setLoggingMsgLevel(axom::slic::message::Info); // Define command line options. - bool handler = true, old = false; + bool handler = true; int gridSize = 5; int numCircles = 2; std::string outputFilePath("output"); @@ -163,9 +128,6 @@ int main(int argc, char **argv) app.add_flag("--handler", handler) ->description("Install a custom error handler that loops forever.") ->capture_default_str(); - app.add_flag("--old", old) - ->description("Use the old MIR method.") - ->capture_default_str(); app.add_option("--gridsize", gridSize) ->description("The number of zones along an axis."); app.add_option("--numcircles", numCircles) @@ -218,14 +180,7 @@ int main(int argc, char **argv) int retval = 0; try { - if(old) - { - retval = runMIROld(gridSize, numCircles, outputFilePath); - } - else - { - retval = runMIR(policy, gridSize, numCircles, outputFilePath); - } + retval = runMIR(policy, gridSize, numCircles, outputFilePath); } catch(std::invalid_argument const &e) { From e864ec1b51859fe73c0da791dd2388f25b2b1b32 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 12:51:57 -0700 Subject: [PATCH 284/290] CUDA fixes. Doc fixes. --- src/axom/mir/docs/sphinx/mir_utilities.rst | 14 +++++++------- src/axom/mir/docs/sphinx/mir_views.rst | 2 +- src/axom/mir/tests/mir_clipfield.cpp | 2 +- .../views/UnstructuredTopologyMixedShapeView.cpp | 4 ++-- .../mir/views/dispatch_unstructured_topology.hpp | 2 +- src/docs/doxygen/Doxyfile.in | 6 +++++- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/axom/mir/docs/sphinx/mir_utilities.rst b/src/axom/mir/docs/sphinx/mir_utilities.rst index 4348f25b14..14843506ea 100644 --- a/src/axom/mir/docs/sphinx/mir_utilities.rst +++ b/src/axom/mir/docs/sphinx/mir_utilities.rst @@ -50,7 +50,7 @@ installs an allocation routine in Conduit that can be used to allocate data thro Axom. The Conduit allocator is set on each ``conduit::Node`` before setting data into the object. -.. literalinclude:: ../../ClipField.hpp +.. literalinclude:: ../../clipping/ClipField.hpp :start-after: _mir_utilities_c2a_begin :end-before: _mir_utilities_c2a_end :language: C++ @@ -87,7 +87,7 @@ and summing them together to produce the new point for the output coordset. Clas ``ClipField`` use ``CoordsetBlender`` to make new coordsets that contain points that were a combination of multiple points in the input coordset. -.. literalinclude:: ../../ClipField.hpp +.. literalinclude:: ../../clipping/ClipField.hpp :start-after: _mir_utilities_coordsetblender_start :end-before: _mir_utilities_coordsetblender_end :language: C++ @@ -101,7 +101,7 @@ new explicit coordset where each point corresponds to a single index from the no stored in SliceData. This class can be used to select a subset of a coordset, reorder nodes in a coordset, or repeat nodes in a coordset. -.. literalinclude:: ../../ExtractZones.hpp +.. literalinclude:: ../../utilities/ExtractZones.hpp :start-after: _mir_utilities_coordsetslicer_begin :end-before: _mir_utilities_coordsetslicer_end :language: C++ @@ -114,7 +114,7 @@ The ``axom::mir::utilities::ExtractZones`` class takes a list of selected zone i a new mesh from a source mesh that includes only the selected zones. There is a derived class ``ExtractZonesAndMatset`` that also extracts a matset, if present. -.. literalinclude:: ../../ExtractZones.hpp +.. literalinclude:: ../../utilities/ExtractZones.hpp :start-after: _mir_utilities_extractzones_begin :end-before: _mir_utilities_extractzones_end :language: C++ @@ -134,7 +134,7 @@ FieldSlicer The ``axom::mir::utilities::blueprint::FieldSlicer`` class selects specific indices from a field and makes a new field. -.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp +.. literalinclude:: ../../tests/mir_slicers.cpp :start-after: _mir_utilities_fieldslicer_begin :end-before: _mir_utilities_fieldslicer_end :language: C++ @@ -160,7 +160,7 @@ The ``axom::mir::utilities::blueprint::MatsetSlicer`` class is similar to the `` class except it slices matsets instead of fields. The same ``SliceData`` can be passed to MatsetSlicer to pull out and assemble a new matset data for a specific list of zones. -.. literalinclude:: ../../ExtractZones.hpp +.. literalinclude:: ../../utilities/ExtractZones.hpp :start-after: _mir_utilities_matsetslicer_begin :end-before: _mir_utilities_matsetslicer_end :language: C++ @@ -189,7 +189,7 @@ O2M (one to many) relation that relates node numbers to the zones that contain t is akin to inverting the normal mesh connectivity which is a map of zones to node ids. The O2M relation is useful for recentering data from the zones to the nodes. -.. literalinclude:: ../../tests/mir_blueprint_utilities.cpp +.. literalinclude:: ../../tests/mir_node_to_zone_relation.cpp :start-after: _mir_utilities_n2zrel_begin :end-before: _mir_utilities_n2zrel_end :language: C++ diff --git a/src/axom/mir/docs/sphinx/mir_views.rst b/src/axom/mir/docs/sphinx/mir_views.rst index 217b4f69f4..d2c812bba6 100644 --- a/src/axom/mir/docs/sphinx/mir_views.rst +++ b/src/axom/mir/docs/sphinx/mir_views.rst @@ -129,7 +129,7 @@ views are needed to supply the sizes, offsets, and shapes arrays. The final unstructured topology view is ``axom::mir::views::UnstructuredTopologyPolyhedralView`` and it provides a view interface to polyhedral meshes. -.. literalinclude:: ../../tests/views/dispatch_unstructured_topology.hpp +.. literalinclude:: ../../views/dispatch_unstructured_topology.hpp :start-after: _mir_views_ph_topoview_begin :end-before: _mir_views_ph_topoview_end :language: C++ diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 0cf0689c71..2279e773eb 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -608,7 +608,7 @@ void braid2d_clip_test(const std::string &type, const std::string &name) n_device_topo.fetch_existing("elements/offsets")); // Make the shape map. - int allocatorID = axom::execution_space::allocatorID(); + volatile int allocatorID = axom::execution_space::allocatorID(); axom::Array values, ids; auto shapeMap = axom::mir::views::buildShapeMap(n_device_topo, values, ids, allocatorID); diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp index 422fe86022..a392ed315b 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp @@ -40,8 +40,8 @@ ShapeMap buildShapeMap(const conduit::Node &n_topo, // Copy the map values to the device memory. const axom::IndexType n = static_cast(sm.size()); - values = axom::Array(n, n, allocatorID); - ids = axom::Array(n, n, allocatorID); + values = axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); + ids = axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); axom::copy(values.data(), valuesvec.data(), n * sizeof(IndexType)); axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); diff --git a/src/axom/mir/views/dispatch_unstructured_topology.hpp b/src/axom/mir/views/dispatch_unstructured_topology.hpp index 6d4748344d..349c19d547 100644 --- a/src/axom/mir/views/dispatch_unstructured_topology.hpp +++ b/src/axom/mir/views/dispatch_unstructured_topology.hpp @@ -164,7 +164,7 @@ void typed_dispatch_unstructured_mixed_topology(const conduit::Node &topo, // Get the allocator that allocated the connectivity. The shape map data // need to go into the same memory space. - const int allocatorID = + volatile int allocatorID = axom::getAllocatorIDForAddress(topo["elements/connectivity"].data_ptr()); // Make the shape map. diff --git a/src/docs/doxygen/Doxyfile.in b/src/docs/doxygen/Doxyfile.in index b6501d755c..0cd26a7f28 100644 --- a/src/docs/doxygen/Doxyfile.in +++ b/src/docs/doxygen/Doxyfile.in @@ -786,7 +786,11 @@ INPUT = @PROJECT_SOURCE_DIR@/axom/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/mint/mesh \ @PROJECT_SOURCE_DIR@/axom/mint/utils \ @PROJECT_SOURCE_DIR@/axom/mint/execution \ - @PROJECT_SOURCE_DIR@/axom/mir/README.md \ + @PROJECT_SOURCE_DIR@/axom/mir/doxygen_mainpage.md \ + @PROJECT_SOURCE_DIR@/axom/mir/ \ + @PROJECT_SOURCE_DIR@/axom/mir/clipping \ + @PROJECT_SOURCE_DIR@/axom/mir/utilities \ + @PROJECT_SOURCE_DIR@/axom/mir/views \ @PROJECT_SOURCE_DIR@/axom/multimat/doxygen_mainpage.md \ @PROJECT_SOURCE_DIR@/axom/multimat \ @PROJECT_SOURCE_DIR@/axom/primal/doxygen_mainpage.md \ From bdcc3023d459e82f1886d0f3334380bbb8795c0e Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 12:55:37 -0700 Subject: [PATCH 285/290] make style --- src/axom/core/examples/core_array_perf.cpp | 18 ++++----- src/axom/inlet/InletVector.hpp | 4 +- src/axom/inlet/VariantKey.cpp | 2 +- src/axom/inlet/VariantKey.hpp | 2 +- .../mint/tests/StructuredMesh_helpers.hpp | 5 +-- .../tests/mint_execution_cell_traversals.cpp | 1 - .../tests/mint_execution_face_traversals.cpp | 1 - .../tests/mint_execution_node_traversals.cpp | 1 - src/axom/mir/MeshTester.cpp | 17 ++++---- src/axom/mir/MeshTester.hpp | 1 + src/axom/mir/tests/mir_mergemeshes.cpp | 20 ++-------- src/axom/mir/tests/mir_slicers.cpp | 40 ++++--------------- .../UnstructuredTopologyMixedShapeView.cpp | 6 ++- src/axom/quest/detail/MarchingCubesImpl.hpp | 4 +- .../examples/quest_candidates_example.cpp | 18 ++++----- ...est_distributed_distance_query_example.cpp | 14 +++---- .../examples/quest_marching_cubes_example.cpp | 18 ++++----- src/axom/spin/SparseOctreeLevel.hpp | 6 +-- 18 files changed, 70 insertions(+), 108 deletions(-) diff --git a/src/axom/core/examples/core_array_perf.cpp b/src/axom/core/examples/core_array_perf.cpp index 25e125feb4..e27115251d 100644 --- a/src/axom/core/examples/core_array_perf.cpp +++ b/src/axom/core/examples/core_array_perf.cpp @@ -171,20 +171,20 @@ int allocatorIdFromPolicy(axom::runtime_policy::Policy policy) : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) policy == axom::runtime_policy::Policy::omp - ? axom::detail::getAllocatorID() - : + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) - policy == axom::runtime_policy::Policy::cuda - ? axom::detail::getAllocatorID() - : + policy == axom::runtime_policy::Policy::cuda + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_HIP) - policy == axom::runtime_policy::Policy::hip - ? axom::detail::getAllocatorID() - : + policy == axom::runtime_policy::Policy::hip + ? axom::detail::getAllocatorID() + : #endif - axom::INVALID_ALLOCATOR_ID; + axom::INVALID_ALLOCATOR_ID; #else int allocatorID = axom::getDefaultAllocatorID(); #endif diff --git a/src/axom/inlet/InletVector.hpp b/src/axom/inlet/InletVector.hpp index e10c728fd1..82f8e42a44 100644 --- a/src/axom/inlet/InletVector.hpp +++ b/src/axom/inlet/InletVector.hpp @@ -102,9 +102,9 @@ struct InletVector * \brief Retrieves the underlying Primal vector ******************************************************************************* */ - operator axom::primal::Vector3D&() { return vec; } + operator axom::primal::Vector3D &() { return vec; } /// \overload - operator const axom::primal::Vector3D&() const { return vec; } + operator const axom::primal::Vector3D &() const { return vec; } }; /*! diff --git a/src/axom/inlet/VariantKey.cpp b/src/axom/inlet/VariantKey.cpp index c97791d50b..b273be84af 100644 --- a/src/axom/inlet/VariantKey.cpp +++ b/src/axom/inlet/VariantKey.cpp @@ -47,7 +47,7 @@ VariantKey::operator int() const return m_int; } -VariantKey::operator const std::string&() const +VariantKey::operator const std::string &() const { if(m_type != VariantKeyType::String) { diff --git a/src/axom/inlet/VariantKey.hpp b/src/axom/inlet/VariantKey.hpp index 2a3bc0e624..adf6314c2b 100644 --- a/src/axom/inlet/VariantKey.hpp +++ b/src/axom/inlet/VariantKey.hpp @@ -95,7 +95,7 @@ class VariantKey ***************************************************************************** */ operator int() const; - operator const std::string&() const; + operator const std::string &() const; /*! ***************************************************************************** diff --git a/src/axom/mint/tests/StructuredMesh_helpers.hpp b/src/axom/mint/tests/StructuredMesh_helpers.hpp index eed27a2d73..9049d6049c 100644 --- a/src/axom/mint/tests/StructuredMesh_helpers.hpp +++ b/src/axom/mint/tests/StructuredMesh_helpers.hpp @@ -901,9 +901,8 @@ inline void check_constructor(const StructuredMesh* m, EXPECT_TRUE(m->hasExplicitCoordinates()); } - CellType cell_type = (mesh_dimension == 3) ? HEX - : (mesh_dimension == 2) ? QUAD - : SEGMENT; + CellType cell_type = + (mesh_dimension == 3) ? HEX : (mesh_dimension == 2) ? QUAD : SEGMENT; EXPECT_EQ(m->getCellType(), cell_type); EXPECT_EQ(m->getNumberOfCellNodes(), getCellInfo(cell_type).num_nodes); EXPECT_EQ(m->getNumberOfCellFaces(), getCellInfo(cell_type).num_faces); diff --git a/src/axom/mint/tests/mint_execution_cell_traversals.cpp b/src/axom/mint/tests/mint_execution_cell_traversals.cpp index 9a2805d9f6..a713f546da 100644 --- a/src/axom/mint/tests/mint_execution_cell_traversals.cpp +++ b/src/axom/mint/tests/mint_execution_cell_traversals.cpp @@ -30,7 +30,6 @@ namespace mint //------------------------------------------------------------------------------ namespace { - template void check_for_all_cells_idx(int dimension) { diff --git a/src/axom/mint/tests/mint_execution_face_traversals.cpp b/src/axom/mint/tests/mint_execution_face_traversals.cpp index ea322a911d..49007c73cf 100644 --- a/src/axom/mint/tests/mint_execution_face_traversals.cpp +++ b/src/axom/mint/tests/mint_execution_face_traversals.cpp @@ -28,7 +28,6 @@ namespace mint //------------------------------------------------------------------------------ namespace { - template void check_for_all_faces(int dimension) { diff --git a/src/axom/mint/tests/mint_execution_node_traversals.cpp b/src/axom/mint/tests/mint_execution_node_traversals.cpp index 4f3713a486..d3e159331d 100644 --- a/src/axom/mint/tests/mint_execution_node_traversals.cpp +++ b/src/axom/mint/tests/mint_execution_node_traversals.cpp @@ -29,7 +29,6 @@ namespace mint //------------------------------------------------------------------------------ namespace { - template void check_for_all_nodes_idx(int dimension) { diff --git a/src/axom/mir/MeshTester.cpp b/src/axom/mir/MeshTester.cpp index 76daf5a546..eabe58979b 100644 --- a/src/axom/mir/MeshTester.cpp +++ b/src/axom/mir/MeshTester.cpp @@ -72,8 +72,7 @@ static axom::float64 calculatePercentOverlapMonteCarlo(int gridSize, { for(int x = 0; x < numSamples; ++x) { - PointType samplePoint({delta_x * x + quadP1[0], - delta_y * y + quadP1[1]}); + PointType samplePoint({delta_x * x + quadP1[0], delta_y * y + quadP1[1]}); if(primal::squared_distance(samplePoint, circleCenter) < dRSq) ++countOverlap; } @@ -534,8 +533,9 @@ void addConcentricCircleMaterial(const TopoView& topoView, { for(int x = 0; x < numSamples; ++x) { - MeshTester::Point2 samplePoint({static_cast(delta_x * x + v0[0]), - static_cast(delta_y * y + v0[1])}); + MeshTester::Point2 samplePoint( + {static_cast(delta_x * x + v0[0]), + static_cast(delta_y * y + v0[1])}); bool isPointSampled = false; for(int cID = 0; cID < numCircles && !isPointSampled; ++cID) { @@ -574,9 +574,9 @@ void MeshTester::initTestCaseFive(int gridSize, int numCircles, conduit::Node& m // Generate the mesh topology generateGrid(gridSize, mesh); - Point2 circleCenter({ - gridSize / 2.f, - gridSize / 2.f}); // all circles are centered around the same point + Point2 circleCenter( + {gridSize / 2.f, + gridSize / 2.f}); // all circles are centered around the same point // Initialize the radii of the circles std::vector circleRadii; @@ -737,8 +737,7 @@ void MeshTester::initTestCaseSix(int gridSize, int numSpheres, conduit::Node& me // all spheres are centered around the same point const float c = static_cast(gridSize / 2.0); - const auto sphereCenter = - PointType({c, c, c}); + const auto sphereCenter = PointType({c, c, c}); // Use the uniform sampling method to generate volume fractions for each material for(int eID = 0; eID < topologyView.numberOfZones(); ++eID) diff --git a/src/axom/mir/MeshTester.hpp b/src/axom/mir/MeshTester.hpp index 8ded4c69db..84fcfae54c 100644 --- a/src/axom/mir/MeshTester.hpp +++ b/src/axom/mir/MeshTester.hpp @@ -43,6 +43,7 @@ class MeshTester using VolFracVec = Vec; using VolumeFractions = Vec; using Point2 = axom::primal::Point; + public: /*! * \brief Default constructor. diff --git a/src/axom/mir/tests/mir_mergemeshes.cpp b/src/axom/mir/tests/mir_mergemeshes.cpp index f45530afac..da61338de3 100644 --- a/src/axom/mir/tests/mir_mergemeshes.cpp +++ b/src/axom/mir/tests/mir_mergemeshes.cpp @@ -160,27 +160,15 @@ struct test_mergemeshes } }; -TEST(mir_mergemeshes, mergemeshes_seq) -{ - test_mergemeshes::test(); -} +TEST(mir_mergemeshes, mergemeshes_seq) { test_mergemeshes::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_mergemeshes, mergemeshes_omp) -{ - test_mergemeshes::test(); -} +TEST(mir_mergemeshes, mergemeshes_omp) { test_mergemeshes::test(); } #endif #if defined(AXOM_USE_CUDA) -TEST(mir_mergemeshes, mergemeshes_cuda) -{ - test_mergemeshes::test(); -} +TEST(mir_mergemeshes, mergemeshes_cuda) { test_mergemeshes::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_mergemeshes, mergemeshes_hip) -{ - test_mergemeshes::test(); -} +TEST(mir_mergemeshes, mergemeshes_hip) { test_mergemeshes::test(); } #endif //------------------------------------------------------------------------------ diff --git a/src/axom/mir/tests/mir_slicers.cpp b/src/axom/mir/tests/mir_slicers.cpp index ef2396aff8..d5af2effe0 100644 --- a/src/axom/mir/tests/mir_slicers.cpp +++ b/src/axom/mir/tests/mir_slicers.cpp @@ -120,30 +120,18 @@ offsets: [0, 1, 3, 5, 6, 9] } }; -TEST(mir_slicers, matsetslice_seq) -{ - test_matset_slice::test(); -} +TEST(mir_slicers, matsetslice_seq) { test_matset_slice::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_slicers, matsetslice_omp) -{ - test_matset_slice::test(); -} +TEST(mir_slicers, matsetslice_omp) { test_matset_slice::test(); } #endif #if defined(AXOM_USE_CUDA) -TEST(mir_slicers, matsetslice_cuda) -{ - test_matset_slice::test(); -} +TEST(mir_slicers, matsetslice_cuda) { test_matset_slice::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_slicers, matsetslice_hip) -{ - test_matset_slice::test(); -} +TEST(mir_slicers, matsetslice_hip) { test_matset_slice::test(); } #endif //------------------------------------------------------------------------------ @@ -449,27 +437,15 @@ struct test_fieldslicer } }; -TEST(mir_slicers, fieldslicer_seq) -{ - test_fieldslicer::test(); -} +TEST(mir_slicers, fieldslicer_seq) { test_fieldslicer::test(); } #if defined(AXOM_USE_OPENMP) -TEST(mir_slicers, fieldslicer_omp) -{ - test_fieldslicer::test(); -} +TEST(mir_slicers, fieldslicer_omp) { test_fieldslicer::test(); } #endif #if defined(AXOM_USE_CUDA) -TEST(mir_slicers, fieldslicer_cuda) -{ - test_fieldslicer::test(); -} +TEST(mir_slicers, fieldslicer_cuda) { test_fieldslicer::test(); } #endif #if defined(AXOM_USE_HIP) -TEST(mir_slicers, fieldslicer_hip) -{ - test_fieldslicer::test(); -} +TEST(mir_slicers, fieldslicer_hip) { test_fieldslicer::test(); } #endif //------------------------------------------------------------------------------ diff --git a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp index a392ed315b..e2f256928d 100644 --- a/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp +++ b/src/axom/mir/views/UnstructuredTopologyMixedShapeView.cpp @@ -40,8 +40,10 @@ ShapeMap buildShapeMap(const conduit::Node &n_topo, // Copy the map values to the device memory. const axom::IndexType n = static_cast(sm.size()); - values = axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); - ids = axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); + values = + axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); + ids = + axom::Array(axom::ArrayOptions::Uninitialized(), n, n, allocatorID); axom::copy(values.data(), valuesvec.data(), n * sizeof(IndexType)); axom::copy(ids.data(), idsvec.data(), n * sizeof(IndexType)); diff --git a/src/axom/quest/detail/MarchingCubesImpl.hpp b/src/axom/quest/detail/MarchingCubesImpl.hpp index 02c1f336fc..4616b7e6e4 100644 --- a/src/axom/quest/detail/MarchingCubesImpl.hpp +++ b/src/axom/quest/detail/MarchingCubesImpl.hpp @@ -124,9 +124,9 @@ class MarchingCubesImpl : public MarchingCubesSingleDomain::ImplBase ? MarchingCubesDataParallelism::hybridParallel #if defined(AXOM_USE_OPENMP) && defined(AXOM_USE_RAJA) : std::is_same::value - ? MarchingCubesDataParallelism::hybridParallel + ? MarchingCubesDataParallelism::hybridParallel #endif - : MarchingCubesDataParallelism::fullParallel; + : MarchingCubesDataParallelism::fullParallel; m_dataParallelism = dataPar; diff --git a/src/axom/quest/examples/quest_candidates_example.cpp b/src/axom/quest/examples/quest_candidates_example.cpp index 4da5867493..e1fe9a6f40 100644 --- a/src/axom/quest/examples/quest_candidates_example.cpp +++ b/src/axom/quest/examples/quest_candidates_example.cpp @@ -212,18 +212,18 @@ void Input::parse(int argc, char** argv, axom::CLI::App& app) : policy == #endif #ifdef AXOM_RUNTIME_POLICY_USE_CUDA - RuntimePolicy::cuda - ? "cuda" - : policy == + RuntimePolicy::cuda + ? "cuda" + : policy == #endif #ifdef AXOM_RUNTIME_POLICY_USE_HIP - RuntimePolicy::hip - ? "hip" - : policy == + RuntimePolicy::hip + ? "hip" + : policy == #endif - RuntimePolicy::seq - ? "seq" - : "policy not valid")); + RuntimePolicy::seq + ? "seq" + : "policy not valid")); } const std::set Input::s_validMethods({ diff --git a/src/axom/quest/examples/quest_distributed_distance_query_example.cpp b/src/axom/quest/examples/quest_distributed_distance_query_example.cpp index 0deeb72f39..861b3ca606 100644 --- a/src/axom/quest/examples/quest_distributed_distance_query_example.cpp +++ b/src/axom/quest/examples/quest_distributed_distance_query_example.cpp @@ -1325,20 +1325,20 @@ int main(int argc, char** argv) //--------------------------------------------------------------------------- // Memory resource. For testing, choose device memory if appropriate. //--------------------------------------------------------------------------- - const std::string umpireResourceName = - params.policy == RuntimePolicy::seq ? "HOST" : + const std::string umpireResourceName = params.policy == RuntimePolicy::seq + ? "HOST" + : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) - params.policy == RuntimePolicy::omp ? "HOST" - : + params.policy == RuntimePolicy::omp ? "HOST" : #endif #if defined(UMPIRE_ENABLE_DEVICE) "DEVICE" #elif defined(UMPIRE_ENABLE_UM) - "UM" + "UM" #elif defined(UMPIRE_ENABLE_PINNED) - "PINNED" + "PINNED" #else - "HOST" + "HOST" #endif ; auto& rm = umpire::ResourceManager::getInstance(); diff --git a/src/axom/quest/examples/quest_marching_cubes_example.cpp b/src/axom/quest/examples/quest_marching_cubes_example.cpp index f324660ecb..c3b5bd9c94 100644 --- a/src/axom/quest/examples/quest_marching_cubes_example.cpp +++ b/src/axom/quest/examples/quest_marching_cubes_example.cpp @@ -1650,20 +1650,20 @@ int allocatorIdToTest(axom::runtime_policy::Policy policy) : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) policy == RuntimePolicy::omp - ? axom::detail::getAllocatorID() - : + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) - policy == RuntimePolicy::cuda - ? axom::detail::getAllocatorID() - : + policy == RuntimePolicy::cuda + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_HIP) - policy == RuntimePolicy::hip - ? axom::detail::getAllocatorID() - : + policy == RuntimePolicy::hip + ? axom::detail::getAllocatorID() + : #endif - axom::INVALID_ALLOCATOR_ID; + axom::INVALID_ALLOCATOR_ID; #else int allocatorID = axom::getDefaultAllocatorID(); #endif diff --git a/src/axom/spin/SparseOctreeLevel.hpp b/src/axom/spin/SparseOctreeLevel.hpp index 5756f2b3ee..b735083dcd 100644 --- a/src/axom/spin/SparseOctreeLevel.hpp +++ b/src/axom/spin/SparseOctreeLevel.hpp @@ -405,9 +405,9 @@ class SparseOctreeLevel : public OctreeLevel const BroodType brood(pt); ConstMapIter blockIt = m_map.find(brood.base()); - return (blockIt == m_map.end()) ? BlockNotInTree - : (blockIt->second[brood.offset()].isLeaf()) ? LeafBlock - : InternalBlock; + return (blockIt == m_map.end()) + ? BlockNotInTree + : (blockIt->second[brood.offset()].isLeaf()) ? LeafBlock : InternalBlock; } private: From 233b0fe0f11533e97598406d2072eabe831347b6 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 14:58:38 -0700 Subject: [PATCH 286/290] Small doc correction. --- src/docs/sphinx/dev_guide/gpu_porting.rst | 79 +++++++++++------------ 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/src/docs/sphinx/dev_guide/gpu_porting.rst b/src/docs/sphinx/dev_guide/gpu_porting.rst index 292cacd665..78be28d428 100644 --- a/src/docs/sphinx/dev_guide/gpu_porting.rst +++ b/src/docs/sphinx/dev_guide/gpu_porting.rst @@ -263,48 +263,9 @@ Do NOT do this: When calling a kernel via ``axom::for_all`` from a surrounding lambda function, consider calling ``axom::for_all`` from a class member method instead. The nvcc compiler will -not compile kernel invokations inside lambda functions. This pattern comes up an intermediate +not compile kernel invokations inside lambda functions. This pattern comes up when an intermediate function is supplied a lambda that uses ``axom::for_all`` such as when handling many data types. -Do not add template specialization for a class/struct from within the scope of another -class/struct; the nvcc compiler does not allow it. Instead, it is necessary to extract -the internal class/struct from the containing class before specializing it. - -Do this: - - .. code-block:: cpp - - namespace internal - { - template - struct B { static void method() { } }; - - template <> - struct B<2> { static void method() { /* 2D-specific method*/ } }; - } - - template - struct A - { - void method() { internal::B::method(); } - }; - -Do NOT do this: - - .. code-block:: cpp - - template - struct A - { - template - struct B { static void method() { } }; - - template <> - struct B<2> { static void method() { /* 2D-specific method*/ } }; - - void method() { B::method(); } - }; - Do this: .. code-block:: cpp @@ -353,6 +314,44 @@ Avoid calling lambdas from kernels. This can work on some systems and not on oth For best odds at a portable algorithm, design your kernel so it is "one level deep", and does not result in calling other functions that are also marked ``AXOM_LAMBDA``. +Do not add template specialization for a class/struct from within the scope of another +class/struct; the nvcc compiler does not allow it. Instead, it is necessary to extract +the internal class/struct from the containing class before specializing it. + +Do this: + + .. code-block:: cpp + + namespace internal + { + template + struct B { static void method() { } }; + + template <> + struct B<2> { static void method() { /* 2D-specific method*/ } }; + } + + template + struct A + { + void method() { internal::B::method(); } + }; + +Do NOT do this: + + .. code-block:: cpp + + template + struct A + { + template + struct B { static void method() { } }; + + template <> + struct B<2> { static void method() { /* 2D-specific method*/ } }; + + void method() { B::method(); } + }; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RAJA::kernel From 83a7ec6b48a320e623459d24c9d2007601279810 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 15:13:00 -0700 Subject: [PATCH 287/290] Fix build and warnings for no-RAJA case. --- src/axom/CMakeLists.txt | 10 +++++++++- src/axom/core/execution/scans.hpp | 4 ++-- src/axom/core/memory_management.hpp | 23 ++++++++++++++--------- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/axom/CMakeLists.txt b/src/axom/CMakeLists.txt index f5a1365c75..1640b8e757 100644 --- a/src/axom/CMakeLists.txt +++ b/src/axom/CMakeLists.txt @@ -36,9 +36,17 @@ axom_add_component(COMPONENT_NAME spin DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONE axom_add_component(COMPONENT_NAME inlet DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME klee DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME quest DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) -axom_add_component(COMPONENT_NAME mir DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) axom_add_component(COMPONENT_NAME multimat DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) +# Add MIR if the prerequisites are found. +if(RAJA_FOUND AND UMPIRE_FOUND AND CONDUIT_FOUND) + axom_add_component(COMPONENT_NAME mir + DEFAULT_STATE ${AXOM_ENABLE_ALL_COMPONENTS}) +else() + message(STATUS "Axom Component MIR turned off due to missing RAJA, UMPIRE, or Conduit.") + set(AXOM_ENABLE_MIR OFF CACHE BOOL "") +endif() + install(TARGETS ${AXOM_COMPONENTS_ENABLED} EXPORT axom-targets DESTINATION lib) diff --git a/src/axom/core/execution/scans.hpp b/src/axom/core/execution/scans.hpp index ddfa3c3d56..9e1bcef565 100644 --- a/src/axom/core/execution/scans.hpp +++ b/src/axom/core/execution/scans.hpp @@ -61,7 +61,7 @@ inline void exclusive_scan(const ContiguousMemoryContainer &input, constexpr bool is_serial = std::is_same::value; AXOM_STATIC_ASSERT(is_serial); - typename decltype(ContiguousMemoryContainer)::value_type total {0}; + typename ContiguousMemoryContainer::value_type total {0}; for(IndexType i = 0; i < input.size(); ++i) { output[i] = total; @@ -111,7 +111,7 @@ inline void inclusive_scan(const ContiguousMemoryContainer &input, constexpr bool is_serial = std::is_same::value; AXOM_STATIC_ASSERT(is_serial); - typename decltype(ContiguousMemoryContainer)::value_type total {0}; + typename ContiguousMemoryContainer::value_type total {0}; for(IndexType i = 0; i < input.size(); ++i) { total += input[i]; diff --git a/src/axom/core/memory_management.hpp b/src/axom/core/memory_management.hpp index 7805f54b5b..5a708fa506 100644 --- a/src/axom/core/memory_management.hpp +++ b/src/axom/core/memory_management.hpp @@ -117,25 +117,27 @@ inline int getDefaultAllocatorID() * \return ID of the allocator that allocated the memory. */ /// &{ +#ifdef AXOM_USE_UMPIRE inline int getAllocatorIDForAddress(void* ptr) { -#ifdef AXOM_USE_UMPIRE umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); return rm.getAllocator(ptr).getId(); -#else - return axom::getDefaultAllocatorID(); -#endif } - inline int getAllocatorIDForAddress(const void* ptr) { -#ifdef AXOM_USE_UMPIRE umpire::ResourceManager& rm = umpire::ResourceManager::getInstance(); return rm.getAllocator(const_cast(ptr)).getId(); +} #else +inline int getAllocatorIDForAddress(void* AXOM_UNUSED_PARAM(ptr)) +{ return axom::getDefaultAllocatorID(); -#endif } +inline int getAllocatorIDForAddress(const void* AXOM_UNUSED_PARAM(ptr)) +{ + return axom::getDefaultAllocatorID(); +} +#endif /// @} /*! @@ -407,15 +409,18 @@ inline int getAllocatorID() * * \return True if the allocator id is for a device; false otherwise. */ +#if defined(AXOM_USE_UMPIRE) inline bool isDeviceAllocator(int allocator_id) { -#if defined(AXOM_USE_UMPIRE) return axom::detail::getAllocatorSpace(allocator_id) == axom::MemorySpace::Device; +} #else +inline bool isDeviceAllocator(int AXOM_UNUSED_PARAM(allocator_id)) +{ return false; -#endif } +#endif } // namespace axom From 2433ca6190e61b4574c8f460c257221e0fe28002 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Thu, 3 Oct 2024 18:17:54 -0700 Subject: [PATCH 288/290] Warnings. Fix some int/IndexType usage. Loosen equiz_3d tolerance a little. --- src/axom/mir/EquiZAlgorithm.hpp | 2 ++ src/axom/mir/reference/MIRUtilities.hpp | 4 ++++ src/axom/mir/tests/mir_clipfield.cpp | 10 ++++++---- src/axom/mir/tests/mir_equiz3d.cpp | 3 ++- src/axom/mir/tests/mir_slicers.cpp | 12 ++++++------ 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index ef909bfc7b..bb5e5e876a 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -1151,6 +1151,8 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm ss1 << "debug_equiz_input_iter." << iter; conduit::relay::io::blueprint::save_mesh(n_mesh_input, ss1.str(), "hdf5"); } +#else + AXOM_UNUSED_VAR(iter); #endif //-------------------------------------------------------------------------- diff --git a/src/axom/mir/reference/MIRUtilities.hpp b/src/axom/mir/reference/MIRUtilities.hpp index 0268b1de24..1a7bc96cc9 100644 --- a/src/axom/mir/reference/MIRUtilities.hpp +++ b/src/axom/mir/reference/MIRUtilities.hpp @@ -228,6 +228,7 @@ inline int getEdgeEndpoint(const mir::Shape shapeType, { return 1; } + break; case mir::Shape::Pyramid: if(midpointVertexID == 5 && isFromVertex) { @@ -294,6 +295,7 @@ inline int getEdgeEndpoint(const mir::Shape shapeType, { return 4; } + break; case mir::Shape::Triangular_Prism: if(midpointVertexID == 6 && isFromVertex) { @@ -369,6 +371,7 @@ inline int getEdgeEndpoint(const mir::Shape shapeType, { return 3; } + break; case mir::Shape::Hexahedron: if(midpointVertexID == 8 && isFromVertex) { @@ -468,6 +471,7 @@ inline int getEdgeEndpoint(const mir::Shape shapeType, { return 7; } + break; default: printf("Edge endpoint case not implemented.\n"); return -1; diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 2279e773eb..1d38301a98 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -337,18 +337,20 @@ struct test_unique axom::copy(deviceIds.data(), ids.data(), sizeof(int) * ids.size()); // Make unique ids. - axom::Array uIds, uIndices; + axom::Array uIds; + axom::Array uIndices; axom::mir::utilities::Unique::execute(ids.view(), uIds, uIndices); // _mir_utilities_unique_end // device->host - axom::Array hostuIds(uIds.size()), hostuIndices(uIndices.size()); + axom::Array hostuIds(uIds.size()); + axom::Array hostuIndices(uIndices.size()); axom::copy(hostuIds.data(), uIds.data(), sizeof(int) * uIds.size()); axom::copy(hostuIndices.data(), uIndices.data(), - sizeof(int) * uIndices.size()); + sizeof(axom::IndexType) * uIndices.size()); // compare results EXPECT_EQ(hostuIds.size(), 12); @@ -858,7 +860,7 @@ TEST(mir_clipfield, strided_structured_2d) strided_structured_clip_test_exec("strided_structured_2d", options); // Clip strided structure on some selected zones. - options["selectedZones"].set(std::vector {{0, 2, 3, 5}}); + options["selectedZones"].set(std::vector {{0, 2, 3, 5}}); strided_structured_clip_test_exec("strided_structured_2d_sel", options); } diff --git a/src/axom/mir/tests/mir_equiz3d.cpp b/src/axom/mir/tests/mir_equiz3d.cpp index 9dfc57f9d1..f73b7bce4e 100644 --- a/src/axom/mir/tests/mir_equiz3d.cpp +++ b/src/axom/mir/tests/mir_equiz3d.cpp @@ -94,7 +94,8 @@ void braid3d_mat_test(const std::string &type, #if defined(AXOM_TESTING_GENERATE_BASELINES) saveBaseline(paths, baselineName, hostMIRMesh); #else - EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh)); + constexpr float tolerance = 1.5e-6; + EXPECT_TRUE(compareBaseline(paths, baselineName, hostMIRMesh, tolerance)); #endif } } diff --git a/src/axom/mir/tests/mir_slicers.cpp b/src/axom/mir/tests/mir_slicers.cpp index d5af2effe0..3719c8834a 100644 --- a/src/axom/mir/tests/mir_slicers.cpp +++ b/src/axom/mir/tests/mir_slicers.cpp @@ -31,12 +31,12 @@ struct test_matset_slice conduit::Node deviceMatset; bputils::copy(deviceMatset, hostMatset); - axom::Array ids {{1, 3, 5}}; - axom::Array selectedZones( + axom::Array ids {{1, 3, 5}}; + axom::Array selectedZones( 3, 3, axom::execution_space::allocatorID()); - axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(int)); + axom::copy(selectedZones.data(), ids.data(), 3 * sizeof(axom::IndexType)); using MatsetView = axom::mir::views::UnibufferMaterialView; @@ -363,12 +363,12 @@ struct test_fieldslicer bputils::copy(deviceMesh, hostMesh); // _mir_utilities_fieldslicer_begin - std::vector indices {0, 1, 2, 7, 8, 9}; - axom::Array sliceIndices( + std::vector indices {0, 1, 2, 7, 8, 9}; + axom::Array sliceIndices( indices.size(), indices.size(), axom::execution_space::allocatorID()); - axom::copy(sliceIndices.data(), indices.data(), sizeof(int) * indices.size()); + axom::copy(sliceIndices.data(), indices.data(), sizeof(axom::IndexType) * indices.size()); bputils::SliceData slice; slice.m_indicesView = sliceIndices.view(); From 03d00d06f7ef698dacf7537d22e618a524ae4cbb Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 4 Oct 2024 13:53:27 -0700 Subject: [PATCH 289/290] Code changes --- src/axom/mir/EquiZAlgorithm.hpp | 70 ++++++++++---------- src/axom/mir/reference/ZooClippingTables.cpp | 51 +++++++------- src/axom/mir/tests/mir_clipfield.cpp | 34 +++++----- src/axom/mir/tests/mir_slicers.cpp | 4 +- src/axom/mir/views/Shapes.hpp | 2 + 5 files changed, 83 insertions(+), 78 deletions(-) diff --git a/src/axom/mir/EquiZAlgorithm.hpp b/src/axom/mir/EquiZAlgorithm.hpp index bb5e5e876a..402f2a0084 100644 --- a/src/axom/mir/EquiZAlgorithm.hpp +++ b/src/axom/mir/EquiZAlgorithm.hpp @@ -796,41 +796,41 @@ class EquiZAlgorithm : public axom::mir::MIRAlgorithm n_InputCoordset.move(n_newCoordset); n_InputFields.move(n_newFields); - // The data are now an unstructured view, probably a mixed shape view. - // Dispatch to an appropriate topo view. - // clang-format off - views::dispatch_explicit_coordset(n_InputCoordset, [&](auto coordsetView) { - using ICoordsetView = decltype(coordsetView); - using ConnectivityType = typename TopologyView::ConnectivityType; - // Dispatch to an appropriate topo view, taking into account the connectivity - // type and the possible shapes that would be supported for the input topology. - views::typed_dispatch_unstructured_topology< - ConnectivityType, - views::view_traits::selected_shapes()>( - n_InputTopo, - [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { - using ITopologyView = decltype(topologyView); - - // Do the next iteration. - iteration(i, - topologyView, - coordsetView, - - allMats, - mixedMats[i], - - n_InputTopo, - n_InputCoordset, - n_InputFields, - - n_options, - - n_newTopo, - n_newCoordset, - n_newFields); - }); - }); - // clang-format on + // Create an appropriate coordset view. + using CSDataType = typename CoordsetView::value_type; + auto coordsetView = axom::mir::views:: + make_explicit_coordset::view( + n_InputCoordset); + + using ICoordsetView = decltype(coordsetView); + using ConnectivityType = typename TopologyView::ConnectivityType; + // Dispatch to an appropriate topo view, taking into account the connectivity + // type and the possible shapes that would be supported for the input topology. + views::typed_dispatch_unstructured_topology< + ConnectivityType, + views::view_traits::selected_shapes()>( + n_InputTopo, + [&](const auto &AXOM_UNUSED_PARAM(shape), auto topologyView) { + using ITopologyView = decltype(topologyView); + + // Do the next iteration. + iteration(i, + topologyView, + coordsetView, + + allMats, + mixedMats[i], + + n_InputTopo, + n_InputCoordset, + n_InputFields, + + n_options, + + n_newTopo, + n_newCoordset, + n_newFields); + }); } } diff --git a/src/axom/mir/reference/ZooClippingTables.cpp b/src/axom/mir/reference/ZooClippingTables.cpp index 08d0145413..e28b4a6eb7 100644 --- a/src/axom/mir/reference/ZooClippingTables.cpp +++ b/src/axom/mir/reference/ZooClippingTables.cpp @@ -9,20 +9,21 @@ namespace axom { namespace mir { -// Quad Vertex Local Indices -// -// 0 7 3 -// @---------@---------@ -// | | -// | | -// | | -// 4 @ @ 6 -// | | -// | | -// | | -// @---------@---------@ -// 1 5 2 -// +/* + Quad Vertex Local Indices + + 0 7 3 + @---------@---------@ + | | + | | + | | + 4 @ @ 6 + | | + | | + | | + @---------@---------@ + 1 5 2 + */ const int quadClipTable[16][19] = { {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, @@ -59,16 +60,18 @@ const std::vector> quadClipTableVec = { {3, 3, 7, 6, 4, 7, 0, 2, 6, 3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, {4, 0, 1, 2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}}; -// Triangle Vertex Local Indices -// 0 -// @ -// / \ - // / \ - // 3 @ @ 5 -// / \ - // / \ - // 1 @-----@-----@ 2 -// 4 +/* + Triangle Vertex Local Indices + 0 + @ + / \ + / \ + 3 @ @ 5 + / \ + / \ + 1 @-----@-----@ 2 + 4 + */ const int triangleClipTable[8][10] = {{3, 0, 1, 2, -1, -1, -1, -1, -1, -1}, {4, 0, 1, 4, 5, 3, 5, 4, 2, -1}, {4, 0, 3, 4, 2, 3, 3, 1, 4, -1}, diff --git a/src/axom/mir/tests/mir_clipfield.cpp b/src/axom/mir/tests/mir_clipfield.cpp index 1d38301a98..441c965270 100644 --- a/src/axom/mir/tests/mir_clipfield.cpp +++ b/src/axom/mir/tests/mir_clipfield.cpp @@ -800,24 +800,22 @@ void strided_structured_clip_test(const std::string &name, bputils::copy(deviceOptions, options); // Create views - axom::mir::views::dispatch_explicit_coordset( - deviceMesh["coordsets/coords"], - [&](auto coordsetView) { - auto topoView = axom::mir::views::make_strided_structured<2>::view( - deviceMesh["topologies/mesh"]); - - using CoordsetView = decltype(coordsetView); - using TopoView = decltype(topoView); - - // Clip the data - axom::mir::clipping::ClipField clipper( - topoView, - coordsetView); - clipper.execute(deviceMesh, deviceOptions, deviceClipMesh); - - // device->host - bputils::copy(hostClipMesh, deviceClipMesh); - }); + auto coordsetView = axom::mir::views::make_explicit_coordset::view( + deviceMesh["coordsets/coords"]); + auto topoView = axom::mir::views::make_strided_structured<2>::view( + deviceMesh["topologies/mesh"]); + + using CoordsetView = decltype(coordsetView); + using TopoView = decltype(topoView); + + // Clip the data + axom::mir::clipping::ClipField clipper( + topoView, + coordsetView); + clipper.execute(deviceMesh, deviceOptions, deviceClipMesh); + + // device->host + bputils::copy(hostClipMesh, deviceClipMesh); // Handle baseline comparison. { diff --git a/src/axom/mir/tests/mir_slicers.cpp b/src/axom/mir/tests/mir_slicers.cpp index 3719c8834a..aa91a6d133 100644 --- a/src/axom/mir/tests/mir_slicers.cpp +++ b/src/axom/mir/tests/mir_slicers.cpp @@ -368,7 +368,9 @@ struct test_fieldslicer indices.size(), indices.size(), axom::execution_space::allocatorID()); - axom::copy(sliceIndices.data(), indices.data(), sizeof(axom::IndexType) * indices.size()); + axom::copy(sliceIndices.data(), + indices.data(), + sizeof(axom::IndexType) * indices.size()); bputils::SliceData slice; slice.m_indicesView = sliceIndices.view(); diff --git a/src/axom/mir/views/Shapes.hpp b/src/axom/mir/views/Shapes.hpp index 14558db531..313d582e7d 100644 --- a/src/axom/mir/views/Shapes.hpp +++ b/src/axom/mir/views/Shapes.hpp @@ -591,7 +591,9 @@ struct Shape : public ShapeTraits */ AXOM_HOST_DEVICE ConnectivityType getId(size_t index) const { +#if defined(AXOM_DEBUG) assert(index < static_cast(m_ids.size())); +#endif return m_ids[index]; } From 259b8df2f17a37c758bb7faed2d01b1ea5a19660 Mon Sep 17 00:00:00 2001 From: Brad Whitlock Date: Fri, 4 Oct 2024 13:54:18 -0700 Subject: [PATCH 290/290] make style on rzwhippet --- src/axom/core/examples/core_array_perf.cpp | 18 ++--- src/axom/inlet/InletVector.hpp | 4 +- src/axom/inlet/VariantKey.cpp | 2 +- src/axom/inlet/VariantKey.hpp | 2 +- .../mint/tests/StructuredMesh_helpers.hpp | 5 +- src/axom/mir/views/NodeArrayView.hpp | 78 +++++++++---------- src/axom/quest/detail/MarchingCubesImpl.hpp | 4 +- .../examples/quest_candidates_example.cpp | 18 ++--- ...est_distributed_distance_query_example.cpp | 14 ++-- .../examples/quest_marching_cubes_example.cpp | 18 ++--- src/axom/spin/SparseOctreeLevel.hpp | 6 +- 11 files changed, 85 insertions(+), 84 deletions(-) diff --git a/src/axom/core/examples/core_array_perf.cpp b/src/axom/core/examples/core_array_perf.cpp index e27115251d..25e125feb4 100644 --- a/src/axom/core/examples/core_array_perf.cpp +++ b/src/axom/core/examples/core_array_perf.cpp @@ -171,20 +171,20 @@ int allocatorIdFromPolicy(axom::runtime_policy::Policy policy) : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) policy == axom::runtime_policy::Policy::omp - ? axom::detail::getAllocatorID() - : + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) - policy == axom::runtime_policy::Policy::cuda - ? axom::detail::getAllocatorID() - : + policy == axom::runtime_policy::Policy::cuda + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_HIP) - policy == axom::runtime_policy::Policy::hip - ? axom::detail::getAllocatorID() - : + policy == axom::runtime_policy::Policy::hip + ? axom::detail::getAllocatorID() + : #endif - axom::INVALID_ALLOCATOR_ID; + axom::INVALID_ALLOCATOR_ID; #else int allocatorID = axom::getDefaultAllocatorID(); #endif diff --git a/src/axom/inlet/InletVector.hpp b/src/axom/inlet/InletVector.hpp index 82f8e42a44..e10c728fd1 100644 --- a/src/axom/inlet/InletVector.hpp +++ b/src/axom/inlet/InletVector.hpp @@ -102,9 +102,9 @@ struct InletVector * \brief Retrieves the underlying Primal vector ******************************************************************************* */ - operator axom::primal::Vector3D &() { return vec; } + operator axom::primal::Vector3D&() { return vec; } /// \overload - operator const axom::primal::Vector3D &() const { return vec; } + operator const axom::primal::Vector3D&() const { return vec; } }; /*! diff --git a/src/axom/inlet/VariantKey.cpp b/src/axom/inlet/VariantKey.cpp index b273be84af..c97791d50b 100644 --- a/src/axom/inlet/VariantKey.cpp +++ b/src/axom/inlet/VariantKey.cpp @@ -47,7 +47,7 @@ VariantKey::operator int() const return m_int; } -VariantKey::operator const std::string &() const +VariantKey::operator const std::string&() const { if(m_type != VariantKeyType::String) { diff --git a/src/axom/inlet/VariantKey.hpp b/src/axom/inlet/VariantKey.hpp index adf6314c2b..2a3bc0e624 100644 --- a/src/axom/inlet/VariantKey.hpp +++ b/src/axom/inlet/VariantKey.hpp @@ -95,7 +95,7 @@ class VariantKey ***************************************************************************** */ operator int() const; - operator const std::string &() const; + operator const std::string&() const; /*! ***************************************************************************** diff --git a/src/axom/mint/tests/StructuredMesh_helpers.hpp b/src/axom/mint/tests/StructuredMesh_helpers.hpp index 9049d6049c..eed27a2d73 100644 --- a/src/axom/mint/tests/StructuredMesh_helpers.hpp +++ b/src/axom/mint/tests/StructuredMesh_helpers.hpp @@ -901,8 +901,9 @@ inline void check_constructor(const StructuredMesh* m, EXPECT_TRUE(m->hasExplicitCoordinates()); } - CellType cell_type = - (mesh_dimension == 3) ? HEX : (mesh_dimension == 2) ? QUAD : SEGMENT; + CellType cell_type = (mesh_dimension == 3) ? HEX + : (mesh_dimension == 2) ? QUAD + : SEGMENT; EXPECT_EQ(m->getCellType(), cell_type); EXPECT_EQ(m->getNumberOfCellNodes(), getCellInfo(cell_type).num_nodes); EXPECT_EQ(m->getNumberOfCellFaces(), getCellInfo(cell_type).num_faces); diff --git a/src/axom/mir/views/NodeArrayView.hpp b/src/axom/mir/views/NodeArrayView.hpp index 83b6fd22a2..954795e0f0 100644 --- a/src/axom/mir/views/NodeArrayView.hpp +++ b/src/axom/mir/views/NodeArrayView.hpp @@ -617,13 +617,13 @@ void Node_to_ArrayView_single(conduit::Node &n, FuncType &&func) } template -void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &... views) +void Node_to_ArrayView_internal(FuncType &&func, Delimiter, View &...views) { func(views...); } template -void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_internal(const conduit::Node &first, Args &&...args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -631,7 +631,7 @@ void Node_to_ArrayView_internal(const conduit::Node &first, Args &&... args) } template -void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_internal(conduit::Node &first, Args &&...args) { Node_to_ArrayView_single(first, [&](auto view) { Node_to_ArrayView_internal(args..., view); @@ -644,7 +644,7 @@ void Node_to_ArrayView_internal(conduit::Node &first, Args &&... args) template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int8_ptr()), @@ -654,13 +654,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int8( template std::enable_if_t Node_to_ArrayView_same_internal_int8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int16_ptr()), @@ -670,13 +670,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int16( template std::enable_if_t Node_to_ArrayView_same_internal_int16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int32_ptr()), @@ -686,13 +686,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int32( template std::enable_if_t Node_to_ArrayView_same_internal_int32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_int64_ptr()), @@ -702,13 +702,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_int64( template std::enable_if_t Node_to_ArrayView_same_internal_int64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint8_ptr()), @@ -718,13 +718,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint8( template std::enable_if_t Node_to_ArrayView_same_internal_uint8( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint16_ptr()), @@ -734,13 +734,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint16( template std::enable_if_t Node_to_ArrayView_same_internal_uint16( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint32_ptr()), @@ -750,13 +750,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint32( template std::enable_if_t Node_to_ArrayView_same_internal_uint32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_uint64_ptr()), @@ -766,13 +766,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_uint64( template std::enable_if_t Node_to_ArrayView_same_internal_uint64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_float32_ptr()), @@ -782,13 +782,13 @@ std::enable_if_t Node_to_ArrayView_same_internal_float32( template std::enable_if_t Node_to_ArrayView_same_internal_float32( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&func, - Args &&... args) + Args &&...args) { func(axom::ArrayView( const_cast(args.as_float64_ptr()), @@ -798,14 +798,14 @@ std::enable_if_t Node_to_ArrayView_same_internal_float64( template std::enable_if_t Node_to_ArrayView_same_internal_float64( FuncType &&AXOM_UNUSED_PARAM(func), - Args &&... AXOM_UNUSED_PARAM(args)) + Args &&...AXOM_UNUSED_PARAM(args)) { } template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, const conduit::Node &first, - Args &&... args) + Args &&...args) { if(first.dtype().is_int8()) { @@ -868,7 +868,7 @@ template void Node_to_ArrayView_same_internal(FuncType &&func, Delimiter, conduit::Node &first, - Args &&... args) + Args &&...args) { if(first.dtype().is_int8()) { @@ -929,13 +929,13 @@ void Node_to_ArrayView_same_internal(FuncType &&func, /// Reorder args template -void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same_internal(const conduit::Node &first, Args &&...args) { Node_to_ArrayView_same_internal(args..., first); } template -void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&...args) { Node_to_ArrayView_same_internal(args..., first); } @@ -961,13 +961,13 @@ void Node_to_ArrayView_same_internal(conduit::Node &first, Args &&... args) * */ template -void Node_to_ArrayView(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } template -void Node_to_ArrayView(conduit::Node &first, Args &&... args) +void Node_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal(first, args..., detail::ArgumentDelimiter); } @@ -988,7 +988,7 @@ void Node_to_ArrayView(conduit::Node &first, Args &&... args) * */ template -void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -996,7 +996,7 @@ void Node_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) +void Node_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal(first, args..., @@ -1008,7 +1008,7 @@ void Node_to_ArrayView_same(conduit::Node &first, Args &&... args) //------------------------------------------------------------------------------ template -void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1017,7 +1017,7 @@ void IndexNode_to_ArrayView(const conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1026,7 +1026,7 @@ void IndexNode_to_ArrayView(conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1035,7 +1035,7 @@ void IndexNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) +void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1047,7 +1047,7 @@ void IndexNode_to_ArrayView_same(conduit::Node &first, Args &&... args) // Float Node to ArrayView. Handle float types. //------------------------------------------------------------------------------ template -void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1056,7 +1056,7 @@ void FloatNode_to_ArrayView(const conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_internal( first, @@ -1065,7 +1065,7 @@ void FloatNode_to_ArrayView(conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, @@ -1074,7 +1074,7 @@ void FloatNode_to_ArrayView_same(const conduit::Node &first, Args &&... args) } template -void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&... args) +void FloatNode_to_ArrayView_same(conduit::Node &first, Args &&...args) { detail::Node_to_ArrayView_same_internal( first, diff --git a/src/axom/quest/detail/MarchingCubesImpl.hpp b/src/axom/quest/detail/MarchingCubesImpl.hpp index 4616b7e6e4..02c1f336fc 100644 --- a/src/axom/quest/detail/MarchingCubesImpl.hpp +++ b/src/axom/quest/detail/MarchingCubesImpl.hpp @@ -124,9 +124,9 @@ class MarchingCubesImpl : public MarchingCubesSingleDomain::ImplBase ? MarchingCubesDataParallelism::hybridParallel #if defined(AXOM_USE_OPENMP) && defined(AXOM_USE_RAJA) : std::is_same::value - ? MarchingCubesDataParallelism::hybridParallel + ? MarchingCubesDataParallelism::hybridParallel #endif - : MarchingCubesDataParallelism::fullParallel; + : MarchingCubesDataParallelism::fullParallel; m_dataParallelism = dataPar; diff --git a/src/axom/quest/examples/quest_candidates_example.cpp b/src/axom/quest/examples/quest_candidates_example.cpp index e1fe9a6f40..4da5867493 100644 --- a/src/axom/quest/examples/quest_candidates_example.cpp +++ b/src/axom/quest/examples/quest_candidates_example.cpp @@ -212,18 +212,18 @@ void Input::parse(int argc, char** argv, axom::CLI::App& app) : policy == #endif #ifdef AXOM_RUNTIME_POLICY_USE_CUDA - RuntimePolicy::cuda - ? "cuda" - : policy == + RuntimePolicy::cuda + ? "cuda" + : policy == #endif #ifdef AXOM_RUNTIME_POLICY_USE_HIP - RuntimePolicy::hip - ? "hip" - : policy == + RuntimePolicy::hip + ? "hip" + : policy == #endif - RuntimePolicy::seq - ? "seq" - : "policy not valid")); + RuntimePolicy::seq + ? "seq" + : "policy not valid")); } const std::set Input::s_validMethods({ diff --git a/src/axom/quest/examples/quest_distributed_distance_query_example.cpp b/src/axom/quest/examples/quest_distributed_distance_query_example.cpp index 861b3ca606..0deeb72f39 100644 --- a/src/axom/quest/examples/quest_distributed_distance_query_example.cpp +++ b/src/axom/quest/examples/quest_distributed_distance_query_example.cpp @@ -1325,20 +1325,20 @@ int main(int argc, char** argv) //--------------------------------------------------------------------------- // Memory resource. For testing, choose device memory if appropriate. //--------------------------------------------------------------------------- - const std::string umpireResourceName = params.policy == RuntimePolicy::seq - ? "HOST" - : + const std::string umpireResourceName = + params.policy == RuntimePolicy::seq ? "HOST" : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) - params.policy == RuntimePolicy::omp ? "HOST" : + params.policy == RuntimePolicy::omp ? "HOST" + : #endif #if defined(UMPIRE_ENABLE_DEVICE) "DEVICE" #elif defined(UMPIRE_ENABLE_UM) - "UM" + "UM" #elif defined(UMPIRE_ENABLE_PINNED) - "PINNED" + "PINNED" #else - "HOST" + "HOST" #endif ; auto& rm = umpire::ResourceManager::getInstance(); diff --git a/src/axom/quest/examples/quest_marching_cubes_example.cpp b/src/axom/quest/examples/quest_marching_cubes_example.cpp index c3b5bd9c94..f324660ecb 100644 --- a/src/axom/quest/examples/quest_marching_cubes_example.cpp +++ b/src/axom/quest/examples/quest_marching_cubes_example.cpp @@ -1650,20 +1650,20 @@ int allocatorIdToTest(axom::runtime_policy::Policy policy) : #if defined(AXOM_RUNTIME_POLICY_USE_OPENMP) policy == RuntimePolicy::omp - ? axom::detail::getAllocatorID() - : + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_CUDA) - policy == RuntimePolicy::cuda - ? axom::detail::getAllocatorID() - : + policy == RuntimePolicy::cuda + ? axom::detail::getAllocatorID() + : #endif #if defined(AXOM_RUNTIME_POLICY_USE_HIP) - policy == RuntimePolicy::hip - ? axom::detail::getAllocatorID() - : + policy == RuntimePolicy::hip + ? axom::detail::getAllocatorID() + : #endif - axom::INVALID_ALLOCATOR_ID; + axom::INVALID_ALLOCATOR_ID; #else int allocatorID = axom::getDefaultAllocatorID(); #endif diff --git a/src/axom/spin/SparseOctreeLevel.hpp b/src/axom/spin/SparseOctreeLevel.hpp index b735083dcd..5756f2b3ee 100644 --- a/src/axom/spin/SparseOctreeLevel.hpp +++ b/src/axom/spin/SparseOctreeLevel.hpp @@ -405,9 +405,9 @@ class SparseOctreeLevel : public OctreeLevel const BroodType brood(pt); ConstMapIter blockIt = m_map.find(brood.base()); - return (blockIt == m_map.end()) - ? BlockNotInTree - : (blockIt->second[brood.offset()].isLeaf()) ? LeafBlock : InternalBlock; + return (blockIt == m_map.end()) ? BlockNotInTree + : (blockIt->second[brood.offset()].isLeaf()) ? LeafBlock + : InternalBlock; } private: