2Correctness tests for CFV.VariationalReconstruction and CFV.ModelEvaluator
3through the Python bindings.
6 - VR construction pipeline (metrics, base+weight, rec coeff)
7 - Constant and linear polynomial exactness
8 - Iterative reconstruction convergence
9 - ModelEvaluator RHS evaluation produces finite results
10 - ModelEvaluator DoReconstructionIter changes uRec
12Uses the Uniform_3x3 periodic mesh (9 quads, [0,3]^2).
14These tests use pytest assertions and are intended for automated CI.
17from __future__
import annotations
24from DNDSR
import DNDS, Geom, CFV
28@pytest.fixture(scope="module")
37 os.path.dirname(__file__),
"..",
"..",
"data",
"mesh", name
41@pytest.fixture(scope="module")
43 """Build VariationalReconstruction_2 on Uniform_3x3 periodic mesh."""
45 mesh, reader, name2Id = create_mesh_from_CGNS(
50 "translation1": [3, 0, 0],
51 "translation2": [0, 3, 0],
58 "cacheDiffBase":
True,
62 "functionalSettings": {
63 "dirWeightScheme":
"HQM_OPT",
64 "geomWeightScheme":
"HQM_SD",
65 "geomWeightPower": 0.5,
70 vfv = CFV.VariationalReconstruction_2(mpi, mesh)
71 vfv.ParseSettings(vfvSettings)
72 vfv.SetPeriodicTransformationsNoOp()
74 bcid_2_bcweight_map = {}
75 for name, bc_id
in name2Id.n2id_map.items():
76 bcid_2_bcweight_map[(bc_id, 0)] = 1.0
77 if name.startswith(
"PERIODIC"):
78 for order_idx
in range(4):
79 bcid_2_bcweight_map[(bc_id, order_idx)] = 1.0
80 vfv.ConstructMetrics()
81 vfv.ConstructBaseAndWeight_map(bcid_2_bcweight_map)
82 vfv.ConstructRecCoeff()
84 return mesh, vfv, name2Id
87@pytest.fixture(scope="module")
89 """Build VariationalReconstruction_2 with Gauss-Green (subs2ndOrder=1)."""
91 mesh, reader, name2Id = create_mesh_from_CGNS(
96 "translation1": [3, 0, 0],
97 "translation2": [0, 3, 0],
104 "cacheDiffBase":
True,
108 "functionalSettings": {
109 "dirWeightScheme":
"HQM_OPT",
110 "geomWeightScheme":
"HQM_SD",
111 "geomWeightPower": 0.5,
116 vfv = CFV.VariationalReconstruction_2(mpi, mesh)
117 vfv.ParseSettings(vfvSettings)
118 vfv.SetPeriodicTransformationsNoOp()
120 bcid_2_bcweight_map = {}
121 for name, bc_id
in name2Id.n2id_map.items():
122 bcid_2_bcweight_map[(bc_id, 0)] = 1.0
123 if name.startswith(
"PERIODIC"):
124 for order_idx
in range(4):
125 bcid_2_bcweight_map[(bc_id, order_idx)] = 1.0
126 vfv.ConstructMetrics()
127 vfv.ConstructBaseAndWeight_map(bcid_2_bcweight_map)
128 vfv.ConstructRecCoeff()
130 return mesh, vfv, name2Id
139 """Verify VR construction pipeline completes and produces valid state."""
142 """Global volume should be 9.0 for [0,3]^2."""
143 _, vfv, _ = periodic_vfv
144 assert vfv.GetGlobalVol() == pytest.approx(9.0, abs=1e-10)
147 """Primary cell barycenters should lie within [-1,2]^2
148 (a superset of the Uniform_3x3 mesh bounds)."""
149 mesh, vfv, _ = periodic_vfv
150 for iCell
in range(mesh.NumCell()):
151 bary = vfv.GetCellBary(iCell)
152 assert -1.0 - 1e-10 <= bary[0] <= 2.0 + 1e-10
153 assert -1.0 - 1e-10 <= bary[1] <= 2.0 + 1e-10
156 mesh, vfv, _ = periodic_vfv
157 for iCell
in range(mesh.NumCell()):
158 assert vfv.GetCellVol(iCell) > 0
161 mesh, vfv, _ = periodic_vfv
162 for iFace
in range(mesh.NumFaceProc()):
163 assert vfv.GetFaceArea(iFace) > 0
172 """Verify DOF array building through VR (BuildURec, BuildUGrad)."""
175 _, vfv, _ = periodic_vfv
177 vfv.BuildURec_D(uRec, 1)
178 assert uRec.Size() > 0
181 mesh, vfv, _ = periodic_vfv
183 vfv.BuildUDof_D(u, 1)
184 assert u.father.Size() == mesh.NumCell()
187 _, vfv, _ = periodic_vfv
188 uGrad = CFV.tUGrad_2xD()
189 vfv.BuildUGrad_D(uGrad, 1)
190 assert uGrad.Size() > 0
199 """Constant field should produce zero reconstruction coefficients."""
202 mesh, vfv, _ = periodic_vfv
204 vfv.BuildUDof_D(u, 1)
207 for iCell
in range(mesh.NumCell()):
208 u_i = np.array(u[iCell], copy=
False)
210 u.trans.startPersistentPull()
211 u.trans.waitPersistentPull()
214 uRecNew = CFV.tURec_D()
215 vfv.BuildURec_D(uRec, 1)
216 vfv.BuildURec_D(uRecNew, 1)
218 eval_obj = CFV.ModelEvaluator(mesh, vfv, {}, 1)
222 eval_obj.DoReconstructionIter(uRec, uRecNew, u, 0.0,
True)
223 uRec, uRecNew = uRecNew, uRec
227 for iCell
in range(mesh.NumCell()):
228 coeffs = np.array(uRec[iCell], copy=
False)
229 max_coeff = max(max_coeff, np.abs(coeffs).max())
231 assert max_coeff < 1e-10, f
"Max rec coeff = {max_coeff}, expected ~0"
240 """Linear field f(x,y) = x should produce accurate reconstruction."""
243 """After iterating, uRec for u=x should contain correct dx coefficient."""
244 mesh, vfv, _ = periodic_vfv
246 vfv.BuildUDof_D(u, 1)
249 for iCell
in range(mesh.NumCell()):
250 bary = vfv.GetCellBary(iCell)
251 u_i = np.array(u[iCell], copy=
False)
253 u.trans.startPersistentPull()
254 u.trans.waitPersistentPull()
257 uRecNew = CFV.tURec_D()
258 vfv.BuildURec_D(uRec, 1)
259 vfv.BuildURec_D(uRecNew, 1)
261 eval_obj = CFV.ModelEvaluator(mesh, vfv, {}, 1)
263 eval_obj.DoReconstructionIter(uRec, uRecNew, u, 0.0,
True)
264 uRec, uRecNew = uRecNew, uRec
268 for iCell
in range(mesh.NumCell()):
269 coeffs = np.array(uRec[iCell], copy=
False)
270 if np.abs(coeffs).max() > 1e-6:
273 assert has_nonzero,
"uRec should have non-zero coefficients for linear field"
282 """Verify ModelEvaluator.EvaluateRHS produces finite results."""
285 mesh, vfv, _ = periodic_vfv
290 vfv.BuildUDof_D(u, 1)
291 vfv.BuildUDof_D(rhs, 1)
292 vfv.BuildURec_D(uRec, 1)
295 for iCell
in range(mesh.NumCell()):
296 bary = vfv.GetCellBary(iCell)
297 u_i = np.array(u[iCell], copy=
False)
298 u_i[0] = np.sin(2.0 * np.pi * bary[0] / 3.0)
299 u.trans.startPersistentPull()
300 u.trans.waitPersistentPull()
302 eval_obj = CFV.ModelEvaluator(
303 mesh, vfv, {
"ax": 1.0,
"ay": 0.0,
"sigma": 0.0}, 1
305 eval_obj.EvaluateRHS(rhs, u, uRec, 0.0)
308 for iCell
in range(mesh.NumCell()):
309 rhs_i = np.array(rhs[iCell], copy=
False)
310 assert np.all(np.isfinite(rhs_i)), (
311 f
"Cell {iCell} RHS is not finite: {rhs_i}"
315 """RHS of advection equation should be nonzero for non-constant field."""
316 mesh, vfv, _ = periodic_vfv
321 vfv.BuildUDof_D(u, 1)
322 vfv.BuildUDof_D(rhs, 1)
323 vfv.BuildURec_D(uRec, 1)
325 for iCell
in range(mesh.NumCell()):
326 bary = vfv.GetCellBary(iCell)
327 u_i = np.array(u[iCell], copy=
False)
328 u_i[0] = np.sin(2.0 * np.pi * bary[0] / 3.0)
329 u.trans.startPersistentPull()
330 u.trans.waitPersistentPull()
332 eval_obj = CFV.ModelEvaluator(
333 mesh, vfv, {
"ax": 1.0,
"ay": 0.0,
"sigma": 0.0}, 1
335 eval_obj.EvaluateRHS(rhs, u, uRec, 0.0)
338 for iCell
in range(mesh.NumCell()):
339 rhs_i = np.array(rhs[iCell], copy=
False)
340 rhs_norm += np.sum(rhs_i ** 2)
341 rhs_norm = np.sqrt(rhs_norm)
342 assert rhs_norm > 1e-6, f
"RHS norm = {rhs_norm}, expected nonzero"
345 """RHS of advection for constant field should be ~zero."""
346 mesh, vfv, _ = periodic_vfv
351 vfv.BuildUDof_D(u, 1)
352 vfv.BuildUDof_D(rhs, 1)
353 vfv.BuildURec_D(uRec, 1)
355 for iCell
in range(mesh.NumCell()):
356 u_i = np.array(u[iCell], copy=
False)
358 u.trans.startPersistentPull()
359 u.trans.waitPersistentPull()
361 eval_obj = CFV.ModelEvaluator(
362 mesh, vfv, {
"ax": 1.0,
"ay": 1.0,
"sigma": 0.0}, 1
364 eval_obj.EvaluateRHS(rhs, u, uRec, 0.0)
367 for iCell
in range(mesh.NumCell()):
368 rhs_i = np.array(rhs[iCell], copy=
False)
369 rhs_norm += np.sum(rhs_i ** 2)
370 rhs_norm = np.sqrt(rhs_norm)
371 assert rhs_norm < 1e-10, f
"RHS norm = {rhs_norm}, expected ~0 for const field"
380 """Verify VR iteration converges."""
383 """Iterative VR should reduce increment over iterations."""
384 mesh, vfv, _ = periodic_vfv
386 vfv.BuildUDof_D(u, 1)
388 for iCell
in range(mesh.NumCell()):
389 bary = vfv.GetCellBary(iCell)
390 u_i = np.array(u[iCell], copy=
False)
391 u_i[0] = np.sin(2.0 * np.pi * bary[0] / 3.0) * np.cos(
392 2.0 * np.pi * bary[1] / 3.0
394 u.trans.startPersistentPull()
395 u.trans.waitPersistentPull()
398 uRecNew = CFV.tURec_D()
399 vfv.BuildURec_D(uRec, 1)
400 vfv.BuildURec_D(uRecNew, 1)
402 eval_obj = CFV.ModelEvaluator(mesh, vfv, {}, 1)
406 eval_obj.DoReconstructionIter(uRec, uRecNew, u, 0.0,
True)
410 for iCell
in range(mesh.NumCell()):
411 diff = np.array(uRecNew[iCell]) - np.array(uRec[iCell])
412 inc += np.sum(diff ** 2)
413 increments.append(np.sqrt(inc))
415 uRec, uRecNew = uRecNew, uRec
419 assert increments[-1] < increments[2], (
420 f
"Not converging: final inc = {increments[-1]}, "
421 f
"early inc = {increments[2]}"
431 """Verify reconstruction matrices are accessible and valid."""
434 """matrixAAInvB should be accessible and contain finite values."""
435 mesh, vfv, _ = periodic_vfv
436 mat = vfv.matrixAAInvB
437 assert mat
is not None
439 m00 = np.array(mat[0, 0], copy=
False)
440 assert np.all(np.isfinite(m00)),
"matrixAAInvB[0,0] contains non-finite"
441 assert m00.shape[0] > 0
442 assert m00.shape[1] > 0
445 """vectorAInvB should be accessible and contain finite values."""
446 mesh, vfv, _ = periodic_vfv
447 vec = vfv.vectorAInvB
448 assert vec
is not None
449 v00 = np.array(vec[0, 0], copy=
False)
450 assert np.all(np.isfinite(v00)),
"vectorAInvB[0,0] contains non-finite"
459 """Verify ModelEvaluator.get_FBoundary returns a callable."""
462 mesh, vfv, _ = periodic_vfv
463 eval_obj = CFV.ModelEvaluator(mesh, vfv, {}, 1)
464 fb = eval_obj.get_FBoundary(0.5)
465 assert fb
is not None
468if __name__ ==
"__main__":
469 pytest.main([__file__,
"-v"])
test_get_fboundary(self, periodic_vfv)
test_constant_field_zero_urec(self, periodic_vfv)
test_linear_x_reconstruction(self, periodic_vfv)
test_vectorAInvB_accessible(self, periodic_vfv)
test_matrixAAInvB_accessible(self, periodic_vfv)
test_rhs_nonzero_for_nonuniform_field(self, periodic_vfv)
test_rhs_zero_for_constant_field(self, periodic_vfv)
test_rhs_finite(self, periodic_vfv)
test_iterative_vr_convergence(self, periodic_vfv)
test_cell_vol_positive(self, periodic_vfv)
test_face_area_positive(self, periodic_vfv)
test_global_vol(self, periodic_vfv)
test_cell_bary_in_range(self, periodic_vfv)
test_build_ugrad_dynamic(self, periodic_vfv)
test_build_urec_dynamic(self, periodic_vfv)
test_build_udof_dynamic(self, periodic_vfv)
str _data_mesh_path(str name)
Lightweight bundle of an MPI communicator and the calling rank's coordinates.