-
Notifications
You must be signed in to change notification settings - Fork 910
Updating SA-Diffusion term Discretisation #2508
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updating SA-Diffusion term Discretisation #2508
Conversation
|
Hi, Thank you for addressing this issue. The source diffusion (please note that this term is known as source diffusion rather than cross production source term) term in the SU2 code is currently treated explicitly (which may limit the CFL numbers of the SA model). One simple way is to adopt the original proposal by Spalart & Allmaras, as you pointed out. One should verify that the total sum of the coefficients is positive, resulting in a diffusive flux. Fortunately, using simple arithmetic averaging will result in a diffusive flux (as opposed to an anti-diffusive flux). Please bear in mind that for the negative version, some care should be taken: please follow the recent paper by Boris Diskin et al: Revised USM3D-ME Implementation of Negative Version of Spalart–Allmaras Turbulence Model AIAA Journal in press. |
|
Thank you for the paper. When extending this to the negative model I applied a very similar approach to the standard version. But after reading the positivity and boundedness discussion, I don't think the implementation I had would not be very stable. Rather the formulation the authors provide increases the stability, particularly due to the treatment of the |
|
To make sure that the formulation is stable, it should be verified that the sum of the coefficients is always positive. Diskin addressed this issue in great detail. |
| SolverSpecificNumerics(iPoint, jPoint); | ||
|
|
||
| /*--- Compute residual, and Jacobians ---*/ | ||
| numerics->ComputeResidual(Residual_i, Residual_j, Jacobian_ii, Jacobian_ij, Jacobian_ji, Jacobian_jj, const_cast<CConfig*>(config)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Residual_* and Jacobian_* are solver members that are not allocated for most solvers anymore.
The easiest way to test this is to compute the residual twice with the previous version you had, but swaping the order of ij (and flipping the normal direction) when setting things in the numerics class.
| numerics->ComputeResidual(Residual_i, Residual_j, Jacobian_ii, Jacobian_ij, Jacobian_ji, Jacobian_jj, const_cast<CConfig*>(config)); | |
| numerics->ComputeResidual(Residual_i, Residual_j, Jacobian_ii, Jacobian_ij, Jacobian_ji, Jacobian_jj, const_cast<CConfig*>(config)); |
| auto jPoint = geometry->edges->GetNode(iEdge, 1); | ||
|
|
||
| /*--- Helper function to compute the flux ---*/ | ||
| auto ComputeFlux = [&](unsigned long point_i, unsigned long point_j, const su2double* normal) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of lambdas 👍
| auto residual_ij = ComputeFlux(iPoint, jPoint, normal); | ||
| auto residual_ji = ComputeFlux(jPoint, iPoint, flipped_normal); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type returned by the numerics is just a view into their internal state. It's not a copy of the residuals and Jacobians.
So if you make these call back to back like this residual_ij and residual_ji will be pointing to the same values because they come from the same numerics class.
You need to compute one, use it to update the residual, then compute the other.
| EdgeFluxes.SubtractBlock(iEdge, residual_ij); | ||
| EdgeFluxes.AddBlock(iEdge, residual_ji); | ||
| if (implicit) { | ||
| Jacobian.UpdateBlocksSub(iEdge, residual_ij.jacobian_i, residual_ij.jacobian_j); | ||
| Jacobian.UpdateBlocks(iEdge, residual_ji.jacobian_i, residual_ji.jacobian_j); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't worry about this reducer stuff for now, it's not used unless you use OpenMP.
| LinSysRes.SubtractBlock(iPoint, residual_ij); | ||
| LinSysRes.AddBlock(jPoint, residual_ji); | ||
| if (implicit) { | ||
| Jacobian.UpdateBlocksSub(iEdge, iPoint, jPoint, residual_ij.jacobian_i, residual_ij.jacobian_j); | ||
| Jacobian.UpdateBlocks(iEdge, iPoint, jPoint, residual_ji.jacobian_i, residual_ji.jacobian_j); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use only one of the residual results to update the Jacobians for now, it doesn't have to be super exact.
| if (implicit) { | ||
| Jacobian_i[0][0] = (0.5*Proj_Mean_GradScalarVar[0]-nu_ij*proj_vector_ij)/sigma; | ||
| Jacobian_j[0][0] = (0.5*Proj_Mean_GradScalarVar[0]+nu_ij*proj_vector_ij)/sigma; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems too approximate. nu_ij can be much smaller that the term multiplying the projected gradient.
| su2double term_1 = (nu_ij + (1 + cb2)*nu_tilde_ij*fn_i) * Proj_Mean_GradScalarVar[0]/sigma; | ||
| su2double term_2 = cb2*nu_tilde_i*fn_i* Proj_Mean_GradScalarVar[0]/sigma; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can try limiting the overall diffusion coefficient (the value that multiplies Proj_Mean_GradScalarVar) to be positive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I exported the coefficient values to a buffer file, many of them appear to be negative. using the exact Jacobians reduces the negativeness, but still causes the solver to diverge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its the projected gradient (Proj_Mean_GradScalarVar[0]) which is causing the negativity. The diffusion coefficient always is positive but the product is not, which is causing the solver to diverge.
|
Sorry, but I didn't follow through with all your work. Did you also modify the Jacobian due to the new additional diffusion flux (actually diffusion and anti-diffusion fluxes)? |
yes, i derived the exact Jacobians for the new discretisation |
|
Do you have a written draft? |
|
for the jacobians? i'll type them up and post them in a moment @YairMO heres the derivation. I've written it in the way it appears in the code. |
pcarruscag
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also need to modify CScalarSolver<VariableType>::SumEdgeFluxes
The convective fluxes still use EdgeFluxes, so one option is to store the difference between ij and ji in the second EdgeFlux vector.
Something like:
for (auto iEdge : geometry->nodes->GetEdges(iPoint)) {
if (iPoint == geometry->edges->GetNode(iEdge, 0)) {
// Flux i-j.
LinSysRes.AddBlock(iPoint, EdgeFluxes.GetBlock(iEdge));
} else {
// Flux j-i.
LinSysRes.SubtractBlock(iPoint, EdgeFluxes.GetBlock(iEdge));
if (EdgeFluxesDiff.size() > 0) {
LinSysRes.SubtractBlock(iPoint, EdgeFluxesDiff.GetBlock(iEdge));
}
}
}
| CSysVector<su2double> EdgeFluxes_ij; /*!< \brief Flux across each edge ij (non-conservative). */ | ||
| CSysVector<su2double> EdgeFluxes_ji; /*!< \brief Flux across each edge ji (non-conservative). */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need this change on the flow solver, just the scalar and turbulence.
| CSysVector<su2double> EdgeFluxes_ij; /*!< \brief Flux across each edge ij (non-conservative). */ | |
| CSysVector<su2double> EdgeFluxes_ji; /*!< \brief Flux across each edge ji (non-conservative). */ |
| if (config->GetKind_Solver() == MAIN_SOLVER::RANS) { | ||
| for (unsigned long iPoint = 0; iPoint < nPoint; ++iPoint) { | ||
|
|
||
| LinSysRes.SetBlock_Zero(iPoint); | ||
|
|
||
| for (auto iEdge : geometry->nodes->GetEdges(iPoint)) { | ||
| if (iPoint == geometry->edges->GetNode(iEdge,0)) | ||
| LinSysRes.AddBlock(iPoint, EdgeFluxes_ij.GetBlock(iEdge)); | ||
| else | ||
| LinSysRes.SubtractBlock(iPoint, EdgeFluxes_ji.GetBlock(iEdge)); | ||
| } | ||
| } | ||
| } | ||
| else SumEdgeFluxes(geometry); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can also be reverted
| if (ReducerStrategy) { | ||
| EdgeFluxes.Initialize(geometry.GetnEdge(), geometry.GetnEdge(), nVar, nullptr); | ||
| EdgeFluxes_ij.Initialize(geometry.GetnEdge(), geometry.GetnEdge(), nVar, nullptr); //initializing only when solver=rans, has no effect | ||
| EdgeFluxes_ji.Initialize(geometry.GetnEdge(), geometry.GetnEdge(), nVar, nullptr); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
| CSysVector<su2double> EdgeFluxes; /*!< \brief Flux across each edge. */ | ||
| CSysVector<su2double> EdgeFluxes_ij; /*!< \brief Flux across each edge ij (non-conservative). */ | ||
| CSysVector<su2double> EdgeFluxes_ji; /*!< \brief Flux across each edge ji (non-conservative). */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to keep using EdgeFluxes as before and just add another one where needed (instead of 2).
| CSysVector<su2double> EdgeFluxes; /*!< \brief Flux across each edge. */ | |
| CSysVector<su2double> EdgeFluxes_ij; /*!< \brief Flux across each edge ij (non-conservative). */ | |
| CSysVector<su2double> EdgeFluxes_ji; /*!< \brief Flux across each edge ji (non-conservative). */ | |
| CSysVector<su2double> EdgeFluxes; /*!< \brief Flux across each edge. */ | |
| CSysVector<su2double> EdgeFluxes_ij; /*!< \brief Flux across each edge ij (non-conservative). */ |
| if (implicit) { | ||
| Jacobian.UpdateBlocksSub(iEdge, residual_ij.jacobian_i, residual_ij.jacobian_j); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the reducer strategy we need to compromise and use an "average" jacobian, i.e. assuming a conservative flux.
With what you have now the Jacobian is getting doubled
| } | ||
|
|
||
| /*--- Compute fluxes and jacobians j->i ---*/ | ||
| su2double flipped_normal[3]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| su2double flipped_normal[3]; | |
| su2double flipped_normal[MAXNDIM]; |
pcarruscag
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can change the regression.yaml file to use your testcases branch with the updated restarts.
| #include "../scalar/scalar_diffusion.hpp" | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| #include "../scalar/scalar_diffusion.hpp" |
5b56b32
into
su2code:feature_sa_diffusion
Proposed Changes
Give a brief overview of your contribution here in a few sentences.$c_{b2}(\tilde{\nu})^2$ ) as a cross-production source term. By applying the product rule and simplifying, the diffusion term can be written as:

The current discretisation of the diffusion terms in the SA model are not consistent with the recommendation of S-A from their original paper [1]. The current implementation treats the non-linear term (
Then discretising gives:

Note that S-A assume that$\nabla\nu=0$ , which does not hold in compressible cases.
The current changes are only for the Base model, I am extending it to the others. Would appreciate any feedback!
[1]: Spalart, Philippe, and Steven Allmaras. "A one-equation turbulence model for aerodynamic flows." 30th aerospace sciences meeting and exhibit. 1992.
Related Work
Resolve any issues (bug fix or feature request), note any related PRs, or mention interactions with the work of others, if any.
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.
pre-commit run --allto format old commits.