24static PetscErrorCode
AssertFileContains(
const char *path,
const char *needle,
const char *context)
30 PetscFunctionBeginUser;
31 fp = fopen(path,
"rb");
33 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open '%s' for assertion.", path);
35 if (fseek(fp, 0, SEEK_END) != 0) {
37 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to seek '%s'.", path);
39 file_size = ftell(fp);
42 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to measure '%s'.", path);
44 if (fseek(fp, 0, SEEK_SET) != 0) {
46 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to rewind '%s'.", path);
49 PetscCall(PetscMalloc1((
size_t)file_size + 1, &buffer));
51 size_t bytes_read = fread(buffer, 1, (
size_t)file_size, fp);
52 if (bytes_read != (
size_t)file_size) {
54 PetscCall(PetscFree(buffer));
55 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to read '%s'.", path);
58 buffer[file_size] =
'\0';
61 PetscCall(
PicurvAssertBool((PetscBool)(strstr(buffer, needle) != NULL), context));
62 PetscCall(PetscFree(buffer));
63 PetscFunctionReturn(0);
75 PetscFunctionBeginUser;
76 fp = fopen(path,
"rb");
78 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open '%s' for assertion.", path);
80 if (fseek(fp, 0, SEEK_END) != 0) {
82 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to seek '%s'.", path);
84 file_size = ftell(fp);
87 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to measure '%s'.", path);
89 if (fseek(fp, 0, SEEK_SET) != 0) {
91 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to rewind '%s'.", path);
94 PetscCall(PetscMalloc1((
size_t)file_size + 1, &buffer));
96 size_t bytes_read = fread(buffer, 1, (
size_t)file_size, fp);
97 if (bytes_read != (
size_t)file_size) {
99 PetscCall(PetscFree(buffer));
100 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to read '%s'.", path);
103 buffer[file_size] =
'\0';
106 PetscCall(
PicurvAssertBool((PetscBool)(strstr(buffer, needle) == NULL), context));
107 PetscCall(PetscFree(buffer));
108 PetscFunctionReturn(0);
122 PetscInt current_row = 0;
124 PetscFunctionBeginUser;
125 PetscCheck(path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV path cannot be NULL.");
126 PetscCheck(header != NULL && header_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV header buffer cannot be NULL or empty.");
127 PetscCheck(row != NULL && row_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV row buffer cannot be NULL or empty.");
128 PetscCheck(row_index >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
"CSV row index must be >= 1.");
130 fp = fopen(path,
"r");
131 PetscCheck(fp != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open CSV '%s'.", path);
132 PetscCheck(fgets(header, (
int)header_len, fp) != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
"CSV header missing in '%s'.", path);
134 while (fgets(row, (
int)row_len, fp) != NULL) {
136 if (current_row == row_index) {
138 PetscFunctionReturn(0);
143 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
"CSV '%s' does not contain data row %d.", path, (
int)row_index);
149static PetscErrorCode
CsvFindColumnIndex(
const char *header,
const char *column_name, PetscInt *index_out)
151 char local_header[4096];
152 char *saveptr = NULL;
156 PetscFunctionBeginUser;
157 PetscCheck(header != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV header cannot be NULL.");
158 PetscCheck(column_name != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV column name cannot be NULL.");
159 PetscCheck(index_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV column index output cannot be NULL.");
160 PetscCheck(strlen(header) <
sizeof(local_header), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
"CSV header is too long for the local parser buffer.");
162 PetscCall(PetscStrncpy(local_header, header,
sizeof(local_header)));
163 token = strtok_r(local_header,
",\r\n", &saveptr);
164 while (token != NULL) {
165 if (strcmp(token, column_name) == 0) {
167 PetscFunctionReturn(0);
169 token = strtok_r(NULL,
",\r\n", &saveptr);
173 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"CSV column '%s' was not found.", column_name);
181 const char *column_name,
185 char local_row[4096];
186 char *saveptr = NULL;
188 PetscInt target_index = -1;
191 PetscFunctionBeginUser;
192 PetscCheck(row != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV row cannot be NULL.");
193 PetscCheck(value != NULL && value_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV value buffer cannot be NULL or empty.");
194 PetscCheck(strlen(row) <
sizeof(local_row), PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
"CSV row is too long for the local parser buffer.");
197 PetscCall(PetscStrncpy(local_row, row,
sizeof(local_row)));
199 token = strtok_r(local_row,
",\r\n", &saveptr);
200 while (token != NULL) {
201 if (index == target_index) {
202 PetscCall(PetscStrncpy(value, token, value_len));
203 PetscFunctionReturn(0);
205 token = strtok_r(NULL,
",\r\n", &saveptr);
209 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"CSV row is missing column '%s'.", column_name);
215static PetscErrorCode
CsvGetColumnInt(
const char *header,
const char *row,
const char *column_name, PetscInt *value_out)
221 PetscFunctionBeginUser;
222 PetscCheck(value_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV integer output cannot be NULL.");
224 parsed = strtol(text, &endptr, 10);
225 PetscCheck(endptr != text, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"CSV column '%s' did not contain an integer.", column_name);
226 *value_out = (PetscInt)parsed;
227 PetscFunctionReturn(0);
233static PetscErrorCode
CsvGetColumnReal(
const char *header,
const char *row,
const char *column_name, PetscReal *value_out)
239 PetscFunctionBeginUser;
240 PetscCheck(value_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"CSV real output cannot be NULL.");
242 parsed = strtod(text, &endptr);
243 PetscCheck(endptr != text, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"CSV column '%s' did not contain a real value.", column_name);
244 *value_out = (PetscReal)parsed;
245 PetscFunctionReturn(0);
264 PetscInt current_row = 0;
266 PetscBool header_found = PETSC_FALSE;
268 PetscFunctionBeginUser;
269 PetscCheck(path != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log path cannot be NULL.");
270 PetscCheck(header != NULL && header_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log header buffer cannot be NULL.");
271 PetscCheck(row != NULL && row_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log row buffer cannot be NULL.");
272 PetscCheck(row_index >= 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE,
"Log row index must be >= 1.");
274 fp = fopen(path,
"r");
275 PetscCheck(fp != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open log '%s'.", path);
277 while (fgets(line,
sizeof(line), fp) != NULL) {
278 if (line[0] ==
'=' || line[0] ==
'-' || line[0] ==
'\n' || line[0] ==
'\r')
continue;
280 PetscCall(PetscStrncpy(header, line, header_len));
281 header_found = PETSC_TRUE;
285 if (current_row == row_index) {
286 PetscCall(PetscStrncpy(row, line, row_len));
288 PetscFunctionReturn(0);
293 if (!header_found) SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
"Log '%s' has no column header.", path);
294 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_FILE_READ,
"Log '%s' does not contain data row %d.", path, (
int)row_index);
300static PetscErrorCode
LogFindColumnIndex(
const char *header,
const char *column_name, PetscInt *index_out)
302 char local_header[8192];
303 char *saveptr = NULL;
307 PetscFunctionBeginUser;
308 PetscCheck(header != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log header cannot be NULL.");
309 PetscCheck(column_name != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log column name cannot be NULL.");
310 PetscCheck(index_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log column index output cannot be NULL.");
312 PetscCall(PetscStrncpy(local_header, header,
sizeof(local_header)));
313 token = strtok_r(local_header,
"|\r\n", &saveptr);
314 while (token != NULL) {
315 while (*token ==
' ') token++;
316 char *end = token + strlen(token) - 1;
317 while (end > token && (*end ==
' ' || *end ==
'\r' || *end ==
'\n')) end--;
319 if (strcmp(token, column_name) == 0) {
321 PetscFunctionReturn(0);
323 token = strtok_r(NULL,
"|\r\n", &saveptr);
326 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"Log column '%s' was not found.", column_name);
334 const char *column_name,
338 char local_row[8192];
339 char *saveptr = NULL;
341 PetscInt target_index = -1;
344 PetscFunctionBeginUser;
345 PetscCheck(row != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log row cannot be NULL.");
346 PetscCheck(value != NULL && value_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log value buffer cannot be NULL.");
349 PetscCall(PetscStrncpy(local_row, row,
sizeof(local_row)));
351 token = strtok_r(local_row,
"|\r\n", &saveptr);
352 while (token != NULL) {
353 if (index == target_index) {
354 while (*token ==
' ') token++;
355 char *end = token + strlen(token) - 1;
356 while (end > token && (*end ==
' ' || *end ==
'\r' || *end ==
'\n')) end--;
358 PetscCall(PetscStrncpy(value, token, value_len));
359 PetscFunctionReturn(0);
361 token = strtok_r(NULL,
"|\r\n", &saveptr);
364 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"Log row is missing column '%s'.", column_name);
370static PetscErrorCode
LogGetColumnInt(
const char *header,
const char *row,
const char *column_name, PetscInt *value_out)
376 PetscFunctionBeginUser;
377 PetscCheck(value_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log integer output cannot be NULL.");
379 parsed = strtol(text, &endptr, 10);
380 PetscCheck(endptr != text, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"Log column '%s' did not contain an integer.", column_name);
381 *value_out = (PetscInt)parsed;
382 PetscFunctionReturn(0);
388static PetscErrorCode
LogGetColumnReal(
const char *header,
const char *row,
const char *column_name, PetscReal *value_out)
394 PetscFunctionBeginUser;
395 PetscCheck(value_out != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Log real output cannot be NULL.");
397 parsed = strtod(text, &endptr);
398 PetscCheck(endptr != text, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG,
"Log column '%s' did not contain a real value.", column_name);
399 *value_out = (PetscReal)parsed;
400 PetscFunctionReturn(0);
420 char tmpdir[PETSC_MAX_PATH_LEN];
421 char capture_path[PETSC_MAX_PATH_LEN];
422 FILE *capture_file = NULL;
423 int saved_stdout = -1;
425 size_t bytes_read = 0;
428 PetscFunctionBeginUser;
429 PetscCheck(fn != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Capture callback cannot be NULL.");
430 PetscCheck(captured != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Capture buffer cannot be NULL.");
431 PetscCheck(captured_len > 0, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ,
"Capture buffer must be non-empty.");
434 PetscCall(PetscSNPrintf(capture_path,
sizeof(capture_path),
"%s/logging.out", tmpdir));
437 saved_stdout = dup(STDOUT_FILENO);
438 PetscCheck(saved_stdout >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS,
"dup(STDOUT_FILENO) failed.");
439 capture_fd = open(capture_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
440 PetscCheck(capture_fd >= 0, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to open capture file '%s'.", capture_path);
441 PetscCheck(dup2(capture_fd, STDOUT_FILENO) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS,
"dup2() failed while redirecting stdout.");
445 ierr = fn(user, simCtx, ctx);
447 PetscCheck(dup2(saved_stdout, STDOUT_FILENO) >= 0, PETSC_COMM_SELF, PETSC_ERR_SYS,
"dup2() failed while restoring stdout.");
452 capture_file = fopen(capture_path,
"r");
453 PetscCheck(capture_file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to read capture file '%s'.", capture_path);
454 bytes_read = fread(captured, 1, captured_len - 1, capture_file);
455 captured[bytes_read] =
'\0';
456 fclose(capture_file);
458 PetscFunctionReturn(0);
466 PetscInt print_interval = *((PetscInt *)ctx);
468 PetscFunctionBeginUser;
471 PetscFunctionReturn(0);
479 PetscInt step = *((PetscInt *)ctx);
481 PetscFunctionBeginUser;
483 PetscFunctionReturn(0);
491 PetscInt step = *((PetscInt *)ctx);
493 PetscFunctionBeginUser;
495 PetscFunctionReturn(0);
505 PetscFunctionBeginUser;
508 PetscFunctionReturn(0);
516 PetscReal *positions = NULL;
517 PetscReal *velocities = NULL;
518 PetscReal *weights = NULL;
519 PetscInt *cell_ids = NULL;
520 PetscInt *status = NULL;
521 PetscReal *psi = NULL;
523 PetscFunctionBeginUser;
526 (*simCtx_out)->np = 2;
527 (*simCtx_out)->particleConsoleOutputFreq = 2;
528 (*simCtx_out)->LoggingFrequency = 1;
530 PetscCall(DMSwarmGetField((*user_out)->swarm,
"position", NULL, NULL, (
void **)&positions));
531 PetscCall(DMSwarmGetField((*user_out)->swarm,
"velocity", NULL, NULL, (
void **)&velocities));
532 PetscCall(DMSwarmGetField((*user_out)->swarm,
"weight", NULL, NULL, (
void **)&weights));
533 PetscCall(DMSwarmGetField((*user_out)->swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
534 PetscCall(DMSwarmGetField((*user_out)->swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
535 PetscCall(DMSwarmGetField((*user_out)->swarm,
"Psi", NULL, NULL, (
void **)&psi));
537 positions[0] = 0.25; positions[1] = 0.50; positions[2] = 0.75;
538 positions[3] = 0.50; positions[4] = 0.50; positions[5] = 0.50;
539 velocities[0] = 1.0; velocities[1] = 2.0; velocities[2] = 3.0;
540 velocities[3] = 4.0; velocities[4] = 5.0; velocities[5] = 6.0;
541 weights[0] = 0.2; weights[1] = 0.3; weights[2] = 0.4;
542 weights[3] = 0.5; weights[4] = 0.5; weights[5] = 0.5;
543 cell_ids[0] = 0; cell_ids[1] = 0; cell_ids[2] = 0;
544 cell_ids[3] = 1; cell_ids[4] = 1; cell_ids[5] = 1;
550 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"Psi", NULL, NULL, (
void **)&psi));
551 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
552 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
553 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"weight", NULL, NULL, (
void **)&weights));
554 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"velocity", NULL, NULL, (
void **)&velocities));
555 PetscCall(DMSwarmRestoreField((*user_out)->swarm,
"position", NULL, NULL, (
void **)&positions));
556 PetscFunctionReturn(0);
566 PetscFunctionBeginUser;
567 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"UserCtx cannot be NULL.");
568 PetscCheck(field != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Velocity field cannot be NULL.");
570 PetscCall(DMDAVecGetArray(user->
fda, field, &arr));
571 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
572 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
573 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
580 PetscCall(DMDAVecRestoreArray(user->
fda, field, &arr));
581 PetscFunctionReturn(0);
589 PetscReal ***arr = NULL;
591 PetscFunctionBeginUser;
592 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"UserCtx cannot be NULL.");
593 PetscCheck(field != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Scalar field cannot be NULL.");
595 PetscCall(DMDAVecGetArray(user->
da, field, &arr));
596 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
597 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
598 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
599 arr[k][j][i] = value;
603 PetscCall(DMDAVecRestoreArray(user->
da, field, &arr));
604 PetscFunctionReturn(0);
615 PetscInt km = user->
KM;
617 PetscFunctionBeginUser;
618 PetscCheck(user != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"UserCtx cannot be NULL.");
619 PetscCheck(field != NULL, PETSC_COMM_SELF, PETSC_ERR_ARG_NULL,
"Velocity field cannot be NULL.");
621 PetscCall(DMDAVecGetArray(user->
fda, field, &arr));
622 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
623 PetscReal z_phase = (2.0 * PETSC_PI * (PetscReal)k) / (PetscReal)km;
624 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
625 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
626 arr[k][j][i].
x = 0.0;
627 arr[k][j][i].
y = v_amp * PetscSinReal(z_phase);
628 arr[k][j][i].
z = w_const;
632 PetscCall(DMDAVecRestoreArray(user->
fda, field, &arr));
633 PetscFunctionReturn(0);
642 PetscFunctionBeginUser;
644 "BCFaceToString should report the negative-x face"));
646 "InitialConditionModeToString should report the zero mode"));
648 "InitialConditionModeToString should report the Cartesian constant mode"));
650 "InitialConditionModeToString should report the streamwise constant mode"));
652 "InitialConditionModeToString should report the Poiseuille mode"));
654 "InitialConditionModeToString should report the file mode"));
656 "InitialConditionModeToString should reject unknown selectors"));
658 "ParticleInitializationToString should report the volume mode"));
660 "LESModelToString should report the constant model"));
662 "MomentumSolverTypeToString should report the explicit solver"));
664 "MomentumSolverTypeToString should report the Newton Krylov solver"));
666 "BCTypeToString should report periodic boundaries"));
668 "BCHandlerTypeToString should report the driven periodic handler"));
670 "ParticleLocationStatusToString should report LOST state"));
671 PetscFunctionReturn(0);
679 PetscFunctionBeginUser;
681 "get_log_level should honor LOG_LEVEL=INFO in this test binary"));
683 PetscFunctionReturn(0);
691 const char *allow_list[] = {
"ComputeSpecificKE",
"WriteEulerianFile"};
693 PetscFunctionBeginUser;
696 "Allowed list should include ComputeSpecificKE"));
698 "Allowed list should exclude unknown function names"));
702 "Empty allow-list should permit all functions"));
703 PetscFunctionReturn(0);
713 PetscFunctionBeginUser;
714 PetscCall(PetscMemzero(&simCtx,
sizeof(simCtx)));
719 "Particle snapshot contract should be enabled when particles and cadence are configured"));
721 "Snapshot should emit on cadence-aligned completed steps"));
723 "Snapshot should not emit off-cadence"));
727 "Zero cadence should disable periodic particle snapshots"));
729 "NULL SimCtx should never emit periodic snapshots"));
730 PetscFunctionReturn(0);
742 memset(&definition, 0,
sizeof(definition));
747 definition.
bounded = PETSC_TRUE;
763 PetscFunctionBeginUser;
764 PetscCall(PetscMemzero(&simCtx,
sizeof(simCtx)));
772 "Statistics snapshot contract should be enabled when a window and cadence are configured"));
774 "Snapshot should emit on cadence-aligned completed steps"));
776 "Snapshot should not emit off-cadence"));
778 "Snapshot should not emit for a step that has not completed"));
782 "Zero cadence should disable periodic statistics snapshots"));
784 "Zero cadence should not emit even on the step that opens a run"));
791 "A configured cadence should not enable snapshots without an accumulating window"));
796 "A disabled subsystem should not emit statistics snapshots"));
799 "NULL SimCtx should never emit periodic snapshots"));
800 PetscFunctionReturn(0);
808 char tmpdir[PETSC_MAX_PATH_LEN];
809 char allow_path[PETSC_MAX_PATH_LEN];
810 char dual_log_path[PETSC_MAX_PATH_LEN];
815 PetscReal distances[6] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0};
819 PetscFunctionBeginUser;
820 PetscCall(PetscMemzero(&cell,
sizeof(cell)));
822 PetscCall(PetscSNPrintf(allow_path,
sizeof(allow_path),
"%s/allowed_functions.txt", tmpdir));
823 PetscCall(PetscSNPrintf(dual_log_path,
sizeof(dual_log_path),
"%s/dual-monitor.log", tmpdir));
825 file = fopen(allow_path,
"w");
826 PetscCheck(file != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to create allow-list file '%s'.", allow_path);
827 fputs(
" ComputeSpecificKE \n", file);
828 fputs(
"# comment-only line\n", file);
829 for (PetscInt i = 0; i < 17; ++i) {
830 fprintf(file,
"Helper_%02d # trailing comment\n", (
int)i);
836 PetscCall(
PicurvAssertIntEqual(18, nfuncs,
"LoadAllowedFunctionsFromFile should trim comments and keep all identifiers"));
837 PetscCall(
PicurvAssertBool((PetscBool)(strcmp(funcs[0],
"ComputeSpecificKE") == 0),
838 "LoadAllowedFunctionsFromFile should trim leading and trailing whitespace"));
839 PetscCall(
PicurvAssertBool((PetscBool)(strcmp(funcs[17],
"Helper_16") == 0),
840 "LoadAllowedFunctionsFromFile should grow past the initial pointer capacity"));
843 for (PetscInt i = 0; i < 8; ++i) {
851 PetscCall(PetscCalloc1(1, &monctx));
853 PetscCheck(monctx->
file_handle != NULL, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN,
"Failed to create dual-monitor log '%s'.", dual_log_path);
857 "DualMonitorDestroy should clear the caller-owned context pointer"));
862 PetscCall(PetscPrintf(PETSC_COMM_SELF,
"\n"));
865 PetscFunctionReturn(0);
875 char tmpdir[PETSC_MAX_PATH_LEN];
876 char continuity_path[PETSC_MAX_PATH_LEN];
877 PetscReal ***p = NULL;
880 PetscErrorCode ierr_minmax = 0;
882 PetscFunctionBeginUser;
885 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
906 PetscCall(PetscSNPrintf(continuity_path,
sizeof(continuity_path),
"%s/Continuity_Metrics.log", simCtx->
log_dir));
908 PetscCall(
AssertFileContains(continuity_path,
"Timestep",
"continuity metrics log should include the header"));
909 PetscCall(
AssertFileContains(continuity_path,
"([3][2][1] = 17)",
"continuity metrics log should include the divergence location"));
910 PetscCall(
AssertFileContains(continuity_path,
"2 | 0",
"continuity metrics log should append later timesteps"));
912 PetscCall(DMDAVecGetArray(user->
da, user->
P, &p));
913 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
914 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucont, &ucont));
915 for (PetscInt k = user->
info.zs; k < user->
info.zs + user->
info.zm; ++k) {
916 for (PetscInt j = user->
info.ys; j < user->
info.ys + user->
info.ym; ++j) {
917 for (PetscInt i = user->
info.xs; i < user->
info.xs + user->
info.xm; ++i) {
918 p[k][j][i] = (PetscReal)(i + j + k);
919 ucat[k][j][i].
x = (PetscReal)i;
920 ucat[k][j][i].
y = (PetscReal)(-j);
921 ucat[k][j][i].
z = (PetscReal)(2 * k);
922 ucont[k][j][i].
x = (PetscReal)(10 + i);
923 ucont[k][j][i].
y = (PetscReal)(20 + j);
924 ucont[k][j][i].
z = (PetscReal)(30 + k);
928 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucont, &ucont));
929 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
930 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &p));
932 PetscCall(DMGlobalToLocalBegin(user->
da, user->
P, INSERT_VALUES, user->
lP));
933 PetscCall(DMGlobalToLocalEnd(user->
da, user->
P, INSERT_VALUES, user->
lP));
934 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
935 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucat, INSERT_VALUES, user->
lUcat));
936 PetscCall(DMGlobalToLocalBegin(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
937 PetscCall(DMGlobalToLocalEnd(user->
fda, user->
Ucont, INSERT_VALUES, user->
lUcont));
944 PetscCall(PetscPushErrorHandler(PetscIgnoreErrorHandler, NULL));
946 PetscCall(PetscPopErrorHandler());
948 "LOG_FIELD_MIN_MAX should reject invalid field IDs"));
952 PetscFunctionReturn(0);
962 PetscReal (*pos_arr)[3] = NULL;
963 PetscReal (*vel_arr)[3] = NULL;
964 Vec position_vec = NULL;
965 Vec analytical_vec = NULL;
966 const PetscScalar *analytical_arr = NULL;
968 PetscFunctionBeginUser;
975 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
976 pos_arr[0][0] = 0.5 * PETSC_PI; pos_arr[0][1] = 0.0; pos_arr[0][2] = 0.0;
977 pos_arr[1][0] = 0.0; pos_arr[1][1] = 0.5 * PETSC_PI; pos_arr[1][2] = 0.0;
978 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void *)&pos_arr));
980 PetscCall(DMSwarmCreateGlobalVectorFromField(user->
swarm,
"position", &position_vec));
981 PetscCall(VecDuplicate(position_vec, &analytical_vec));
982 PetscCall(VecCopy(position_vec, analytical_vec));
985 PetscCall(DMSwarmGetField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
986 PetscCall(VecGetArrayRead(analytical_vec, &analytical_arr));
987 for (PetscInt particle = 0; particle < 2; ++particle) {
988 vel_arr[particle][0] = PetscRealPart(analytical_arr[3 * particle + 0]);
989 vel_arr[particle][1] = PetscRealPart(analytical_arr[3 * particle + 1]);
990 vel_arr[particle][2] = PetscRealPart(analytical_arr[3 * particle + 2]);
992 PetscCall(VecRestoreArrayRead(analytical_vec, &analytical_arr));
993 PetscCall(DMSwarmRestoreField(user->
swarm,
"velocity", NULL, NULL, (
void *)&vel_arr));
995 PetscCall(VecDestroy(&analytical_vec));
996 PetscCall(DMSwarmDestroyGlobalVectorFromField(user->
swarm,
"position", &position_vec));
999 PetscFunctionReturn(0);
1009 char tmpdir[PETSC_MAX_PATH_LEN];
1010 char metrics_path[PETSC_MAX_PATH_LEN];
1011 PetscReal *positions = NULL;
1012 PetscReal *psi = NULL;
1013 PetscInt *cell_ids = NULL;
1014 PetscInt *status = NULL;
1015 PetscInt particle = 0;
1017 PetscFunctionBeginUser;
1021 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1034 PetscCall(DMSwarmGetField(user->
swarm,
"position", NULL, NULL, (
void **)&positions));
1035 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
1036 PetscCall(DMSwarmGetField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
1037 PetscCall(DMSwarmGetField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
1039 for (PetscInt k = 0; k < 3; ++k) {
1040 for (PetscInt j = 0; j < 3; ++j) {
1041 for (PetscInt i = 0; i < 3; ++i) {
1042 positions[3 * particle + 0] = (i + 0.5) / 4.0;
1043 positions[3 * particle + 1] = (j + 0.5) / 4.0;
1044 positions[3 * particle + 2] = (k + 0.5) / 4.0;
1045 cell_ids[3 * particle + 0] = i;
1046 cell_ids[3 * particle + 1] = j;
1047 cell_ids[3 * particle + 2] = k;
1049 psi[particle] = 2.0;
1055 PetscCall(DMSwarmRestoreField(user->
swarm,
"Psi", NULL, NULL, (
void **)&psi));
1056 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_location_status", NULL, NULL, (
void **)&status));
1057 PetscCall(DMSwarmRestoreField(user->
swarm,
"DMSwarm_CellID", NULL, NULL, (
void **)&cell_ids));
1058 PetscCall(DMSwarmRestoreField(user->
swarm,
"position", NULL, NULL, (
void **)&positions));
1063 PetscCall(PetscSNPrintf(metrics_path,
sizeof(metrics_path),
"%s/scatter_metrics.csv", simCtx->
log_dir));
1066 "Scatter metrics CSV header should include relative_L2_error"));
1068 "3,3.000000e-01,27,27,27,1.000000e+00,1.000000e+00,5.400000e+01,5.400000e+01,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00",
1069 "Scatter metrics CSV should record the expected constant-field zero-error row"));
1073 PetscFunctionReturn(0);
1083 PetscInt print_interval = 1;
1084 char captured[8192];
1086 PetscFunctionBeginUser;
1089 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Position (x,y,z)") != NULL),
1090 "LOG_PARTICLE_FIELDS should print the particle table header"));
1091 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Weights (a1,a2,a3)") != NULL),
1092 "LOG_PARTICLE_FIELDS should print the weight-column header"));
1094 PetscFunctionReturn(0);
1105 char captured[8192];
1107 PetscFunctionBeginUser;
1110 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Particle states at step 4") != NULL),
1111 "EmitParticleConsoleSnapshot should print the step banner"));
1112 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Position (x,y,z)") != NULL),
1113 "EmitParticleConsoleSnapshot should reuse the particle table output"));
1115 PetscFunctionReturn(0);
1134 char captured[8192];
1136 PetscFunctionBeginUser;
1138 PetscCall(VecSet(user->
Nvert, 0.0));
1150 for (PetscInt offered = 0; offered <= step; ++offered) {
1155 captured,
sizeof(captured)));
1156 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Statistics windows at step 2") != NULL),
1157 "EmitStatisticsConsoleSnapshot should print the step banner"));
1158 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"console_window") != NULL),
1159 "EmitStatisticsConsoleSnapshot should name each configured window"));
1160 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"samples=2") != NULL),
1161 "EmitStatisticsConsoleSnapshot should report the accepted sample count"));
1162 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"valid=[") != NULL),
1163 "EmitStatisticsConsoleSnapshot should report mask coverage once storage carries samples"));
1167 captured,
sizeof(captured)));
1169 "a disabled subsystem emits no statistics console output"));
1176 PetscFunctionReturn(0);
1183 PetscReal ***p = NULL;
1184 const DMDALocalInfo info = user->
info;
1186 PetscFunctionBeginUser;
1187 PetscCall(DMDAVecGetArray(user->
da, user->
P, &p));
1188 for (PetscInt k = info.zs; k < info.zs + info.zm; ++k)
1189 for (PetscInt j = info.ys; j < info.ys + info.ym; ++j)
1190 for (PetscInt i = info.xs; i < info.xs + info.xm; ++i) p[k][j][i] = value;
1191 PetscCall(DMDAVecRestoreArray(user->
da, user->
P, &p));
1192 PetscFunctionReturn(0);
1204 PetscInt console_frequency)
1206 PetscFunctionBeginUser;
1214 for (PetscInt step = 0; step <= 3; ++step) {
1221 PetscFunctionReturn(0);
1228 PetscBool equal = PETSC_FALSE;
1230 PetscFunctionBeginUser;
1231 PetscCall(VecEqual(expected, actual, &equal));
1233 PetscFunctionReturn(0);
1252 char captured[8192];
1255 PetscFunctionBeginUser;
1258 PetscCall(VecSet(user->
Nvert, 0.0));
1266 "both cadences accept the same states"));
1268 "both cadences accumulate the same total weight"));
1270 "per-point sample counts are independent of the console cadence"));
1272 "per-point weights are independent of the console cadence"));
1274 "means are independent of the console cadence"));
1276 "second moments are independent of the console cadence"));
1284 captured,
sizeof(captured)));
1285 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"console_window") != NULL),
1286 "the reporting cadence emits console output at this log level"));
1295 PetscFunctionReturn(0);
1305 char tmpdir[PETSC_MAX_PATH_LEN];
1306 char metrics_path[PETSC_MAX_PATH_LEN];
1308 PetscFunctionBeginUser;
1311 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1323 PetscCall(PetscSNPrintf(metrics_path,
sizeof(metrics_path),
"%s/Particle_Metrics.log", simCtx->
log_dir));
1324 PetscCall(
PicurvAssertFileExists(metrics_path,
"LOG_PARTICLE_METRICS should write Particle_Metrics.log"));
1325 PetscCall(
AssertFileContains(metrics_path,
"Timestep Metrics",
"Particle metrics log should include the caller-provided stage label"));
1326 PetscCall(
AssertFileContains(metrics_path,
"Occupied Cells",
"Particle metrics log should include the metrics table header"));
1327 PetscCall(
AssertFileContains(metrics_path,
"Lost Total",
"Particle metrics log should include the cumulative-loss column"));
1328 PetscCall(
AssertFileContains(metrics_path,
"| 1 | 7 | 2",
"Particle metrics log should record both per-step and cumulative loss values"));
1331 PetscFunctionReturn(0);
1341 char tmpdir[PETSC_MAX_PATH_LEN];
1342 char metrics_path[PETSC_MAX_PATH_LEN];
1344 PetscFunctionBeginUser;
1347 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1371 PetscCall(PetscSNPrintf(metrics_path,
sizeof(metrics_path),
"%s/search_metrics.csv", simCtx->
log_dir));
1373 PetscCall(
AssertFileContains(metrics_path,
"search_attempts",
"Search metrics CSV header should include search_attempts"));
1374 PetscCall(
AssertFileContains(metrics_path,
"search_population",
"Search metrics CSV header should include search_population"));
1375 PetscCall(
AssertFileContains(metrics_path,
"max_particle_pass_depth",
"Search metrics CSV header should include max_particle_pass_depth"));
1376 PetscCall(
AssertFileContains(metrics_path,
"search_work_index",
"Search metrics CSV header should include search_work_index"));
1377 PetscCall(
AssertFileContains(metrics_path,
"re_search_fraction",
"Search metrics CSV header should include re_search_fraction"));
1378 PetscCall(
AssertFileContains(metrics_path,
"lost_cumulative",
"Search metrics CSV header should include the cumulative-loss column"));
1379 PetscCall(
AssertFileContains(metrics_path,
"2,2.000000e-01,2,1,7,2,3,4,2.500000e+00,6,1,2,3,1,3,1.500000e+00,2,1,1,10,2,1,5.000000e-01,5.000000e+00,1.000000e+00",
"Search metrics CSV should record the V2 raw and derived search metrics"));
1382 PetscFunctionReturn(0);
1392 char captured[8192];
1395 PetscFunctionBeginUser;
1397 PetscCall(VecSet(user->
P, 7.0));
1398 PetscCall(DMGlobalToLocalBegin(user->
da, user->
P, INSERT_VALUES, user->
lP));
1399 PetscCall(DMGlobalToLocalEnd(user->
da, user->
P, INSERT_VALUES, user->
lP));
1401 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Field Anatomy Log: [P]") != NULL),
1402 "LOG_FIELD_ANATOMY should print the requested field name"));
1403 PetscCall(
PicurvAssertBool((PetscBool)(strstr(captured,
"Layout: [Cell-Centered]") != NULL),
1404 "LOG_FIELD_ANATOMY should report the inferred data layout"));
1406 PetscFunctionReturn(0);
1415 char tmpdir[PETSC_MAX_PATH_LEN];
1416 char timestep_path[PETSC_MAX_PATH_LEN];
1417 char summary_path[PETSC_MAX_PATH_LEN];
1418 static char selected_name[] =
"FlowSolver";
1419 char *selected_funcs[] = {selected_name};
1421 PetscFunctionBeginUser;
1422 PetscCall(PetscMemzero(&simCtx,
sizeof(simCtx)));
1424 PetscCall(PetscStrncpy(simCtx.
log_dir, tmpdir,
sizeof(simCtx.
log_dir)));
1449 PetscCall(PetscSNPrintf(summary_path,
sizeof(summary_path),
"%s/ProfilingSummary_Solver.log", simCtx.
log_dir));
1453 "profiling timestep summary should contain the CSV header"));
1455 "profiling timestep summary should log selected functions"));
1457 "profiling timestep summary should omit unselected functions in selected mode"));
1459 "profiling final summary should include its table banner"));
1461 "profiling final summary should include selected functions"));
1463 "profiling final summary should include total-time entries for unselected functions"));
1465 PetscFunctionReturn(0);
1474 char tmpdir[PETSC_MAX_PATH_LEN];
1475 char memory_path[PETSC_MAX_PATH_LEN];
1477 PetscFunctionBeginUser;
1478 PetscCall(PetscMemzero(&simCtx,
sizeof(simCtx)));
1480 PetscCall(PetscStrncpy(simCtx.
log_dir, tmpdir,
sizeof(simCtx.
log_dir)));
1488 PetscCall(PetscMemorySetGetMaximumUsage());
1495 "runtime memory log should contain readable column labels"));
1497 "runtime memory log should contain a step row"));
1499 "runtime memory log should contain a final row"));
1501 "runtime memory log should record final reason"));
1508 "disabled runtime memory log should not create a file"));
1510 PetscFunctionReturn(0);
1520 char tmpdir[PETSC_MAX_PATH_LEN];
1521 char log_path[PETSC_MAX_PATH_LEN];
1522 PetscBool exists = PETSC_FALSE;
1524 PetscFunctionBeginUser;
1527 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1532 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/solution_convergence.log", simCtx->
log_dir));
1533 PetscCall(PetscTestFile(log_path,
'r', &exists));
1535 "disabled solution convergence must not create a log"));
1539 PetscFunctionReturn(0);
1549 char tmpdir[PETSC_MAX_PATH_LEN];
1550 char log_path[PETSC_MAX_PATH_LEN];
1556 PetscReal ***nvert = NULL;
1557 PetscReal mean_speed = NAN;
1558 PetscReal mean_speed_ref = NAN;
1559 PetscReal mean_speed_abs = NAN;
1560 PetscReal p_abs = NAN;
1561 PetscInt has_reference_1 = 0;
1562 PetscInt has_reference_2 = 0;
1564 PetscFunctionBeginUser;
1567 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1576 PetscCall(DMDAVecGetArray(user->
da, user->
Nvert, &nvert));
1577 nvert[1][1][1] = 1.0;
1578 PetscCall(DMDAVecRestoreArray(user->
da, user->
Nvert, &nvert));
1579 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
1580 ucat[1][1][1].
x = 999.0;
1581 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
1590 PetscCall(DMDAVecGetArray(user->
fda, user->
Ucat, &ucat));
1591 ucat[1][1][1].
x = 999.0;
1592 PetscCall(DMDAVecRestoreArray(user->
fda, user->
Ucat, &ucat));
1595 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/solution_convergence.log", simCtx->
log_dir));
1607 PetscCall(
PicurvAssertBool((PetscBool)(strcmp(mode,
"steady_deterministic") == 0),
1608 "steady solution convergence row should record the mode name"));
1610 "the first steady solution-convergence row should be a warmup row"));
1612 "steady solution convergence should compare against the previous solved step"));
1614 "steady solution convergence should mask IBM-marked solid cells"));
1616 "steady solution convergence should report the previous-step mean speed"));
1618 "steady solution convergence should report mean-speed drift"));
1620 "steady solution convergence pressure drift should be gauge invariant"));
1624 PetscFunctionReturn(0);
1634 char tmpdir[PETSC_MAX_PATH_LEN];
1635 char log_path[PETSC_MAX_PATH_LEN];
1640 PetscInt has_reference_1 = 0;
1641 PetscInt has_reference_2 = 0;
1642 PetscInt has_reference_3 = 0;
1643 PetscInt phase_step_1 = -1;
1644 PetscInt phase_step_2 = -1;
1645 PetscInt phase_step_3 = -1;
1646 PetscReal mean_speed_ref_2 = NAN;
1647 PetscReal mean_speed_abs_2 = NAN;
1649 PetscFunctionBeginUser;
1652 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1676 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/solution_convergence.log", simCtx->
log_dir));
1677 PetscCall(
PicurvAssertFileExists(log_path,
"periodic solution-convergence logging should write the log"));
1691 "the first periodic phase visit should log warmup without a reference"));
1693 "the first periodic cycle should fully warm up before comparisons begin"));
1695 "the repeated periodic phase visit should compare against the stored reference"));
1697 "periodic solution convergence should log the current phase slot"));
1699 "periodic solution convergence should log distinct phase slots during warmup"));
1701 "periodic solution convergence should reuse the same phase slot on later cycles"));
1703 "periodic solution convergence should report the stored phase-aligned reference"));
1705 "periodic solution convergence should report the phase-aligned drift"));
1709 PetscFunctionReturn(0);
1719 char tmpdir[PETSC_MAX_PATH_LEN];
1720 char log_path[PETSC_MAX_PATH_LEN];
1723 PetscReal mean_speed_window = NAN;
1724 PetscReal mean_speed_window_prev = NAN;
1725 PetscReal mean_speed_window_abs = NAN;
1726 PetscReal mean_speed_rms_window = NAN;
1727 PetscReal mean_ke_window = NAN;
1728 PetscReal mean_ke_window_prev = NAN;
1729 PetscReal mean_ke_window_abs = NAN;
1730 PetscReal mean_ke_rms_window_abs = NAN;
1731 PetscInt has_reference = 0;
1732 const PetscReal speed_samples[4] = {1.0, 3.0, 2.0, 4.0};
1734 PetscFunctionBeginUser;
1737 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1742 for (PetscInt step = 0; step < 4; ++step) {
1743 simCtx->
step = step + 1;
1744 simCtx->
ti = 0.1 * (PetscReal)(step + 1);
1749 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/solution_convergence.log", simCtx->
log_dir));
1750 PetscCall(
PicurvAssertFileExists(log_path,
"statistical solution-convergence logging should write the log"));
1754 PetscCall(
LogGetColumnReal(header, row,
"spd_win_prev", &mean_speed_window_prev));
1755 PetscCall(
LogGetColumnReal(header, row,
"spd_win_abs", &mean_speed_window_abs));
1756 PetscCall(
LogGetColumnReal(header, row,
"spd_rms_win", &mean_speed_rms_window));
1758 PetscCall(
LogGetColumnReal(header, row,
"ke_win_prev", &mean_ke_window_prev));
1759 PetscCall(
LogGetColumnReal(header, row,
"ke_win_abs", &mean_ke_window_abs));
1760 PetscCall(
LogGetColumnReal(header, row,
"ke_rms_abs", &mean_ke_rms_window_abs));
1763 "statistical solution convergence should emit adjacent-window drift once two windows exist"));
1765 "statistical solution convergence should report the current mean-speed window"));
1767 "statistical solution convergence should report the previous mean-speed window"));
1769 "statistical solution convergence should report mean-speed window drift"));
1771 "statistical solution convergence should report current mean-speed RMS"));
1773 "statistical solution convergence should report the current kinetic-energy window"));
1775 "statistical solution convergence should report the previous kinetic-energy window"));
1777 "statistical solution convergence should report kinetic-energy window drift"));
1779 "statistical solution convergence should report RMS kinetic-energy drift"));
1783 PetscFunctionReturn(0);
1798 const PetscInt km_values[] = {8, 16};
1799 const PetscInt n_cases = (PetscInt)(
sizeof(km_values) /
sizeof(km_values[0]));
1801 PetscFunctionBeginUser;
1802 for (PetscInt t = 0; t < n_cases; ++t) {
1803 const PetscInt km = km_values[t];
1806 char tmpdir[PETSC_MAX_PATH_LEN];
1807 char log_path[PETSC_MAX_PATH_LEN];
1810 PetscReal mean_ke = NAN;
1813 PETSC_TRUE, PETSC_TRUE, PETSC_TRUE));
1815 PetscCall(PetscStrncpy(simCtx->
log_dir, tmpdir,
sizeof(simCtx->
log_dir)));
1827 PetscCall(PetscSNPrintf(log_path,
sizeof(log_path),
"%s/solution_convergence.log", simCtx->
log_dir));
1831 "periodic sinusoidal field mean KE must equal 0.5025 (no duplicate endpoint)"));
1836 PetscFunctionReturn(0);
1845 PetscErrorCode ierr;
1872 (void)setenv(
"LOG_LEVEL",
"INFO", 1);
1874 ierr = PetscInitialize(&argc, &argv, NULL,
"PICurv logging tests");
1879 ierr =
PicurvRunTests(
"unit-logging", cases,
sizeof(cases) /
sizeof(cases[0]));
1887 ierr = PetscFinalize();
PetscErrorCode SetAnalyticalSolutionForParticles(Vec tempVec, SimCtx *simCtx)
Applies the analytical solution to particle velocity vector.
PetscErrorCode CalculateParticleCountPerCell(UserCtx *user)
Counts particles in each cell of the DMDA 'da' and stores the result in user->ParticleCount.
FieldId
Compile-time identity for a catalogued Eulerian field.
PetscErrorCode ScatterAllParticleFieldsToEulerFields(UserCtx *user)
Scatters a predefined set of particle fields to their corresponding Eulerian fields.
Logging utilities and macros for PETSc-based applications.
PetscErrorCode LOG_FIELD_MIN_MAX(UserCtx *user, FieldId field_id)
Computes and logs the local and global min/max values of a 3-component vector field.
void set_allowed_functions(const char **functionList, int count)
Sets the global list of function names that are allowed to log.
PetscErrorCode LOG_PARTICLE_METRICS(UserCtx *user, const char *stageName)
Logs particle swarm metrics, adapting its behavior based on a boolean flag in SimCtx.
PetscBool ShouldEmitPeriodicStatisticsConsoleSnapshot(const struct SimCtx *simCtx, PetscInt completed_step)
Reports whether a completed step falls on the console snapshot cadence.
const char * BCHandlerTypeToString(BCHandlerType handler_type)
Converts a BCHandlerType enum to its string representation.
PetscBool is_function_allowed(const char *functionName)
Checks if a given function is in the allow-list.
PetscErrorCode DualMonitorDestroy(void **ctx)
Destroys the DualMonitorCtx.
PetscBool IsStatisticsConsoleSnapshotEnabled(const struct SimCtx *simCtx)
Reports whether the periodic statistics console snapshot is enabled.
PetscErrorCode LOG_INTERPOLATION_ERROR(UserCtx *user)
Logs the interpolation error between the analytical and computed solutions.
PetscBool ShouldEmitPeriodicParticleConsoleSnapshot(const SimCtx *simCtx, PetscInt completed_step)
Returns whether a particle console snapshot should be emitted for the.
const char * BCFaceToString(BCFace face)
Returns the canonical log token for a boundary-face enum value.
PetscErrorCode FreeAllowedFunctions(char **funcs, PetscInt n)
Free an array previously returned by LoadAllowedFunctionsFromFile().
PetscBool IsParticleConsoleSnapshotEnabled(const SimCtx *simCtx)
Returns whether periodic particle console snapshots are enabled.
PetscErrorCode print_log_level(void)
Prints the current logging level to the console.
PetscErrorCode EmitParticleConsoleSnapshot(UserCtx *user, SimCtx *simCtx, PetscInt step)
Emits one particle console snapshot into the main solver log.
PetscErrorCode ProfilingFinalize(SimCtx *simCtx)
the profiling excercise and build a profiling summary which is then printed to a log file.
PetscErrorCode LoadAllowedFunctionsFromFile(const char filename[], char ***funcsOut, PetscInt *nOut)
Load function names from a text file.
PetscErrorCode EmitStatisticsConsoleSnapshot(UserCtx *user, const struct SimCtx *simCtx, PetscInt step)
Emits one console snapshot of window progress.
void PrintProgressBar(PetscInt step, PetscInt startStep, PetscInt totalSteps, PetscReal currentTime)
Prints a progress bar to the console.
PetscErrorCode RuntimeMemoryLogSample(SimCtx *simCtx, PetscInt step, const char *event, const char *reason)
Append a reduced runtime memory sample to the configured memory log.
LogLevel get_log_level()
Retrieves the current logging level from the environment variable LOG_LEVEL.
PetscErrorCode ProfilingLogTimestepSummary(SimCtx *simCtx, PetscInt step)
Logs the performance summary for the current timestep and resets timers.
PetscErrorCode LOG_FACE_DISTANCES(PetscReal *d)
Prints the signed distances to each face of the cell.
PetscErrorCode LOG_PARTICLE_FIELDS(UserCtx *user, PetscInt printInterval)
Prints particle fields in a table that automatically adjusts its column widths.
void _ProfilingEnd(const char *func_name)
Internal profiling hook invoked by PROFILE_FUNCTION_END.
const char * BCTypeToString(BCType type)
Returns the canonical log token for a boundary mathematical type.
PetscErrorCode CalculateAdvancedParticleMetrics(UserCtx *user)
Computes advanced particle statistics and stores them in SimCtx.
const char * ParticleLocationStatusToString(ParticleLocationStatus level)
A function that outputs the name of the current level in the ParticleLocation enum.
PetscErrorCode LOG_SCATTER_METRICS(UserCtx *user)
Logs particle-to-grid scatter verification metrics for the prescribed scalar truth path.
PetscErrorCode LOG_SOLUTION_CONVERGENCE(SimCtx *simCtx)
Logs physical solution-convergence metrics once per completed timestep.
PetscErrorCode LOG_CONTINUITY_METRICS(UserCtx *user)
Logs continuity metrics for a single block to a file.
PetscErrorCode LOG_FIELD_ANATOMY(UserCtx *user, FieldId field_id, const char *stage_name)
Logs the anatomy of a specified field at key boundary locations, respecting the solver's specific gri...
PetscErrorCode LOG_SEARCH_METRICS(UserCtx *user)
Writes compact runtime search metrics to CSV and optionally to console.
const char * InitialConditionModeToString(InitialConditionMode mode)
Convert an initial-condition mode to a string representation.
PetscErrorCode ProfilingInitialize(SimCtx *simCtx)
Initializes the custom profiling system using configuration from SimCtx.
@ LOG_INFO
Informational messages about program execution.
const char * LESModelToString(LESModelType LESFlag)
Returns the canonical log token for an LES model selector.
PetscErrorCode LOG_CELL_VERTICES(const Cell *cell, PetscMPIInt rank)
Prints the coordinates of a cell's vertices.
PetscErrorCode ProfilingResetTimestepCounters(void)
Resets per-timestep profiling counters for the next solver step.
const char * MomentumSolverTypeToString(MomentumSolverType SolverFlag)
Returns the canonical log token for a momentum-solver selector.
const char * ParticleInitializationToString(ParticleInitializationType ParticleInitialization)
Returns the canonical log token for a particle-initialization mode.
void _ProfilingStart(const char *func_name)
Internal profiling hook invoked by PROFILE_FUNCTION_BEGIN.
Context for a dual-purpose KSP monitor.
PetscErrorCode InitializeSolutionConvergenceState(SimCtx *simCtx)
Allocates any runtime storage required by solution-convergence logging.
Per-window PETSc accumulator storage and pointwise application.
Vec weight
Per-point valid weight.
Vec * mean
One per field, matching that field's layout.
PetscErrorCode PicurvWindowStorageCreate(UserCtx *user, const PicurvWindowDefinition *definition, PicurvWindowStorage *storage)
Allocates the accumulator state one window owns on one block.
Vec * m2
One per field; NULL when no second moment was requested.
Vec count
Per-point accepted sample count.
PetscErrorCode PicurvWindowStorageDestroy(PicurvWindowStorage *storage)
Releases accumulator state previously created for one window.
Independent accumulator state for one window on one block.
Window lifecycle, scheduling, and weighting for the field-statistics pipeline.
PicurvWindowFieldRequest fields[16]
PetscErrorCode FieldStatisticsUpdateWindows(struct SimCtx *simCtx, PetscInt step, PetscReal time)
Offers one completed state to every configured window.
PetscReal end_time
Requested end; ignored when bounded is false.
PicurvCadenceKind cadence_kind
PetscInt step_cadence
Used when cadence_kind is step; must be positive.
#define PICURV_WINDOW_NAME_LENGTH
Maximum stored length of a window name, including the terminator.
PetscErrorCode PicurvWindowInit(PicurvWindow *window, const PicurvWindowDefinition *definition)
Validates a definition and initializes a window to the pending state.
PetscBool want_second
Also keep the centered second moment.
PetscBool bounded
False for an open-ended window.
PetscInt field_id
Catalogued Eulerian field identity.
@ PICURV_WEIGHTING_SAMPLE
Equal weight per accepted state.
@ PICURV_CADENCE_STEP
Every n completed steps from activation.
PicurvWeighting weighting
Runtime state of one window.
The scientifically immutable definition of one window.
static PetscErrorCode TestParticleConsoleSnapshotCadence(void)
Tests periodic particle console snapshot enablement and cadence.
static PetscErrorCode SetUniformScalarField(UserCtx *user, Vec field, PetscReal value)
Fills one scalar field with a uniform constant state.
static PetscErrorCode TestFieldAnatomyLogging(void)
Tests stdout field-anatomy logging on the corrected production-like DM fixture.
static PetscErrorCode LogGetColumnText(const char *header, const char *row, const char *column_name, char *value, size_t value_len)
Extracts one pipe-delimited log cell as text by header name.
static PetscErrorCode InvokeParticleConsoleSnapshot(UserCtx *user, SimCtx *simCtx, void *ctx)
Adapts EmitParticleConsoleSnapshot() to the generic stdout-capture callback shape.
static PetscErrorCode TestStatisticsConsoleSnapshotLogging(void)
Tests statistics console snapshot content, and its silence when disabled.
static PetscErrorCode TestParticleConsoleSnapshotLogging(void)
Tests console snapshot logging against the public periodic-snapshot helper.
static PetscErrorCode CsvGetColumnText(const char *header, const char *row, const char *column_name, char *value, size_t value_len)
Extracts one CSV cell as text by header name.
static PetscErrorCode TestStatisticsConsoleSnapshotCadence(void)
Tests periodic statistics console snapshot enablement and cadence.
static PetscErrorCode CaptureLoggingOutput(UserCtx *user, SimCtx *simCtx, CapturedLoggingFn fn, void *ctx, char *captured, size_t captured_len)
Captures stdout emitted by one logging helper into a temporary file-backed buffer.
static PetscErrorCode ReadCsvHeaderAndRow(const char *path, PetscInt row_index, char *header, size_t header_len, char *row, size_t row_len)
Reads the CSV header and the requested 1-based data row from a text file.
static PetscErrorCode TestGetLogLevelFromEnvironment(void)
Tests that log level selection honors the environment variable.
int main(int argc, char **argv)
Runs the unit-logging PETSc test binary.
static PetscErrorCode TestLoggingContinuityAndFieldDiagnostics(void)
Tests continuity, min/max, and anatomy logging helpers on minimal runtime fixtures.
static PetscErrorCode AssertVecsIdentical(Vec expected, Vec actual, const char *context)
Asserts two accumulator vectors are bit-identical.
static PetscErrorCode TestPeriodicSinusoidalMeanKE(void)
Regression test: volume-averaged mean KE of a sinusoidal field in a fully periodic domain.
PetscErrorCode(* CapturedLoggingFn)(UserCtx *user, SimCtx *simCtx, void *ctx)
static PetscErrorCode TestSolutionConvergenceStatisticalLogging(void)
Tests statistical solution-convergence sliding-window metrics.
static PetscErrorCode SetUniformPressure(UserCtx *user, PetscReal value)
Sets the pressure field to a uniform value across the owned range.
static PetscErrorCode AssertFileNotContains(const char *path, const char *needle, const char *context)
Asserts that one text file does not contain an excluded substring.
static PetscErrorCode AccumulateAtConsoleCadence(SimCtx *simCtx, UserCtx *user, PicurvWindow *window, PicurvWindowStorage *storage, const PicurvWindowDefinition *definition, PetscInt console_frequency)
Drives one accumulation run at a given console cadence, in the runloop's order.
static PicurvWindowDefinition LoggingStatisticsDefinition(void)
Builds the window definition backing this suite's statistics console fixture.
static PetscErrorCode InvokeFieldAnatomyLog(UserCtx *user, SimCtx *simCtx, void *ctx)
Adapts LOG_FIELD_ANATOMY() to the generic stdout-capture callback shape.
static PetscErrorCode TestSolutionConvergencePeriodicLogging(void)
Tests periodic solution-convergence warmup and phase-aligned reference reuse.
static PetscErrorCode TestStringConversionHelpers(void)
Tests string-conversion helpers for configured enums and unknown values.
static PetscErrorCode SetSinusoidalVZField(UserCtx *user, Vec field, PetscReal v_amp, PetscReal w_const)
Fills one Cartesian velocity field with u=0, v=v_amp*sin(2π*k/km), w=w_const.
static PetscErrorCode TestLoggingFileParsingAndFormattingHelpers(void)
Tests logging-side file parsing, helper formatting, and progress utilities.
static PetscErrorCode TestAllowedFunctionsFilter(void)
Tests the function allow-list filter used by the logging layer.
static PetscErrorCode TestInterpolationErrorLogging(void)
Tests interpolation-error logging against an analytically matched particle field.
static PetscErrorCode SeedLoggingParticleFixture(SimCtx **simCtx_out, UserCtx **user_out)
Creates a small particle-bearing runtime fixture used by logging tests.
static PetscErrorCode TestScatterMetricsLogging(void)
Tests file-backed scatter metrics logging against a fully occupied constant field.
static PetscErrorCode TestParticleMetricsLogging(void)
Tests file-backed particle metrics logging after derived metrics are computed.
static PetscErrorCode CsvGetColumnReal(const char *header, const char *row, const char *column_name, PetscReal *value_out)
Extracts one CSV cell as a real by header name.
static PetscErrorCode TestSearchMetricsLogging(void)
Tests file-backed search metrics logging with the compact CSV contract.
static PetscErrorCode InvokeStatisticsConsoleSnapshot(UserCtx *user, SimCtx *simCtx, void *ctx)
Adapts EmitStatisticsConsoleSnapshot() to the generic stdout-capture callback shape.
static PetscErrorCode TestParticleFieldTableLogging(void)
Tests stdout particle-table logging on a production-like swarm fixture.
static PetscErrorCode CsvFindColumnIndex(const char *header, const char *column_name, PetscInt *index_out)
Returns the zero-based column index for one CSV header field.
static PetscErrorCode TestSolutionConvergenceDisabled(void)
Verifies -solution_convergence_enabled=false suppresses the convergence writer.
static PetscErrorCode LogFindColumnIndex(const char *header, const char *column_name, PetscInt *index_out)
Returns the zero-based column index for one pipe-delimited header field.
static PetscErrorCode SetUniformVelocityField(UserCtx *user, Vec field, PetscReal ux, PetscReal uy, PetscReal uz)
Fills one Cartesian velocity field with a uniform constant state.
static PetscErrorCode ReadLogHeaderAndRow(const char *path, PetscInt row_index, char *header, size_t header_len, char *row, size_t row_len)
Reads the column header and the requested 1-based data row from a pipe-delimited solution-convergence...
static PetscErrorCode AssertFileContains(const char *path, const char *needle, const char *context)
Asserts that one text file contains a required substring.
static PetscErrorCode CsvGetColumnInt(const char *header, const char *row, const char *column_name, PetscInt *value_out)
Extracts one CSV cell as an integer by header name.
static PetscErrorCode LogGetColumnReal(const char *header, const char *row, const char *column_name, PetscReal *value_out)
Extracts one pipe-delimited log cell as a real by header name.
static PetscErrorCode TestSolutionConvergenceSteadyLogging(void)
Tests steady solution-convergence log output, IBM masking, and gauge-invariant pressure drift.
static PetscErrorCode TestProfilingLifecycleHelpers(void)
Tests profiling helper lifecycle logging for timestep and final-summary outputs.
static PetscErrorCode LogGetColumnInt(const char *header, const char *row, const char *column_name, PetscInt *value_out)
Extracts one pipe-delimited log cell as an integer by header name.
static PetscErrorCode TestConsoleCadenceDoesNotChangeAccumulation(void)
Tests that the console cadence has no effect on any accumulated result.
static PetscErrorCode InvokeParticleFieldLog(UserCtx *user, SimCtx *simCtx, void *ctx)
Adapts LOG_PARTICLE_FIELDS() to the generic stdout-capture callback shape.
static PetscErrorCode TestRuntimeMemoryLogHelpers(void)
Tests runtime memory log header, step rows, final rows, and disabled mode.
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 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.
PetscErrorCode PicurvCreateSwarmPair(UserCtx *user, PetscInt nlocal, const char *post_field_name)
Creates matched solver and post-processing swarms for tests.
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 PicurvAssertFileExists(const char *path, const char *context)
Asserts that a filesystem path exists as a readable file.
PetscErrorCode PicurvAssertIntEqual(PetscInt expected, PetscInt actual, const char *context)
Asserts that two integer values are equal.
PetscErrorCode PicurvPopulateIdentityMetrics(UserCtx *user)
Populates identity metric vectors on the minimal grid fixture.
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.
PetscInt fieldStatisticsWindowCount
PetscBool profilingFinalSummary
char profilingTimestepFile[PETSC_MAX_PATH_LEN]
PetscInt64 searchLocatedCount
PetscInt statisticsConsoleOutputFreq
PetscInt64 searchLostCount
@ PARTICLE_INIT_VOLUME
Random volumetric distribution across the domain.
PetscBool runtimeMemoryLogEnabled
Enable the rank-reduced runtime memory log.
PetscInt64 boundaryClampCount
PetscInt particlesLostLastStep
PetscInt64 traversalStepsSum
@ BC_HANDLER_PERIODIC_DRIVEN_CONSTANT_FLUX
PetscInt64 searchPopulation
PetscBool solutionConvergenceEnabled
char runtimeMemoryLogFile[PETSC_MAX_PATH_LEN]
File name written under log_dir.
PetscBool runtimeMemoryLogStarted
True after rank 0 writes the log header.
char profilingTimestepMode[32]
PetscBool fieldStatisticsEnabled
@ MOMENTUM_SOLVER_EXPLICIT_RK
@ MOMENTUM_SOLVER_NEWTON_KRYLOV
PetscInt solutionConvergencePeriodSteps
PetscInt64 bboxGuessFallbackCount
VerificationScalarConfig verificationScalar
PetscInt64 bboxGuessSuccessCount
struct PicurvWindow * fieldStatisticsWindows
char log_dir[PETSC_MAX_PATH_LEN]
PetscInt64 maxParticlePassDepth
PetscInt64 maxTraversalSteps
PetscBool runtimeMemoryLogHasPrevious
True after the first process-memory sample.
char ** profilingSelectedFuncs
PetscInt solutionConvergenceWindowSteps
PetscInt particlesLostCumulative
PetscInt nProfilingSelectedFuncs
PetscInt particlesMigratedLastStep
struct PicurvWindowStorage * fieldStatisticsStorage
char AnalyticalSolutionType[PETSC_MAX_PATH_LEN]
InitialConditionMode
Selects the algorithm used to populate a fresh Eulerian velocity field.
@ IC_MODE_CONSTANT_CARTESIAN
@ IC_MODE_CONSTANT_STREAMWISE
PetscInt particleConsoleOutputFreq
SearchMetricsState searchMetrics
PetscInt migrationPassesLastStep
@ SOLUTION_CONVERGENCE_PERIODIC_DETERMINISTIC
@ SOLUTION_CONVERGENCE_STATISTICAL_STEADY
@ SOLUTION_CONVERGENCE_STEADY_DETERMINISTIC
SolutionConvergenceMode solutionConvergenceMode
PetscInt64 searchAttempts
PetscInt64 maxTraversalFailCount
Cmpnts vertices[8]
Coordinates of the eight vertices of the cell.
PetscReal particleLoadImbalance
Defines the vertices of a single hexahedral grid cell.
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.