Time-accurate discrete adjoint gradients for unsteady problems with grid movement - #766
Conversation
| GridVel = new su2double[nDim]; | ||
| GridVel_Old = new su2double[nDim]; | ||
| GridVel_n = new su2double[nDim]; | ||
| GridVel_n1 = new su2double[nDim]; |
There was a problem hiding this comment.
Do we always need this arrays or just in case of the discrete adjoint ?
There was a problem hiding this comment.
Wrapped the allocation it in a Discrete Adjoint conditional.
There was a problem hiding this comment.
To give some background why: In the reverse sweep (say, with dual time stepping 2nd order) always the n-2 timestep is loaded from disk into memory. In that process, Coordinates and GridVels are stored. So compared to the corresponding primal timestep the adjoint had data from 2 timesteps before (...thinking in primal time) if these additional containers and corresponding data pushing wouldn't be there.
I thought it is more convenient than recomputing as all the data is already there in the restart files (and it was faster to code). I might also be beneficial for cases where GridVel is not a fixed function like in rigid motion but depends on the problem itself -> then it can be simply read from the restart file.
| Restart_Flow = false; | ||
|
|
||
| if (GetGrid_Movement()) { | ||
| SU2_MPI::Error("Dynamic mesh movement currently not supported for the discrete adjoint solver.", CURRENT_FUNCTION); |
There was a problem hiding this comment.
I guess it only works for rigid movement at the moment then, am I correct ?
There was a problem hiding this comment.
Correct. Now checks Kind_GridMovement != RIGID_MOTION and throws an error.
There was a problem hiding this comment.
Rigid motion cases have been used for the verification at the moment but GCL is included to handle changing volumes of a mesh in the primal solvers. Therefore the changes in this request should capture both rigid motion and deforming mesh problems for discrete adjoints as the grid velocities are now captured correctly. Please advise if I'm missing to account for something else here? If anyone can point me to a good single zone mesh deformation test case that already exists for the primal solver, I can investigate the discrete adjoint explicitly for a deforming mesh problem as well.
There was a problem hiding this comment.
As of now, deformable meshes are mostly used in FSI (where the coupling terms should be accounted for) but outside of that, there are not many methods to run deformable, single-zone test cases available. I guess something could be done using SURFACE_MOVEMENT = DEFORMING and a pitching/plunging airfoil, but I don't believe the adjoint will work out of the box, as the Grid Velocities are recomputed several times in one round of deformation.
However, with the capabilities in #760, I believe it should be relatively easy to set up a case with time-dependent boundary displacements.
| } | ||
|
|
||
| inline void CPoint::SetGridVel_Old(su2double *val_gridvel) { | ||
| for (unsigned short iDim = 0; iDim < nDim; iDim++) |
There was a problem hiding this comment.
Please change these tabs to spaces.
There was a problem hiding this comment.
Yep, went over all 'Files changed' and fixed all indentation mismatches I saw.
Btw: In e.g. dual_grid_structure.hpp, most indentations are done using tabs and there are a lot of trailing whitespaces. Is it useful to change it in the whole file? Would be work of a few minutes but the latest git blame would be kindof misleading. I also noticed (which is probably) @WallyMaier in a branch cleanup just doing that cosmetic stuff. This is only a style(-guide) question.
There was a problem hiding this comment.
Since we are currently restructuring the files anyway, we should do that clean up when creating/moving new files. This way we avoid duplicate conflicts.
There was a problem hiding this comment.
Yep, please wait on the larger white space cleanup until we divide dual_grid_structure.hpp into files for each subclass.
There was a problem hiding this comment.
Hi all, I did start to create a clean up branch, referencing the the style guide. I can continue with it (and skip the dual grid). But I think with new version coming in, we should clean up files that are going to be restructured.
economon
left a comment
There was a problem hiding this comment.
LGTM. Thanks @cvencro @TobiKattmann for getting this working (and finding/fixing a couple of bugs in the process).
| unsigned short iMesh; | ||
| int Direct_Iter; | ||
| bool heat = config[val_iZone]->GetWeakly_Coupled_Heat(); | ||
| bool grid_movement_bool = config[val_iZone]->GetGrid_Movement(); |
There was a problem hiding this comment.
Can you please switch 'grid_movement_bool' to 'grid_movement' to keep with the convention throughout the rest of the code? Eventually, we could use something more expressive for the name like 'gridIsMoving' (find and replace everywhere), but for now, consistency is enough
There was a problem hiding this comment.
Yep, but I would actually prefer bool gridIsMoving as grid_movement is used as a larger container for CVolumetricMovement ***grid_movement.
rsanfer
left a comment
There was a problem hiding this comment.
Hi @cvencro, @TobiKattmann ,
thank you so much for this PR, which addresses a certainly interesting capability to compute adjoints on moving meshes. However, I have a question and would like to open a discussion about the generalization of the feature.
I have seen that this PR allocates a lot of memory for GridVel_Old, GridVel_n, and GridVel_n1, and additionally Coord_Old. I see how this can work for rigid grid movement, as the Grid Velocities are then imposed (so, they are independent variables) and the grid is not really moving physically.
However, for deforming cases we might run into problems, as in that case the Grid Velocities are magnitudes that are derived from the grid positions. In fact, at time n, only the grid velocity u enters the dual-time stepping equation ALE-NS equation, but the dependency of u on previous time steps n, n-1 links directly to the mesh deformations z,
,
where at the same time we would need to differentiate with respect to the variables z for correct geometrical adjoint sensitivities.
Additionally, we already have those variables allocated as Coord_p1, Coord_n and Coord_n1 (in this mesh solver) or Solution, Solution_time_n and Solution_time_n1 in #760. So, really, obtaining the GridVel vector would just be an additional FD computation both for rigid movement and dynamic grids, and a lot of memory savings are possible.
Do you think it could be possible to adapt it for this PR? If not, it should be something to consider as further work (as I would expect there is quite a bit of interest on dynamic meshes), maybe using the basis of #760 (which should make it easier).
| Restart_Flow = false; | ||
|
|
||
| if (GetGrid_Movement()) { | ||
| SU2_MPI::Error("Dynamic mesh movement currently not supported for the discrete adjoint solver.", CURRENT_FUNCTION); |
There was a problem hiding this comment.
As of now, deformable meshes are mostly used in FSI (where the coupling terms should be accounted for) but outside of that, there are not many methods to run deformable, single-zone test cases available. I guess something could be done using SURFACE_MOVEMENT = DEFORMING and a pitching/plunging airfoil, but I don't believe the adjoint will work out of the box, as the Grid Velocities are recomputed several times in one round of deformation.
However, with the capabilities in #760, I believe it should be relatively easy to set up a case with time-dependent boundary displacements.
| unsigned short iMesh; | ||
| int Direct_Iter; | ||
| bool heat = config[val_iZone]->GetWeakly_Coupled_Heat(); | ||
| bool grid_IsMoving = config[val_iZone]->GetGrid_Movement(); |
There was a problem hiding this comment.
For consistency, could you rename grid_IsMoving to grid_movement?
There was a problem hiding this comment.
@economon mentions that as well above :) but CVolumetricMovement ***grid_movement is an input to that function, so the compiler throws an error declaration of ‘bool grid_movement’ shadows a parameter
| * \return Grid velocity at the point. | ||
| */ | ||
| su2double *GetGridVel(void); | ||
|
|
There was a problem hiding this comment.
Can you indent these functions to align with the rest of the file?
There was a problem hiding this comment.
In the file mostly tabs are used for indentation, and github seems to display 4 spaces per tab. The added code follows https://su2code.github.io/docs/Style-Guide/ using 2 spaces as indentation.
I can of course change it to be consistent with the file using tabs if that is wanted. Above in pedros review that topic came already up.
There was a problem hiding this comment.
Oh sorry, I didn't notice before. Let's leave it like that then.
| * \param[in] val_value - Value of the gradient. | ||
| */ | ||
| void SetGridVel_Grad(unsigned short val_var, unsigned short val_dim, su2double val_value); | ||
| * \brief Set the value of the grid velocity at the point. |
Hey @rsanfer, I think that is ok for this PR. After a short search I guess such a routine does not exist yet. Or can you point me to the right direction? |
|
The routine you are looking for is probably this one:
|
|
Hi @rsanfer and @TobiKattmann , I agree with the more generalised solution for rigid and deforming meshes too and this is excellent timing of #760 to combine the new approach. |
…b.com/su2code/SU2 into feature_dynamic_mesh_discrete_adjoint
…current mesh solver for calculation of grid velocities
…sh_discrete_adjoint Conflicts: Common/src/config_structure.cpp
|
|
||
| /*--- Secondary sensitivities must be computed with a certain frequency. ---*/ | ||
| /*--- It is also done at the beginning so all memory gets allocated. ---*/ | ||
| if ((Adjoint_Iter % config->GetWrt_Sol_Freq() == 0) && (SecondaryVariables != NONE)){ |
There was a problem hiding this comment.
This SecondaryRecording is now removed entirely, as it introduces a bug for the unsteady adjoint. The Volume Mesh Sensitivity setting is a += in each physical timestep for unsteady cases and SecondaryRecording is also called in Postprocess() right after the Run(). I.e. if it is called multiple times in one physical timestep the sensitivity adds up. With this code modification SecondaryRecording gets only called once per timestep in Postprocess.
For steady cases the implications are: The memory for the secondary recording gets allocated after the 'flow adjoint iteration' ... so it could be really annoying if the simulation crashes due to lack of memory after waiting for convergence. More importantly: You cannot use an intermediate restart file for e.g. DOT_AD as it is does not hold any/correct sensitivities. So it might be better to only disable this conditional for unsteady computations as I see the advantage of having this for steady computations. What do you think @rsanfer
There was a problem hiding this comment.
I honestly prefer it that way as it is much faster for steady-state computations in moving meshes (a lot less unnecessary recordings). In fact that was my first implementation, but then I added this SecondaryRecording after discussion with @talbring, because of the drawbacks you mention. No 100% what's the best approach...
There was a problem hiding this comment.
My only concern is the memory thing, because usually the recording with coordinates as inputs takes more memory than the one with the solution variables. I am not so worried about the intermediate restart files, since you can just do a restart to get the correct sensitivity values.
Lets do it without the SecondaryRecording. If it really leads to problems, we can always change it back.
|
@TobiKattmann The validation looks perfect. Happy that it works now. |
|
Remember to revert the Travis changes. |
|
Alright, the su2code/Testcases repo is already merged, .travis.yml is reverted. If there are no further request than this can be merged from my standpoint. |
|
Thanks. Merging it in. Just as a reminder: without a Tutorial or Documentation, there will be no advertising :/ |

Proposed Changes
This PR, created together with @TobiKattmann , makes the necessary changes to obtain time-accurate discrete adjoint gradients for unsteady problems with grid movement. Main changes are for the handling of grid velocities of previous time steps.
Regression test case to follow.
Related Work
depends on #767 unsteady incompressible + ALE. Then a tested discrete adjoint for unsteady incompressible flow can be merged with this.
Edit @TobiKattmann: PR767 is merged so I remove the dependent label
PR Checklist