Skip to content

Commit 6fd44de

Browse files
rois1995claude
andcommitted
Use wave strengths that match the 3D P matrix in L2Roe, LMRoe and the explicit general-gas Roe
In 3D the first three columns of the P matrix combine the entropy wave, weighted by n_j, with the shear waves. Their strengths are n_j (Delta rho - Delta p / c^2) + (Delta u x n)_j; the entropy jump was used unweighted in the first one only, so a contact or shear layer on a face not aligned with x got a wrong flux. The standard Roe scheme already projects the jump with P^-1 since #299. Unit test roe_contact_tests.cpp: a contact and a shear layer give the upwind flux in 2D and 3D. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEL91DW5WPbPwgFtCvHga6
1 parent 343a1a0 commit 6fd44de

3 files changed

Lines changed: 144 additions & 9 deletions

File tree

‎SU2_CFD/src/numerics/flow/convection/roe.cpp‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,12 @@ void CUpwL2Roe_Flow::FinalizeResidual(su2double *val_residual, su2double **val_J
334334
delta_wave[2] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
335335
delta_wave[3] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
336336
} else {
337-
delta_wave[0] = delta_rho - delta_p/RoeSoundSpeed2;
338-
delta_wave[1] = (UnitNormal[0]*delta_vel[2]-UnitNormal[2]*delta_vel[0])*zeta;
339-
delta_wave[2] = (UnitNormal[1]*delta_vel[0]-UnitNormal[0]*delta_vel[1])*zeta;
337+
/*--- The first three columns of the 3D P matrix combine the entropy wave (weighted by the normal) with the
338+
shear waves, so their strengths are n_j (Delta rho - Delta p / c^2) + (Delta u x n)_j. ---*/
339+
const su2double delta_entropy = delta_rho - delta_p/RoeSoundSpeed2;
340+
delta_wave[0] = UnitNormal[0]*delta_entropy + (delta_vel[1]*UnitNormal[2]-delta_vel[2]*UnitNormal[1])*zeta;
341+
delta_wave[1] = UnitNormal[1]*delta_entropy + (delta_vel[2]*UnitNormal[0]-delta_vel[0]*UnitNormal[2])*zeta;
342+
delta_wave[2] = UnitNormal[2]*delta_entropy + (delta_vel[0]*UnitNormal[1]-delta_vel[1]*UnitNormal[0])*zeta;
340343
delta_wave[3] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
341344
delta_wave[4] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
342345
}
@@ -407,9 +410,12 @@ void CUpwLMRoe_Flow::FinalizeResidual(su2double *val_residual, su2double **val_J
407410
delta_wave[2] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
408411
delta_wave[3] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
409412
} else {
410-
delta_wave[0] = delta_rho - delta_p/RoeSoundSpeed2;
411-
delta_wave[1] = (UnitNormal[0]*delta_vel[2]-UnitNormal[2]*delta_vel[0]);
412-
delta_wave[2] = (UnitNormal[1]*delta_vel[0]-UnitNormal[0]*delta_vel[1]);
413+
/*--- The first three columns of the 3D P matrix combine the entropy wave (weighted by the normal) with the
414+
shear waves, so their strengths are n_j (Delta rho - Delta p / c^2) + (Delta u x n)_j. ---*/
415+
const su2double delta_entropy = delta_rho - delta_p/RoeSoundSpeed2;
416+
delta_wave[0] = UnitNormal[0]*delta_entropy + (delta_vel[1]*UnitNormal[2]-delta_vel[2]*UnitNormal[1]);
417+
delta_wave[1] = UnitNormal[1]*delta_entropy + (delta_vel[2]*UnitNormal[0]-delta_vel[0]*UnitNormal[2]);
418+
delta_wave[2] = UnitNormal[2]*delta_entropy + (delta_vel[0]*UnitNormal[1]-delta_vel[1]*UnitNormal[0]);
413419
delta_wave[3] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
414420
delta_wave[4] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
415421
}
@@ -901,9 +907,12 @@ CNumerics::ResidualType<> CUpwGeneralRoe_Flow::ComputeResidual(const CConfig* co
901907
delta_wave[2] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
902908
delta_wave[3] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
903909
} else {
904-
delta_wave[0] = delta_rho - delta_p/(RoeSoundSpeed*RoeSoundSpeed);
905-
delta_wave[1] = UnitNormal[0]*delta_vel[2]-UnitNormal[2]*delta_vel[0];
906-
delta_wave[2] = UnitNormal[1]*delta_vel[0]-UnitNormal[0]*delta_vel[1];
910+
/*--- The first three columns of the 3D P matrix combine the entropy wave (weighted by the normal) with the
911+
shear waves, so their strengths are n_j (Delta rho - Delta p / c^2) + (Delta u x n)_j. ---*/
912+
const su2double delta_entropy = delta_rho - delta_p/(RoeSoundSpeed*RoeSoundSpeed);
913+
delta_wave[0] = UnitNormal[0]*delta_entropy + delta_vel[1]*UnitNormal[2]-delta_vel[2]*UnitNormal[1];
914+
delta_wave[1] = UnitNormal[1]*delta_entropy + delta_vel[2]*UnitNormal[0]-delta_vel[0]*UnitNormal[2];
915+
delta_wave[2] = UnitNormal[2]*delta_entropy + delta_vel[0]*UnitNormal[1]-delta_vel[1]*UnitNormal[0];
907916
delta_wave[3] = proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
908917
delta_wave[4] = -proj_delta_vel + delta_p/(RoeDensity*RoeSoundSpeed);
909918
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*!
2+
* \file roe_contact_tests.cpp
3+
* \brief Roe-type schemes must resolve a contact discontinuity and a shear layer exactly (upwind flux), in 2D
4+
* and 3D, on faces not aligned with the axes.
5+
* \version 8.5.0 "Harrier"
6+
*
7+
* SU2 Project Website: https://su2code.github.io
8+
*
9+
* The SU2 Project is maintained by the SU2 Foundation
10+
* (http://su2foundation.org)
11+
*
12+
* Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md)
13+
*
14+
* SU2 is free software; you can redistribute it and/or
15+
* modify it under the terms of the GNU Lesser General Public
16+
* License as published by the Free Software Foundation; either
17+
* version 2.1 of the License, or (at your option) any later version.
18+
*
19+
* SU2 is distributed in the hope that it will be useful,
20+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22+
* Lesser General Public License for more details.
23+
*
24+
* You should have received a copy of the GNU Lesser General Public
25+
* License along with SU2. If not, see <http://www.gnu.org/licenses/>.
26+
*/
27+
28+
#include "catch.hpp"
29+
#include <cmath>
30+
#include <memory>
31+
#include <sstream>
32+
#include "../../../SU2_CFD/include/numerics/flow/convection/roe.hpp"
33+
34+
namespace {
35+
36+
constexpr su2double gamma = 1.4;
37+
38+
std::unique_ptr<CConfig> MakeConfig(bool implicit) {
39+
std::stringstream options;
40+
options << "SOLVER= EULER\nCONV_NUM_METHOD_FLOW= ROE\nENTROPY_FIX_COEFF= 0.0\nROE_KAPPA= 0.5\n";
41+
options << "TIME_DISCRE_FLOW= " << (implicit ? "EULER_IMPLICIT" : "EULER_EXPLICIT") << "\n";
42+
return std::make_unique<CConfig>(options, SU2_COMPONENT::SU2_CFD, false);
43+
}
44+
45+
/*--- Primitive (T, u, v, [w], p, rho, h, c) and secondary (dp/drho_e, dp/de_rho) variables of an ideal gas. ---*/
46+
void State(unsigned short nDim, su2double rho, const su2double* vel, su2double p, su2double* V, su2double* S) {
47+
su2double q2 = 0.0;
48+
for (unsigned short iDim = 0; iDim < nDim; iDim++) {
49+
V[iDim + 1] = vel[iDim];
50+
q2 += vel[iDim] * vel[iDim];
51+
}
52+
V[0] = p / (rho * 287.058);
53+
V[nDim + 1] = p;
54+
V[nDim + 2] = rho;
55+
V[nDim + 3] = gamma / (gamma - 1) * p / rho + 0.5 * q2;
56+
V[nDim + 4] = sqrt(gamma * p / rho);
57+
S[0] = p / rho;
58+
S[1] = (gamma - 1) * rho;
59+
}
60+
61+
template <class Scheme>
62+
std::unique_ptr<CNumerics> MakeScheme(unsigned short nDim, const CConfig* config) {
63+
return std::make_unique<Scheme>(nDim, nDim + 2, config);
64+
}
65+
template <>
66+
std::unique_ptr<CNumerics> MakeScheme<CUpwRoe_Flow>(unsigned short nDim, const CConfig* config) {
67+
return std::make_unique<CUpwRoe_Flow>(nDim, nDim + 2, config, false);
68+
}
69+
70+
/*--- Two states with the same pressure and normal velocity, different density and (with shear) tangential
71+
velocity: a contact discontinuity, whose exact (upwind) flux is the flux of the upstream state. ---*/
72+
template <class Scheme>
73+
void CheckContact(const char* name, unsigned short nDim, const su2double* vel, bool implicit, bool shear) {
74+
auto config = MakeConfig(implicit);
75+
auto numerics = MakeScheme<Scheme>(nDim, config.get());
76+
77+
const su2double p = 1.0, rho_i = 1.4, rho_j = 0.7;
78+
const su2double n2[2] = {0.6, 0.8}, n3[3] = {2.0 / 7, 3.0 / 7, 6.0 / 7};
79+
const su2double* n = (nDim == 2) ? n2 : n3;
80+
const su2double t2[2] = {0.16, -0.12}, t3[3] = {0.15, -0.1, 0.0}; // tangential velocity jumps
81+
const su2double* t = (nDim == 2) ? t2 : t3;
82+
su2double vel_j[3] = {0.0};
83+
for (unsigned short iDim = 0; iDim < nDim; iDim++) vel_j[iDim] = vel[iDim] + (shear ? t[iDim] : 0.0);
84+
85+
su2double Vi[10] = {0.0}, Vj[10] = {0.0}, Si[2], Sj[2];
86+
State(nDim, rho_i, vel, p, Vi, Si);
87+
State(nDim, rho_j, vel_j, p, Vj, Sj);
88+
numerics->SetPrimitive(Vi, Vj);
89+
numerics->SetSecondary(Si, Sj);
90+
numerics->SetNormal(n);
91+
const auto res = numerics->ComputeResidual(config.get());
92+
93+
su2double un = 0.0;
94+
for (unsigned short iDim = 0; iDim < nDim; iDim++) un += vel[iDim] * n[iDim];
95+
const su2double rho_up = (un >= 0.0) ? rho_i : rho_j;
96+
const su2double* V_up = (un >= 0.0) ? Vi : Vj;
97+
98+
CAPTURE(name, nDim, un, implicit, shear);
99+
CHECK(res.residual[0] == Approx(rho_up * un).margin(1e-12));
100+
for (unsigned short iDim = 0; iDim < nDim; iDim++)
101+
CHECK(res.residual[iDim + 1] == Approx(rho_up * V_up[iDim + 1] * un + p * n[iDim]).margin(1e-12));
102+
CHECK(res.residual[nDim + 1] == Approx(rho_up * V_up[nDim + 3] * un).margin(1e-12));
103+
}
104+
105+
template <class Scheme>
106+
void CheckContacts(const char* name, bool implicit = true, bool shear = true) {
107+
const su2double zero[3] = {0.0, 0.0, 0.0}, moving[3] = {0.3, -0.1, 0.2}, reverse[3] = {-0.3, 0.1, -0.2};
108+
for (const unsigned short nDim : {2, 3}) {
109+
for (const auto* vel : {zero, moving, reverse}) {
110+
CheckContact<Scheme>(name, nDim, vel, implicit, false);
111+
if (shear) CheckContact<Scheme>(name, nDim, vel, implicit, true);
112+
}
113+
}
114+
}
115+
116+
} // namespace
117+
118+
TEST_CASE("Roe schemes resolve a contact and a shear layer exactly", "[Roe]") {
119+
CheckContacts<CUpwRoe_Flow>("Roe");
120+
CheckContacts<CUpwGeneralRoe_Flow>("general Roe", true);
121+
CheckContacts<CUpwGeneralRoe_Flow>("general Roe", false);
122+
CheckContacts<CUpwLMRoe_Flow>("LMRoe");
123+
/*--- L2Roe scales the tangential velocity jump at low Mach number (not upwind by design). ---*/
124+
CheckContacts<CUpwL2Roe_Flow>("L2Roe", true, false);
125+
}

‎UnitTests/meson.build‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ su2_cfd_tests = files(['Common/CConfig_tests.cpp',
1515
'Common/containers/CLookupTable_tests.cpp',
1616
'Common/toolboxes/multilayer_perceptron/CLookUp_ANN_tests.cpp',
1717
'SU2_CFD/numerics/CNumerics_tests.cpp',
18+
'SU2_CFD/numerics/roe_contact_tests.cpp',
1819
'SU2_CFD/fluid/CFluidModel_tests.cpp',
1920
'SU2_CFD/gradients.cpp',
2021
'SU2_CFD/solvers/CNEMOEulerSolver_tests.cpp',

0 commit comments

Comments
 (0)