Solves the momentum equations using dual-time Picard iteration with Jameson RK smoothing.
Local to this translation unit.
743{
744 (void)ibm;
745 (void)fsi;
746
749
750
751 const PetscInt ti = simCtx->
step;
752 const PetscReal dt = simCtx->
dt;
753
754
756 const PetscReal alfa[] = {0.25, 1.0/3.0, 0.5, 1.0};
757 Vec *pRhs;
758
759
760 PetscBool force_restart = PETSC_FALSE;
761
762
764 const PetscReal tol_abs_delta = simCtx->
mom_atol;
765 const PetscReal tol_rtol_delta = simCtx->
mom_rtol;
766
767
768 PetscErrorCode ierr;
769 PetscMPIInt rank;
770 PetscInt istage, pseudo_iter;
771 PetscInt accepted_iter = 0, rejected_iter = 0, recovery_streak = 0;
772 PetscReal ts, te, cput;
773
774
775 PetscReal global_norm_delta = 10.0;
776 PetscReal global_rel_delta = 1.0;
777 PetscReal global_norm_resid = 1.0;
778 PetscReal global_rel_resid = 1.0;
779 PetscReal smoothed_trial_ratio = 1.0;
780
781
782
783
784
785
786
787
788 PetscReal lambda_max = 0.0;
789 PetscReal dtau_min, dtau_max;
790
791
792 PetscReal *delta_sol_norm_init, *delta_sol_norm_prev, *delta_sol_norm_curr, *delta_sol_rel_curr;
793 PetscReal *resid_norm_init, *resid_norm_prev, *resid_norm_curr, *resid_rel_curr;
794 PetscReal *pseudo_dtau;
795 PetscReal *trial_ratio_log;
796 PetscReal last_accepted_resid;
797
798 PetscFunctionBeginUser;
800 ierr = MPI_Comm_rank(PETSC_COMM_WORLD, &rank); CHKERRQ(ierr);
802
803
804 ierr = PetscMalloc2(block_number, &delta_sol_norm_init, block_number, &delta_sol_norm_prev); CHKERRQ(ierr);
805 ierr = PetscMalloc2(block_number, &delta_sol_norm_curr, block_number, &delta_sol_rel_curr); CHKERRQ(ierr);
806 ierr = PetscMalloc2(block_number, &resid_norm_init, block_number, &resid_norm_prev); CHKERRQ(ierr);
807 ierr = PetscMalloc2(block_number, &resid_norm_curr, block_number, &pseudo_dtau); CHKERRQ(ierr);
808 ierr = PetscMalloc1(block_number, &resid_rel_curr); CHKERRQ(ierr);
809 ierr = PetscMalloc1(block_number, &trial_ratio_log); CHKERRQ(ierr);
810 ierr = PetscMalloc1(block_number, &pRhs); CHKERRQ(ierr);
811 last_accepted_resid = 0.0;
812
813 ierr = PetscTime(&ts); CHKERRQ(ierr);
814
815
816
817 if (block_number > 1) {
818
819 }
820
821 for (PetscInt bi = 0; bi < block_number; bi++) {
823
824
825
826
827 ierr = VecDuplicate(user[bi].Ucont, &user[bi].Rhs); CHKERRQ(ierr);
828 ierr = VecDuplicate(user[bi].Rhs, &pRhs[bi]); CHKERRQ(ierr);
829 ierr = VecDuplicate(user[bi].Ucont, &user[bi].dUcont); CHKERRQ(ierr);
830 ierr = VecDuplicate(user[bi].Ucont, &user[bi].pUcont); CHKERRQ(ierr);
831
832
833 ierr = VecCopy(user[bi].Ucont, user[bi].pUcont); CHKERRQ(ierr);
834
835
837
838
839 ierr = VecCopy(user[bi].Rhs, pRhs[bi]); CHKERRQ(ierr);
840
841
842 ierr = VecNorm(user[bi].Rhs, NORM_INFINITY, &resid_norm_init[bi]); CHKERRQ(ierr);
843
844
845 resid_norm_prev[bi] = resid_norm_init[bi];
846 delta_sol_norm_prev[bi] = 1000.0;
847
848
849 LOG_ALLOW(
GLOBAL,
LOG_INFO,
" Block %d | Max RHS = %.6f | initial pseudo-CFL = %.4f .\n", bi, resid_norm_init[bi], cfl);
850 }
851
852
853
854
855
856
857
858
859
860
864
865
866 for (PetscInt bi = 0; bi < block_number; bi++) {
867 pseudo_dtau[bi] = cfl / lambda_max;
868 }
869
870
871
872
873
874 {
875 PetscBool shadow = PETSC_FALSE;
876 ierr = PetscOptionsGetBool(NULL, NULL, "-mom_stability_shadow", &shadow, NULL); CHKERRQ(ierr);
877 if (shadow) {
882
883
885 "Momentum scale [shadow]: legacy_dtau=%.4e new_dtau=%.4e ratio=%.4f limiter=%s | "
886 "lambda_legacy=%.4e lambda_new=%.4e (B=%.4e C=%.4e D=%.4e) | "
887 "lt=%.4e lc=%.4e lv=%.4e | cell=(%d,%d,%d) blk=%d class=%d onesided=%d\n",
888 cfl / lambda_max, cfl / rep.
lambda, rep.
lambda / lambda_max, lim,
892 }
893 }
894
896 "Dual-time solver: lambda_max=%.4e [1/s] dtau_init=%.4e dtau range [%.4e, %.4e] "
897 "CFL range [%.4f, %.4f] rejection_threshold=%.3f (EMA alpha=%.2f) "
898 "growth=%.3f reduction=%.3f max_accepted=%d.\n",
899 lambda_max, cfl / lambda_max, dtau_min, dtau_max,
904 max_pseudo_steps);
905
906
907 pseudo_iter = 0;
908 PetscBool residual_convergence_enabled =
910 PetscBool converged = PETSC_FALSE;
911 PetscBool last_trial_nonfinite = PETSC_FALSE;
912
913
914 const PetscInt max_total_attempts = max_pseudo_steps * 3;
915 while (!converged && accepted_iter < max_pseudo_steps && pseudo_iter < max_total_attempts)
916 {
917 pseudo_iter++;
918 force_restart = PETSC_FALSE;
919
920
921 for (PetscInt bi = 0; bi < block_number; bi++) {
922 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
923 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
924 const char *staggered_fields[] = {"Ucont"};
927 }
928
929 for (PetscInt bi = 0; bi < block_number; bi++) {
930
931
932 for (istage = 0; istage < 4; istage++) {
933
935
936
937
938
939 ierr = VecWAXPY(user[bi].Ucont,
940 pseudo_dtau[bi] * alfa[istage],
941 user[bi].Rhs,
942 user[bi].pUcont); CHKERRQ(ierr);
943
944
945 const char *staggered_fields[] = {"Ucont"};
947
949
950
952
953 }
954
955
956
957
958
959
960 ierr = VecWAXPY(user[bi].dUcont, -1.0, user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
961
962
963 ierr = VecNorm(user[bi].dUcont, NORM_INFINITY, &delta_sol_norm_curr[bi]); CHKERRQ(ierr);
964 ierr = VecNorm(user[bi].Rhs, NORM_INFINITY, &resid_norm_curr[bi]); CHKERRQ(ierr);
965
966
967 if (pseudo_iter == 1) {
968 delta_sol_norm_init[bi] = delta_sol_norm_curr[bi];
969 delta_sol_rel_curr[bi] = 1.0;
970 resid_rel_curr[bi] = 1.0;
971
972 } else {
973 if (delta_sol_norm_init[bi] > 1.0e-10)
974 delta_sol_rel_curr[bi] = delta_sol_norm_curr[bi] / delta_sol_norm_init[bi];
975 else
976 delta_sol_rel_curr[bi] = 0.0;
977
978 if(resid_norm_init[bi] > 1.0e-10)
979 resid_rel_curr[bi] = resid_norm_curr[bi] / resid_norm_init[bi];
980 else
981 resid_rel_curr[bi] = 0.0;
982 }
983
984
985 {
986 const PetscReal resid_floor_log = 1.0e-30;
987 if (resid_norm_prev[bi] > resid_floor_log)
988 trial_ratio_log[bi] = resid_norm_curr[bi] / resid_norm_prev[bi];
989 else if (resid_norm_curr[bi] <= resid_floor_log)
990 trial_ratio_log[bi] = 0.0;
991 else
992 trial_ratio_log[bi] = PETSC_MAX_REAL;
993 }
994 }
995
996
997 global_norm_delta = -1.0e20;
998 global_rel_delta = -1.0e20;
999 global_norm_resid = -1.0e20;
1000 global_rel_resid = -1.0e20;
1001
1002 for (PetscInt bi = 0; bi < block_number; bi++) {
1003 global_norm_delta = PetscMax(delta_sol_norm_curr[bi], global_norm_delta);
1004 global_rel_delta = PetscMax(delta_sol_rel_curr[bi], global_rel_delta);
1005 global_norm_resid = PetscMax(resid_norm_curr[bi], global_norm_resid);
1006 global_rel_resid = PetscMax(resid_rel_curr[bi], global_rel_resid);
1007 }
1008 ierr = PetscTime(&te); CHKERRQ(ierr);
1009 cput = te - ts;
1011 pseudo_iter, global_norm_delta, global_rel_delta, global_rel_resid, cput);
1012
1013
1014 const PetscReal resid_floor = 1.0e-30;
1015 PetscReal global_trial_ratio = 0.0;
1016 PetscBool global_nonfinite = PETSC_FALSE;
1017 for (PetscInt bi = 0; bi < block_number; bi++) {
1018 PetscReal ratio;
1019 if (resid_norm_prev[bi] > resid_floor) {
1020 ratio = resid_norm_curr[bi] / resid_norm_prev[bi];
1021 } else if (resid_norm_curr[bi] <= resid_floor) {
1022 ratio = 0.0;
1023 } else {
1024 ratio = PETSC_MAX_REAL;
1025 }
1026 global_trial_ratio = PetscMax(global_trial_ratio, ratio);
1027 global_nonfinite = (PetscBool)(global_nonfinite ||
1028 PetscIsInfOrNanReal(delta_sol_norm_curr[bi]) ||
1029 PetscIsInfOrNanReal(resid_norm_curr[bi]) ||
1030 PetscIsInfOrNanReal(ratio));
1031 }
1032
1033 PetscReal reduced_trial_ratio;
1034 PetscMPIInt local_nonfinite = global_nonfinite ? 1 : 0;
1035 PetscMPIInt reduced_nonfinite = 0;
1036 ierr = MPI_Allreduce(&global_trial_ratio, &reduced_trial_ratio, 1, MPIU_REAL, MPI_MAX, PETSC_COMM_WORLD); CHKERRQ(ierr);
1037 ierr = MPI_Allreduce(&local_nonfinite, &reduced_nonfinite, 1, MPI_INT, MPI_LOR, PETSC_COMM_WORLD); CHKERRQ(ierr);
1038 global_trial_ratio = reduced_trial_ratio;
1039 global_nonfinite = reduced_nonfinite ? PETSC_TRUE : PETSC_FALSE;
1040 last_trial_nonfinite = global_nonfinite;
1041
1042
1043
1044 if (!global_nonfinite) {
1047 }
1049 " [k=%d] raw_ratio=%.4e smoothed_ratio=%.4e (threshold=%.3f) | "
1050 "|R_prev|=%.6e | |R_curr|=%.6e | CFL=%.6f\n",
1051 pseudo_iter, global_trial_ratio, smoothed_trial_ratio,
1053 resid_norm_prev[0], resid_norm_curr[0], pseudo_dtau[0]);
1054
1056
1057 force_restart = global_nonfinite;
1058 } else {
1059 force_restart = (PetscBool)(global_nonfinite ||
1061 }
1062
1063 if (force_restart) {
1064
1065
1066
1067 PetscReal old_dtau = pseudo_dtau[0];
1069 rejected_iter++;
1070 recovery_streak = 0;
1071 for (PetscInt bi = 0; bi < block_number; bi++) {
1072 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
1073 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
1074 const char *staggered_fields[] = {"Ucont"};
1077 pseudo_dtau[bi] = next_dtau;
1078 }
1080 " Trial %d REJECTED (raw_ratio=%.4e, smoothed=%.4e, nonfinite=%d); "
1081 "dtau %.4e -> %.4e (cfl_eff %.4f -> %.4f)%s\n",
1082 pseudo_iter, global_trial_ratio, smoothed_trial_ratio, (int)global_nonfinite,
1083 old_dtau, next_dtau, old_dtau * lambda_max, next_dtau * lambda_max,
1084 (old_dtau == next_dtau) ? " [AT FLOOR — no dtau reduction]" : "");
1085
1086 if (!rank) {
1087 for (PetscInt bi = 0; bi < block_number; bi++) {
1088 FILE *f;
1089 char filen[PETSC_MAX_PATH_LEN + 128];
1090 ierr = PetscSNPrintf(filen, sizeof(filen),
1091 "%s/Momentum_Solver_DualTime_Picard_Jameson_RK_History_Block_%1d.log",
1092 simCtx->
log_dir, bi); CHKERRQ(ierr);
1094 f = fopen(filen, "w");
1095 else
1096 f = fopen(filen, "a");
1098 PetscFPrintf(PETSC_COMM_WORLD, f,
"# Continuation from step %" PetscInt_FMT
"\n", simCtx->
StartStep);
1099
1100 PetscFPrintf(PETSC_COMM_WORLD, f,
1101 "Step: %d | PseudoIter(k): %d | dtau: %.6e | cfl_eff: %.4f | |dUk|: %le | "
1102 "|dUk|/|dU0|: %le | |Rk|: %le | |Rk|/|R0|: %le | "
1103 "trial_ratio: %le | smoothed_ratio: %le | status: rejected | "
1104 "dtau_after: %.6e | cfl_eff_after: %.4f\n",
1105 (int)ti, (int)pseudo_iter, old_dtau, old_dtau * lambda_max,
1106 delta_sol_norm_curr[bi], delta_sol_rel_curr[bi],
1107 resid_norm_curr[bi], resid_rel_curr[bi],
1108 trial_ratio_log[bi], smoothed_trial_ratio,
1109 next_dtau, next_dtau * lambda_max);
1110 fclose(f);
1111 }
1112 }
1113 if (old_dtau <= dtau_min) {
1114 if (global_nonfinite) {
1115 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_CONV_FAILED,
1116 "Momentum solver produced a non-finite trial at minimum pseudo-CFL.");
1117 }
1118
1119
1121 " Trial %d REJECTED (ratio=%.4e) at minimum dtau (%.4e, cfl_eff=%.4f) "
1122 "with no further reduction possible; breaking retry loop.\n",
1123 pseudo_iter, global_trial_ratio, old_dtau, old_dtau * lambda_max);
1124 break;
1125 }
1126 continue;
1127 }
1128
1129 accepted_iter++;
1130 last_trial_nonfinite = PETSC_FALSE;
1131 last_accepted_resid = resid_norm_curr[0];
1132 for (PetscInt bi = 0; bi < block_number; bi++) {
1133 ierr = VecCopy(user[bi].Ucont, user[bi].pUcont); CHKERRQ(ierr);
1134 ierr = VecCopy(user[bi].Rhs, pRhs[bi]); CHKERRQ(ierr);
1135 resid_norm_prev[bi] = resid_norm_curr[bi];
1136 delta_sol_norm_prev[bi] = delta_sol_norm_curr[bi];
1137 }
1138
1139
1140
1141
1142 PetscReal old_dtau = pseudo_dtau[0];
1143 PetscReal next_dtau = old_dtau;
1144 if (global_trial_ratio < 0.90) {
1145
1147 recovery_streak = 0;
1148 } else if (global_trial_ratio <= 1.0) {
1149
1150 recovery_streak++;
1151 if (recovery_streak >= 3) {
1153 recovery_streak = 0;
1154 }
1155 } else {
1156
1158 recovery_streak = 0;
1159 }
1160 next_dtau = PetscMin(next_dtau, dtau_max);
1161 next_dtau = PetscMax(next_dtau, dtau_min);
1162 for (PetscInt bi = 0; bi < block_number; bi++) pseudo_dtau[bi] = next_dtau;
1163
1165 " Trial %d ACCEPTED (raw_ratio=%.4e, smoothed=%.4e); |dU|=%.6e | "
1166 "dtau %.4e -> %.4e (cfl_eff %.4f -> %.4f)\n",
1167 pseudo_iter, global_trial_ratio, smoothed_trial_ratio, global_norm_delta,
1168 old_dtau, next_dtau, old_dtau * lambda_max, next_dtau * lambda_max);
1169
1170
1171 if (!rank) {
1172 for (PetscInt bi = 0; bi < block_number; bi++) {
1173 FILE *f;
1174 char filen[PETSC_MAX_PATH_LEN + 128];
1175 ierr = PetscSNPrintf(filen, sizeof(filen),
1176 "%s/Momentum_Solver_DualTime_Picard_Jameson_RK_History_Block_%1d.log",
1177 simCtx->
log_dir, bi); CHKERRQ(ierr);
1179 f = fopen(filen, "w");
1180 else
1181 f = fopen(filen, "a");
1183 PetscFPrintf(PETSC_COMM_WORLD, f,
"# Continuation from step %" PetscInt_FMT
"\n", simCtx->
StartStep);
1184
1185 PetscFPrintf(PETSC_COMM_WORLD, f,
1186 "Step: %d | PseudoIter(k): %d | dtau: %.6e | cfl_eff: %.4f | |dUk|: %le | "
1187 "|dUk|/|dU0|: %le | |Rk|: %le | |Rk|/|R0|: %le | "
1188 "trial_ratio: %le | smoothed_ratio: %le | status: accepted | "
1189 "dtau_after: %.6e | cfl_eff_after: %.4f\n",
1190 (int)ti, (int)pseudo_iter, old_dtau, old_dtau * lambda_max,
1191 delta_sol_norm_curr[bi], delta_sol_rel_curr[bi],
1192 resid_norm_curr[bi], resid_rel_curr[bi],
1193 trial_ratio_log[bi], smoothed_trial_ratio,
1194 next_dtau, next_dtau * lambda_max);
1195 fclose(f);
1196 }
1197 }
1198
1199 PetscBool update_pass;
1200 PetscBool residual_pass = PETSC_TRUE;
1201 if (residual_convergence_enabled) {
1202 update_pass = (PetscBool)(global_norm_delta <= tol_abs_delta && global_rel_delta <= tol_rtol_delta);
1203 residual_pass = (PetscBool)(
1204 (simCtx->
mom_resid_atol > 0.0 && global_norm_resid <= simCtx->mom_resid_atol) ||
1206 } else {
1207 update_pass = (PetscBool)(global_norm_delta <= tol_abs_delta && global_rel_delta <= tol_rtol_delta);
1208 }
1209 converged = (PetscBool)(update_pass && residual_pass);
1210
1211 if (block_number > 1) {
1212
1213 }
1214 }
1215
1216 if (last_trial_nonfinite) {
1217 SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_CONV_FAILED,
1218 "Momentum solver exhausted its attempt limit while recovering from a non-finite trial.");
1219 }
1220
1221
1222
1223
1224
1225 PetscReal next_dtau_start = pseudo_dtau[0];
1226 PetscReal next_cfl_warmstart = next_dtau_start * lambda_max;
1227
1228 if (!rank) {
1230 " Step %d finished: dtau=%.4e cfl_eff=%.4f lambda_max=%.4e [1/s]. "
1231 "Next step warm-starts at cfl=%.4f.\n",
1232 simCtx->
step, next_dtau_start, next_cfl_warmstart, lambda_max, next_cfl_warmstart);
1233 }
1234
1235
1238
1239
1240
1241
1242 if (accepted_iter == 0) {
1243 for (PetscInt bi = 0; bi < block_number; bi++) {
1244 ierr = VecCopy(user[bi].pUcont, user[bi].Ucont); CHKERRQ(ierr);
1245 ierr = VecCopy(pRhs[bi], user[bi].Rhs); CHKERRQ(ierr);
1246 }
1247 }
1248 for (PetscInt bi = 0; bi < block_number; bi++) {
1249 const char *staggered_fields[] = {"Ucont"};
1252 }
1253
1255 if (!converged) {
1256 PetscPrintf(PETSC_COMM_WORLD,
1257 "[WARNING] Momentum solver step %d: reached %d total attempts (%d accepted, %d rejected) "
1258 "without convergence; continuing from last accepted finite state.\n",
1259 (int)ti, pseudo_iter, accepted_iter, rejected_iter);
1260 }
1261 if (accepted_iter == 0) {
1262 PetscPrintf(PETSC_COMM_WORLD,
1263 "[WARNING] Momentum solver step %d: no pseudo-time trials were accepted; "
1264 "retaining physical-step entry state.\n", (int)ti);
1265 }
1267 "Momentum solver finished: %d attempts (%d accepted, %d rejected) of %d max accepted / %d hard cap, "
1268 "converged=%s, last accepted |R|=%.6e, last accepted |dU|=%.6e, "
1269 "next_dtau=%.4e (cfl=%.4f).\n",
1270 pseudo_iter, accepted_iter, rejected_iter, max_pseudo_steps, max_total_attempts,
1271 converged ? "yes" : "no",
1272 (accepted_iter > 0) ? last_accepted_resid : resid_norm_init[0],
1273 (accepted_iter > 0) ? delta_sol_norm_prev[0] : 0.0,
1274 next_dtau_start, next_cfl_warmstart);
1275
1276
1277 for (PetscInt bi = 0; bi < block_number; bi++) {
1278 ierr = VecDestroy(&user[bi].Rhs); CHKERRQ(ierr);
1279 ierr = VecDestroy(&user[bi].dUcont); CHKERRQ(ierr);
1280 ierr = VecDestroy(&user[bi].pUcont); CHKERRQ(ierr);
1281 ierr = VecDestroy(&pRhs[bi]); CHKERRQ(ierr);
1282 }
1283 ierr = PetscFree(pRhs); CHKERRQ(ierr);
1284
1285 ierr = PetscFree2(delta_sol_norm_init, delta_sol_norm_prev);CHKERRQ(ierr);
1286 ierr = PetscFree2(delta_sol_norm_curr, delta_sol_rel_curr);CHKERRQ(ierr);
1287 ierr = PetscFree2(resid_norm_init, resid_norm_prev);CHKERRQ(ierr);
1288 ierr = PetscFree2(resid_norm_curr, pseudo_dtau); CHKERRQ(ierr);
1289 ierr = PetscFree(resid_rel_curr); CHKERRQ(ierr);
1290 ierr = PetscFree(trial_ratio_log); CHKERRQ(ierr);
1291
1293 PetscFunctionReturn(0);
1294}
@ LOG_TRACE
Very fine-grained tracing information for in-depth debugging.
@ LOG_WARNING
Non-critical issues that warrant attention.
static PetscErrorCode ComputeGlobalSpectralRadiusEstimate(UserCtx *user, PetscInt block_number, PetscReal dt, PetscReal *lambda_max_out)
Compute a conservative global pseudo-time spectral radius estimate.
PetscErrorCode ComputeTotalResidual(UserCtx *user)
Shared implementation of ComputeTotalResidual().
PetscErrorCode ComputeMomentumStabilityEstimate(UserCtx *user, PetscInt block_number, PetscReal dt, MomStabCandidate candidate, MomStabilityReport *rep)
Practical conservative momentum pseudo-time stability estimate (shadow).
Diagnostic report produced by ComputeMomentumStabilityEstimate().
PetscReal mom_last_lambda_max
PetscReal pseudo_cfl_reduction_factor
PetscBool mom_last_converged
PetscBool no_pseudo_cfl_backtrack
char log_dir[PETSC_MAX_PATH_LEN]
PetscReal mom_dt_jameson_residual_norm_noise_allowance_factor
PetscReal pseudo_cfl_growth_factor
PetscInt mom_max_pseudo_steps
PetscReal mom_ratio_ema_alpha