23#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
46static Eigen::Matrix<t_real, 3, Eigen::Dynamic> NodeCoords(
ElemType t)
55 std::uniform_real_distribution<t_real> u01(0.05, 0.95);
56 std::uniform_real_distribution<t_real> um11(-0.8, 0.8);
64 return MakePoint(um11(rng));
68 t_real xi = u01(rng) * 0.5;
69 t_real et = u01(rng) * (1.0 - xi) * 0.8;
70 return MakePoint(xi, et);
73 return MakePoint(um11(rng), um11(rng));
76 t_real xi = u01(rng) * 0.3;
77 t_real et = u01(rng) * (1.0 - xi) * 0.3;
78 t_real zt = u01(rng) * (1.0 - xi - et) * 0.8;
79 return MakePoint(xi, et, zt);
82 return MakePoint(um11(rng), um11(rng), um11(rng));
85 t_real xi = u01(rng) * 0.5;
86 t_real et = u01(rng) * (1.0 - xi) * 0.8;
88 return MakePoint(xi, et, zt);
93 t_real zt = u01(rng) * 0.8;
94 t_real scale = (1.0 - zt) * 0.8;
95 t_real xi = um11(rng) * scale;
96 t_real et = um11(rng) * scale;
97 return MakePoint(xi, et, zt);
105static constexpr ElemType AllTypes[] = {
111static constexpr ElemType Elem2DWithFaces[] = {
115static constexpr ElemType Elem3DWithFaces[] = {
119static constexpr ElemType O1Types[] = {
123static constexpr ElemType O2Types[] = {
130TEST_CASE(
"ElementTraits: basic identification fields are consistent")
132 for (
auto t : AllTypes)
138 CHECK(e.GetDim() >= 1);
139 CHECK(e.GetDim() <= 3);
140 CHECK(e.GetNumNodes() >= e.GetNumVertices());
141 CHECK(e.GetNumVertices() >= 2);
144 CHECK((e.GetOrder() == 1 || e.GetOrder() == 2));
151TEST_CASE(
"ElementTraits: standard coordinates have correct dimensions")
153 for (
auto t : AllTypes)
155 auto coords = NodeCoords(t);
160 CHECK(coords.rows() == 3);
163 CHECK(coords.cols() == e.GetNumNodes());
170 CHECK(coords(1, i) == doctest::Approx(0.0));
171 CHECK(coords(2, i) == doctest::Approx(0.0));
180 CHECK(coords(2, i) == doctest::Approx(0.0));
186TEST_CASE(
"ElementTraits: face definitions are consistent for 2D elements")
188 for (
auto t : Elem2DWithFaces)
193 int numFaces = e.GetNumFaces();
196 for (
int iFace = 0; iFace < numFaces; iFace++)
209TEST_CASE(
"ElementTraits: face definitions are consistent for 3D elements")
211 for (
auto t : Elem3DWithFaces)
216 int numFaces = e.GetNumFaces();
219 for (
int iFace = 0; iFace < numFaces; iFace++)
232TEST_CASE(
"ElementTraits: ExtractFaceNodes works correctly")
237 std::vector<DNDS::index> nodes = {0, 1, 2};
238 std::array<DNDS::index, 2> faceNodes;
242 CHECK(faceNodes[0] == 0);
243 CHECK(faceNodes[1] == 1);
246 e.ExtractFaceNodes(1, nodes, faceNodes);
247 CHECK(faceNodes[0] == 1);
248 CHECK(faceNodes[1] == 2);
251 e.ExtractFaceNodes(2, nodes, faceNodes);
252 CHECK(faceNodes[0] == 2);
253 CHECK(faceNodes[1] == 0);
259 std::vector<DNDS::index> nodes = {0, 1, 2, 3};
260 std::array<DNDS::index, 2> faceNodes;
263 for (
int i = 0; i < 4; i++)
267 CHECK(faceNodes[0] < 4);
268 CHECK(faceNodes[1] < 4);
273TEST_CASE(
"ElementTraits: order elevation data is consistent for O1 elements")
276 for (
auto t : O1Types)
293 CHECK(e.GetNumElev_O1O2() > 0);
297TEST_CASE(
"ElementTraits: order elevation data for specific elements")
303 CHECK(e.GetNumElev_O1O2() == 1);
313 CHECK(e.ObtainElevatedElem().type ==
Tri6);
314 CHECK(e.GetNumElev_O1O2() == 3);
321 CHECK(e.GetNumElev_O1O2() == 5);
328 CHECK(e.GetNumElev_O1O2() == 19);
332TEST_CASE(
"ElementTraits: O2 elements do not have further elevation")
335 for (
auto t : O2Types)
342 CHECK(e.GetNumElev_O1O2() == 0);
346TEST_CASE(
"ElementTraits: ExtractElevNodeSpanNodes works correctly")
351 std::vector<DNDS::index> nodes = {0, 1, 2};
352 std::array<DNDS::index, 2> spanNodes;
355 for (
int i = 0; i < e.GetNumElev_O1O2(); i++)
358 CHECK(spanNodes[0] < 3);
359 CHECK(spanNodes[1] < 3);
364TEST_CASE(
"ElementTraits: bisection data is valid for O2 elements")
369 CHECK(e.GetO2NumBisect() == 2);
372 for (
int i = 0; i < e.GetO2NumBisect(); i++)
381 CHECK(e.GetO2NumBisect() == 4);
382 for (
int i = 0; i < e.GetO2NumBisect(); i++)
384 CHECK(e.ObtainO2BisectElem(i).type ==
Tri3);
391 CHECK(e.GetO2NumBisect() == 8);
392 for (
int i = 0; i < e.GetO2NumBisect(); i++)
394 CHECK(e.ObtainO2BisectElem(i).type ==
Tet4);
401 CHECK(e.GetO2NumBisect() == 8);
402 for (
int i = 0; i < e.GetO2NumBisect(); i++)
404 CHECK(e.ObtainO2BisectElem(i).type ==
Hex8);
411 CHECK(e.GetO2NumBisect() == 8);
412 for (
int i = 0; i < e.GetO2NumBisect(); i++)
419TEST_CASE(
"ElementTraits: VTK conversion works correctly")
422 for (
auto t : AllTypes)
428 std::vector<t_real> nodes(e.GetNumNodes());
429 for (
int i = 0; i < e.GetNumNodes(); i++)
430 nodes[i] =
static_cast<t_real>(i);
436 CHECK(vtkCellType > 0);
439 CHECK(vtkNodes.size() <= e.GetNumNodes());
440 CHECK(vtkNodes.size() > 0);
444TEST_CASE(
"ElementTraits: VTK node order is a valid permutation for simple elements")
449 for (
size_t i = 0; i < 2; i++)
450 seen.insert(traits.vtkNodeOrder[i]);
451 CHECK(seen.size() == 2);
452 CHECK(*seen.begin() == 0);
453 CHECK(*seen.rbegin() == 1);
458 for (
size_t i = 0; i < 3; i++)
459 seen.insert(traits.vtkNodeOrder[i]);
460 CHECK(seen.size() == 3);
462 CHECK(traits.vtkNodeOrder[0] == 0);
463 CHECK(traits.vtkNodeOrder[1] == 2);
464 CHECK(traits.vtkNodeOrder[2] == 1);
469 for (
size_t i = 0; i < 6; i++)
470 seen.insert(traits.vtkNodeOrder[i]);
471 CHECK(seen.size() == 6);
476 for (
size_t i = 0; i < 8; i++)
477 seen.insert(traits.vtkNodeOrder[i]);
478 CHECK(seen.size() == 8);
479 CHECK(traits.vtkCellType == 12);
489 constexpr int nTrials = 10;
490 std::mt19937 rng(42);
492 for (
auto t : AllTypes)
496 for (
int trial = 0; trial < nTrials; trial++)
498 auto p = RandomInteriorPoint(t, rng);
503 CHECK(sum == doctest::Approx(1.0).epsilon(1e-12));
508TEST_CASE(
"Shape functions: nodal interpolation (Kronecker delta)")
510 for (
auto t : AllTypes)
513 auto coords = NodeCoords(t);
534TEST_CASE(
"Shape functions: D1 derivatives vs finite difference")
536 constexpr int nTrials = 5;
538 constexpr t_real tol = 1e-5;
539 std::mt19937 rng(123);
541 for (
auto t : AllTypes)
545 int dim = e.GetDim();
548 for (
int trial = 0; trial < nTrials; trial++)
550 auto p = RandomInteriorPoint(t, rng);
556 for (
int d = 0; d < dim; d++)
568 t_real fd = (Np(0, j) - Nm(0, j)) / (2 * h);
572 CHECK(an == doctest::Approx(fd).epsilon(tol));
579TEST_CASE(
"Shape functions: D2 derivatives vs finite difference of D1")
582 constexpr t_real tol = 1e-3;
583 std::mt19937 rng(456);
585 for (
auto t : AllTypes)
589 int dim = e.GetDim();
592 auto p = RandomInteriorPoint(t, rng);
595 e.GetDiNj(p, DiNj, 2);
598 for (
int d = 0; d < dim; d++)
610 t_real fd_d2 = (D1p(d, j) - D1m(d, j)) / (2 * h);
617 row = 3 + (d == 0 ? 0 : 2);
624 CHECK(an == doctest::Approx(fd_d2).epsilon(tol));
630TEST_CASE(
"Shape functions: all derivatives are finite")
633 std::mt19937 rng(789);
635 for (
auto t : AllTypes)
638 auto p = RandomInteriorPoint(t, rng);
642 e.GetDiNj(p, DiNj, 3);
649 CHECK(std::isfinite(DiNj(i, j)));
Eigen::Matrix< t_real, Eigen::Dynamic, Eigen::Dynamic > tDiNj
Eigen::Matrix< t_real, 3, Eigen::Dynamic > tD1Nj
DNDS_DEVICE_CALLABLE constexpr decltype(auto) DispatchElementType(ElemType t, Func &&func)
Static dispatch over element types at compile time.
DNDS_DEVICE_CALLABLE constexpr t_real ParamSpaceVolume(ParamSpace ps)
Get the volume of a parametric space.
std::pair< int, std::vector< index > > ToVTKVertsAndData(Element e, const TIn &vin)
Eigen::Matrix< t_real, 3, Eigen::Dynamic > GetStandardCoord(ElemType t)
Eigen::RowVector< t_real, Eigen::Dynamic > tNj
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
DNDS_DEVICE_CALLABLE constexpr ParamSpace GetParamSpace() const
constexpr Element ObtainElevNodeSpan(t_index iNodeElev) const
DNDS_DEVICE_CALLABLE constexpr Element ObtainFace(t_index iFace) const
constexpr Element ObtainElevatedElem() const
void ExtractFaceNodes(t_index iFace, const TIn &nodes, TOut &faceNodesOut)
DNDS_DEVICE_CALLABLE constexpr t_index GetDim() const
void ExtractElevNodeSpanNodes(t_index iNodeElev, const TIn &nodes, TOut &spanNodes)
DNDS_DEVICE_CALLABLE constexpr t_index GetNumNodes() const
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")