Files
ViewDesignEngine/tests/mesh/test_hex_mesh.cpp
T

272 lines
9.1 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include "vde/mesh/fea_mesh.h"
#include "vde/brep/modeling.h"
#include <cmath>
#include <map>
using namespace vde::mesh;
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// 1. mapped_hex_mesh — 映射法六面体测试
// ═══════════════════════════════════════════════════════════
TEST(MappedHexTest, BoxMappedMesh_NonEmpty) {
auto box = make_box(2.0, 2.0, 2.0);
FaceRegion src;
src.face_indices = {0};
src.name = "bottom";
FaceRegion tgt;
tgt.face_indices = {1};
tgt.name = "top";
auto mesh = mapped_hex_mesh(box, src, tgt, 3);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
}
TEST(MappedHexTest, BoxMappedMesh_ValidConnectivity) {
auto box = make_box(1.0, 1.0, 1.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
auto mesh = mapped_hex_mesh(box, src, tgt, 2);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
TEST(MappedHexTest, BoxMappedMesh_LayerCount) {
auto box = make_box(1.0, 1.0, 1.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
int layers = 4;
auto mesh = mapped_hex_mesh(box, src, tgt, layers);
// 顶点数 = 源面顶点 × (layers + 1)
EXPECT_GT(mesh.num_vertices(), 0u);
}
// ═══════════════════════════════════════════════════════════
// 2. submapped_hex_mesh — 子映射法六面体测试
// ═══════════════════════════════════════════════════════════
TEST(SubmappedHexTest, BoxSubmappedMesh_NonEmpty) {
auto box = make_box(1.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
TEST(SubmappedHexTest, BoxSubmappedMesh_ValidConnectivity) {
auto box = make_box(2.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
TEST(SubmappedHexTest, SphereSubmappedMesh_NonTrivial) {
auto sphere = make_sphere(1.0);
auto mesh = submapped_hex_mesh(sphere);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
// ═══════════════════════════════════════════════════════════
// 3. multi_block_hex_mesh — 多块结构化六面体测试
// ═══════════════════════════════════════════════════════════
TEST(MultiBlockHexTest, EmptyBlocks_FallbackGrid) {
auto box = make_box(2.0, 2.0, 2.0);
// blocks为空时回退到4×4×4均匀栅格
std::vector<BlockRegion> blocks;
auto mesh = multi_block_hex_mesh(box, blocks);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
// 4×4×4 grid → 4^3=64 elements
EXPECT_GE(mesh.num_elements(), 64u);
}
TEST(MultiBlockHexTest, SingleBlock_TFILinear) {
auto box = make_box(1.0, 1.0, 1.0);
BlockRegion block;
block.corner_vertices = {
Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0),
Point3D(0,0,1), Point3D(1,0,1), Point3D(1,1,1), Point3D(0,1,1)
};
block.n_i = 2;
block.n_j = 2;
block.n_k = 2;
std::vector<BlockRegion> blocks = {block};
auto mesh = multi_block_hex_mesh(box, blocks);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
TEST(MultiBlockHexTest, MultiBlocks_ValidConnectivity) {
auto box = make_box(2.0, 2.0, 2.0);
BlockRegion b1;
b1.corner_vertices = {
Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0),
Point3D(0,0,1), Point3D(1,0,1), Point3D(1,1,1), Point3D(0,1,1)
};
b1.n_i = 2; b1.n_j = 2; b1.n_k = 2;
BlockRegion b2;
b2.corner_vertices = {
Point3D(1,0,0), Point3D(2,0,0), Point3D(2,1,0), Point3D(1,1,0),
Point3D(1,0,1), Point3D(2,0,1), Point3D(2,1,1), Point3D(1,1,1)
};
b2.n_i = 2; b2.n_j = 2; b2.n_k = 2;
std::vector<BlockRegion> blocks = {b1, b2};
auto mesh = multi_block_hex_mesh(box, blocks);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
// ═══════════════════════════════════════════════════════════
// 4. hex_quality_optimization — 六面体质量优化 + untangling
// ═══════════════════════════════════════════════════════════
TEST(HexQualityOptTest, UnitCube_QualityOptimization) {
// 创建单位立方体六面体网格
FEAMesh mesh;
mesh.element_type = FEAElementType::Hex8;
mesh.vertices = {
{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
{0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}
};
mesh.elements.push_back({0,1,2,3,4,5,6,7});
mesh.material_ids.push_back(0);
HexOptimizationParams params;
params.untangle = true;
params.laplacian_smooth = true;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_GT(result.initial_quality.total_elements, 0u);
EXPECT_GE(result.final_quality.avg_jacobian, 0.0);
}
TEST(HexQualityOptTest, QualityReport_AfterOptimization) {
auto box = make_box(2.0, 2.0, 2.0);
// 生成结构化六面体网格
std::vector<BlockRegion> blocks;
auto mesh = multi_block_hex_mesh(box, blocks);
auto initial = fea_quality_report(mesh);
EXPECT_GT(initial.total_elements, 0u);
HexOptimizationParams params;
params.laplacian_smooth = true;
params.max_iterations = 10;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, initial.total_elements);
EXPECT_GE(result.final_quality.avg_jacobian, 0.0);
EXPECT_LE(result.final_quality.avg_jacobian, 1.0);
}
TEST(HexQualityOptTest, Untangle_Inverted) {
// 创建一个轻微扭曲的六面体
FEAMesh mesh;
mesh.element_type = FEAElementType::Hex8;
mesh.vertices = {
{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
{0,0,1}, {1,0,1}, {0.3,0.3,1}, {0,1,1} // 顶点6向内扭曲
};
mesh.elements.push_back({0,1,2,3,4,5,6,7});
mesh.material_ids.push_back(0);
HexOptimizationParams params;
params.untangle = true;
params.laplacian_smooth = true;
params.optimization_based_smooth = false;
params.max_iterations = 10;
auto result = hex_quality_optimization(mesh, params);
EXPECT_GT(result.final_quality.avg_jacobian, 0.0);
}
// ═══════════════════════════════════════════════════════════
// 5. 综合集成测试
// ═══════════════════════════════════════════════════════════
TEST(HexIntegrationTest, MappedThenOptimize) {
auto box = make_box(2.0, 2.0, 2.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
auto mesh = mapped_hex_mesh(box, src, tgt, 3);
if (mesh.num_elements() == 0) {
GTEST_SKIP() << "mapped_hex_mesh returned empty (no quad faces available)";
}
HexOptimizationParams params;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, mesh.num_elements());
}
TEST(HexIntegrationTest, SubmappedThenOptimize) {
auto box = make_box(1.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
HexOptimizationParams params;
params.laplacian_smooth = true;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, mesh.num_elements());
}