22 PetscBool exists = PETSC_FALSE;
24 PetscFunctionBeginUser;
25 PetscCall(PetscTestDirectory(path,
'r', &exists));
27 PetscFunctionReturn(0);
37 PetscFunctionBeginUser;
38 file = fopen(path,
"w");
39 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open '%s' for writing.", path);
40 fputs(contents, file);
42 PetscFunctionReturn(0);
51 size_t control_path_len)
53 char bcs_path[PETSC_MAX_PATH_LEN];
54 char post_path[PETSC_MAX_PATH_LEN];
55 char output_dir[PETSC_MAX_PATH_LEN];
56 char log_dir[PETSC_MAX_PATH_LEN];
57 char control_buffer[8192];
59 PetscFunctionBeginUser;
61 PetscCall(PetscSNPrintf(bcs_path,
sizeof(bcs_path),
"%s/bcs.run", tmpdir));
62 PetscCall(PetscSNPrintf(post_path,
sizeof(post_path),
"%s/post.run", tmpdir));
63 PetscCall(PetscSNPrintf(output_dir,
sizeof(output_dir),
"%s/results", tmpdir));
64 PetscCall(PetscSNPrintf(log_dir,
sizeof(log_dir),
"%s/logs", tmpdir));
65 PetscCall(PetscSNPrintf(control_path, control_path_len,
"%s/test.control", tmpdir));
73 "-Zeta INLET constant_velocity vx=0.0 vy=0.0 vz=1.5\n"
74 "+Zeta OUTLET conservation\n"));
80 "output_particles = false\n"));
81 PetscCall(PetscSNPrintf(
83 sizeof(control_buffer),
93 "-profiling_timestep_mode off\n"
94 "-profiling_final_summary true\n"
95 "-postprocessing_config_file %s\n"
111 "-euler_field_source solve\n"
112 "-mom_solver_type DUALTIME_PICARD_RK4\n"
113 "-mom_dt_rk4_residual_norm_noise_allowance_factor 1.07\n"
114 "-mom_resid_atol 1.0e-8\n"
115 "-mom_resid_rtol 1.0e-3\n"
116 "-mom_nk_pic_monitor\n"
120 "-particle_console_output_freq 0\n"
127 "-allow_unsafe_log_dir true\n"
137 PetscFunctionReturn(0);
145 PetscFunctionBeginUser;
147 PetscFunctionReturn(0);
155 PetscFunctionBeginUser;
157 PetscFunctionReturn(0);
165 char control_path[PETSC_MAX_PATH_LEN];
168 PetscFunctionBeginUser;
169 PetscCall(PetscOptionsClear(NULL));
171 PetscCall(PetscOptionsSetValue(NULL,
"-control_file", control_path));
174 *simCtx_out = simCtx;
175 PetscFunctionReturn(0);
189 const char *refused[] = {
"-nblk 2",
"-imm 1",
"-fsi 1",
"-rfsi 1",
190 "-mframe 1",
"-rframe 1",
"-mhv 1",
"-lv 1"};
192 PetscFunctionBeginUser;
193 for (
size_t n = 0; n <
sizeof(refused) /
sizeof(refused[0]); ++n) {
194 char tmpdir[PETSC_MAX_PATH_LEN];
195 char control_path[PETSC_MAX_PATH_LEN];
198 PetscErrorCode setup_ierr;
201 PetscCall(PetscOptionsClear(NULL));
203 file = fopen(control_path,
"a");
204 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to reopen '%s'.", control_path);
205 fprintf(file,
"%s\n", refused[n]);
207 PetscCall(PetscOptionsSetValue(NULL,
"-control_file", control_path));
209 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
211 PetscCall(PetscPopErrorHandler());
212 PetscCall(PetscSNPrintf(message,
sizeof(message),
"setup must refuse '%s'", refused[n]));
217 PetscCall(PetscFree(simCtx));
218 PetscCall(PetscOptionsClear(NULL));
221 PetscFunctionReturn(0);
233 PetscErrorCode *ierr_out,
char *tmpdir,
236 char control_path[PETSC_MAX_PATH_LEN];
239 PetscFunctionBeginUser;
240 PetscCall(PetscOptionsClear(NULL));
242 file = fopen(control_path,
"a");
243 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to reopen '%s'.", control_path);
244 fputs(extra_lines, file);
246 PetscCall(PetscOptionsSetValue(NULL,
"-control_file", control_path));
247 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
249 PetscCall(PetscPopErrorHandler());
250 PetscFunctionReturn(0);
259 const char *cases[] = {
"",
"-iem_constant 3.5\n",
"-iem_constant 0\n",
"-iem_constant -1\n",
260 "-iem_constant nan\n",
"-iem_constant inf\n"};
261 const PetscReal expected[] = {2.0, 3.5, 0.0, -1.0, -1.0, -1.0};
263 PetscFunctionBeginUser;
264 for (
size_t n = 0; n <
sizeof(cases) /
sizeof(cases[0]); ++n) {
265 char tmpdir[PETSC_MAX_PATH_LEN];
267 PetscErrorCode setup_ierr = 0;
270 if (expected[n] >= 0.0) {
271 PetscCall(
PicurvAssertIntEqual(0, setup_ierr,
"a non-negative -iem_constant must be accepted"));
273 "the IEM constant must follow -iem_constant, default 2.0"));
276 "a negative or non-finite -iem_constant must be refused"));
279 PetscCall(PetscOptionsClear(NULL));
282 PetscFunctionReturn(0);
291 const char *cases[] = {
"-mg_level 3\n",
"-mg_level 3\n-euler_field_source analytical\n",
292 "-mg_level 3\n-euler_field_source load\n"};
293 const PetscInt expected[] = {3, 1, 1};
295 PetscFunctionBeginUser;
296 for (
size_t n = 0; n <
sizeof(cases) /
sizeof(cases[0]); ++n) {
297 char tmpdir[PETSC_MAX_PATH_LEN];
299 PetscErrorCode setup_ierr = 0;
304 "only a solved flow keeps its coarse grid levels"));
306 PetscCall(PetscOptionsClear(NULL));
309 PetscFunctionReturn(0);
319 char tmpdir[PETSC_MAX_PATH_LEN], line[64];
321 PetscErrorCode setup_ierr = 0;
323 PetscFunctionBeginUser;
324 if (
sizeof(PetscInt) > 4) PetscFunctionReturn(0);
325 PetscCall(PetscSNPrintf(line,
sizeof(line),
"-numParticles %" PetscInt_FMT
"\n", (PetscInt)(PETSC_MAX_INT / 3) + 1));
328 "a particle count past PETSC_MAX_INT / 3 must be refused with 32-bit indices"));
330 PetscCall(PetscOptionsClear(NULL));
332 PetscFunctionReturn(0);
340 const char *options[] = {
"-timeStep 0\n",
"-timeStep -1\n"};
342 PetscFunctionBeginUser;
343 for (
size_t n = 0; n <
sizeof(options) /
sizeof(options[0]); ++n) {
345 char tmpdir[PETSC_MAX_PATH_LEN];
346 PetscErrorCode setup_ierr;
349 "setup must propagate rejection of a nonpositive post interval"));
353 PetscFunctionReturn(0);
368 const char *options =
"-numParticles 400\n"
370 "-particle_fields_count 1\n"
371 "-particle_fields_0_name Psi\n"
372 "-particle_fields_0_expr_0 \"x + 2*pid\"\n";
375 char tmpdir[PETSC_MAX_PATH_LEN];
377 const PetscReal *psi = NULL, *position = NULL;
378 const PetscInt64 *pid = NULL;
379 PetscReal particle_sum = 0.0, eulerian_sum = 0.0;
381 PetscFunctionBeginUser;
383 tmpdir,
sizeof(tmpdir)));
389 PetscCall(DMSwarmGetLocalSize(user->
swarm, &nlocal));
390 PetscCall(DMSwarmGetField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
391 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void **)&position));
392 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_pid", NULL, NULL, (
void **)&pid));
393 for (PetscInt p = 0; p < nlocal; ++p) {
394 const PetscReal expected = position[3 * p] * simCtx->
scaling.
L_ref + 2.0 * (PetscReal)pid[p];
395 PetscCall(
PicurvAssertRealNear(expected, psi[p], 1.0e-12,
"each particle carries its initial value"));
396 particle_sum += psi[p];
398 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_pid", NULL, NULL, (
void **)&pid));
399 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void **)&position));
400 PetscCall(DMSwarmRestoreField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
401 PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, &particle_sum, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD));
404 "the t=0 scatter carries the initial value to the Eulerian mean"));
408 const DMDALocalInfo info = user->
info;
409 const PetscReal ***mean = NULL, ***count = NULL;
410 PetscReal checked = 0.0;
412 PetscCall(DMDAVecGetArrayRead(user->
da, user->
Psi, &mean));
414 for (PetscInt k = info.zs; k < info.zs + info.zm; k++)
415 for (PetscInt j = info.ys; j < info.ys + info.ym; j++)
416 for (PetscInt i = info.xs; i < info.xs + info.xm; i++) {
418 const PetscInt index[3] = {i, j, k}, extent[3] = {info.mx, info.my, info.mz};
419 PetscInt dummy_axes = 0, axis = -1;
421 for (PetscInt d = 0; d < 3; d++) {
422 if (index[d] == 0 || index[d] == extent[d] - 1) { dummy_axes++; axis = d; }
424 if (dummy_axes != 1)
continue;
427 const PetscInt step = (index[axis] == 0) ? 1 : -1;
428 const PetscInt ai = i + (axis == 0 ? step : 0), aj = j + (axis == 1 ? step : 0),
429 ak = k + (axis == 2 ? step : 0);
431 if (count[ak][aj][ai] < 0.5)
continue;
433 "the scatter fills each dummy cell with the adjacent cell mean"));
437 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
ParticleCount, &count));
438 PetscCall(DMDAVecRestoreArrayRead(user->
da, user->
Psi, &mean));
439 PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, &checked, 1, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD));
441 "the fixture must seed particles next to a non-periodic face"));
446 PetscFunctionReturn(0);
459 const char *options[] = {
"",
"-iem_constant 3.5\n",
"-iem_constant 0\n"};
460 const PetscReal constants[] = {2.0, 3.5, 0.0};
462 PetscFunctionBeginUser;
463 for (
size_t n = 0; n <
sizeof(constants) /
sizeof(constants[0]); ++n) {
466 char tmpdir[PETSC_MAX_PATH_LEN];
468 PetscReal *psi = NULL, *diffusivity = NULL, *psi_initial = NULL, *cell_mean = NULL;
469 PetscInt *cell = NULL;
475 &simCtx, &user, tmpdir,
sizeof(tmpdir)));
483 PetscCall(VecSet(user->
lAj, 1.0));
484 PetscCall(DMSwarmGetLocalSize(user->
swarm, &nlocal));
485 PetscCall(
PicurvAssertBool((PetscBool)(nlocal > 0),
"IEM fixture must contain particles"));
486 PetscCall(DMSwarmGetField(user->
swarm, psi_name, NULL, NULL, (
void **)&psi));
487 PetscCall(DMSwarmGetField(user->
swarm, diff_name, NULL, NULL, (
void **)&diffusivity));
488 PetscCall(DMSwarmGetField(user->
swarm, cell_name, NULL, NULL, (
void **)&cell));
489 PetscCall(PetscMalloc2(nlocal, &psi_initial, nlocal, &cell_mean));
490 for (PetscInt p = 0; p < nlocal; ++p) {
491 psi[p] = psi_initial[p] = (p % 2) ? 0.7 : 0.1;
492 diffusivity[p] = 0.2;
495 for (PetscInt p = 0; p < nlocal; ++p) {
496 PetscReal sum = 0.0, count = 0.0;
497 for (PetscInt q = 0; q < nlocal; ++q) {
498 if (cell[3*q] == cell[3*p] && cell[3*q+1] == cell[3*p+1] && cell[3*q+2] == cell[3*p+2]) {
499 sum += psi_initial[q]; count += 1.0;
502 cell_mean[p] = sum / count;
504 PetscCall(DMSwarmRestoreField(user->
swarm, cell_name, NULL, NULL, (
void **)&cell));
505 PetscCall(DMSwarmRestoreField(user->
swarm, diff_name, NULL, NULL, (
void **)&diffusivity));
506 PetscCall(DMSwarmRestoreField(user->
swarm, psi_name, NULL, NULL, (
void **)&psi));
508 PetscCall(DMSwarmGetField(user->
swarm, psi_name, NULL, NULL, (
void **)&psi));
509 for (PetscInt p = 0; p < nlocal; ++p) {
510 if (constants[n] == 0.0) {
512 "a zero IEM constant must leave Psi bit-identical"));
514 PetscCall(
PicurvAssertRealNear(cell_mean[p] + (psi_initial[p] - cell_mean[p]) * PetscExpReal(-constants[n] * 0.2 * 0.5),
515 psi[p], 1.e-12,
"configured IEM constant must relax each particle toward its cell's current mean"));
518 PetscCall(DMSwarmRestoreField(user->
swarm, psi_name, NULL, NULL, (
void **)&psi));
519 PetscCall(PetscFree2(psi_initial, cell_mean));
523 PetscFunctionReturn(0);
536 const char *options =
"-particle_fields_count 1\n"
537 "-particle_fields_0_name Psi\n"
538 "-particle_fields_0_expr_0 \"where(uniform() < 0.5, 0, 1)\"\n";
541 char tmpdir[PETSC_MAX_PATH_LEN];
543 PetscReal *psi = NULL, *diffusivity = NULL;
544 PetscReal sums[2][2] = {{0.0, 0.0}, {0.0, 0.0}};
546 PetscFunctionBeginUser;
548 tmpdir,
sizeof(tmpdir)));
554 PetscCall(DMSwarmGetLocalSize(user->
swarm, &nlocal));
555 PetscCall(DMSwarmGetField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
556 PetscCall(DMSwarmGetField(user->
swarm,
"Diffusivity", NULL, NULL, (
void **)&diffusivity));
557 for (PetscInt p = 0; p < nlocal; ++p) {
558 diffusivity[p] = 0.2;
559 sums[0][0] += psi[p];
560 sums[0][1] += psi[p] * psi[p];
562 PetscCall(DMSwarmRestoreField(user->
swarm,
"Diffusivity", NULL, NULL, (
void **)&diffusivity));
563 PetscCall(DMSwarmRestoreField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
567 PetscCall(DMSwarmGetField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
568 for (PetscInt p = 0; p < nlocal; ++p) {
569 sums[1][0] += psi[p];
570 sums[1][1] += psi[p] * psi[p];
572 PetscCall(DMSwarmRestoreField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
573 PetscCallMPI(MPI_Allreduce(MPI_IN_PLACE, sums, 4, MPIU_REAL, MPI_SUM, PETSC_COMM_WORLD));
575 PetscCall(
PicurvAssertBool((PetscBool)(sums[0][0] > 0.0 && sums[0][0] < (PetscReal)simCtx->
np),
576 "the fixture must start with both values present"));
578 "relaxing toward each particle's own cell mean conserves the scalar"));
580 "the update must reduce the scalar's spread"));
584 PetscFunctionReturn(0);
596 const char *options =
"-particle_fields_count 1\n"
597 "-particle_fields_0_name Psi\n"
598 "-particle_fields_0_expr_0 \"where(uniform() < 0.5, 0, 1)\"\n"
599 "-particle_restart_mode init\n";
602 char tmpdir[PETSC_MAX_PATH_LEN];
603 Vec saved = NULL, local_saved = NULL;
604 PetscReal saved_norm = 0.0, difference = 0.0;
606 PetscFunctionBeginUser;
608 tmpdir,
sizeof(tmpdir)));
612 PetscCall(VecDuplicate(user->
Psi, &saved));
613 PetscCall(VecCopy(user->
Psi, saved));
614 PetscCall(VecNorm(saved, NORM_INFINITY, &saved_norm));
615 PetscCall(
PicurvAssertBool((PetscBool)(saved_norm > 0.0),
"the fixture must scatter a nonzero Psi"));
618 PetscCall(PetscCalloc1(1, &simCtx->
pps));
621 PetscCall(VecSet(user->
Psi, 0.0));
622 PetscCall(VecSet(user->
lPsi, 0.0));
625 PetscCall(VecAXPY(saved, -1.0, user->
Psi));
626 PetscCall(VecNorm(saved, NORM_INFINITY, &difference));
627 PetscCall(
PicurvAssertRealNear(0.0, difference, 1.0e-12,
"post-processing restores the saved Psi average"));
628 PetscCall(DMCreateLocalVector(user->
da, &local_saved));
629 PetscCall(DMGlobalToLocalBegin(user->
da, user->
Psi, INSERT_VALUES, local_saved));
630 PetscCall(DMGlobalToLocalEnd(user->
da, user->
Psi, INSERT_VALUES, local_saved));
631 PetscCall(VecAXPY(local_saved, -1.0, user->
lPsi));
632 PetscCall(VecNorm(local_saved, NORM_INFINITY, &difference));
633 PetscCall(
PicurvAssertRealNear(0.0, difference, 1.0e-12,
"the ghosted Psi is refreshed for nodal averaging"));
636 PetscCall(PetscFree(simCtx->
pps));
637 PetscCall(VecDestroy(&local_saved));
638 PetscCall(VecDestroy(&saved));
641 PetscFunctionReturn(0);
651 char tmpdir[PETSC_MAX_PATH_LEN];
653 PetscFunctionBeginUser;
656 PetscCall(
PicurvAssertBool((PetscBool)(user->
bbox.
min_coords.
x >= -2.0e-6),
"runtime fixture bbox xmin should stay inside normalized domain tolerance"));
657 PetscCall(
PicurvAssertBool((PetscBool)(user->
bbox.
max_coords.
x <= 1.0 + 2.0e-6),
"runtime fixture bbox xmax should stay inside normalized domain tolerance"));
658 PetscCall(
PicurvAssertBool((PetscBool)(user->
bbox.
max_coords.
y <= 1.0 + 2.0e-6),
"runtime fixture bbox ymax should stay inside normalized domain tolerance"));
659 PetscCall(
PicurvAssertBool((PetscBool)(user->
bbox.
max_coords.
z <= 1.0 + 2.0e-6),
"runtime fixture bbox zmax should stay inside normalized domain tolerance"));
660 PetscCall(
PicurvAssertBool((PetscBool)(simCtx->
bboxlist != NULL),
"runtime fixture should gather bboxlist through SetupDomainRankInfo"));
663 "runtime fixture rank bbox entry should include the local bbox extent"));
667 PetscFunctionReturn(0);
677 DMDALocalInfo dm_info[2];
678 const char *names[2] = {
"da",
"fda"};
680 PetscFunctionBeginUser;
681 PetscCall(DMDAGetLocalInfo(user->
da, &dm_info[0]));
682 PetscCall(DMDAGetLocalInfo(user->
fda, &dm_info[1]));
683 for (PetscInt d = 0; d < 2; ++d) {
684 const DMDALocalInfo *a = &dm_info[d], *b = &user->
info;
685 const PetscInt got[15] = {a->mx, a->my, a->mz, a->xs, a->ys, a->zs, a->xm, a->ym, a->zm,
686 a->gxs, a->gys, a->gzs, a->gxm, a->gym, a->gzm};
687 const PetscInt want[15] = {b->mx, b->my, b->mz, b->xs, b->ys, b->zs, b->xm, b->ym, b->zm,
688 b->gxs, b->gys, b->gzs, b->gxm, b->gym, b->gzm};
690 for (PetscInt f = 0; f < 15; ++f) {
691 PetscCall(PetscSNPrintf(msg,
sizeof(msg),
"user->info range %" PetscInt_FMT
" should match %s", f, names[d]));
694 PetscCall(
PicurvAssertBool((PetscBool)(a->bx == b->bx && a->by == b->by && a->bz == b->bz),
695 "user->info boundary types should match the DM"));
697 PetscFunctionReturn(0);
707 char tmpdir[PETSC_MAX_PATH_LEN];
708 char results_dir[PETSC_MAX_PATH_LEN];
709 char logs_dir[PETSC_MAX_PATH_LEN];
712 PetscFunctionBeginUser;
716 PetscCall(PetscSNPrintf(results_dir,
sizeof(results_dir),
"%s/results", tmpdir));
717 PetscCall(PetscSNPrintf(logs_dir,
sizeof(logs_dir),
"%s/logs", tmpdir));
718 PetscCall(
PicurvAssertBool((PetscBool)(user->
da != NULL),
"SetupGridAndSolvers should allocate da"));
719 PetscCall(
PicurvAssertBool((PetscBool)(user->
fda != NULL),
"SetupGridAndSolvers should allocate coordinate DM"));
721 for (PetscInt level = 0; level < simCtx->
usermg.
mglevels; ++level) {
722 for (PetscInt bi = 0; bi < simCtx->
block_number; ++bi) {
730 PetscCall(
AssertDirectoryExists(results_dir,
"SetupSimulationEnvironment should create the output directory"));
731 PetscCall(
AssertDirectoryExists(logs_dir,
"SetupSimulationEnvironment should create the log directory"));
734 PetscCall(DMDAVecGetArrayRead(user->
fda, user->
Ucont, &ucont));
736 "InitializeEulerianState should seed a positive inlet-aligned interior field"));
738 "InitializeEulerianState should initialize a spatially consistent interior field"));
739 PetscCall(DMDAVecRestoreArrayRead(user->
fda, user->
Ucont, &ucont));
743 PetscFunctionReturn(0);
753 char tmpdir[PETSC_MAX_PATH_LEN];
755 PetscInt *cell_ids = NULL;
756 PetscInt *status = NULL;
758 PetscFunctionBeginUser;
765 PetscCall(
PicurvAssertBool((PetscBool)(user->
swarm != NULL),
"InitializeParticleSwarm should allocate the solver swarm"));
766 PetscCall(DMSwarmGetLocalSize(user->
swarm, &nlocal));
767 PetscCall(
PicurvAssertIntEqual(8, nlocal,
"single-rank lifecycle particle setup should own all seeded particles"));
769 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
770 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
771 for (PetscInt p = 0; p < nlocal; ++p) {
772 PetscCall(
PicurvAssertBool((PetscBool)(cell_ids[3 * p + 0] >= 0),
"settled particles should have a valid i cell id"));
773 PetscCall(
PicurvAssertBool((PetscBool)(cell_ids[3 * p + 1] >= 0),
"settled particles should have a valid j cell id"));
774 PetscCall(
PicurvAssertBool((PetscBool)(cell_ids[3 * p + 2] >= 0),
"settled particles should have a valid k cell id"));
777 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
778 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
782 PetscFunctionReturn(0);
792 char tmpdir[PETSC_MAX_PATH_LEN];
793 char metrics_path[PETSC_MAX_PATH_LEN];
795 PetscFunctionBeginUser;
815 PetscCall(PetscSNPrintf(metrics_path,
sizeof(metrics_path),
"%s/results/analysis/metrics/scatter_metrics.csv", tmpdir));
817 "PerformInitializedParticleSetup should emit scatter_metrics.csv for a zero-step scalar verification run"));
819 "zero-step scalar verification setup should leave the swarm initialized"));
823 PetscFunctionReturn(0);
833 PetscRandom randx = NULL, randy = NULL, randz = NULL;
834 PetscRandom rand_i = NULL, rand_j = NULL, rand_k = NULL;
835 PetscScalar sample_x = 0.0, sample_i = 0.0;
836 PetscReal seconds = 0.0;
838 PetscFunctionBeginUser;
850 PetscCall(
PicurvAssertRealNear(12.5, seconds, 1.0e-12,
"RuntimeWalltimeGuardParsePositiveSeconds parsed value"));
852 PetscCall(PetscRandomGetValue(randx, &sample_x));
853 PetscCall(PetscRandomGetValue(rand_i, &sample_i));
854 PetscCall(
PicurvAssertBool((PetscBool)(PetscRealPart(sample_x) >= 0.0 && PetscRealPart(sample_x) <= 1.0),
855 "InitializeRandomGenerators should honor the configured bbox interval"));
856 PetscCall(
PicurvAssertBool((PetscBool)(PetscRealPart(sample_i) >= 0.0 && PetscRealPart(sample_i) <= 1.0),
857 "InitializeLogicalSpaceRNGs should generate logical coordinates in [0,1]"));
859 PetscCall(PetscRandomDestroy(&randx));
860 PetscCall(PetscRandomDestroy(&randy));
861 PetscCall(PetscRandomDestroy(&randz));
862 PetscCall(PetscRandomDestroy(&rand_i));
863 PetscCall(PetscRandomDestroy(&rand_j));
864 PetscCall(PetscRandomDestroy(&rand_k));
866 PetscFunctionReturn(0);
874 SimCtx *context_only = NULL;
876 char context_tmpdir[PETSC_MAX_PATH_LEN];
877 char grid_tmpdir[PETSC_MAX_PATH_LEN];
879 PetscFunctionBeginUser;
880 PetscCall(
BuildContextOnly(&context_only, context_tmpdir,
sizeof(context_tmpdir)));
881 PetscCall(
PicurvAssertBool((PetscBool)(context_only != NULL),
"CreateSimulationContext should allocate the top-level SimCtx"));
884 "deprecated RK4 selector should normalize to the Jameson solver enum"));
886 "deprecated RK4 residual-noise option should populate the Jameson control"));
888 "momentum absolute residual tolerance should be parsed"));
890 "momentum relative residual tolerance should be parsed"));
892 "Newton structured history monitor should be parsed"));
893 PetscCall(PetscOptionsClear(NULL));
901 "SetupGridAndSolvers should allocate baseline Eulerian vectors"));
904 PetscFunctionReturn(0);
917 Vec coordinates = NULL;
918 Vec local_coordinates = NULL;
919 PetscErrorCode unknown_name_ierr;
920 PetscErrorCode unavailable_view_ierr;
922 PetscFunctionBeginUser;
928 "catalog entry should retain its declared FieldId"));
931 "every field descriptor should have a canonical name"));
933 descriptor->
dof == 2 ||
934 descriptor->
dof == 3),
935 "every ghost-updatable catalog field should declare a supported dof"));
938 "every phase-one field should advertise ghost-update capability"));
941 "canonical field names should round-trip to their IDs"));
944 "FieldCanonicalName should return catalog-owned canonical text"));
949 "Nu_t compatibility alias should resolve case-insensitively"));
952 "CS compatibility alias should resolve case-insensitively"));
955 "diagnostic face-center name should resolve to the canonical field"));
959 "Ucont should retain component-staggered topology metadata"));
961 "Ucont should retain component-staggered ghost repair metadata"));
964 "Csi should retain I-face topology metadata"));
966 "Csi should retain I-face ghost repair metadata"));
969 "IAj should retain scalar face-field metadata"));
975 "P field view should bind the existing scalar DM and Vec pair"));
980 "Ucat field view should bind the existing vector DM and Vec pair"));
982 PetscCall(DMGetCoordinates(user->
da, &coordinates));
983 PetscCall(DMGetCoordinatesLocal(user->
da, &local_coordinates));
988 "coordinate field view should bind PETSc-owned coordinate vectors"));
990 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
991 unknown_name_ierr =
FieldIdFromName(
"NotARegisteredEulerianField", &parsed_id);
992 PetscCall(PetscPopErrorHandler());
994 "unknown field names should fail at the name-ingress boundary"));
996 "failed field-name resolution should return FIELD_ID_INVALID"));
998 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1000 PetscCall(PetscPopErrorHandler());
1002 "catalogued optional fields without storage should report unavailable state"));
1005 PetscFunctionReturn(0);
1015 PetscErrorCode unknown_name_ierr;
1017 PetscFunctionBeginUser;
1021 "particle catalog entry should retain its declared ID"));
1024 "every particle field should have a canonical DMSwarm name"));
1027 "persistent particle fields should declare one or three components"));
1030 "canonical particle field names should round-trip to their IDs"));
1033 "ParticleFieldName should return catalog-owned PETSc text"));
1038 "particle velocity alias should resolve case-insensitively"));
1041 "migration-status output alias should resolve to the PETSc field"));
1046 "Psi should advertise its active particle-to-Eulerian scatter"));
1048 "particle Psi should target Eulerian Psi"));
1051 "particle IDs should be marked as PETSc-managed"));
1053 "particle IDs should retain 64-bit storage metadata"));
1055 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
1057 PetscCall(PetscPopErrorHandler());
1059 "unknown particle names should fail at the text-ingress boundary"));
1061 "failed particle name resolution should return the invalid ID"));
1062 PetscFunctionReturn(0);
1073 PetscFunctionBeginUser;
1076 PetscCall(VecSet(user->
P, 4.25));
1077 PetscCall(VecSet(user->
lP, -1.0));
1080 "typed scalar ghost update should scatter P"));
1082 PetscCall(VecSet(user->
Ucat, 7.5));
1083 PetscCall(VecSet(user->
lUcat, -1.0));
1086 "typed cell-vector ghost update should scatter Ucat"));
1088 PetscCall(VecSet(user->
IAj, 2.0));
1089 PetscCall(VecSet(user->
lIAj, -1.0));
1092 "typed scalar face-field ghost update should scatter IAj"));
1094 PetscCall(VecSet(user->
Ucont, 3.0));
1095 PetscCall(VecSet(user->
lUcont, -1.0));
1098 "typed component-staggered ghost update should scatter Ucont"));
1101 PetscFunctionReturn(0);
1109 PetscErrorCode ierr;
1131 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv setup lifecycle tests");
1136 ierr =
PicurvRunTests(
"unit-setup", cases,
sizeof(cases) /
sizeof(cases[0]));
1142 ierr = PetscFinalize();
Header file for Particle related physics modules.
PetscErrorCode UpdateAllParticleFields(UserCtx *user)
Orchestrates the update of all physical properties for particles.
Header file for Particle Swarm management functions.
PetscErrorCode InitializeParticleSwarm(SimCtx *simCtx)
High-level particle initialization orchestrator for a simulation run.
PetscErrorCode FieldIdFromName(const char *field_name, FieldId *field_id)
Resolve a user-facing field name once into its typed identity.
@ FIELD_CAPABILITY_GHOST_UPDATE
@ FIELD_SYNC_COMPONENT_STAGGERED
unsigned int capabilities
const char * FieldCanonicalName(FieldId field_id)
Return the canonical printable name for an ID.
PetscErrorCode FieldGetView(UserCtx *user, FieldId field_id, FieldView *view)
Resolve the existing DM and global/local vectors for one field.
@ FIELD_LAYOUT_COMPONENT_STAGGERED
const char * canonical_name
FieldSyncClass sync_class
PetscErrorCode FieldGetDescriptor(FieldId field_id, const FieldDescriptor **descriptor)
Return immutable metadata for a valid field identifier.
FieldId
Compile-time identity for a catalogued Eulerian field.
Immutable metadata for one field identity.
Non-owning runtime objects resolved for one field and UserCtx.
PetscErrorCode InitializeEulerianState(SimCtx *simCtx)
High-level orchestrator to set the complete initial state of the Eulerian solver.
PetscErrorCode ReadSimulationFields(UserCtx *user, PetscInt ti)
Reads binary field data for velocity, pressure, and other required vectors.
PetscErrorCode WriteCheckpointBundle(SimCtx *simCtx, const char *reason)
Write and atomically publish one complete checkpoint bundle.
const char * ParticleFieldName(ParticleFieldId field_id)
Return the canonical PETSc DMSwarm name for an ID.
ParticleFieldId
Compile-time identity for a persistent solver-particle field.
@ PARTICLE_FIELD_ID_LOCATION_STATUS
@ PARTICLE_FIELD_ID_CELL_ID
@ PARTICLE_FIELD_ID_COUNT
@ PARTICLE_FIELD_ID_DIFFUSIVITY
@ PARTICLE_FIELD_ID_INVALID
@ PARTICLE_FIELD_ID_VELOCITY
unsigned int capabilities
ParticleFieldRegistration registration
PetscErrorCode ParticleFieldIdFromName(const char *field_name, ParticleFieldId *field_id)
Resolve a canonical name or registered alias at a text-ingress boundary.
@ PARTICLE_FIELD_CAPABILITY_EULERIAN_SCATTER
@ PARTICLE_FIELD_REGISTRATION_PETSC
PetscErrorCode ParticleFieldGetDescriptor(ParticleFieldId field_id, const ParticleFieldDescriptor **descriptor)
Return immutable metadata for a valid particle field ID.
const char * canonical_name
FieldId eulerian_scatter_target
Immutable metadata for one persistent particle field.
PetscErrorCode PerformInitializedParticleSetup(SimCtx *simCtx)
Finalizes the simulation setup at t=0, ensuring a consistent state before time marching.
PetscErrorCode InitializeRandomGenerators(UserCtx *user, PetscRandom *randx, PetscRandom *randy, PetscRandom *randz)
Initializes random number generators for assigning particle properties.
PetscErrorCode SetupGridAndSolvers(SimCtx *simCtx)
The main orchestrator for setting up all grid-related components.
PetscErrorCode SetupSimulationEnvironment(SimCtx *simCtx)
Verifies and prepares the complete I/O environment for a simulation run.
PetscErrorCode InitializeLogicalSpaceRNGs(PetscInt base_seed, PetscRandom *rand_logic_i, PetscRandom *rand_logic_j, PetscRandom *rand_logic_k)
Initializes random number generators for logical space operations [0.0, 1.0).
PetscErrorCode CreateSimulationContext(int argc, char **argv, SimCtx **p_simCtx)
Allocates and populates the master SimulationContext object.
PetscErrorCode UpdateLocalGhosts(UserCtx *user, FieldId field_id)
Updates the local vector (including ghost points) from its corresponding global vector.
PetscBool RuntimeWalltimeGuardParsePositiveSeconds(const char *text, PetscReal *seconds_out)
Parse a positive floating-point seconds value from runtime metadata.
static PetscErrorCode TestParticleFieldCatalogMetadata(void)
Verifies the separate DMSwarm field catalog and its Eulerian bridge metadata.
static PetscErrorCode TestConfiguredIEMUpdatesSwarm(void)
Checks configured IEM relaxation through the production swarm update.
static PetscErrorCode TestSetupRejectsNonpositivePostInterval(void)
Ensures setup propagates invalid post intervals before either executable can loop.
static PetscErrorCode TestUnsolvedFlowUsesOneGridLevel(void)
Ensures a run that solves no pressure builds one grid level whatever -mg_level asks,...
static PetscErrorCode BuildContextOnly(SimCtx **simCtx_out, char *tmpdir, size_t tmpdir_len)
Builds only the top-level simulation context used by partial-initialization cleanup tests.
static PetscErrorCode PrepareContextOnlyConfig(char *tmpdir, size_t tmpdir_len, char *control_path, size_t control_path_len)
Creates the control-file bundle needed for the context-only cleanup test.
static PetscErrorCode TryContextWithExtraLines(const char *extra_lines, SimCtx **simCtx_out, PetscErrorCode *ierr_out, char *tmpdir, size_t tmpdir_len)
Builds a context from the fixture control file with extra lines appended.
int main(int argc, char **argv)
Runs the unit-setup PETSc test binary.
static PetscErrorCode TestIEMRelaxesTowardOwnCellMean(void)
Checks that IEM relaxes each particle toward its own cell's scattered mean.
static PetscErrorCode TestIEMConstantIsConfigurable(void)
Tests that the IEM mixing constant defaults to 2.0, follows -iem_constant, accepts zero (no micromixi...
static PetscErrorCode TestParticleCountMustFitPetscInt(void)
Ensures setup refuses a particle count whose widest swarm field PetscInt cannot index.
static PetscErrorCode AssertDirectoryExists(const char *path, const char *context)
Asserts that a directory path exists and is readable.
static PetscErrorCode TestSetupLifecycleScatterMetricsAtStepZero(void)
Tests that step-zero scalar verification emits scatter_metrics.csv during initialized setup.
static PetscErrorCode TestFieldCatalogMetadataAndViews(void)
Verifies catalog completeness, stable name resolution, and runtime view binding.
static PetscErrorCode TestSetupLifecycleRandomGeneratorsAndCleanup(void)
Tests standalone RNG initialization helpers and minimal-context cleanup.
static PetscErrorCode TestPostprocessorLoadsSavedParticleAverages(void)
Checks that post-processing loads the particle averages a checkpoint holds.
static PetscErrorCode WriteContextOnlyFile(const char *path, const char *contents)
Writes one small temporary text file used by the partial-lifecycle context-only fixture.
static PetscErrorCode BuildLifecycleContext(PetscBool enable_particles, SimCtx **simCtx_out, char *tmpdir, size_t tmpdir_len)
Builds a full setup fixture through environment, grid, BC, and domain-rank initialization.
static PetscErrorCode FreeLifecycleContext(SimCtx **simCtx_ptr)
Finalizes and frees one lifecycle test context, then clears any PETSc options used to build it.
static PetscErrorCode TestSetupLifecycleParticleInitialization(void)
Tests particle-swarm initialization and deterministic settlement on a tiny fully initialized case.
static PetscErrorCode TestSetupLifecycleCoreSolverSetup(void)
Tests the core setup lifecycle through environment, grid, BC, rank-info, and Eulerian-state initializ...
static PetscErrorCode AssertStoredLocalInfoMatchesDMs(UserCtx *user)
Asserts that one block's stored local info matches what its DMs report.
static PetscErrorCode TestSharedRuntimeFixtureContracts(void)
Tests that the shared richer runtime fixture mirrors normalized production setup contracts.
static PetscErrorCode TestSetupRejectsUnimplementedFeatureFlags(void)
Tests that setup refuses every flag whose feature is planned but not implemented.
static PetscErrorCode TestConfiguredParticleInitialValue(void)
Checks a configured particle initial value through the production setup path.
static PetscErrorCode TestFieldCatalogTypedGhostUpdates(void)
Verifies typed ghost updates preserve scalar, vector, face, and staggered behavior.
static PetscErrorCode TestSetupLifecycleCleanupAcrossInitializationStates(void)
Tests cleanup after partial and fuller setup states without requiring unsupported double-finalization...
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 PicurvAssertRealNear(PetscReal expected, PetscReal actual, PetscReal tol, const char *context)
Asserts that two real values agree within tolerance.
PetscErrorCode PicurvDestroyMinimalContexts(SimCtx **simCtx_ptr, UserCtx **user_ptr)
Destroys minimal SimCtx/UserCtx fixtures and all owned PETSc objects.
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.
PetscErrorCode PicurvAssertFileExists(const char *path, const char *context)
Asserts that a filesystem path exists as a readable file.
PetscErrorCode PicurvBuildTinyRuntimeContextWithOptions(const char *bcs_contents, PetscBool enable_particles, const char *extra_options, SimCtx **simCtx_out, UserCtx **user_out, char *tmpdir, size_t tmpdir_len)
Builds the tiny runtime context with extra control-file options appended.
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.
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.
PetscBool mom_nk_monitor_history
PetscBool inletFaceDefined
BoundaryFaceConfig boundary_faces[6]
BCFace identifiedInletBCFace
PetscReal iem_constant
IEM mixing constant C_IEM in Omega = C_IEM Gamma / Delta^2 (default 2.0).
Cmpnts max_coords
Maximum x, y, z coordinates of the bounding box.
char output_dir[PETSC_MAX_PATH_LEN]
Cmpnts min_coords
Minimum x, y, z coordinates of the bounding box.
@ MOMENTUM_SOLVER_DUALTIME_PICARD_JAMESON_RK
RankCellInfo * RankCellInfoMap
VerificationScalarConfig verificationScalar
char source_dir[PETSC_MAX_PATH_LEN]
char eulerianSource[PETSC_MAX_PATH_LEN]
PetscReal mom_dt_jameson_residual_norm_noise_allowance_factor
PetscInt particleRandomSeed
Base seed for every particle RNG stream (-particle_random_seed).
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
@ EXEC_MODE_POSTPROCESSOR
MomentumSolverType mom_solver_type
struct ParticleFieldPlan * particleFieldPlan
Configured initial particle-field values, or NULL.
A 3D point or vector with PetscScalar components.
The master context for the entire simulation.
User-defined context containing data specific to a single computational grid level.