Skip to content
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

Fixed indexing error in PID controller #4855

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open

Conversation

erikhide
Copy link

@erikhide erikhide commented Sep 8, 2023

The PID controller formula (see articles referred to in the comments in the OPM code, e.g. "D. Kuzmin and S. Turek, Numerical simulation of turbulent bubbly flows (2004)") as implemented in the current version of OPM contains an indexing error.

Comparing the implementation and PID controller formula, errors_[0] correspond to $e_{n-2}$, errors_[1] to $e_{n-1}$, and errors_[2] to $e_n$.

@bska
Copy link
Member

bska commented Sep 8, 2023

jenkins build this please

@bska
Copy link
Member

bska commented Sep 8, 2023

Hm, if this is indeed wrong–I don't have the reference on hand for easy verification–then it's an error that's been with us for a very long time. As far as I can tell this code was first introduced in OPM/opm-core#662 and has remained more or less unchanged, though moved and renamed a number of times, since then.

@erikhide
Copy link
Author

erikhide commented Sep 8, 2023

After looking at it quite a few times, I'm more or less certain that the indexing was wrong in the original implementation. And yes, the PID controller in OPM has remained unchanged since at least 2015. The time stepping algorithm seems to have been adapted to the current PID controller implementation, so there might be a need to update other parts and/or default parameter values to make it work with the new implementation.

That said, there is nothing inherently wrong with using the existing version, but as it stands now it is not a correct implementation of the PID controller as derived in the literature.

@totto82
Copy link
Member

totto82 commented Sep 8, 2023

Note that the PID controller has in my opinion never worked as intended. It was therefore removed as the default time-stepper long time ago. Thanks for looking into this. If you can make it to work, it would be great.

@alfbr
Copy link
Member

alfbr commented Sep 13, 2023

benchmark please

@atgeirr
Copy link
Member

atgeirr commented Sep 13, 2023

Note that the PID controller has in my opinion never worked as intended. It was therefore removed as the default time-stepper long time ago. Thanks for looking into this. If you can make it to work, it would be great.

Our current default is "pid+newtoniteration" which still includes the erroneous pid implementation. I am very much in favour of merging this to correct it, but we must deal with the fallout including failed tests etc. and potentially also change other things as suggested by @erikhide:

so there might be a need to update other parts and/or default parameter values to make it work with the new implementation.

Just a hunch: I cannot remember to have ever seen the verbose messages from the PID function. It might of course be activated in some places, but is it possible that it never was, and only after the current change is it relevant? If so, we could try to make the default TimeStepControl just "newtoniteration" and see if that is easier to deal with.

@ytelses
Copy link

ytelses commented Sep 14, 2023

Benchmark result overview:

Test Configuration Relative
opm-git OPM Benchmark: drogon - Threads: 1 0.998
opm-git OPM Benchmark: drogon - Threads: 8 1.169
opm-git OPM Benchmark: punqs3 - Threads: 1 0.979
opm-git OPM Benchmark: punqs3 - Threads: 8 1.026
opm-git OPM Benchmark: smeaheia - Threads: 1 1.009
opm-git OPM Benchmark: smeaheia - Threads: 8 1.123
opm-git OPM Benchmark: spe10_model_1 - Threads: 1 1.059
opm-git OPM Benchmark: spe10_model_1 - Threads: 8 1
opm-git OPM Benchmark: flow_mpi_extra - Threads: 1 1.019
opm-git OPM Benchmark: flow_mpi_extra - Threads: 8 1.062
opm-git OPM Benchmark: flow_mpi_norne - Threads: 1 1.026
opm-git OPM Benchmark: flow_mpi_norne - Threads: 8 1.012
opm-git OPM Benchmark: flow_mpi_norne_4c_msw - Threads: 1 1.021
opm-git OPM Benchmark: flow_mpi_norne_4c_msw - Threads: 8 1.007
  • Speed-up = Total time master / Total time pull request. Above 1.0 is an improvement. *

View result details @ https://www.ytelses.com/opm/?page=result&id=2228

@totto82
Copy link
Member

totto82 commented Sep 14, 2023

The benchmark results looks very promising.

Our current default is "pid+newtoniteration" which still includes the erroneous pid implementation

My memory is getting worse. I remember looking into this 5 years ago and concluded that PID didn't work as expected but I never noticed the bug. I agree with @atgeirr that this should be merged and with @erikhide that we need to test this and possibly also tune the timestepping differently. Have you done any testing on only using "pid". The "newton" part have some issues at the moment. For some cases it can get stuck on very small timesteps due to some unlucky combinations of NUPCOL and group/well switching.

@erikhide
Copy link
Author

I have tried to use only "pid" as input to --time-step-control, but so far I cannot say anything conclusive about what works best. My understanding is that the "target iteration approach" was added to ensure that the time steps remained low, because the PID controller tended to favour larger time steps than what might be preferable. The time stepping algorithm as a whole (as currently implemented) finds several different time step proposals, and always chooses the smallest one. Hence, it might become rather conservative in some cases and possibly also fail to increase the time steps when it is both possible and desirable.

I'm also working on some other alternatives (other heuristics than the PID controller, as well as other versions of the PID controller itself), and I'm trying to decide how best to compare different time stepping algorithms to ensure both fast computations and robust behaviour. This might take some time, but hopefully possible improvements will come sometime in the near future.

@bska
Copy link
Member

bska commented Sep 14, 2023

I'm also working on some other alternatives (other heuristics than the PID controller, as well as other versions of the PID controller itself), and I'm trying to decide how best to compare different time stepping algorithms to ensure both fast computations and robust behaviour. This might take some time, but hopefully possible improvements will come sometime in the near future.

This is very exciting news! Thank you so much for attacking this area 👍

@@ -206,7 +206,7 @@ namespace Opm
const double kD = 0.01 ;
const double newDt = (dt * std::pow( errors_[ 1 ] / errors_[ 2 ], kP ) *
std::pow( tol_ / errors_[ 2 ], kI ) *
std::pow( errors_[0]*errors_[0]/errors_[ 1 ]/errors_[ 2 ], kD ));
std::pow( errors_[1]*errors_[1]/errors_[ 0 ]/errors_[ 2 ], kD ));
Copy link
Member

Choose a reason for hiding this comment

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

I've looked a little bit closer at this. Suppose errors_[0] == 0, which it theoretically could be since the conditional above only checks errors_[1] and errors_[2]. Then newDt will be non-finite, typically inf. Is that reasonable here?

Copy link
Author

@erikhide erikhide Oct 16, 2023

Choose a reason for hiding this comment

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

I guess the errors_[1] and errors[2] were included in the conditional because they appeared in the denominators of the PID formula, and hence it was only relevant to check those. However, having changed indexing, errors_[0] (and not errors_[1]) appears in the denominator. It might therefore be reasonable to add errors[0] to (and possibly just remove errors_[1] from) the conditional.

Copy link
Member

Choose a reason for hiding this comment

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

It might therefore be reasonable to add errors[0] to [...] the conditional.

Yes, that's probably needed.

and possibly just remove errors_[1] from the conditional

I'd be wary of doing that. If errors_[1] = 0 we'll get newDt = 0 as well and that seems unexpected to me. Maybe I'm just misunderstanding something.

Copy link
Author

Choose a reason for hiding this comment

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

You're probably right. It appears that that might have been a problem earlier, though. Regardless, it certainly shouldn't hurt to add errors_[0] == 0 to the conditional.

I added it now.

Copy link
Member

Choose a reason for hiding this comment

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

It appears that that (errors_[0] = 0 giving newDt = 0) might have been a problem earlier, though

Agreed. I only remarked on this possibility because we're in the area now so it's an opportunity to fix it. For what it's worth, I tried this PR on a field case and ran into that exact problem. Otherwise I might not have noticed it.

@bska
Copy link
Member

bska commented Oct 17, 2023

jenkins build this please

Copy link
Member

@bska bska left a comment

Choose a reason for hiding this comment

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

Thank you for the update. I think this is starting to look good so I'm running a build check to assess the impact of the change.

@@ -206,7 +206,7 @@ namespace Opm
const double kD = 0.01 ;
const double newDt = (dt * std::pow( errors_[ 1 ] / errors_[ 2 ], kP ) *
std::pow( tol_ / errors_[ 2 ], kI ) *
std::pow( errors_[0]*errors_[0]/errors_[ 1 ]/errors_[ 2 ], kD ));
std::pow( errors_[1]*errors_[1]/errors_[ 0 ]/errors_[ 2 ], kD ));
Copy link
Member

Choose a reason for hiding this comment

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

It appears that that (errors_[0] = 0 giving newDt = 0) might have been a problem earlier, though

Agreed. I only remarked on this possibility because we're in the area now so it's an opportunity to fix it. For what it's worth, I tried this PR on a field case and ran into that exact problem. Otherwise I might not have noticed it.

@alfbr
Copy link
Member

alfbr commented Apr 25, 2024

Any chance we can get this in?

@bska
Copy link
Member

bska commented Apr 25, 2024

Any chance we can get this in?

There's absolutely a chance we can get it in. We're a little constrained by the way we handle updates to reference solutions in the time leading up to a new official release, so it'll definitely be easier to get it in once the new release is finalised.

@bska
Copy link
Member

bska commented Apr 25, 2024

jenkins build this please

@bska
Copy link
Member

bska commented Apr 25, 2024

jenkins build this please

Note to @erikhide : The above build request is just to refresh my memory of the impact of this PR. We are going to merge this regardless.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants