[WIP] Pressure Based Solver - #2812
Conversation
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
Implements the full class structure required for the pressure-based solver in a minimal form. The code compiles and runs but does not yet contain any numerical/physical implementation. Future work will focus on implementing solver logic. Note: In the previous attempts (see related work) there is noticeable code duplication between CIncEuler and CPBIncEuler. The final architecture may be revised depending on how the implementation of CPBIncEuler evolves.
Centered residual is not yet implemented as the old code did not have a working version. Upwind residual is functional but requires cleanup and move to more appropiate file.
Variables are a work in progress and still include some commented out code related to energy and pressure
The current viscous residual of the poisson equation uses an isotropic diffusion coefficient, this should not be the case and should thus be replaced
|
I will have to review the code myself first, it is not ready for review as of yet. |
thijsaalbers
left a comment
There was a problem hiding this comment.
The current version of the solver is ready for review. It is however not yet finished and there is a list of known issues that I am still working through. I have updated the PR description with the current status and known limitations; please read that before reviewing the implementation.
pcarruscag
left a comment
There was a problem hiding this comment.
There are some errors in the implementation, which I made myself when I first implemented SIMPLE, and they are easy to make because certain simplifications look equivalent but are not.
For professional reasons I should discuss this only in terms of my prior work.
Here is an implementation that I think is mostly correct: https://github.com/pcarruscag/Flow-Solver-Experiments/blob/main/lib/flow/src/flow_simple.cpp
It's written in a funny way because it was my first exercise into porting something to GPUs, but follow the operations, and their order, not the code itself.
The main error here is that you do not carry nor correct the face velocities (or face mass flows), nor use them to discretize momentum and scalars.
You correct the nodal velocities (equivalent to cell centers) but that is not the same thing.
I was not nice enough to my future self to document why in my old code, but I'm somewhat confident an AI can explain the nuance.
|
@pcarruscag Thanks for the very usefull explanation of the issue. I think I now more or less fixed it. I also rewrote CPBFluidIteration.cpp, and I think the flow of the algorithm (and the changes I now made to it) should be clear in there. |
| * \param[in] poisson - If preconditioner should use settings defined for the poisson solver. | ||
| */ | ||
| void Initialize(unsigned long npoint, unsigned long npointdomain, unsigned short nvar, unsigned short neqn, | ||
| bool EdgeConnect, CGeometry* geometry, const CConfig* config, bool needTranspPtr = false, | ||
| bool grad_mode = false, bool allow_quant = false); | ||
| bool grad_mode = false, bool allow_quant = false, bool poisson = false); |
There was a problem hiding this comment.
Instead of grad_mode and poisson, we should just pass the enum value that we want to override.
Using a std::optional.
| Enable_Cuda, /*!< \brief Flag for switching GPU computing*/ | ||
| Integrated_HeatFlux; /*!< \brief Flag for heat flux BC whether it deals with integrated values.*/ | ||
| Integrated_HeatFlux, /*!< \brief Flag for heat flux BC whether it deals with integrated values.*/ | ||
| Pressure_based; /*!< \brief FLag to check if we are using a pressure-based system.*/ |
There was a problem hiding this comment.
| Pressure_based; /*!< \brief FLag to check if we are using a pressure-based system.*/ | |
| Pressure_Based; /*!< \brief Flag to check if we are using a pressure-based system.*/ |
| /*! | ||
| * \brief Get the relaxation coefficient of the pressure correction for SIMPLE solver. | ||
| * \return relaxation coefficient of the pressure correction for SIMPLE solver | ||
| */ | ||
| su2double GetRelaxation_Factor_Pressure(void) const { return Relaxation_Factor_Pressure; } | ||
|
|
||
| /*! | ||
| * \brief Get the relaxation coefficient of the momentum correction for SIMPLE solver. | ||
| * \return relaxation coefficient of the momentum correction for SIMPLE solver | ||
| */ | ||
| su2double GetRelaxation_Factor_Momentum(void) const { return Relaxation_Factor_Momentum; } | ||
|
|
||
| /*! | ||
| * \brief Get the coefficient for the removal of the transient term in the poisson solver coefficients. | ||
| * \return coefficient for the removal of the transient term in the poisson solver coefficients. | ||
| */ | ||
| su2double GetTransient_Term_Removal_Factor(void) const { return Transient_Term_Removal_Factor; } | ||
|
|
||
| /*! | ||
| * \brief Verify if there is mixing plane interface specified from config file. | ||
| * \return boolean. | ||
| */ | ||
| bool GetBoolAutomaticRelaxationFactors(void) const { return AutomaticRelaxationFactors; } |
There was a problem hiding this comment.
Bundle the SIMPLE-specific options in a struct like we do for turbulence models and ILU options please, that way there is a singe funtion to return them.
| MESH_DISPLACEMENTS , /*!< \brief Mesh displacements at the interface. */ | ||
| SOLUTION_TIME_N , /*!< \brief Solution at time n. */ | ||
| SOLUTION_TIME_N1 , /*!< \brief Solution at time n-1. */ | ||
| MOM_COEFF , /*!< \brief Momentum coefficient for the pressure-based poisson solver. */ |
There was a problem hiding this comment.
| MOM_COEFF , /*!< \brief Momentum coefficient for the pressure-based poisson solver. */ | |
| MOM_COEFF , /*!< \brief Momentum coefficient for the Rhie-Chow scheme. */ |
| /*--- Dirichlet condition for temperature at far-field (if energy is active). ---*/ | ||
| if (!pressure_based) { | ||
|
|
||
| V_infty[prim_idx.Temperature()] = GetTemperature_Inf(); | ||
| V_infty[prim_idx.Temperature()] = GetTemperature_Inf(); |
There was a problem hiding this comment.
Same comment about just one if with continue in this case. Makes it a lot easier to review what is going on.
| /*--- Mass flux is computed over all edges ---*/ | ||
| for (auto color : EdgeColoring) { | ||
| SU2_OMP_FOR_DYN(nextMultiple(OMP_MIN_SIZE, color.groupSize)) | ||
| for (auto k = 0ul; k < color.size; ++k) { | ||
| auto iEdge = color.indices[k]; |
There was a problem hiding this comment.
You also don't need the coloring here since you write to the edges directly.
Coloring is for writing to the points of the edge.
| } | ||
| /*--- 1. Interpolate the pressure gradient based on node values ---*/ | ||
| for (iDim = 0; iDim < nDim; iDim++) { | ||
| Grad_Avg = 0.5*(nodes->GetGradient_Primitive(iPoint,0,iDim) + nodes->GetGradient_Primitive(jPoint,0,iDim)); |
There was a problem hiding this comment.
use the primitive indices class of the solver instead of hardcoding the index (0) please
| /*--- Rhie Chow interpolation ---*/ | ||
| Coord_i = geometry->nodes->GetCoord(iPoint); | ||
| Coord_j = geometry->nodes->GetCoord(jPoint); | ||
| dist_ij_2 = 0.0; | ||
| for (iDim = 0; iDim < nDim; iDim++) { | ||
| Edge_Vector[iDim] = Coord_j[iDim]-Coord_i[iDim]; | ||
| dist_ij_2 += Edge_Vector[iDim]*Edge_Vector[iDim]; | ||
| } | ||
| /*--- 1. Interpolate the pressure gradient based on node values ---*/ | ||
| for (iDim = 0; iDim < nDim; iDim++) { | ||
| Grad_Avg = 0.5*(nodes->GetGradient_Primitive(iPoint,0,iDim) + nodes->GetGradient_Primitive(jPoint,0,iDim)); | ||
| GradP_in[iDim] = Grad_Avg; | ||
| } | ||
|
|
||
| /*--- 2. Compute pressure gradient at the face ---*/ | ||
| /*--- Eq 15.62 F Moukalled, L Mangani M. Darwish OpenFOAM and uFVM book. ---*/ | ||
| GradP_proj = 0.0; | ||
| for (iDim = 0; iDim < nDim; iDim++) { | ||
| GradP_proj += GradP_in[iDim]*Edge_Vector[iDim]; | ||
| } | ||
| if (dist_ij_2 != 0.0) { | ||
| for (iDim = 0; iDim < nDim; iDim++) { | ||
| GradP_f[iDim] = GradP_in[iDim] - (GradP_proj - (nodes->GetPressure(jPoint) - nodes->GetPressure(iPoint)))*Edge_Vector[iDim]/ dist_ij_2; | ||
| } | ||
| } |
There was a problem hiding this comment.
Can you extract a common helper from the numerics classes? this is similar to the corrected gradient calculation.
|
|
||
| /*--- Velocity corrections ---*/ | ||
| for (iDim = 0; iDim < nDim; ++iDim) { | ||
| nodes->SetSolution(iPoint, iDim + 1, nodes->GetSolution(iPoint,iDim + 1) + alpha_u * velocityCorrection[iPoint][iDim]); |
There was a problem hiding this comment.
This isn't how alpha_u is typically used in SIMPLE
There was a problem hiding this comment.
Thats correct. Only as we currently use only pseudo-time stepping, there is not really a direct method for the user to influence the velocity corrections. It's not entirely necessary either indeed as the user can alter the CFL, which indirectly influences the time step size, which in turn indirectly functions as a form of alpha_u. So it is removable if you prefer, it doesn't matter that much.
We could also add an option to change the time step size based on the value of alpha_u and the jacobian matrix, which is mathematically equivalent to using a steady-state SIMPLE algorithm (with alpha_u as the underrelaxation value). This would however completely side-step the CFL parameter. So would this even be desired?
There was a problem hiding this comment.
If CFL works ok and alpha doesn't do much, I would say remove alpha.
Traditional implementations do not relax the velocity and mass flow corrections directly.
Proposed Changes
The current work (part of GSoC) provides a working version of a pressure-based algorithm for the incompressible flow solver as an alternative to the existing Density-based solver. Below, the reader may find the algorithm which has been implemented, as well as the current progress of the code and challenges. All the way at the bottom one can find performance comparisons between the DB and PB solvers for some test cases.
Algorithm
A lot of versions of pressure-based algorithms exist, and many different versions can be implemented. Here, we opt for versions of the original SIMPLE/PISO algorithm, it is briefly defined here for clarity.
First, the momentum equations are solved, starting from the previous time step's velocity$\vec{u}^{(0)}$, pressure$p^{(0)}$ , and face velocity $\vec{u}_f^{(0)}$ . The resulting momentum is the predicted momentum, here its discretized form is shown, as its coefficients are used in the subsequent equations
The subsequent momentum is not necessarily incompressible, the pressure correction equation can be derived by rewriting it as follows, using a term often called H by A
Note how a simplification is used here where the HbyA term is neglected. Subtracting these two equations yields the first pressure correction equation for$p'$ as
Make note that for the divergence here, we require the face mass fluxes, which are computed using Rhie-Chow interpolation to avoid odd-even decoupling. After the equation is solved, using the pressure correction$p'$ , the pressure and momentum are corrected according to
So far, this is equal to a pseudo-transient version of the SIMPLE algorithm. This algorithm however suffers from a very tight stability condition on the time-step size. Therefore, multiple pressure corrections can be applied, which for two corrections is originally called the PISO algorithm.
The second pressure correction does not neglect the HbyA term, which then results in the equation
And the new correction equations are defined as
Note that HbyA here uses the previous velocity correction and is thus the same quantity as the one used in the second pressure correction equation. Later pressure correction equations follow analogously.
Progress:
Issues
Performance:
The Poisson solver can sometimes struggle a lot due to high Reynolds numbers and fine meshes, and thus require a ridiculous number of iterations to converge reasonably. Possible fixes include adding multigrid support or a DIC preconditioner (far less efficient). Multigrid support is tricky as SU2 currently only considers multigrid for the main (flow) solver and not for auxiliary solvers.
Convergence issues with RANS (SA and SST) on fine meshes with high Reynolds numbers. Tests have shown that cases such as flow over a flat plate converges fine. However, external aerodynamic cases such as the naca0012 RANS test case do not converge well at all. The convergence does slightly improve when we switch out the mesh for a more uniform unstructured mesh without large aspect ratio cells in the wake of the airfoil, although this only slightly helps. The flat plate turbulence test case also uses large aspect ratio cells so this is not the sole issue. The RANS solver also often requires many iterations of the Poisson solver to converge reasonably, this is however not the reason for the lack of convergence.
Periodic boundary conditions have not been implemented/tested at all as of yet.
Any code related to adjoints has not been considered at all either.
Code:
TODO list
Related Work
This work is based on earlier attempts by Nitish Anand (2024) and Akshay Koodly (2021), see feature branches feature_PBFlow_V8 and feature_Pressure_based respectively. Also see PR #2210
PR Checklist
pre-commit run --allto format old commits.Result showcase
Inviscid Hydrofoil
Convergence history of the inviscid flow around a hydrofoil at a 5 degree aoa.
The pressure coefficient along the surface of the hydrofoil at a 5 degree aoa and the corresponding lift coefficients, X-FOIL predicts C_L=0.6.
Lid Driven Cavity
Convergence history of the lid driven cavity problem, note that CFL=60 is the highest stable CFL for the PB solver, whereas the DB solver does not have this CFL related stability issue.
Flatplate RANS
The skin friction coefficient for turbulent flow over a (rough) flat plate with SA.