diff --git a/Applications/Utils/MeshEdit/Vtu2Grid.cpp b/Applications/Utils/MeshEdit/Vtu2Grid.cpp index e25412b662aaa3756cc2ebc9c0a0826ea12778bc..8ee5777bfa76e358e2b992354ee87b124c69da78 100644 --- a/Applications/Utils/MeshEdit/Vtu2Grid.cpp +++ b/Applications/Utils/MeshEdit/Vtu2Grid.cpp @@ -8,14 +8,6 @@ */ #include <tclap/CmdLine.h> -#include <vtkAbstractArray.h> -#include <vtkCellData.h> -#include <vtkCellLocator.h> -#include <vtkDataArray.h> -#include <vtkDoubleArray.h> -#include <vtkIntArray.h> -#include <vtkSmartPointer.h> -#include <vtkUnstructuredGrid.h> #include <vtkXMLUnstructuredGridReader.h> #include "InfoLib/GitInfo.h" @@ -24,116 +16,11 @@ #include "MeshLib/MeshSearch/ElementSearch.h" #include "MeshToolsLib/MeshEditing/RemoveMeshComponents.h" #include "MeshToolsLib/MeshGenerators/MeshGenerator.h" +#include "MeshToolsLib/MeshGenerators/VoxelGridFromMesh.h" #ifdef USE_PETSC #include <mpi.h> #endif - -std::string const cell_id_name = "CellIds"; - -std::array<std::size_t, 3> getDimensions(MathLib::Point3d const& min, - MathLib::Point3d const& max, - std::array<double, 3> const& cellsize) -{ - return { - static_cast<std::size_t>(std::ceil((max[0] - min[0]) / cellsize[0])), - static_cast<std::size_t>(std::ceil((max[1] - min[1]) / cellsize[1])), - static_cast<std::size_t>(std::ceil((max[2] - min[2]) / cellsize[2]))}; -} - -std::vector<int> assignCellIds(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, - MathLib::Point3d const& min, - std::array<std::size_t, 3> const& dims, - std::array<double, 3> const& cellsize) -{ - vtkSmartPointer<vtkCellLocator> locator = - vtkSmartPointer<vtkCellLocator>::New(); - locator->SetDataSet(mesh); - locator->Update(); - - std::vector<int> cell_ids; - cell_ids.reserve(dims[0] * dims[1] * dims[2]); - std::array<double, 3> const grid_max = {min[0] + dims[0] * cellsize[0], - min[1] + dims[1] * cellsize[1], - min[2] + dims[2] * cellsize[2]}; - for (double k = min[2] + (cellsize[2] / 2.0); k < grid_max[2]; - k += cellsize[2]) - { - for (double j = min[1] + (cellsize[1] / 2.0); j < grid_max[1]; - j += cellsize[1]) - { - for (double i = min[0] + (cellsize[0] / 2.0); i < grid_max[0]; - i += cellsize[0]) - { - double pnt[3] = {i, j, k}; - cell_ids.push_back(static_cast<int>(locator->FindCell(pnt))); - } - } - } - return cell_ids; -} - -bool removeUnusedGridCells(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, - std::unique_ptr<MeshLib::Mesh>& grid) -{ - MeshLib::ElementSearch search(*grid); - std::size_t const n_elems_marked = search.searchByPropertyValueRange<int>( - cell_id_name, 0, static_cast<int>(mesh->GetNumberOfCells()), true); - - if (n_elems_marked == grid->getNumberOfElements()) - { - ERR("No valid elements found. Aborting..."); - return false; - } - - if (n_elems_marked) - { - grid.reset(MeshToolsLib::removeElements( - *grid, search.getSearchedElementIDs(), "trimmed_grid")); - } - return true; -} - -template <typename T, typename VTK_TYPE> -void mapArray(MeshLib::Mesh& grid, VTK_TYPE vtk_arr, std::string const& name) -{ - MeshLib::PropertyVector<int> const& cell_ids = - *grid.getProperties().getPropertyVector<int>( - cell_id_name, MeshLib::MeshItemType::Cell, 1); - std::vector<T>& arr = *grid.getProperties().createNewPropertyVector<T>( - name, MeshLib::MeshItemType::Cell, vtk_arr->GetNumberOfComponents()); - std::size_t const n_elems = cell_ids.size(); - arr.resize(n_elems); - for (std::size_t j = 0; j < n_elems; ++j) - arr[j] = vtk_arr->GetValue(cell_ids[j]); -} - -void mapMeshArraysOntoGrid(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, - std::unique_ptr<MeshLib::Mesh>& grid) -{ - vtkSmartPointer<vtkCellData> const cell_data = mesh->GetCellData(); - for (int i = 0; i < cell_data->GetNumberOfArrays(); ++i) - { - std::string const& name = cell_data->GetArrayName(i); - vtkSmartPointer<vtkDoubleArray> const dbl_arr = - dynamic_cast<vtkDoubleArray*>(cell_data->GetArray(name.c_str())); - if (dbl_arr) - { - mapArray<double, vtkSmartPointer<vtkDoubleArray>>(*grid, dbl_arr, - name); - continue; - } - vtkSmartPointer<vtkIntArray> const int_arr = - dynamic_cast<vtkIntArray*>(cell_data->GetArray(name.c_str())); - if (int_arr) - { - mapArray<int, vtkSmartPointer<vtkIntArray>>(*grid, int_arr, name); - continue; - } - WARN("Ignoring array '{:s}', array type not implemented...", name); - } -} - int main(int argc, char* argv[]) { TCLAP::CmdLine cmd( @@ -191,10 +78,22 @@ int main(int argc, char* argv[]) #endif return -1; } + using namespace MeshToolsLib::MeshGenerator; double const x_size = x_arg.getValue(); double const y_size = (y_arg.isSet()) ? y_arg.getValue() : x_arg.getValue(); double const z_size = (z_arg.isSet()) ? z_arg.getValue() : x_arg.getValue(); + + if (x_size <= 0 || y_size <= 0 || z_size <= 0) + { + ERR("A cellsize ({},{},{}) is not allowed to be <= 0", x_size, y_size, + z_size); +#ifdef USE_PETSC + MPI_Finalize(); +#endif + return -1; + } + std::array<double, 3> const cellsize = {x_size, y_size, z_size}; vtkSmartPointer<vtkXMLUnstructuredGridReader> reader = @@ -208,19 +107,32 @@ int main(int argc, char* argv[]) std::array<double, 3>{bounds[0], bounds[2], bounds[4]}); MathLib::Point3d const max( std::array<double, 3>{bounds[1], bounds[3], bounds[5]}); - std::array<std::size_t, 3> const dims = getDimensions(min, max, cellsize); + std::array<double, 3> ranges = {max[0] - min[0], max[1] - min[1], + max[2] - min[2]}; + if (ranges[0] < 0 || ranges[1] < 0 || ranges[2] < 0) + { + ERR("The range ({},{},{}) is not allowed to be < 0", + ranges[0], ranges[1], ranges[2]); +#ifdef USE_PETSC + MPI_Finalize(); +#endif + return -1; + } + std::array<std::size_t, 3> const dims = + VoxelGridFromMesh::getNumberOfVoxelPerDimension(ranges, cellsize); std::unique_ptr<MeshLib::Mesh> grid( MeshToolsLib::MeshGenerator::generateRegularHexMesh( dims[0], dims[1], dims[2], cellsize[0], cellsize[1], cellsize[2], min, "grid")); - std::vector<int> const tmp_ids = assignCellIds(mesh, min, dims, cellsize); + std::vector<int> const tmp_ids = + VoxelGridFromMesh::assignCellIds(mesh, min, dims, cellsize); std::vector<int>& cell_ids = *grid->getProperties().createNewPropertyVector<int>( - cell_id_name, MeshLib::MeshItemType::Cell, 1); + VoxelGridFromMesh::cell_id_name, MeshLib::MeshItemType::Cell, 1); std::copy(tmp_ids.cbegin(), tmp_ids.cend(), std::back_inserter(cell_ids)); - if (!removeUnusedGridCells(mesh, grid)) + if (!VoxelGridFromMesh::removeUnusedGridCells(mesh, grid)) { #ifdef USE_PETSC MPI_Finalize(); @@ -228,7 +140,7 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } - mapMeshArraysOntoGrid(mesh, grid); + VoxelGridFromMesh::mapMeshArraysOntoGrid(mesh, grid); if (MeshLib::IO::writeMeshToFile(*grid, output_arg.getValue()) != 0) { diff --git a/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.cpp b/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a8d0284f27bf789bc2b3066cdaeb454a05c85624 --- /dev/null +++ b/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.cpp @@ -0,0 +1,154 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ +#include "VoxelGridFromMesh.h" + +#include <vtkAbstractArray.h> +#include <vtkCellData.h> +#include <vtkCellLocator.h> +#include <vtkDataArray.h> +#include <vtkDoubleArray.h> +#include <vtkIntArray.h> +#include <vtkXMLUnstructuredGridReader.h> + +#include <cassert> +#include <cmath> + +#include "InfoLib/GitInfo.h" +#include "MathLib/Point3d.h" +#include "MeshLib/MeshSearch/ElementSearch.h" +#include "MeshLib/MeshSearch/MeshElementGrid.h" +#include "MeshToolsLib/MeshEditing/RemoveMeshComponents.h" +#include "MeshToolsLib/MeshGenerators/MeshGenerator.h" + +namespace MeshToolsLib::MeshGenerator::VoxelGridFromMesh +{ +std::array<std::size_t, 3> getNumberOfVoxelPerDimension( + std::array<double, 3> const& ranges, std::array<double, 3> const& cellsize) +{ + if (cellsize[0] <= 0 || cellsize[1] <= 0 || cellsize[2] <= 0) + { + OGS_FATAL("A cellsize ({},{},{}) is not allowed to be <= 0", + cellsize[0], cellsize[1], cellsize[2]); + } + std::array<double, 3> numberOfVoxel = {ranges[0] / cellsize[0], + ranges[1] / cellsize[1], + ranges[2] / cellsize[2]}; + + if (ranges[0] < 0 || ranges[1] < 0 || ranges[2] < 0) + { + OGS_FATAL( + "The difference of max-min ({},{},{}) is not allowed to be < 0", + ranges[0], ranges[1], ranges[2]); + } + std::replace(numberOfVoxel.begin(), numberOfVoxel.end(), 0, 1); + + return {static_cast<std::size_t>(std::lround(numberOfVoxel[0])), + static_cast<std::size_t>(std::lround(numberOfVoxel[1])), + static_cast<std::size_t>(std::lround(numberOfVoxel[2]))}; +} + +std::vector<int> assignCellIds(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + MathLib::Point3d const& min, + std::array<std::size_t, 3> const& dims, + std::array<double, 3> const& cellsize) +{ + vtkSmartPointer<vtkCellLocator> locator = + vtkSmartPointer<vtkCellLocator>::New(); + locator->SetDataSet(mesh); + locator->Update(); + + std::vector<int> cell_ids; + cell_ids.reserve(dims[0] * dims[1] * dims[2]); + std::array<double, 3> const grid_max = {min[0] + dims[0] * cellsize[0], + min[1] + dims[1] * cellsize[1], + min[2] + dims[2] * cellsize[2]}; + + double const start[3] = {min[0] + cellsize[0] / 2., + min[1] + cellsize[1] / 2., + min[2] + cellsize[2] / 2.}; + double pnt[3]; + for (pnt[2] = start[2]; pnt[2] < grid_max[2]; pnt[2] += cellsize[2]) + { + for (pnt[1] = start[1]; pnt[1] < grid_max[1]; pnt[1] += cellsize[1]) + { + for (pnt[0] = start[0]; pnt[0] < grid_max[0]; pnt[0] += cellsize[0]) + { + cell_ids.push_back(static_cast<int>(locator->FindCell(pnt))); + } + } + } + return cell_ids; +} + +bool removeUnusedGridCells(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + std::unique_ptr<MeshLib::Mesh>& grid) +{ + MeshLib::ElementSearch search(*grid); + std::size_t const n_elems_marked = search.searchByPropertyValueRange<int>( + cell_id_name, 0, static_cast<int>(mesh->GetNumberOfCells()), true); + + if (n_elems_marked == grid->getNumberOfElements()) + { + ERR("No valid elements found. Aborting..."); + return false; + } + + if (n_elems_marked) + { + grid.reset(MeshToolsLib::removeElements( + *grid, search.getSearchedElementIDs(), "trimmed_grid")); + } + return true; +} + +// PropertyVector is required to contain parameter cell_id_name +template <typename T, typename VTK_TYPE> +void mapArray(MeshLib::Mesh& grid, VTK_TYPE vtk_arr, std::string const& name) +{ + auto const* cell_ids = grid.getProperties().getPropertyVector<int>( + cell_id_name, MeshLib::MeshItemType::Cell, 1); + if (cell_ids == nullptr) + { + // Error message + return; + } + auto& arr = *grid.getProperties().createNewPropertyVector<T>( + name, MeshLib::MeshItemType::Cell, vtk_arr->GetNumberOfComponents()); + std::size_t const n_elems = cell_ids->size(); + arr.resize(n_elems); + for (std::size_t j = 0; j < n_elems; ++j) + arr[j] = vtk_arr->GetValue((*cell_ids)[j]); +} + +void mapMeshArraysOntoGrid(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + std::unique_ptr<MeshLib::Mesh>& grid) +{ + vtkSmartPointer<vtkCellData> const cell_data = mesh->GetCellData(); + for (int i = 0; i < cell_data->GetNumberOfArrays(); ++i) + { + std::string const& name = cell_data->GetArrayName(i); + vtkSmartPointer<vtkDoubleArray> const dbl_arr = + dynamic_cast<vtkDoubleArray*>(cell_data->GetArray(name.c_str())); + if (dbl_arr) + { + mapArray<double, vtkSmartPointer<vtkDoubleArray>>(*grid, dbl_arr, + name); + continue; + } + vtkSmartPointer<vtkIntArray> const int_arr = + dynamic_cast<vtkIntArray*>(cell_data->GetArray(name.c_str())); + if (int_arr) + { + mapArray<int, vtkSmartPointer<vtkIntArray>>(*grid, int_arr, name); + continue; + } + WARN("Ignoring array '{:s}', array type not implemented...", name); + } +} +} // namespace MeshToolsLib::MeshGenerator::VoxelGridFromMesh \ No newline at end of file diff --git a/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.h b/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..aef731e64cd1886a5b41dfe92fb367be59c072f2 --- /dev/null +++ b/MeshToolsLib/MeshGenerators/VoxelGridFromMesh.h @@ -0,0 +1,52 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + * + */ + +#pragma once + +#include <vtkSmartPointer.h> +#include <vtkUnstructuredGrid.h> + +#include <array> +#include <memory> + +namespace MeshLib +{ +class Mesh; +} + +namespace MathLib +{ +class Point3d; +} +namespace MeshToolsLib::MeshGenerator::VoxelGridFromMesh +{ +static std::string const cell_id_name = "CellIds"; + +/// getNumberOfVoxelPerDimension is used to calculate how many voxel fit into +/// a bounding box. For this calculation the difference of min and max point of +/// the bounding box is divided by the cell size, for every dimension. The +/// calculation is restricted to work only with positive values for the cell +/// size. If the difference between min and max is zero, we assign one voxel for +/// the respective dimension. +std::array<std::size_t, 3> getNumberOfVoxelPerDimension( + std::array<double, 3> const& ranges, std::array<double, 3> const& cellsize); + +std::vector<int> assignCellIds(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + MathLib::Point3d const& min, + std::array<std::size_t, 3> const& dims, + std::array<double, 3> const& cellsize); + +// grid has to contain a PropertyVector with the name 'CellIds' +bool removeUnusedGridCells(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + std::unique_ptr<MeshLib::Mesh>& grid); + +void mapMeshArraysOntoGrid(vtkSmartPointer<vtkUnstructuredGrid> const& mesh, + std::unique_ptr<MeshLib::Mesh>& grid); +}; // namespace MeshToolsLib::MeshGenerator::VoxelGridFromMesh \ No newline at end of file diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 3dbcdfaa98e98071aa4da060aa09fea772b4de10..374245558425d119dd84f584ccd31dbbcdeeda41 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -24,6 +24,7 @@ append_source_files(TEST_SOURCES GeoLib/IO) append_source_files(TEST_SOURCES MaterialLib) append_source_files(TEST_SOURCES MathLib) append_source_files(TEST_SOURCES MeshLib) +append_source_files(TEST_SOURCES MeshToolsLib) append_source_files(TEST_SOURCES MeshGeoToolsLib) append_source_files(TEST_SOURCES_NUMLIB NumLib) # Disable Unity build for some files @@ -93,6 +94,7 @@ target_link_libraries( MaterialLib MathLib MeshLib + MeshToolsLib NumLib ParameterLib $<$<NOT:$<BOOL:${OGS_BUILD_WHEEL}>>:ProcessLib> diff --git a/Tests/MeshToolsLib/TestGetDimensions.cpp b/Tests/MeshToolsLib/TestGetDimensions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3d49aeace8cc600195caad18afa9f35e24d3b6dd --- /dev/null +++ b/Tests/MeshToolsLib/TestGetDimensions.cpp @@ -0,0 +1,50 @@ +/** + * \file + * \date 2023-05-31 + * \brief Tests for getNumberOfVoxelPerDimension() + * + * \copyright + * Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include <gtest/gtest.h> + +#include <array> + +#include "MathLib/Point3d.h" +#include "MeshToolsLib/MeshGenerators/VoxelGridFromMesh.h" + +TEST(MeshToolsLib, getNumberOfVoxelPerDimension) +{ + std::array<double, 3> const cellsize = {1, 2, 5}; + std::array<double, 3> const ranges = {10, 10, 10}; + std::array<std::size_t, 3> const dims = MeshToolsLib::MeshGenerator:: + VoxelGridFromMesh::getNumberOfVoxelPerDimension(ranges, cellsize); + + ASSERT_EQ(10, dims[0]); + ASSERT_EQ(5, dims[1]); + ASSERT_EQ(2, dims[2]); +} + +TEST(MeshToolsLib, getNumberOfVoxelPerDimensionMinMax) +{ + std::array<double, 3> const cellsize = {1, 2, 5}; + std::array<double, 3> const ranges = {0, 0, 10}; + std::array<std::size_t, 3> const dims = MeshToolsLib::MeshGenerator:: + VoxelGridFromMesh::getNumberOfVoxelPerDimension(ranges, cellsize); + + ASSERT_EQ(1, dims[0]); + ASSERT_EQ(1, dims[1]); + ASSERT_EQ(2, dims[2]); +} + +TEST(MeshToolsLib, getNumberOfVoxelPerDimensionCellSize) +{ + std::array<double, 3> const cellsize = {0, 2, 5}; + std::array<double, 3> const ranges = {1, 2, 5}; + ASSERT_ANY_THROW(MeshToolsLib::MeshGenerator::VoxelGridFromMesh:: + getNumberOfVoxelPerDimension(ranges, cellsize)); +} \ No newline at end of file