Skip to content

Pass the edge length, not its square, to the coarse NEMO viscous Jacobian - #2883

Open
bellonarts wants to merge 3 commits into
su2code:developfrom
babybluechips:fix_nemo_coarse_viscous_distance
Open

Pass the edge length, not its square, to the coarse NEMO viscous Jacobian#2883
bellonarts wants to merge 3 commits into
su2code:developfrom
babybluechips:fix_nemo_coarse_viscous_distance

Conversation

@bellonarts

Copy link
Copy Markdown

Proposed Changes

CAvgGrad_NEMO::ComputeResidual (SU2_CFD/src/numerics/NEMO/NEMO_diffusion.cpp) computes the edge vector between the two nodes and then assigns

dist_ij = GeometryToolbox::SquaredNorm(nDim, dist_ij_2);

before passing dist_ij to GetViscousProjJacs, which expects the distance, not its square. The corrected-gradient sibling in the same file already takes the square root (dist_ij = sqrt(dist_ij_2);). This PR applies the same square root in CAvgGrad_NEMO, a one-line change. The residual is untouched; only the analytic viscous Jacobian changes, by a factor of the edge length.

Where the defect is reached. CDriver.cpp instantiates CAvgGrad_NEMO for the interior viscous term (visc_term) on every multigrid level above the finest, and for the boundary viscous term (visc_bound_term) on every level including MESH_0. On MESH_0 the visc_bound_term numerics is consumed by only three boundary routines: CNEMOEulerSolver::BC_Far_Field, CNEMOEulerSolver::BC_Supersonic_Inlet and the inherited CFVMFlowSolverBase::BC_Fluid_Interface. The NEMO wall routines (BC_HeatFlux*, BC_Isothermal*, BC_Smoluchowski_Maxwell) assemble their own fluxes and never call it, and the viscous calls in BC_Inlet and BC_Outlet are commented out. So the defect reaches every NEMO Navier-Stokes run that uses multigrid, a far-field or supersonic-inlet marker, or a fluid interface, through the viscous Jacobian of those terms; a single-grid run whose markers are only walls, outlets and symmetry planes is untouched. The residual equations are unchanged; the corrected implicit operator changes the convergence path.

A unit test is added to UnitTests/SU2_CFD/numerics/CNumerics_tests.cpp: "NEMO coarse viscous Jacobian uses Euclidean edge length". It introduces a small NEMOViscousFixture (a 2-D AIR-5 edge from (0,0) to (3,4) with unit normal (0.6, 0.8), laminar viscosity 2, and a linear velocity gradient along the edge) and compares a central finite difference of the projected momentum flux against the analytic jacobian_i and jacobian_j returned by CAvgGrad_NEMO. The momentum block of GetViscousProjJacs is mu / d (theta I + n n^T / 3) with theta = |n|^2, so on this edge the analytic directional Jacobian must be -8/15 on the i side and +8/15 on the j side to 1e-12, and match the finite difference to 1e-8. Stock develop passes the squared length 25 in place of the distance 5 and returns 8/75, so the test fails there and passes with this change. The fixture, its helpers and the test case are commented in the source.

Regression effect (iteration 10, x86-64 Linux build of develop at 07aa46b). All three affected public cases use a MARKER_FAR boundary, which is how they meet the corrected Jacobian on a single grid. On the viscous cone (visc_cone, serial and parallel) and the super-catalytic wedge (super_cat, parallel) the columns that move are rms[Rho_2], rms[Rho_3] and rms[Rho_4], the three species that are identically zero in those cases (values near -20.5, the machine-zero floor); their physical columns match the same pod's stock run to the printed digits. The visc_cone hunks also carry shifts of at most 4e-6 in physical columns (serial rms[Rho_0] -5.215230 to -5.215233, CD 0.093206 to 0.093207; parallel -5.215234 to -5.215238, rms[RhoE] 1.262701 to 1.262697). Those are pod-versus-CI build noise, present in the pod's stock run as well, not an effect of this change; since SU2's regression harness compares the printed digits exactly, the final digits for these vectors are the ones this PR's CI prints. The ionized cylinder (ion_gy, parallel) is different: it has no identically-zero species, so its physical residual columns rms[Rho_0..5] and rms[RhoE] move by up to 1.05e-2 (for example rms[Rho_1] -4.165562 to -4.156741 and rms[Rho_2] -4.702662 to -4.692113), which is the expected footprint of a changed implicit operator on a converging case; its force, heat-flux and lift columns are unchanged. The expected vectors are rebaselined accordingly for visc_cone (serial and parallel), super_cat (parallel) and ion_gy (parallel). Every other NEMO regression case is unchanged. The final x86 digits are for this PR's CI to confirm, and the aarch64 vectors are left untouched for a maintainer since that workflow runs only on push to develop.

This source change is independently reviewable, but its visc_cone, super_cat, and ion_gy regression lines overlap the energy-under-relaxation and corrected-viscous-Jacobian PRs. These PRs should merge sequentially; whichever lands later must rebase and refresh the overlapping exact vectors from that PR's CI run.

Related Work

The NEMOViscousFixture added here is shared with a follow-up PR on the corrected-gradient NEMO viscous Jacobian (CAvgGradCorrected_NEMO, distinct left block and subtractive edge assembly), which will add only its own test case to the fixture; the PR number will be added here once it is opened. This change is also the prerequisite for any NEMO multigrid work, since the coarse-level interior viscous Jacobian is built exclusively by CAvgGrad_NEMO.

PR Checklist

Put an X by all that apply. You can fill this out after submitting the PR. If you have any questions, don't hesitate to ask! We want to help. These are a guide for you to know what the reviewers will be looking for in your contribution.

  • I am submitting my contribution to the develop branch.
  • My contribution generates no new compiler warnings (try with --warnlevel=3 when using meson).
  • My contribution is commented and consistent with SU2 style (https://su2code.github.io/docs_v7/Style-Guide/).
  • I used the pre-commit hook to prevent dirty commits and used pre-commit run --all to format old commits.
  • I have added a test case that demonstrates my contribution, if necessary.
  • I have updated appropriate documentation (Tutorials, Docs Page, config_template.cpp), if necessary.

…bian

CAvgGrad_NEMO::ComputeResidual assigned the squared norm of the edge
vector to dist_ij and passed it to GetViscousProjJacs, which expects the
distance. Take the square root, as CAvgGradCorrected_NEMO already does.
Only the analytic viscous Jacobian changes; the residual is untouched.

CDriver.cpp uses CAvgGrad_NEMO for the interior viscous term on every
coarse multigrid level and for the boundary viscous term on every level
including MESH_0, where it is consumed only by BC_Far_Field,
BC_Supersonic_Inlet and the inherited BC_Fluid_Interface (the NEMO wall
routines build their own fluxes). The defect therefore reaches every NEMO
Navier-Stokes run that uses multigrid, a far-field or supersonic-inlet
marker, or a fluid interface.

Add a commented finite-difference unit test on a 2-D AIR-5 edge
(NEMOViscousFixture) that fails on develop (8/75) and passes with this
change (8/15), and rebaseline the regression vectors that move as a
consequence: the machine-zero species columns of visc_cone (serial and
parallel) and super_cat (parallel), and the physical residual columns
rms[Rho_0..5] and rms[RhoE] of ion_gy (parallel), which shift by up to
1.05e-2 because that case has no identically-zero species.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@pcarruscag pcarruscag left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a good find, but just to be sure. Have you run two cases to convergence to further verify that this makes things better?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want tests that duplicate a ton of code to prove something that is trivial to prove correct by inspection. Make this substantially simpler or revert.

su2double dist_ij_2[MAXNDIM] = {0.0};
GeometryToolbox::Distance(nDim, Coord_j, Coord_i, dist_ij_2);
dist_ij = GeometryToolbox::SquaredNorm(nDim, dist_ij_2);
dist_ij = sqrt(GeometryToolbox::SquaredNorm(nDim, dist_ij_2));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a norm function

bellonarts and others added 2 commits September 5, 2026 18:44
Use GeometryToolbox::Norm directly and remove the 179-line NEMO fixture/test at maintainer request. The affected public NEMO regression cases retain path coverage.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Update the serial and two-rank viscous-cone aarch64 expectations from GitHub Actions run 33998938091 at exact pre-update head ab76f7f. Artifact digest: sha256:279e65a52098a2829127441d352dd4d88d0979ae7fdb8870a9c779d076a537e6. No x86 expectation or solver source changes.
bellonarts added a commit to babybluechips/SU2 that referenced this pull request Sep 6, 2026
Reseal PRs su2code#2883-su2code#2885 at heads b8504d8, 2925b4a, and 8bfba2f, switch their NEMO jobs from harvest to strict vector verification, and apply the public regression drivers' 1e-5 default when tol remains unset. S2/S3 stay harvest-only because they preserve the pre-vector-update cumulative stages.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants