Skip to content

Added Windowing Regularizers for time averaged outputs.#836

Merged
talbring merged 34 commits intosu2code:developfrom
ScSteffen:feature_WND
Jan 15, 2020
Merged

Added Windowing Regularizers for time averaged outputs.#836
talbring merged 34 commits intosu2code:developfrom
ScSteffen:feature_WND

Conversation

@ScSteffen
Copy link
Contributor

@ScSteffen ScSteffen commented Dec 11, 2019

Proposed Changes

Give a brief overview of your contribution here in a few sentences.
Based on the observations of Albring et al. in "Challenges in Sensitivity Computations for (D)DES and URANS" we implemented the windowing regularizers proposed in the work of Krakos et al. "Sensitivity analysis of limit cycle oscillations" and adapted it to the AD framework of SU2.
We call the feature in the following "windowing".
Windowing can be applied if one takes the (windowed) time average of a periodic function, i.e. in the case of an instationary flow field that exhibits periodic behaviour.

  • We changed the functionality of the output-field-prefix TAVG and D_TAVG from a simple time average to a time averaged weighted with a windowing function. For the direct run this is just a post-processing step and changes happen only in signal_processing_toolbox and Coutput.
  • We adapted the seeding for the adjoint run from a the classic time average to a windowed time average.
  • Tests have shown, that higher order windowing regularizers, tend to converge quickly with respect to the averaged time span. We have implemented a "timeConvergenceMonitor", that acts similarly to the convergence monitoring of the inner convergence. It applies a cauchy convergence criterion to the chosen windowed time average output coefficients.
  • The cauchy convergence criterion works for direct run, adjoint run and shape optimization scripts
  • In case of shape optimization, the script remembers the final time iteration of the direct run. If the final time step is smaller than the "original" final time step stated in the configuration file, it adapts the starting time of the adjoint run; and the time to seed the objective function, such that the seeding does not exceed the original time to end the seeding.

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.

  • I am submitting my contribution to the develop branch.
  • My contribution generates no new compiler warnings (try with the '-Wall -Wextra -Wno-unused-parameter -Wno-empty-body' compiler flags).
  • My contribution is commented and consistent with SU2 style.
  • 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.

@pr-triage pr-triage bot added the PR: draft label Dec 11, 2019
Copy link
Member

@pcarruscag pcarruscag left a comment

Choose a reason for hiding this comment

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

Hi @SteffenMath,

Welcome to SU2 (it seems it is your first PR), it looks like a good generalisation of the moving averages, I leave some small code comments below. As a general point does this averaging rely on fixed timesteps?

Cheers,
Pedro

Comment on lines +5434 to +5440
unsigned long GetStartWindowIteration(void);

/*!
* \brief Get Index of the window function used as weight in the cost functional
* \return
*/
unsigned short GetWindowIdx(void);
Copy link
Member

Choose a reason for hiding this comment

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

These two methods should be const.
To be consistent with the prevailing naming convention GetWindowIdx should be called something like GetKindWindow (have a look at similar methods). Also, although not done in other methods, the return type for such functions should be the enumerator type (that can avoid some mix-ups).

/*!
* \brief types of window (weight) functions for cost functional
*/
enum WND_FUNCTION {
Copy link
Member

Choose a reason for hiding this comment

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

I would not abbreviate "window".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The enum is now called WINDOW_FUNCTION

su2double GetWndWeight(int fctIdx, unsigned long CurTimeIdx, unsigned long endTimeIdx);

protected:
const su2double PI_NUMBER = 4.0 * atan(1.0); /*!< \brief Pi number. */
Copy link
Member

Choose a reason for hiding this comment

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

I think pi is already defined somewhere as a literal, or definitely in some library header file, it would be better to use one of those instead of storing it as a class member.

Copy link
Contributor Author

@ScSteffen ScSteffen Dec 17, 2019

Choose a reason for hiding this comment

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

I'm using now the constant defined in "option_structure.hpp".

Comment on lines +36 to +40
WindowingTools(){}
~WindowingTools(){}

/*! \brief Returns the value of a windowing function given by fctIdx at CurTimeIdx with given endTimeIdx (i.e. endTimeIdx=nTimeIter, if one starts windowing at time t =0.) */
su2double GetWndWeight(int fctIdx, unsigned long CurTimeIdx, unsigned long endTimeIdx);
Copy link
Member

Choose a reason for hiding this comment

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

We do this in other places, but if the ctor and dtor are empty you are better off not defining them.
User defined destructors disable compiler generated move/copy constructors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've deleted both constructor and destructor.

Comment on lines +32 to +39
su2double WindowingTools::GetWndWeight(int fctIdx, unsigned long CurTimeIdx, unsigned long endTimeIdx){
switch (fctIdx){
case 1: return HannWindow(CurTimeIdx, endTimeIdx);
case 2: return HannSquaredWindow(CurTimeIdx, endTimeIdx);
case 3: return BumpWindow(CurTimeIdx, endTimeIdx);
default:return 1.0;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Here the type of fctIdx should be the enumerator, and the "cases" should be actual names not the numbers.
All these functions can be const or static if you use the globally defined pi.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

GetWndWeight now uses the enum WINDOW_FUNCTION.

}
}
su2double WindowingTools::HannWindow(unsigned long i, unsigned long endTimeIdx){
if(i==0) return 0; //catch div by zero error
Copy link
Member

Choose a reason for hiding this comment

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

There is no risk of division by 0

Copy link
Contributor Author

@ScSteffen ScSteffen Dec 17, 2019

Choose a reason for hiding this comment

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

I refactored that

@pr-triage pr-triage bot removed the PR: draft label Dec 11, 2019
…lprocessingtoolbox to CWindowingTools; Implemented time convergence monitor in SU2_CFD for singlezone direct runs; Adapted python scripts such that shape_optimization.py, direct_differentiation.py and discrete_adjoint.py works with time convergence functionality; Adapted tavg ouptut in python scripts such that they can handle windowed averaged output safely.
@pr-triage pr-triage bot added the PR: draft label Dec 17, 2019
@ScSteffen
Copy link
Contributor Author

ScSteffen commented Dec 17, 2019

Hi @SteffenMath,

Welcome to SU2 (it seems it is your first PR), it looks like a good generalisation of the moving averages, I leave some small code comments below. As a general point does this averaging rely on fixed timesteps?

Cheers,
Pedro

Hi @pcarruscag,

thanks for your quick reply and the usefull comments. I've pushed and updated version, that includes changes to your suggestions.
Regarding your general question: The windowing approach is in theory not dependent on a fixed time-stepsize, however the current implementation relies on a fixed stepsize. This can be changed quite easily in the CWindowingTools.cpp file and the corresponding class, should there be demand.

Best
Steffen

@pr-triage pr-triage bot removed the PR: draft label Dec 17, 2019
@pr-triage pr-triage bot added the PR: draft label Dec 17, 2019
@ScSteffen ScSteffen requested a review from pcarruscag January 6, 2020 15:34
Copy link
Member

@pcarruscag pcarruscag left a comment

Choose a reason for hiding this comment

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

See my note on multizone cases but otherwise I think the c++ side is good to go.
It would be good to have a tutorial on the feature or to at least to update the list of options.
Someone more familiar than me with the python side should have a look. If no one has anything to say I'll approve next week, thank you again for the contribution!

if (time_stepping){
if (TimeIter < IterAvg_Obj){
seeding = 1.0/((su2double)IterAvg_Obj);
seeding = windowEvaluator.GetWndWeight(config->GetKindWindow(),TimeIter, IterAvg_Obj-1)/ ((su2double)IterAvg_Obj);
Copy link
Member

Choose a reason for hiding this comment

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

Don't use "old style" casts (compiler warning) use static_cast or su2double(IterAvg_Obj)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed it :)

Comment on lines +544 to +547

bool CDiscAdjSinglezoneDriver::GetTimeConvergence() const{
return false;
}
Copy link
Member

Choose a reason for hiding this comment

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

This could be inline in the hpp file.

Copy link
Contributor

Choose a reason for hiding this comment

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

Not addressed yet

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sry forgot about this one. Changed it in the last commit :)

Comment on lines +136 to +138
for (unsigned short iField = 0; iField < config->GetnWndConv_Field(); iField++){
wndConvFields.emplace_back(config->GetWndConv_Field(iField));
}
Copy link
Member

Choose a reason for hiding this comment

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

General note (not very important in this case) whenever you know how many things you are inserting into the vector you should reserve before.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I added it nevertheless. Thanks for the feedback!

* \brief gets Convergence on physical time scale, (deactivated in adjoint case)
* \param none
*/
virtual bool GetTimeConvergence() const;
Copy link
Member

Choose a reason for hiding this comment

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

The virtual specifier you should use here is override i.e. bool GetTimeConvergence() const override, this immediately says to anyone reading the code that you are overriding functionality inherited from a parent class.
virtual should be used on the first class in the hierarchy that defines the method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay I'm going to fix that.

* \brief gets Convergence on physical time scale
* \param none
*/
virtual bool GetTimeConvergence() const;
Copy link
Member

Choose a reason for hiding this comment

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

What are the implications for multizone problems?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At the moment, the convergence tools only work for single-zone problems, however it should be easy to extend it. I will speak about it with @talbring.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. If you want to merge the PR before that is done please add an error message or warning somewhere if someone tries to use the feature in multizone problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added an error message for this case.

@@ -0,0 +1,416 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Copy link
Member

Choose a reason for hiding this comment

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

As a general note on examples, I would try to clean the config as much as possible to really put the focus on the new options you added, for example if MG_LEVEL=0 all other MG options don't do anything, if you use the Roe scheme the JST coefficients are not important and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have removed default options, that are unnecessary for a meaningful test case.

Copy link
Member

@talbring talbring left a comment

Choose a reason for hiding this comment

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

Thanks @SteffenMath !

I was following up with him in person. So no further comments from my side, everything looks good.

@pcarruscag Small documentation/tutorials will be added soon.

…f one tries to use the windowing Convergence features in Multizone cases
Copy link
Member

@pcarruscag pcarruscag left a comment

Choose a reason for hiding this comment

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

Lgtm. Thanks @SteffenMath, just the code factor issues left to address.

@TobiKattmann
Copy link
Contributor

I'll read through the PR over the weekend, so I would appreciate this staying open until Monday.

Copy link
Contributor

@TobiKattmann TobiKattmann left a comment

Choose a reason for hiding this comment

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

Hi @SteffenMath ,

I have to read the mentioned papers and try it out to understand everything a bit better, but still it looks really good.
There are some points concerning comments / doxygen-documentation which can be improved, especially the hpp-file without doxygen-documentation. But I blame primarily your supervisor for that 😉

Cheers, Tobi

Copy link
Contributor

@TobiKattmann TobiKattmann left a comment

Choose a reason for hiding this comment

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

Hi Steffen,
just a few more things below.
If you address these then this can be merged from my point of view.

Comment on lines +101 to +105
/*!
* \brief gets Convergence on physical time scale
* \param none
*/
inline virtual bool GetTimeConvergence() const{
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment cleanup

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

* \param endTimeIdx - Number of time steps to average over
* \return Value of the window-weight-function at time CurTimeIdx with end-time endTimeIdx
*/
su2double HannWindow(unsigned long i, unsigned long endTimeIdx) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I understand you are using unsigned long i CurTimeIdx and currentIter in this file which all have the same meaning : Current time iteration of the solver
For consistency it would be nice to choose one for your implementation, preferably CurTimeIdx or something new CurrTimeIter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I replaced the variable-names in questions with curTimeIter.

Comment on lines +35 to +41
/*! \brief Returns the value of a windowing function given by fctIdx at time-step CurTimeIdx with given time-frame endTimeIdx (i.e. endTimeIdx=nTimeIter, if one starts windowing at time t =0.)
* \param windowId - enum specifing the used window
* \param CurTimeIdx - Current time iteration of the solver
* \param endTimeIdx - Number of time steps to average over
* \return Value of the window-weight-function at time CurTimeIdx with time-frame endTimeIdx
*/
su2double GetWndWeight(WINDOW_FUNCTION windowId, unsigned long CurTimeIdx, unsigned long endTimeIdx) const;
Copy link
Contributor

Choose a reason for hiding this comment

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

here (in the \brief) it is probably windowId instead of fctIdx?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right.

@talbring talbring merged commit eb8584f into su2code:develop Jan 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants