18#define DOCTEST_CONFIG_IMPLEMENT
28#include <nlohmann/json.hpp>
33static constexpr int g_dim = 3;
34static constexpr int g_nv = 1;
42static std::string meshPath(
const std::string &name)
44 std::string f(__FILE__);
45 for (
int i = 0; i < 4; i++)
47 auto pos = f.rfind(
'/');
48 if (pos == std::string::npos)
50 if (pos != std::string::npos)
53 return f +
"/data/mesh/" + name;
57 const std::string &file,
60 auto mesh = std::make_shared<UnstructuredMesh>(g_mpi, g_dim);
64 mesh->SetPeriodicGeometry(
65 tPoint{Lx, 0, 0}, zero, zero,
66 tPoint{0, Ly, 0}, zero, zero,
67 tPoint{0, 0, Lz}, zero, zero);
69 reader.ReadFromCGNSSerial(meshPath(file));
70 reader.Deduplicate1to1Periodic();
71 reader.BuildCell2Cell();
75 reader.MeshPartitionCell2Cell(pOpt);
76 reader.PartitionReorderToMeshCell2Cell();
78 mesh->RecoverNode2CellAndNode2Bnd();
79 mesh->RecoverCell2CellAndBnd2Cell();
80 mesh->BuildGhostPrimary();
81 mesh->AdjGlobal2LocalPrimary();
83 mesh->InterpolateFace();
84 mesh->AssertOnFaces();
114 auto vr = std::make_shared<tVR>(g_mpi, mesh);
117 nlohmann::ordered_json j;
118 defaultSettings.WriteIntoJson(j);
120 int order = recMethodOrder(method);
121 j[
"maxOrder"] =
order;
122 j[
"intOrder"] = std::max(
order + 2, 5);
123 j[
"cacheDiffBase"] =
true;
124 j[
"SORInstead"] =
false;
125 j[
"jacobiRelax"] = 1.0;
129 j[
"subs2ndOrder"] = 1;
133 j[
"subs2ndOrder"] = 0;
134 j[
"functionalSettings"][
"dirWeightScheme"] =
"HQM_OPT";
135 j[
"functionalSettings"][
"geomWeightScheme"] =
"HQM_SD";
136 j[
"functionalSettings"][
"geomWeightPower"] = 0.5;
137 j[
"functionalSettings"][
"geomWeightBias"] = 1.0;
140 vr->parseSettings(j);
141 if (mesh->isPeriodic)
142 vr->SetPeriodicTransformations();
143 vr->ConstructMetrics();
144 vr->ConstructBaseAndWeight();
145 vr->ConstructRecCoeff();
154static tVR::TFBoundary<g_nv> g_zeroBC =
157{
return Eigen::Vector<DNDS::real, g_nv>::Zero(); };
167 const tVR::TFBoundary<g_nv> &bc,
172 auto mesh = vr->mesh;
177 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
179 auto qCell = vr->GetCellQuad(iCell);
180 Eigen::Vector<DNDS::real, g_nv> uc;
182 qCell.IntegrationSimple(
184 [&](
auto &vInc,
int iG)
186 vInc(0) = exactFunc(vr->GetCellQuadraturePPhys(iCell, iG)) *
187 vr->GetCellJacobiDet(iCell, iG);
189 u[iCell] = uc / vr->GetCellVol(iCell);
191 u.
trans.startPersistentPull();
192 u.
trans.waitPersistentPull();
197 vr->BuildUGrad(uGrad, 1);
198 vr->DoReconstruction2ndGrad<g_nv>(uGrad, u, bc, 1);
199 uGrad.trans.startPersistentPull();
200 uGrad.trans.waitPersistentPull();
202 static const auto Seq012 = Eigen::seq(Eigen::fix<0>, Eigen::fix<g_dim - 1>);
204 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
206 auto qCell = vr->GetCellQuad(iCell);
208 qCell.IntegrationSimple(
212 tPoint pPhy = vr->GetCellQuadraturePPhys(iCell, iG);
214 (uGrad[iCell].transpose() *
215 (pPhy - vr->GetCellBary(iCell))(Seq012))(0);
217 vInc = std::abs(uRecVal - uExact) * vr->GetCellJacobiDet(iCell, iG);
223 return errGlobal / vr->GetGlobalVol();
228 vr->BuildURec(uRec, 1);
229 vr->BuildURec(uRecNew, 1);
232 for (
int iter = 0; iter < maxIters; iter++)
234 vr->DoReconstructionIter<g_nv>(
235 uRec, uRecNew, u, bc,
true);
238 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
239 incLocal += (uRecNew[iCell] - uRec[iCell]).array().square().sum();
242 incGlobal = std::sqrt(incGlobal / mesh->NumCellGlobal());
244 std::swap(uRec, uRecNew);
245 uRec.trans.startPersistentPull();
246 uRec.trans.waitPersistentPull();
249 if (printProgress && g_mpi.
rank == 0 && (iter < 5 || (iter + 1) % 20 == 0))
250 std::cout <<
" iter " << iter + 1 <<
" inc = "
251 << std::scientific << incGlobal << std::endl;
253 if (convTol > 0 && incGlobal < convTol)
255 if (printProgress && g_mpi.
rank == 0)
256 std::cout <<
" converged at iter " << iter + 1 << std::endl;
262 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
264 auto qCell = vr->GetCellQuad(iCell);
266 qCell.IntegrationSimple(
270 Eigen::VectorXd baseVal =
271 vr->GetIntPointDiffBaseValue(
272 iCell, -1, -1, iG, std::array<int, 1>{0}, 1) *
274 DNDS::real uRecVal = baseVal(0) + u[iCell](0);
275 DNDS::real uExact = exactFunc(vr->GetCellQuadraturePPhys(iCell, iG));
276 vInc = std::abs(uRecVal - uExact) * vr->GetCellJacobiDet(iCell, iG);
282 return errGlobal / vr->GetGlobalVol();
293 MPI_Init(&argc, &argv);
297 std::cout <<
"=== Building 3D mesh ===" << std::endl;
300 g_mesh3d = buildMesh3D(
"Uniform32_3D_Periodic.cgns", 1.0, 1.0, 1.0);
303 std::cout <<
"=== 3D mesh built, nCellGlobal="
304 << g_mesh3d->NumCellGlobal() <<
" ===" << std::endl;
306 doctest::Context ctx;
307 ctx.applyCommandLine(argc, argv);
322{
return std::sin(g_k * p[0]) * std::cos(g_k * p[1]) * std::cos(g_k * p[2]); };
325{
return std::cos(g_k * p[0]) + std::cos(g_k * p[1]) + std::cos(g_k * p[2]); };
331static constexpr DNDS::real GOLDEN_NOT_ACQUIRED = 1e300;
343 std::cout <<
"[3D/const/GG] err = " << std::scientific <<
err << std::endl;
353 std::cout <<
"[3D/const/P1-HQM] err = " << std::scientific <<
err << std::endl;
363 std::cout <<
"[3D/const/P2-HQM] err = " << std::scientific <<
err << std::endl;
411static const int g_n3dTests =
sizeof(g_3dTests) /
sizeof(g_3dTests[0]);
413TEST_CASE(
"3D: golden-value regression for smooth functions")
415 for (
int ti = 0; ti < g_n3dTests; ti++)
417 auto &tc = g_3dTests[ti];
418 std::string label = std::string(
"3D/") + tc.
funcName +
"/" +
419 recMethodName3D(tc.method);
421 SUBCASE(label.c_str())
423 auto vr = buildVR3D(g_mesh3d, tc.method);
425 g_zeroBC, tc.maxIters, tc.convTol,
false);
428 std::cout <<
"[" << label <<
"] err = " << std::scientific
429 << std::setprecision(10) <<
err << std::endl;
432 CHECK_FALSE(std::isnan(
err));
434 if (tc.golden < GOLDEN_NOT_ACQUIRED)
435 CHECK(
err == doctest::Approx(tc.golden).epsilon(1e-6));
451 vr->BuildURec(uRec, 1);
452 vr->BuildURec(uRecNew, 1);
454 auto mesh = vr->mesh;
455 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
457 auto qCell = vr->GetCellQuad(iCell);
458 Eigen::Vector<DNDS::real, g_nv> uc;
460 qCell.IntegrationSimple(
462 [&](
auto &vInc,
int iG)
464 vInc(0) = sinCos3D(vr->GetCellQuadraturePPhys(iCell, iG)) *
465 vr->GetCellJacobiDet(iCell, iG);
467 u[iCell] = uc / vr->GetCellVol(iCell);
469 u.
trans.startPersistentPull();
470 u.
trans.waitPersistentPull();
474 for (
int iter = 0; iter < 200; iter++)
476 vr->DoReconstructionIter<g_nv>(uRec, uRecNew, u, g_zeroBC,
true);
479 for (
DNDS::index iCell = 0; iCell < mesh->NumCell(); iCell++)
480 incLocal += (uRecNew[iCell] - uRec[iCell]).array().square().sum();
483 incGlobal = std::sqrt(incGlobal / mesh->NumCellGlobal());
485 std::swap(uRec, uRecNew);
486 uRec.trans.startPersistentPull();
487 uRec.trans.waitPersistentPull();
490 if (incGlobal < 1e-14)
492 convergedAt = iter + 1;
498 std::cout <<
"[3D/Convergence P1-HQM] " << convergedAt
499 <<
" iters, final inc = " << std::scientific << lastInc << std::endl;
501 CHECK(convergedAt > 0);
502 CHECK(convergedAt < 200);
515 g_zeroBC, 200, 1e-14,
false);
517 g_zeroBC, 200, 1e-14,
false);
520 std::cout <<
"[3D] P2/P1 ratio = " << errP2 / errP1 << std::endl;
522 CHECK(errP2 < errP1);
529TEST_CASE(
"3D: cell volumes sum to 1.0 (unit cube)")
534 std::cout <<
"[3D] globalVol = " << std::setprecision(15) <<
globalVol << std::endl;
Primary solver state container: an ArrayEigenMatrix pair with MPI-collective vector-space operations.
The VR class that provides any information needed in high-order CFV.
the host side operators are provided as implemented
DNDS_CONSTANT const real pi
π in double precision (matches DNDS_E_PI macro).
int64_t index
Global row / DOF index type (signed 64-bit; handles multi-billion-cell meshes).
std::shared_ptr< T > ssp
Shortened alias for std::shared_ptr used pervasively in DNDSR.
double real
Canonical floating-point scalar used throughout DNDSR (double precision).
DNDS_CONSTANT const real veryLargeReal
Catch-all upper bound ("practically infinity") for physical scalars.
const MPI_Datatype DNDS_MPI_REAL
MPI datatype matching real (= MPI_REAL8).
TTrans trans
Ghost-communication engine bound to father and son.
A means to translate nlohmann json into c++ primitive data types and back; and stores then during com...
Lightweight bundle of an MPI communicator and the calling rank's coordinates.
int rank
This rank's 0-based index within comm (-1 until initialised).
MPI_Comm comm
The underlying MPI communicator handle.
void setWorld()
Initialise the object to MPI_COMM_WORLD. Requires MPI_Init to have run.
std::function< DNDS::real(const tPoint &)> ScalarFunc3D
TEST_CASE("3D: VFV P2 HQM error < P1 on sinCos3D")
if(g_mpi.rank==0) std CHECK(globalVol==doctest::Approx(1.0).epsilon(1e-10))
CFV::VariationalReconstruction< g_dim > tVR