PICurv 0.1.0
A Parallel Particle-In-Cell Solver for Curvilinear LES
Loading...
Searching...
No Matches
test_support.c
Go to the documentation of this file.
1/**
2 * @file test_support.c
3 * @brief Shared C test fixtures, assertions, and PETSc helper utilities.
4 */
5
6#include "test_support.h"
7
8#include "grid.h"
9#include "io.h"
10#include "setup.h"
11
12#include <errno.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
18#include <unistd.h>
19/**
20 * @brief Destroys a PETSc vector only when the handle is non-null.
21 */
22
23static PetscErrorCode DestroyVecIfSet(Vec *vec)
24{
25 PetscFunctionBeginUser;
26 if (vec && *vec) {
27 PetscCall(VecDestroy(vec));
28 }
29 PetscFunctionReturn(0);
30}
31/**
32 * @brief Destroys a PETSc DM only when the handle is non-null.
33 */
34
35static PetscErrorCode DestroyDMIfSet(DM *dm)
36{
37 PetscFunctionBeginUser;
38 if (dm && *dm) {
39 PetscCall(DMDestroy(dm));
40 }
41 PetscFunctionReturn(0);
42}
43/**
44 * @brief Destroys a PETSc matrix only when the handle is non-null.
45 */
46
47static PetscErrorCode DestroyMatIfSet(Mat *mat)
48{
49 PetscFunctionBeginUser;
50 if (mat && *mat) {
51 PetscCall(MatDestroy(mat));
52 }
53 PetscFunctionReturn(0);
54}
55/**
56 * @brief Destroys a PETSc KSP only when the handle is non-null.
57 */
58
59static PetscErrorCode DestroyKSPIfSet(KSP *ksp)
60{
61 PetscFunctionBeginUser;
62 if (ksp && *ksp) {
63 PetscCall(KSPDestroy(ksp));
64 }
65 PetscFunctionReturn(0);
66}
67/**
68 * @brief Destroys a PETSc nullspace only when the handle is non-null.
69 */
70
71static PetscErrorCode DestroyNullSpaceIfSet(MatNullSpace *nullsp)
72{
73 PetscFunctionBeginUser;
74 if (nullsp && *nullsp) {
75 PetscCall(MatNullSpaceDestroy(nullsp));
76 }
77 PetscFunctionReturn(0);
78}
79/**
80 * @brief Destroys a PETSc random generator only when the handle is non-null.
81 */
82
83static PetscErrorCode DestroyRandomIfSet(PetscRandom *rand_ctx)
84{
85 PetscFunctionBeginUser;
86 if (rand_ctx && *rand_ctx) {
87 PetscCall(PetscRandomDestroy(rand_ctx));
88 }
89 PetscFunctionReturn(0);
90}
91/**
92 * @brief Registers one DMSwarm field used by the C test fixtures.
93 */
94
95static PetscErrorCode RegisterSwarmFieldForTests(DM swarm, const char *field_name, PetscInt field_dim, PetscDataType dtype)
96{
97 PetscFunctionBeginUser;
98 PetscCall(DMSwarmRegisterPetscDatatypeField(swarm, field_name, field_dim, dtype));
99 PetscFunctionReturn(0);
100}
101/**
102 * @brief Allocates and zeroes a global vector from the provided DM.
103 */
104
105static PetscErrorCode CreateZeroedGlobalVector(DM dm, Vec *vec)
106{
107 PetscFunctionBeginUser;
108 PetscCall(DMCreateGlobalVector(dm, vec));
109 PetscCall(VecSet(*vec, 0.0));
110 PetscFunctionReturn(0);
111}
112/**
113 * @brief Allocates and zeroes a local vector from the provided DM.
114 */
115
116static PetscErrorCode CreateZeroedLocalVector(DM dm, Vec *vec)
117{
118 PetscFunctionBeginUser;
119 PetscCall(DMCreateLocalVector(dm, vec));
120 PetscCall(VecSet(*vec, 0.0));
121 PetscFunctionReturn(0);
122}
123/**
124 * @brief Duplicates and zeroes a vector.
125 */
126
127static PetscErrorCode CreateZeroedDuplicate(Vec src, Vec *vec)
128{
129 PetscFunctionBeginUser;
130 PetscCall(VecDuplicate(src, vec));
131 PetscCall(VecSet(*vec, 0.0));
132 PetscFunctionReturn(0);
133}
134/**
135 * @brief Runs a named C test suite and prints pass/fail progress markers.
136 */
137
138PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
139{
140 PetscFunctionBeginUser;
141
142 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "==> Running %s (%zu tests)\n", suite_name, case_count));
143 for (size_t i = 0; i < case_count; ++i) {
144 PetscCall(PetscPrintf(PETSC_COMM_WORLD, " -> %s\n", cases[i].name));
145 PetscCall(cases[i].fn());
146 PetscCall(PetscPrintf(PETSC_COMM_WORLD, " [PASS] %s\n", cases[i].name));
147 }
148
149 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "==> %s complete\n", suite_name));
150 PetscFunctionReturn(0);
151}
152/**
153 * @brief Ensures a directory exists for test output.
154 */
155
156PetscErrorCode PicurvEnsureDir(const char *path)
157{
158 PetscFunctionBeginUser;
159 if (mkdir(path, 0777) != 0 && errno != EEXIST) {
160 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Failed to create directory '%s': %s", path, strerror(errno));
161 }
162 PetscFunctionReturn(0);
163}
164/**
165 * @brief Creates a unique temporary directory for one test case.
166 */
167
168PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
169{
170 PetscFunctionBeginUser;
171 if (!path || path_len < 24) {
172 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Temp directory buffer is missing or too small.");
173 }
174
175 PetscCall(PetscSNPrintf(path, path_len, "/tmp/picurv-test-XXXXXX"));
176 if (!mkdtemp(path)) {
177 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "mkdtemp failed for '%s': %s", path, strerror(errno));
178 }
179 PetscFunctionReturn(0);
180}
181/**
182 * @brief Recursively removes a temporary directory created by PicurvMakeTempDir.
183 */
184
185PetscErrorCode PicurvRemoveTempDir(const char *path)
186{
187 char cmd[512];
188
189 PetscFunctionBeginUser;
190 if (!path || path[0] == '\0') PetscFunctionReturn(0);
191 /* Safety: only remove paths under /tmp/picurv-test- */
192 if (strncmp(path, "/tmp/picurv-test-", 17) != 0) {
193 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
194 "Refusing to remove path outside /tmp/picurv-test-*: '%s'", path);
195 }
196 PetscCall(PetscSNPrintf(cmd, sizeof(cmd), "rm -rf '%s'", path));
197 if (system(cmd) != 0) {
198 PetscCall(PetscPrintf(PETSC_COMM_SELF, "Warning: failed to remove temp dir '%s'\n", path));
199 }
200 PetscFunctionReturn(0);
201}
202/**
203 * @brief Writes one small temporary text file used by the richer runtime fixtures.
204 */
205
206static PetscErrorCode WriteTextFileForTests(const char *path, const char *contents)
207{
208 FILE *file = NULL;
209
210 PetscFunctionBeginUser;
211 file = fopen(path, "w");
212 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Failed to open '%s' for writing.", path);
213 fputs(contents, file);
214 fclose(file);
215 PetscFunctionReturn(0);
216}
217/**
218 * @brief Creates a tiny control-file bundle used by richer runtime fixtures built through the setup path.
219 */
220
221static PetscErrorCode PrepareTinyRuntimeConfig(const char *bcs_contents,
222 PetscBool enable_particles,
223 char *tmpdir,
224 size_t tmpdir_len,
225 char *control_path,
226 size_t control_path_len)
227{
228 char bcs_path[PETSC_MAX_PATH_LEN];
229 char post_path[PETSC_MAX_PATH_LEN];
230 char output_dir[PETSC_MAX_PATH_LEN];
231 char log_dir[PETSC_MAX_PATH_LEN];
232 char control_buffer[8192];
233 const char *particle_block = NULL;
234 const char *default_bcs =
235 "-Xi WALL noslip\n"
236 "+Xi WALL noslip\n"
237 "-Eta WALL noslip\n"
238 "+Eta WALL noslip\n"
239 "-Zeta INLET constant_velocity vx=0.0 vy=0.0 vz=1.5\n"
240 "+Zeta OUTLET conservation\n";
241
242 PetscFunctionBeginUser;
243 PetscCall(PicurvMakeTempDir(tmpdir, tmpdir_len));
244 PetscCall(PetscSNPrintf(bcs_path, sizeof(bcs_path), "%s/bcs.run", tmpdir));
245 PetscCall(PetscSNPrintf(post_path, sizeof(post_path), "%s/post.run", tmpdir));
246 PetscCall(PetscSNPrintf(output_dir, sizeof(output_dir), "%s/results", tmpdir));
247 PetscCall(PetscSNPrintf(log_dir, sizeof(log_dir), "%s/logs", tmpdir));
248 PetscCall(PetscSNPrintf(control_path, control_path_len, "%s/test.control", tmpdir));
249
250 PetscCall(WriteTextFileForTests(bcs_path, bcs_contents ? bcs_contents : default_bcs));
251 PetscCall(WriteTextFileForTests(
252 post_path,
253 "startTime = 0\n"
254 "endTime = 1\n"
255 "timeStep = 1\n"
256 "output_particles = false\n"));
257
258 if (enable_particles) {
259 particle_block =
260 "-numParticles 8\n"
261 "-pinit 2\n"
262 "-psrc_x 0.5\n"
263 "-psrc_y 0.5\n"
264 "-psrc_z 0.5\n"
265 "-particle_restart_mode init\n";
266 } else {
267 particle_block =
268 "-numParticles 0\n"
269 "-pinit 2\n";
270 }
271
272 PetscCall(PetscSNPrintf(
273 control_buffer,
274 sizeof(control_buffer),
275 "-start_step 0\n"
276 "-totalsteps 2\n"
277 "-ren 100.0\n"
278 "-dt 0.001\n"
279 "-finit 1\n"
280 "-ucont_x 0.0\n"
281 "-ucont_y 0.0\n"
282 "-ucont_z 1.5\n"
283 "-bcs_files %s\n"
284 "-profiling_timestep_mode off\n"
285 "-profiling_final_summary true\n"
286 "-postprocessing_config_file %s\n"
287 "-grid\n"
288 "-im 6\n"
289 "-jm 6\n"
290 "-km 6\n"
291 "-xMins 0.0\n"
292 "-xMaxs 1.0\n"
293 "-yMins 0.0\n"
294 "-yMaxs 1.0\n"
295 "-zMins 0.0\n"
296 "-zMaxs 1.0\n"
297 "-rxs 1.0\n"
298 "-rys 1.0\n"
299 "-rzs 1.0\n"
300 "-cgrids 0\n"
301 "-nblk 1\n"
302 "-euler_field_source solve\n"
303 "-mom_solver_type EXPLICIT_RK\n"
304 "-mg_level 1\n"
305 "-poisson 0\n"
306 "-tio 0\n"
307 "-particle_console_output_freq 0\n"
308 "-logfreq 1\n"
309 "-output_dir %s\n"
310 "-restart_dir %s\n"
311 "-log_dir %s\n"
312 "%s",
313 bcs_path,
314 post_path,
315 output_dir,
316 output_dir,
317 log_dir,
318 particle_block));
319 PetscCall(WriteTextFileForTests(control_path, control_buffer));
320 PetscFunctionReturn(0);
321}
322/**
323 * @brief Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
324 */
326 UserCtx **user_out,
327 PetscInt mx,
328 PetscInt my,
329 PetscInt mz,
330 PetscBool x_periodic,
331 PetscBool y_periodic,
332 PetscBool z_periodic)
333{
334 SimCtx *simCtx = NULL;
335 UserCtx *user = NULL;
336 BoundingBox *boxes = NULL;
337 PetscInt da_mx = mx + 1;
338 PetscInt da_my = my + 1;
339 PetscInt da_mz = mz + 1;
340 DMBoundaryType x_boundary = x_periodic ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE;
341 DMBoundaryType y_boundary = y_periodic ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE;
342 DMBoundaryType z_boundary = z_periodic ? DM_BOUNDARY_PERIODIC : DM_BOUNDARY_NONE;
343 PetscInt stencil_width = (x_periodic || y_periodic || z_periodic) ? 3 : 1;
344
345 PetscFunctionBeginUser;
346 if (!simCtx_out || !user_out) {
347 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "Output pointers cannot be NULL.");
348 }
349
350 PetscCall(PetscCalloc1(1, &simCtx));
351 PetscCall(PetscCalloc1(1, &user));
352
353 PetscCallMPI(MPI_Comm_rank(PETSC_COMM_WORLD, &simCtx->rank));
354 PetscCallMPI(MPI_Comm_size(PETSC_COMM_WORLD, &simCtx->size));
355 simCtx->block_number = 1;
356 simCtx->dt = 0.1;
357 simCtx->tiout = 2;
358 simCtx->forceScalingFactor = 1.0;
359 simCtx->LoggingFrequency = 1;
360 simCtx->exec_mode = EXEC_MODE_SOLVER;
362 simCtx->poisson = 0;
363 simCtx->ren = 1.0;
364 simCtx->schmidt_number = 1.0;
365 simCtx->StartStep = 0;
366 simCtx->StepsToRun = 1;
367 simCtx->step = 1;
368 simCtx->np = 0;
369 simCtx->i_periodic = x_periodic ? 1 : 0;
370 simCtx->j_periodic = y_periodic ? 1 : 0;
371 simCtx->k_periodic = z_periodic ? 1 : 0;
372 PetscCall(PetscStrncpy(simCtx->euler_subdir, "euler", sizeof(simCtx->euler_subdir)));
373 PetscCall(PetscStrncpy(simCtx->particle_subdir, "particles", sizeof(simCtx->particle_subdir)));
374 PetscCall(PetscStrncpy(simCtx->output_dir, "/tmp", sizeof(simCtx->output_dir)));
375 PetscCall(PetscStrncpy(simCtx->restart_dir, "/tmp", sizeof(simCtx->restart_dir)));
376 PetscCall(PetscStrncpy(simCtx->log_dir, "/tmp", sizeof(simCtx->log_dir)));
377 simCtx->mglevels = 1;
378 simCtx->usermg.mglevels = 1;
379 PetscCall(PetscCalloc1(1, &simCtx->usermg.mgctx));
380 simCtx->usermg.mgctx[0].thislevel = 0;
381 PetscCall(PetscCalloc1(simCtx->size, &simCtx->bboxlist));
382 PetscCall(PetscRandomCreate(PETSC_COMM_WORLD, &simCtx->BrownianMotionRNG));
383 PetscCall(PetscRandomSetType(simCtx->BrownianMotionRNG, PETSCRAND48));
384 PetscCall(PetscRandomSetInterval(simCtx->BrownianMotionRNG, 0.0, 1.0));
385 PetscCall(PetscRandomSetSeed(simCtx->BrownianMotionRNG, 12345));
386 PetscCall(PetscRandomSeed(simCtx->BrownianMotionRNG));
387
388 user->simCtx = simCtx;
389 user->_this = 0;
390 user->thislevel = 0;
391 user->mglevels = 1;
392 user->IM = mx;
393 user->JM = my;
394 user->KM = mz;
395 user->Min_X = 0.0;
396 user->Min_Y = 0.0;
397 user->Min_Z = 0.0;
398 user->Max_X = 1.0;
399 user->Max_Y = 1.0;
400 user->Max_Z = 1.0;
401 user->rx = 1.0;
402 user->ry = 1.0;
403 user->rz = 1.0;
404 user->bbox.min_coords.x = 0.0;
405 user->bbox.min_coords.y = 0.0;
406 user->bbox.min_coords.z = 0.0;
407 user->bbox.max_coords.x = 1.0;
408 user->bbox.max_coords.y = 1.0;
409 user->bbox.max_coords.z = 1.0;
410 simCtx->usermg.mgctx[0].user = user;
411 for (PetscMPIInt rank_idx = 0; rank_idx < simCtx->size; ++rank_idx) {
412 simCtx->bboxlist[rank_idx].min_coords.x = 0.0;
413 simCtx->bboxlist[rank_idx].min_coords.y = 0.0;
414 simCtx->bboxlist[rank_idx].min_coords.z = 0.0;
415 simCtx->bboxlist[rank_idx].max_coords.x = 1.0;
416 simCtx->bboxlist[rank_idx].max_coords.y = 1.0;
417 simCtx->bboxlist[rank_idx].max_coords.z = 1.0;
418 }
419
420 PetscCall(DMDACreate3d(PETSC_COMM_WORLD,
421 x_boundary, y_boundary, z_boundary,
422 DMDA_STENCIL_BOX,
423 da_mx, da_my, da_mz,
424 PETSC_DECIDE, PETSC_DECIDE, PETSC_DECIDE,
425 1, stencil_width,
426 NULL, NULL, NULL,
427 &user->da));
428 PetscCall(DMSetUp(user->da));
429 PetscCall(DMGetCoordinateDM(user->da, &user->fda));
430 PetscCall(PetscObjectReference((PetscObject)user->fda));
431 PetscCall(DMDASetUniformCoordinates(user->da, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0));
432 PetscCall(DMDAGetLocalInfo(user->da, &user->info));
433 PetscCall(ComputeLocalBoundingBox(user, &user->bbox));
434 PetscCall(GatherAllBoundingBoxes(user, &boxes));
435 PetscCall(BroadcastAllBoundingBoxes(user, &boxes));
436 for (PetscMPIInt rank_idx = 0; rank_idx < simCtx->size; ++rank_idx) {
437 simCtx->bboxlist[rank_idx] = boxes[rank_idx];
438 }
439 free(boxes);
440
441 PetscCall(CreateZeroedGlobalVector(user->da, &user->P));
442 PetscCall(CreateZeroedLocalVector(user->da, &user->lP));
443 PetscCall(CreateZeroedGlobalVector(user->da, &user->Phi));
444 PetscCall(CreateZeroedLocalVector(user->da, &user->lPhi));
445 PetscCall(CreateZeroedGlobalVector(user->da, &user->Nvert));
446 PetscCall(CreateZeroedLocalVector(user->da, &user->lNvert));
447 PetscCall(CreateZeroedGlobalVector(user->da, &user->ParticleCount));
448 PetscCall(CreateZeroedLocalVector(user->da, &user->lParticleCount));
449 PetscCall(CreateZeroedGlobalVector(user->da, &user->Psi));
450 PetscCall(CreateZeroedLocalVector(user->da, &user->lPsi));
451 PetscCall(CreateZeroedGlobalVector(user->da, &user->Qcrit));
452 PetscCall(CreateZeroedGlobalVector(user->da, &user->P_nodal));
453 PetscCall(CreateZeroedGlobalVector(user->da, &user->Psi_nodal));
454 PetscCall(CreateZeroedGlobalVector(user->da, &user->Aj));
455 PetscCall(CreateZeroedLocalVector(user->da, &user->lAj));
456 PetscCall(CreateZeroedGlobalVector(user->da, &user->Diffusivity));
457 PetscCall(CreateZeroedLocalVector(user->da, &user->lDiffusivity));
458 PetscCall(CreateZeroedGlobalVector(user->da, &user->B));
459 PetscCall(CreateZeroedGlobalVector(user->da, &user->R));
460
461 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Ucat));
462 PetscCall(CreateZeroedLocalVector(user->fda, &user->lUcat));
463 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Ucont));
464 PetscCall(CreateZeroedLocalVector(user->fda, &user->lUcont));
465 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Csi));
466 PetscCall(CreateZeroedLocalVector(user->fda, &user->lCsi));
467 PetscCall(CreateZeroedDuplicate(user->Csi, &user->Eta));
468 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lEta));
469 PetscCall(CreateZeroedDuplicate(user->Csi, &user->Zet));
470 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lZet));
471 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Cent));
472 PetscCall(CreateZeroedLocalVector(user->fda, &user->lCent));
473 PetscCall(CreateZeroedGlobalVector(user->fda, &user->GridSpace));
474 PetscCall(CreateZeroedLocalVector(user->fda, &user->lGridSpace));
475 PetscCall(CreateZeroedLocalVector(user->fda, &user->Centx));
476 PetscCall(CreateZeroedLocalVector(user->fda, &user->Centy));
477 PetscCall(CreateZeroedLocalVector(user->fda, &user->Centz));
478 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Ucat_nodal));
479 PetscCall(CreateZeroedGlobalVector(user->fda, &user->DiffusivityGradient));
480 PetscCall(CreateZeroedLocalVector(user->fda, &user->lDiffusivityGradient));
481 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Rhs));
482 PetscCall(CreateZeroedGlobalVector(user->fda, &user->dUcont));
483 PetscCall(CreateZeroedGlobalVector(user->fda, &user->pUcont));
484 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Bcs.Ubcs));
485 PetscCall(CreateZeroedGlobalVector(user->fda, &user->Bcs.Uch));
486
487 PetscCall(CreateZeroedDuplicate(user->Ucont, &user->Ucont_o));
488 PetscCall(CreateZeroedDuplicate(user->lUcont, &user->lUcont_o));
489 PetscCall(CreateZeroedDuplicate(user->Ucont, &user->Ucont_rm1));
490 PetscCall(CreateZeroedDuplicate(user->lUcont, &user->lUcont_rm1));
491 PetscCall(CreateZeroedDuplicate(user->Ucat, &user->Ucat_o));
492 PetscCall(CreateZeroedDuplicate(user->P, &user->P_o));
493 PetscCall(CreateZeroedDuplicate(user->Nvert, &user->Nvert_o));
494 PetscCall(CreateZeroedLocalVector(user->da, &user->lNvert_o));
495 PetscCall(CreateZeroedDuplicate(user->Csi, &user->ICsi));
496 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lICsi));
497 PetscCall(CreateZeroedDuplicate(user->Csi, &user->IEta));
498 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lIEta));
499 PetscCall(CreateZeroedDuplicate(user->Csi, &user->IZet));
500 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lIZet));
501 PetscCall(CreateZeroedDuplicate(user->Csi, &user->JCsi));
502 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lJCsi));
503 PetscCall(CreateZeroedDuplicate(user->Csi, &user->JEta));
504 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lJEta));
505 PetscCall(CreateZeroedDuplicate(user->Csi, &user->JZet));
506 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lJZet));
507 PetscCall(CreateZeroedDuplicate(user->Csi, &user->KCsi));
508 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lKCsi));
509 PetscCall(CreateZeroedDuplicate(user->Csi, &user->KEta));
510 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lKEta));
511 PetscCall(CreateZeroedDuplicate(user->Csi, &user->KZet));
512 PetscCall(CreateZeroedDuplicate(user->lCsi, &user->lKZet));
513 PetscCall(CreateZeroedDuplicate(user->Aj, &user->IAj));
514 PetscCall(CreateZeroedDuplicate(user->lAj, &user->lIAj));
515 PetscCall(CreateZeroedDuplicate(user->Aj, &user->JAj));
516 PetscCall(CreateZeroedDuplicate(user->lAj, &user->lJAj));
517 PetscCall(CreateZeroedDuplicate(user->Aj, &user->KAj));
518 PetscCall(CreateZeroedDuplicate(user->lAj, &user->lKAj));
519
520 PetscCall(PicurvPopulateUniformCellCenters(user));
521 PetscCall(PicurvPopulateIdentityMetrics(user));
522
523 *simCtx_out = simCtx;
524 *user_out = user;
525 PetscFunctionReturn(0);
526}
527
528/**
529 * @brief Builds minimal SimCtx and UserCtx fixtures for C unit tests.
530 */
531PetscErrorCode PicurvCreateMinimalContexts(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz)
532{
533 PetscFunctionBeginUser;
534 PetscCall(PicurvCreateMinimalContextsWithPeriodicity(simCtx_out, user_out, mx, my, mz, PETSC_FALSE, PETSC_FALSE, PETSC_FALSE));
535 PetscFunctionReturn(0);
536}
537
538/**
539 * @brief Populates cell center coordinates for a uniform grid on [0,1]^3.
540 *
541 * Uses the shifted-index convention: cell (i,j,k) is stored at array index
542 * (i+1, j+1, k+1). For mx cells on [0,1], cell i has center at (i+0.5)/mx.
543 */
545{
546 Cmpnts ***cent = NULL;
547 PetscInt mx = user->IM;
548 PetscInt my = user->JM;
549 PetscInt mz = user->KM;
550
551 PetscFunctionBeginUser;
552 PetscCall(DMDAVecGetArray(user->fda, user->Cent, &cent));
553 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; k++) {
554 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; j++) {
555 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; i++) {
556 cent[k][j][i].x = (i - 0.5) / (PetscReal)mx;
557 cent[k][j][i].y = (j - 0.5) / (PetscReal)my;
558 cent[k][j][i].z = (k - 0.5) / (PetscReal)mz;
559 }
560 }
561 }
562 PetscCall(DMDAVecRestoreArray(user->fda, user->Cent, &cent));
563 PetscCall(DMGlobalToLocalBegin(user->fda, user->Cent, INSERT_VALUES, user->lCent));
564 PetscCall(DMGlobalToLocalEnd(user->fda, user->Cent, INSERT_VALUES, user->lCent));
565 PetscFunctionReturn(0);
566}
567
568/**
569 * @brief Populates identity metric vectors on the minimal grid fixture.
570 */
571
573{
574 Cmpnts ***csi = NULL;
575 Cmpnts ***eta = NULL;
576 Cmpnts ***zet = NULL;
577 Cmpnts ***icsi = NULL;
578 Cmpnts ***ieta = NULL;
579 Cmpnts ***izet = NULL;
580 Cmpnts ***jcsi = NULL;
581 Cmpnts ***jeta = NULL;
582 Cmpnts ***jzet = NULL;
583 Cmpnts ***kcsi = NULL;
584 Cmpnts ***keta = NULL;
585 Cmpnts ***kzet = NULL;
586 PetscReal ***aj = NULL;
587 PetscReal ***iaj = NULL;
588 PetscReal ***jaj = NULL;
589 PetscReal ***kaj = NULL;
590
591 PetscFunctionBeginUser;
592 PetscCall(DMDAVecGetArray(user->fda, user->Csi, &csi));
593 PetscCall(DMDAVecGetArray(user->fda, user->Eta, &eta));
594 PetscCall(DMDAVecGetArray(user->fda, user->Zet, &zet));
595 PetscCall(DMDAVecGetArray(user->fda, user->ICsi, &icsi));
596 PetscCall(DMDAVecGetArray(user->fda, user->IEta, &ieta));
597 PetscCall(DMDAVecGetArray(user->fda, user->IZet, &izet));
598 PetscCall(DMDAVecGetArray(user->fda, user->JCsi, &jcsi));
599 PetscCall(DMDAVecGetArray(user->fda, user->JEta, &jeta));
600 PetscCall(DMDAVecGetArray(user->fda, user->JZet, &jzet));
601 PetscCall(DMDAVecGetArray(user->fda, user->KCsi, &kcsi));
602 PetscCall(DMDAVecGetArray(user->fda, user->KEta, &keta));
603 PetscCall(DMDAVecGetArray(user->fda, user->KZet, &kzet));
604 PetscCall(DMDAVecGetArray(user->da, user->Aj, &aj));
605 PetscCall(DMDAVecGetArray(user->da, user->IAj, &iaj));
606 PetscCall(DMDAVecGetArray(user->da, user->JAj, &jaj));
607 PetscCall(DMDAVecGetArray(user->da, user->KAj, &kaj));
608
609 for (PetscInt k = user->info.zs; k < user->info.zs + user->info.zm; ++k) {
610 for (PetscInt j = user->info.ys; j < user->info.ys + user->info.ym; ++j) {
611 for (PetscInt i = user->info.xs; i < user->info.xs + user->info.xm; ++i) {
612 csi[k][j][i].x = 1.0; csi[k][j][i].y = 0.0; csi[k][j][i].z = 0.0;
613 eta[k][j][i].x = 0.0; eta[k][j][i].y = 1.0; eta[k][j][i].z = 0.0;
614 zet[k][j][i].x = 0.0; zet[k][j][i].y = 0.0; zet[k][j][i].z = 1.0;
615 icsi[k][j][i] = csi[k][j][i];
616 ieta[k][j][i] = eta[k][j][i];
617 izet[k][j][i] = zet[k][j][i];
618 jcsi[k][j][i] = csi[k][j][i];
619 jeta[k][j][i] = eta[k][j][i];
620 jzet[k][j][i] = zet[k][j][i];
621 kcsi[k][j][i] = csi[k][j][i];
622 keta[k][j][i] = eta[k][j][i];
623 kzet[k][j][i] = zet[k][j][i];
624 aj[k][j][i] = 1.0;
625 iaj[k][j][i] = 1.0;
626 jaj[k][j][i] = 1.0;
627 kaj[k][j][i] = 1.0;
628 }
629 }
630 }
631
632 PetscCall(DMDAVecRestoreArray(user->fda, user->Csi, &csi));
633 PetscCall(DMDAVecRestoreArray(user->fda, user->Eta, &eta));
634 PetscCall(DMDAVecRestoreArray(user->fda, user->Zet, &zet));
635 PetscCall(DMDAVecRestoreArray(user->fda, user->ICsi, &icsi));
636 PetscCall(DMDAVecRestoreArray(user->fda, user->IEta, &ieta));
637 PetscCall(DMDAVecRestoreArray(user->fda, user->IZet, &izet));
638 PetscCall(DMDAVecRestoreArray(user->fda, user->JCsi, &jcsi));
639 PetscCall(DMDAVecRestoreArray(user->fda, user->JEta, &jeta));
640 PetscCall(DMDAVecRestoreArray(user->fda, user->JZet, &jzet));
641 PetscCall(DMDAVecRestoreArray(user->fda, user->KCsi, &kcsi));
642 PetscCall(DMDAVecRestoreArray(user->fda, user->KEta, &keta));
643 PetscCall(DMDAVecRestoreArray(user->fda, user->KZet, &kzet));
644 PetscCall(DMDAVecRestoreArray(user->da, user->Aj, &aj));
645 PetscCall(DMDAVecRestoreArray(user->da, user->IAj, &iaj));
646 PetscCall(DMDAVecRestoreArray(user->da, user->JAj, &jaj));
647 PetscCall(DMDAVecRestoreArray(user->da, user->KAj, &kaj));
648
649 PetscCall(DMGlobalToLocalBegin(user->fda, user->Csi, INSERT_VALUES, user->lCsi));
650 PetscCall(DMGlobalToLocalEnd(user->fda, user->Csi, INSERT_VALUES, user->lCsi));
651 PetscCall(DMGlobalToLocalBegin(user->fda, user->Eta, INSERT_VALUES, user->lEta));
652 PetscCall(DMGlobalToLocalEnd(user->fda, user->Eta, INSERT_VALUES, user->lEta));
653 PetscCall(DMGlobalToLocalBegin(user->fda, user->Zet, INSERT_VALUES, user->lZet));
654 PetscCall(DMGlobalToLocalEnd(user->fda, user->Zet, INSERT_VALUES, user->lZet));
655 PetscCall(DMGlobalToLocalBegin(user->fda, user->ICsi, INSERT_VALUES, user->lICsi));
656 PetscCall(DMGlobalToLocalEnd(user->fda, user->ICsi, INSERT_VALUES, user->lICsi));
657 PetscCall(DMGlobalToLocalBegin(user->fda, user->IEta, INSERT_VALUES, user->lIEta));
658 PetscCall(DMGlobalToLocalEnd(user->fda, user->IEta, INSERT_VALUES, user->lIEta));
659 PetscCall(DMGlobalToLocalBegin(user->fda, user->IZet, INSERT_VALUES, user->lIZet));
660 PetscCall(DMGlobalToLocalEnd(user->fda, user->IZet, INSERT_VALUES, user->lIZet));
661 PetscCall(DMGlobalToLocalBegin(user->fda, user->JCsi, INSERT_VALUES, user->lJCsi));
662 PetscCall(DMGlobalToLocalEnd(user->fda, user->JCsi, INSERT_VALUES, user->lJCsi));
663 PetscCall(DMGlobalToLocalBegin(user->fda, user->JEta, INSERT_VALUES, user->lJEta));
664 PetscCall(DMGlobalToLocalEnd(user->fda, user->JEta, INSERT_VALUES, user->lJEta));
665 PetscCall(DMGlobalToLocalBegin(user->fda, user->JZet, INSERT_VALUES, user->lJZet));
666 PetscCall(DMGlobalToLocalEnd(user->fda, user->JZet, INSERT_VALUES, user->lJZet));
667 PetscCall(DMGlobalToLocalBegin(user->fda, user->KCsi, INSERT_VALUES, user->lKCsi));
668 PetscCall(DMGlobalToLocalEnd(user->fda, user->KCsi, INSERT_VALUES, user->lKCsi));
669 PetscCall(DMGlobalToLocalBegin(user->fda, user->KEta, INSERT_VALUES, user->lKEta));
670 PetscCall(DMGlobalToLocalEnd(user->fda, user->KEta, INSERT_VALUES, user->lKEta));
671 PetscCall(DMGlobalToLocalBegin(user->fda, user->KZet, INSERT_VALUES, user->lKZet));
672 PetscCall(DMGlobalToLocalEnd(user->fda, user->KZet, INSERT_VALUES, user->lKZet));
673 PetscCall(DMGlobalToLocalBegin(user->da, user->Nvert, INSERT_VALUES, user->lNvert));
674 PetscCall(DMGlobalToLocalEnd(user->da, user->Nvert, INSERT_VALUES, user->lNvert));
675 PetscCall(DMGlobalToLocalBegin(user->da, user->Aj, INSERT_VALUES, user->lAj));
676 PetscCall(DMGlobalToLocalEnd(user->da, user->Aj, INSERT_VALUES, user->lAj));
677 PetscCall(DMGlobalToLocalBegin(user->da, user->IAj, INSERT_VALUES, user->lIAj));
678 PetscCall(DMGlobalToLocalEnd(user->da, user->IAj, INSERT_VALUES, user->lIAj));
679 PetscCall(DMGlobalToLocalBegin(user->da, user->JAj, INSERT_VALUES, user->lJAj));
680 PetscCall(DMGlobalToLocalEnd(user->da, user->JAj, INSERT_VALUES, user->lJAj));
681 PetscCall(DMGlobalToLocalBegin(user->da, user->KAj, INSERT_VALUES, user->lKAj));
682 PetscCall(DMGlobalToLocalEnd(user->da, user->KAj, INSERT_VALUES, user->lKAj));
683 PetscCall(DMGlobalToLocalBegin(user->da, user->P, INSERT_VALUES, user->lP));
684 PetscCall(DMGlobalToLocalEnd(user->da, user->P, INSERT_VALUES, user->lP));
685 PetscCall(DMGlobalToLocalBegin(user->da, user->Psi, INSERT_VALUES, user->lPsi));
686 PetscCall(DMGlobalToLocalEnd(user->da, user->Psi, INSERT_VALUES, user->lPsi));
687 PetscCall(DMGlobalToLocalBegin(user->fda, user->Ucat, INSERT_VALUES, user->lUcat));
688 PetscCall(DMGlobalToLocalEnd(user->fda, user->Ucat, INSERT_VALUES, user->lUcat));
689 PetscCall(DMGlobalToLocalBegin(user->fda, user->Ucont, INSERT_VALUES, user->lUcont));
690 PetscCall(DMGlobalToLocalEnd(user->fda, user->Ucont, INSERT_VALUES, user->lUcont));
691 PetscCall(DMGlobalToLocalBegin(user->fda, user->Cent, INSERT_VALUES, user->lCent));
692 PetscCall(DMGlobalToLocalEnd(user->fda, user->Cent, INSERT_VALUES, user->lCent));
693 PetscFunctionReturn(0);
694}
695/**
696 * @brief Creates matched solver and post-processing swarms for tests.
697 */
698
699PetscErrorCode PicurvCreateSwarmPair(UserCtx *user, PetscInt nlocal, const char *post_field_name)
700{
701 PetscFunctionBeginUser;
702 if (!user) {
703 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "UserCtx cannot be NULL.");
704 }
705
706 PetscCall(DMCreate(PETSC_COMM_WORLD, &user->swarm));
707 PetscCall(DMSetType(user->swarm, DMSWARM));
708 PetscCall(DMSetDimension(user->swarm, 3));
709 PetscCall(DMSwarmSetType(user->swarm, DMSWARM_BASIC));
710 PetscCall(DMSwarmSetCellDM(user->swarm, user->da));
711 PetscCall(RegisterSwarmFieldForTests(user->swarm, "position", 3, PETSC_REAL));
712 PetscCall(RegisterSwarmFieldForTests(user->swarm, "velocity", 3, PETSC_REAL));
713 PetscCall(RegisterSwarmFieldForTests(user->swarm, "DMSwarm_CellID", 3, PETSC_INT));
714 PetscCall(RegisterSwarmFieldForTests(user->swarm, "weight", 3, PETSC_REAL));
715 PetscCall(RegisterSwarmFieldForTests(user->swarm, "Diffusivity", 1, PETSC_REAL));
716 PetscCall(RegisterSwarmFieldForTests(user->swarm, "DiffusivityGradient", 3, PETSC_REAL));
717 PetscCall(RegisterSwarmFieldForTests(user->swarm, "Psi", 1, PETSC_REAL));
718 PetscCall(RegisterSwarmFieldForTests(user->swarm, "DMSwarm_location_status", 1, PETSC_INT));
719 PetscCall(DMSwarmFinalizeFieldRegister(user->swarm));
720 PetscCall(DMSwarmSetLocalSizes(user->swarm, nlocal, 0));
721
722 PetscCall(DMCreate(PETSC_COMM_WORLD, &user->post_swarm));
723 PetscCall(DMSetType(user->post_swarm, DMSWARM));
724 PetscCall(DMSetDimension(user->post_swarm, 3));
725 PetscCall(DMSwarmSetType(user->post_swarm, DMSWARM_BASIC));
726 PetscCall(DMSwarmSetCellDM(user->post_swarm, user->da));
727 PetscCall(RegisterSwarmFieldForTests(user->post_swarm, post_field_name, 1, PETSC_REAL));
728 PetscCall(DMSwarmFinalizeFieldRegister(user->post_swarm));
729 PetscCall(DMSwarmSetLocalSizes(user->post_swarm, nlocal, 0));
730 PetscFunctionReturn(0);
731}
732/**
733 * @brief Builds a tiny runtime context through the real setup path for behavior-level tests.
734 */
735
736PetscErrorCode PicurvBuildTinyRuntimeContext(const char *bcs_contents,
737 PetscBool enable_particles,
738 SimCtx **simCtx_out,
739 UserCtx **user_out,
740 char *tmpdir,
741 size_t tmpdir_len)
742{
743 char control_path[PETSC_MAX_PATH_LEN];
744 SimCtx *simCtx = NULL;
745
746 PetscFunctionBeginUser;
747 PetscCheck(simCtx_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL, "SimCtx output cannot be NULL.");
748
749 PetscCall(PetscOptionsClear(NULL));
750 PetscCall(PrepareTinyRuntimeConfig(bcs_contents, enable_particles, tmpdir, tmpdir_len, control_path, sizeof(control_path)));
751 PetscCall(PetscOptionsSetValue(NULL, "-control_file", control_path));
752 PetscCall(CreateSimulationContext(0, NULL, &simCtx));
753 simCtx->exec_mode = EXEC_MODE_SOLVER;
754 PetscCall(SetupSimulationEnvironment(simCtx));
755 PetscCall(SetupGridAndSolvers(simCtx));
756 PetscCall(SetupBoundaryConditions(simCtx));
757 PetscCall(SetupDomainRankInfo(simCtx));
758
759 *simCtx_out = simCtx;
760 if (user_out) {
761 *user_out = simCtx->usermg.mgctx[simCtx->usermg.mglevels - 1].user;
762 }
763 PetscFunctionReturn(0);
764}
765/**
766 * @brief Finalizes and frees a runtime context built by `PicurvBuildTinyRuntimeContext`.
767 */
768
769PetscErrorCode PicurvDestroyRuntimeContext(SimCtx **simCtx_ptr)
770{
771 PetscFunctionBeginUser;
772 if (simCtx_ptr && *simCtx_ptr) {
773 PetscCall(FinalizeSimulation(*simCtx_ptr));
774 *simCtx_ptr = NULL;
775 }
776 PetscCall(PetscOptionsClear(NULL));
777 PetscFunctionReturn(0);
778}
779/**
780 * @brief Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
781 */
782
783PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
784{
785 UserCtx *user = NULL;
786 SimCtx *simCtx = NULL;
787
788 PetscFunctionBeginUser;
789 if (user_ptr) {
790 user = *user_ptr;
791 }
792 if (simCtx_ptr) {
793 simCtx = *simCtx_ptr;
794 }
795
796 if (simCtx) {
797 PetscCall(DestroySolutionConvergenceState(simCtx));
798 }
799
800 if (user) {
801 PetscCall(DestroyDMIfSet(&user->swarm));
802 PetscCall(DestroyDMIfSet(&user->post_swarm));
803
804 PetscCall(DestroyVecIfSet(&user->P));
805 PetscCall(DestroyVecIfSet(&user->lP));
806 PetscCall(DestroyVecIfSet(&user->Phi));
807 PetscCall(DestroyVecIfSet(&user->lPhi));
808 PetscCall(DestroyVecIfSet(&user->Nvert));
809 PetscCall(DestroyVecIfSet(&user->lNvert));
810 PetscCall(DestroyVecIfSet(&user->ParticleCount));
811 PetscCall(DestroyVecIfSet(&user->lParticleCount));
812 PetscCall(DestroyVecIfSet(&user->Psi));
813 PetscCall(DestroyVecIfSet(&user->lPsi));
814 PetscCall(DestroyVecIfSet(&user->Qcrit));
815 PetscCall(DestroyVecIfSet(&user->P_nodal));
816 PetscCall(DestroyVecIfSet(&user->Psi_nodal));
817 PetscCall(DestroyVecIfSet(&user->Ucat));
818 PetscCall(DestroyVecIfSet(&user->lUcat));
819 PetscCall(DestroyVecIfSet(&user->Ucont));
820 PetscCall(DestroyVecIfSet(&user->lUcont));
821 PetscCall(DestroyVecIfSet(&user->Csi));
822 PetscCall(DestroyVecIfSet(&user->lCsi));
823 PetscCall(DestroyVecIfSet(&user->Eta));
824 PetscCall(DestroyVecIfSet(&user->lEta));
825 PetscCall(DestroyVecIfSet(&user->Zet));
826 PetscCall(DestroyVecIfSet(&user->lZet));
827 PetscCall(DestroyVecIfSet(&user->Aj));
828 PetscCall(DestroyVecIfSet(&user->lAj));
829 PetscCall(DestroyVecIfSet(&user->IAj));
830 PetscCall(DestroyVecIfSet(&user->lIAj));
831 PetscCall(DestroyVecIfSet(&user->JAj));
832 PetscCall(DestroyVecIfSet(&user->lJAj));
833 PetscCall(DestroyVecIfSet(&user->KAj));
834 PetscCall(DestroyVecIfSet(&user->lKAj));
835 PetscCall(DestroyVecIfSet(&user->Diffusivity));
836 PetscCall(DestroyVecIfSet(&user->lDiffusivity));
837 PetscCall(DestroyVecIfSet(&user->DiffusivityGradient));
838 PetscCall(DestroyVecIfSet(&user->lDiffusivityGradient));
839 PetscCall(DestroyVecIfSet(&user->CS));
840 PetscCall(DestroyVecIfSet(&user->lCs));
841 PetscCall(DestroyVecIfSet(&user->Nu_t));
842 PetscCall(DestroyVecIfSet(&user->lNu_t));
843 PetscCall(DestroyVecIfSet(&user->Ucont_o));
844 PetscCall(DestroyVecIfSet(&user->lUcont_o));
845 PetscCall(DestroyVecIfSet(&user->Ucat_o));
846 PetscCall(DestroyVecIfSet(&user->P_o));
847 PetscCall(DestroyVecIfSet(&user->Nvert_o));
848 PetscCall(DestroyVecIfSet(&user->lNvert_o));
849 PetscCall(DestroyVecIfSet(&user->Ucont_rm1));
850 PetscCall(DestroyVecIfSet(&user->lUcont_rm1));
851 PetscCall(DestroyVecIfSet(&user->Rhs));
852 PetscCall(DestroyVecIfSet(&user->dUcont));
853 PetscCall(DestroyVecIfSet(&user->pUcont));
854 PetscCall(DestroyVecIfSet(&user->CellFieldAtCorner));
855 PetscCall(DestroyVecIfSet(&user->lCellFieldAtCorner));
856 PetscCall(DestroyVecIfSet(&user->B));
857 PetscCall(DestroyVecIfSet(&user->R));
858 PetscCall(DestroyVecIfSet(&user->Cent));
859 PetscCall(DestroyVecIfSet(&user->lCent));
860 PetscCall(DestroyVecIfSet(&user->GridSpace));
861 PetscCall(DestroyVecIfSet(&user->lGridSpace));
862 PetscCall(DestroyVecIfSet(&user->Centx));
863 PetscCall(DestroyVecIfSet(&user->Centy));
864 PetscCall(DestroyVecIfSet(&user->Centz));
865 PetscCall(DestroyVecIfSet(&user->Ucat_nodal));
866 PetscCall(DestroyVecIfSet(&user->Bcs.Ubcs));
867 PetscCall(DestroyVecIfSet(&user->Bcs.Uch));
868 PetscCall(DestroyMatIfSet(&user->A));
869 PetscCall(DestroyMatIfSet(&user->C));
870 PetscCall(DestroyKSPIfSet(&user->ksp));
871 PetscCall(DestroyNullSpaceIfSet(&user->nullsp));
872 PetscCall(PetscFree(user->RankCellInfoMap));
873 user->RankCellInfoMap = NULL;
874 for (PetscInt face = BC_FACE_NEG_X; face <= BC_FACE_POS_Z; ++face) {
875 if (user->boundary_faces[face].params) {
877 user->boundary_faces[face].params = NULL;
878 }
879 }
880
881 PetscCall(DestroyDMIfSet(&user->fda));
882 PetscCall(DestroyDMIfSet(&user->da));
883 PetscCall(PetscFree(user));
884 if (user_ptr) {
885 *user_ptr = NULL;
886 }
887 }
888
889 if (simCtx) {
890 PetscCall(DestroyRandomIfSet(&simCtx->BrownianMotionRNG));
891 PetscCall(PetscFree(simCtx->bboxlist));
892 PetscCall(PetscFree(simCtx->usermg.mgctx));
893 PetscCall(PetscFree(simCtx->pps));
894 PetscCall(PetscFree(simCtx));
895 if (simCtx_ptr) {
896 *simCtx_ptr = NULL;
897 }
898 }
899
900 PetscFunctionReturn(0);
901}
902/**
903 * @brief Asserts that two real values agree within tolerance.
904 */
905
906PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
907{
908 PetscFunctionBeginUser;
909 if (PetscAbsReal(expected - actual) > tol) {
910 PetscCall(PetscPrintf(PETSC_COMM_WORLD,
911 "[FAIL] %s | expected=%0.12e actual=%0.12e tol=%0.12e\n",
912 context, (double)expected, (double)actual, (double)tol));
913 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Assertion failed.");
914 }
915 PetscFunctionReturn(0);
916}
917/**
918 * @brief Asserts that two integer values are equal.
919 */
920
921PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
922{
923 PetscFunctionBeginUser;
924 if (expected != actual) {
925 PetscCall(PetscPrintf(PETSC_COMM_WORLD,
926 "[FAIL] %s | expected=%" PetscInt_FMT " actual=%" PetscInt_FMT "\n",
927 context, expected, actual));
928 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Assertion failed.");
929 }
930 PetscFunctionReturn(0);
931}
932/**
933 * @brief Asserts that one boolean condition is true.
934 */
935
936PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
937{
938 PetscFunctionBeginUser;
939 if (!value) {
940 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "[FAIL] %s\n", context));
941 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Assertion failed.");
942 }
943 PetscFunctionReturn(0);
944}
945/**
946 * @brief Asserts that a filesystem path exists as a readable file.
947 */
948
949PetscErrorCode PicurvAssertFileExists(const char *path, const char *context)
950{
951 PetscBool exists = PETSC_FALSE;
952
953 PetscFunctionBeginUser;
954 PetscCall(PetscTestFile(path, 'r', &exists));
955 if (!exists) {
956 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "[FAIL] %s | missing file: %s\n", context, path));
957 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Expected file is missing.");
958 }
959 PetscFunctionReturn(0);
960}
961/**
962 * @brief Asserts that a PETSc vector is spatially constant within tolerance.
963 */
964
965PetscErrorCode PicurvAssertVecConstant(Vec vec, PetscScalar expected, PetscReal tol, const char *context)
966{
967 PetscReal vmin = 0.0;
968 PetscReal vmax = 0.0;
969
970 PetscFunctionBeginUser;
971 PetscCall(VecMin(vec, NULL, &vmin));
972 PetscCall(VecMax(vec, NULL, &vmax));
973 PetscCall(PicurvAssertRealNear((PetscReal)expected, vmin, tol, context));
974 PetscCall(PicurvAssertRealNear((PetscReal)expected, vmax, tol, context));
975 PetscFunctionReturn(0);
976}
Public interface for grid, solver, and metric setup routines.
PetscErrorCode BroadcastAllBoundingBoxes(UserCtx *user, BoundingBox **bboxlist)
Broadcasts the bounding box information collected on rank 0 to all other ranks.
Definition grid.c:883
PetscErrorCode ComputeLocalBoundingBox(UserCtx *user, BoundingBox *localBBox)
Computes the local bounding box of the grid on the current process.
Definition grid.c:679
PetscErrorCode GatherAllBoundingBoxes(UserCtx *user, BoundingBox **allBBoxes)
Gathers local bounding boxes from all MPI processes to rank 0.
Definition grid.c:821
Public interface for data input/output routines.
void FreeBC_ParamList(BC_Param *head)
Frees an entire linked list of boundary-condition parameters.
Definition io.c:302
PetscErrorCode SetupDomainRankInfo(SimCtx *simCtx)
Sets up the full rank communication infrastructure, including neighbor ranks and bounding box exchang...
Definition setup.c:2323
PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
The main orchestrator for setting up all grid-related components.
Definition setup.c:1280
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Verifies and prepares the complete I/O environment for a simulation run.
Definition setup.c:961
PetscErrorCode DestroySolutionConvergenceState(SimCtx *simCtx)
Frees any runtime storage allocated for solution-convergence logging.
Definition setup.c:98
PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
Allocates and populates the master SimulationContext object.
Definition setup.c:151
PetscErrorCode SetupBoundaryConditions(SimCtx *simCtx)
(Orchestrator) Sets up all boundary conditions for the simulation.
Definition setup.c:1782
PetscErrorCode FinalizeSimulation(SimCtx *simCtx)
Main cleanup function for the entire simulation context.
Definition setup.c:3337
static PetscErrorCode DestroyKSPIfSet(KSP *ksp)
Destroys a PETSc KSP only when the handle is non-null.
PetscErrorCode PicurvMakeTempDir(char *path, size_t path_len)
Creates a unique temporary directory for one test case.
PetscErrorCode PicurvCreateMinimalContexts(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz)
Builds minimal SimCtx and UserCtx fixtures for C unit tests.
PetscErrorCode PicurvEnsureDir(const char *path)
Ensures a directory exists for test output.
PetscErrorCode PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
static PetscErrorCode DestroyDMIfSet(DM *dm)
Destroys a PETSc DM only when the handle is non-null.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
PetscErrorCode PicurvCreateMinimalContextsWithPeriodicity(SimCtx **simCtx_out, UserCtx **user_out, PetscInt mx, PetscInt my, PetscInt mz, PetscBool x_periodic, PetscBool y_periodic, PetscBool z_periodic)
Builds minimal SimCtx and UserCtx fixtures for C unit tests with configurable periodicity.
static PetscErrorCode DestroyNullSpaceIfSet(MatNullSpace *nullsp)
Destroys a PETSc nullspace only when the handle is non-null.
static PetscErrorCode CreateZeroedLocalVector(DM dm, Vec *vec)
Allocates and zeroes a local vector from the provided DM.
static PetscErrorCode PrepareTinyRuntimeConfig(const char *bcs_contents, PetscBool enable_particles, char *tmpdir, size_t tmpdir_len, char *control_path, size_t control_path_len)
Creates a tiny control-file bundle used by richer runtime fixtures built through the setup path.
PetscErrorCode PicurvCreateSwarmPair(UserCtx *user, PetscInt nlocal, const char *post_field_name)
Creates matched solver and post-processing swarms for tests.
static PetscErrorCode WriteTextFileForTests(const char *path, const char *contents)
Writes one small temporary text file used by the richer runtime fixtures.
static PetscErrorCode DestroyRandomIfSet(PetscRandom *rand_ctx)
Destroys a PETSc random generator only when the handle is non-null.
PetscErrorCode PicurvDestroyRuntimeContext(SimCtx **simCtx_ptr)
Finalizes and frees a runtime context built by PicurvBuildTinyRuntimeContext.
PetscErrorCode PicurvRunTests(const char *suite_name, const PicurvTestCase *cases, size_t case_count)
Runs a named C test suite and prints pass/fail progress markers.
PetscErrorCode PicurvBuildTinyRuntimeContext(const char *bcs_contents, PetscBool enable_particles, SimCtx **simCtx_out, UserCtx **user_out, char *tmpdir, size_t tmpdir_len)
Builds a tiny runtime context through the real setup path for behavior-level tests.
static PetscErrorCode CreateZeroedDuplicate(Vec src, Vec *vec)
Duplicates and zeroes a vector.
static PetscErrorCode DestroyVecIfSet(Vec *vec)
Destroys a PETSc vector only when the handle is non-null.
PetscErrorCode PicurvPopulateUniformCellCenters(UserCtx *user)
Populates cell center coordinates for a uniform grid on [0,1]^3.
PetscErrorCode PicurvAssertFileExists(const char *path, const char *context)
Asserts that a filesystem path exists as a readable file.
static PetscErrorCode RegisterSwarmFieldForTests(DM swarm, const char *field_name, PetscInt field_dim, PetscDataType dtype)
Registers one DMSwarm field used by the C test fixtures.
PetscErrorCode PicurvAssertVecConstant(Vec vec, PetscScalar expected, PetscReal tol, const char *context)
Asserts that a PETSc vector is spatially constant within tolerance.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
static PetscErrorCode CreateZeroedGlobalVector(DM dm, Vec *vec)
Allocates and zeroes a global vector from the provided DM.
PetscErrorCode PicurvPopulateIdentityMetrics(UserCtx *user)
Populates identity metric vectors on the minimal grid fixture.
static PetscErrorCode DestroyMatIfSet(Mat *mat)
Destroys a PETSc matrix only when the handle is non-null.
PetscErrorCode PicurvAssertBool(PetscBool value, const char *context)
Asserts that one boolean condition is true.
PetscErrorCode PicurvRemoveTempDir(const char *path)
Recursively removes a temporary directory created by PicurvMakeTempDir.
Shared declarations for the PICurv C test fixture and assertion layer.
Named test case descriptor consumed by PicurvRunTests.
Vec lDiffusivityGradient
Definition variables.h:860
Vec lCent
Definition variables.h:879
Vec GridSpace
Definition variables.h:879
Vec P_nodal
Definition variables.h:908
Vec JCsi
Definition variables.h:882
Vec KAj
Definition variables.h:883
UserCtx * user
Definition variables.h:536
Vec JEta
Definition variables.h:882
Vec Zet
Definition variables.h:879
Vec Rhs
Definition variables.h:864
PetscReal schmidt_number
Definition variables.h:723
PetscMPIInt rank
Definition variables.h:654
PetscInt mglevels
Definition variables.h:895
BoundaryFaceConfig boundary_faces[6]
Definition variables.h:848
MatNullSpace nullsp
Definition variables.h:870
PetscInt block_number
Definition variables.h:726
Vec lIEta
Definition variables.h:881
Vec lIZet
Definition variables.h:881
Vec lNvert
Definition variables.h:856
Vec Phi
Definition variables.h:856
char euler_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:674
PetscReal forceScalingFactor
Definition variables.h:737
SimCtx * simCtx
Back-pointer to the master simulation context.
Definition variables.h:833
Vec IZet
Definition variables.h:881
Vec Centz
Definition variables.h:880
Vec IEta
Definition variables.h:881
PetscReal Min_X
Definition variables.h:840
PetscInt KM
Definition variables.h:839
PetscInt tiout
Definition variables.h:663
Vec lZet
Definition variables.h:879
UserMG usermg
Definition variables.h:778
Vec Csi
Definition variables.h:879
PetscReal ren
Definition variables.h:699
DM post_swarm
Definition variables.h:907
Vec lUcont_rm1
Definition variables.h:864
Vec lIAj
Definition variables.h:881
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
Definition variables.h:156
Vec lCellFieldAtCorner
Definition variables.h:867
PetscInt _this
Definition variables.h:843
Vec lKEta
Definition variables.h:883
char output_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:673
PetscReal dt
Definition variables.h:666
PetscReal ry
Definition variables.h:844
PetscInt StepsToRun
Definition variables.h:662
PetscInt k_periodic
Definition variables.h:727
Vec Ucat_nodal
Definition variables.h:909
Vec lPsi
Definition variables.h:904
PetscInt np
Definition variables.h:753
PetscReal Max_Y
Definition variables.h:840
PetscInt thislevel
Definition variables.h:537
Vec DiffusivityGradient
Definition variables.h:860
Vec lJCsi
Definition variables.h:882
Vec lCs
Definition variables.h:886
Vec Ucont
Definition variables.h:856
PetscInt StartStep
Definition variables.h:661
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
Definition variables.h:155
Vec Ubcs
Physical Cartesian velocity at boundary faces. Full 3D array but only boundary-face entries are meani...
Definition variables.h:121
@ MOMENTUM_SOLVER_EXPLICIT_RK
Definition variables.h:503
Vec Qcrit
Definition variables.h:910
PetscScalar x
Definition variables.h:101
Vec JZet
Definition variables.h:882
Vec Centx
Definition variables.h:880
BCS Bcs
Definition variables.h:851
Vec lPhi
Definition variables.h:856
Vec lParticleCount
Definition variables.h:903
Vec lUcont_o
Definition variables.h:863
RankCellInfo * RankCellInfoMap
Definition variables.h:902
Vec Ucat_o
Definition variables.h:863
PetscInt poisson
Definition variables.h:695
char particle_subdir[PETSC_MAX_PATH_LEN]
Definition variables.h:675
BoundingBox * bboxlist
Definition variables.h:756
Vec lKZet
Definition variables.h:883
Vec Eta
Definition variables.h:879
char log_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:676
Vec lNu_t
Definition variables.h:886
Vec Nu_t
Definition variables.h:886
PetscReal rz
Definition variables.h:844
Vec lJEta
Definition variables.h:882
Vec lCsi
Definition variables.h:879
Vec lGridSpace
Definition variables.h:879
PetscInt thislevel
Definition variables.h:895
BC_Param * params
Definition variables.h:338
Vec ICsi
Definition variables.h:881
PetscScalar z
Definition variables.h:101
Vec pUcont
Definition variables.h:864
Vec lKCsi
Definition variables.h:883
Vec Ucat
Definition variables.h:856
Vec ParticleCount
Definition variables.h:903
Vec Ucont_o
Definition variables.h:863
Vec CellFieldAtCorner
Definition variables.h:867
PetscInt JM
Definition variables.h:839
PetscInt mglevels
Definition variables.h:543
PetscReal Min_Z
Definition variables.h:840
PetscInt mglevels
Definition variables.h:694
Vec lJZet
Definition variables.h:882
Vec Nvert_o
Definition variables.h:863
Vec IAj
Definition variables.h:881
Vec Psi_nodal
Definition variables.h:911
Vec JAj
Definition variables.h:882
Vec KEta
Definition variables.h:883
PetscReal Max_X
Definition variables.h:840
Vec Ucont_rm1
Definition variables.h:864
PetscReal Min_Y
Definition variables.h:840
PetscInt i_periodic
Definition variables.h:727
Vec lUcont
Definition variables.h:856
PetscInt step
Definition variables.h:659
Vec Diffusivity
Definition variables.h:859
Vec lAj
Definition variables.h:879
PetscRandom BrownianMotionRNG
Definition variables.h:767
Vec lICsi
Definition variables.h:881
DMDALocalInfo info
Definition variables.h:837
Vec dUcont
Definition variables.h:864
Vec lUcat
Definition variables.h:856
PostProcessParams * pps
Definition variables.h:817
PetscScalar y
Definition variables.h:101
PetscMPIInt size
Definition variables.h:655
@ EXEC_MODE_SOLVER
Definition variables.h:624
PetscInt IM
Definition variables.h:839
Vec lEta
Definition variables.h:879
KSP ksp
Definition variables.h:870
Vec KZet
Definition variables.h:883
Vec Cent
Definition variables.h:879
Vec Nvert
Definition variables.h:856
Vec KCsi
Definition variables.h:883
MGCtx * mgctx
Definition variables.h:546
Vec lDiffusivity
Definition variables.h:859
Vec lNvert_o
Definition variables.h:863
Vec Centy
Definition variables.h:880
PetscReal rx
Definition variables.h:844
ExecutionMode exec_mode
Definition variables.h:670
Vec lJAj
Definition variables.h:882
BoundingBox bbox
Definition variables.h:841
PetscReal Max_Z
Definition variables.h:840
MomentumSolverType mom_solver_type
Definition variables.h:691
Vec lKAj
Definition variables.h:883
char restart_dir[PETSC_MAX_PATH_LEN]
Definition variables.h:672
PetscInt LoggingFrequency
Definition variables.h:783
Vec Psi
Definition variables.h:904
Vec P_o
Definition variables.h:863
Vec Uch
Characteristic velocity for boundary conditions.
Definition variables.h:122
@ BC_FACE_NEG_X
Definition variables.h:245
@ BC_FACE_POS_Z
Definition variables.h:247
PetscInt j_periodic
Definition variables.h:727
Defines a 3D axis-aligned bounding box.
Definition variables.h:154
A 3D point or vector with PetscScalar components.
Definition variables.h:100
The master context for the entire simulation.
Definition variables.h:651
User-defined context containing data specific to a single computational grid level.
Definition variables.h:830