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
Prevent crashes near critical density due to saturation calc #2173
Conversation
Sometimes, the backwards ancillary solver fails near the critical point. This can happen if the ancillary function at its limit (i.e., critical temperature) does not reach the desired seek value (e.g., density). Because we are only usually using these to get a guess value, we can extrapolate them without issue to avoid a crash near the critical point. This helps to resolve CoolProp#2154.
Solving for the saturation curve near the critical point can be unstable. This commit fixes that in a few ways. 1) Prevent the solver from overshooting and prevent a negative temperature or density. 2) If it does fail, try again with a smaller omega value. If the solver fails after all of this, we will still throw to allow something downstream to handle it. This helps to address issue CoolProp#2154.
This is amazing!! I have known of this problem for a long time now, but didn't have the bandwidth to do anything about it. Can you also double-check that you have fixed DQ inputs? I added some editorial comments on your PR. |
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.
Overall, looks good, I'd like to keep the function signature the same as much as possible.
@@ -555,7 +555,8 @@ void SaturationSolvers::saturation_PHSU_pure(HelmholtzEOSMixtureBackend& HEOS, C | |||
} | |||
} while (error > 1e-9); | |||
} | |||
void SaturationSolvers::saturation_D_pure(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl rhomolar, saturation_D_pure_options& options) { | |||
void SaturationSolvers::saturation_D_pure(HelmholtzEOSMixtureBackend &HEOS, CoolPropDbl rhomolar, saturation_D_pure_options &options, int max_iterations=200) |
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 the max_arguments should be a field in the options class rather than a new argument. That's why I made that options class in the first place.
void saturation_T_pure(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl T, saturation_T_pure_options& options); | ||
void saturation_T_pure_Akasaka(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl T, saturation_T_pure_Akasaka_options& options); | ||
void saturation_T_pure_Maxwell(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl T, saturation_T_pure_Akasaka_options& options); | ||
|
||
/** |
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.
Empty comment, please remove
src/Backends/Helmholtz/VLERoutines.h
Outdated
@@ -66,13 +66,11 @@ static CoolPropDbl Wilson_lnK_factor(const HelmholtzEOSMixtureBackend& HEOS, Coo | |||
return log(pci / p) + 5.373 * (1 + omegai) * (1 - Tci / T); | |||
}; | |||
|
|||
void saturation_D_pure(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl rhomolar, saturation_D_pure_options& options); | |||
void saturation_D_pure(HelmholtzEOSMixtureBackend& HEOS, CoolPropDbl rhomolar, saturation_D_pure_options& options, int max_iter); |
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 comment about the max_iter argument
I was unaware that DQ inputs were a problem, so I had not looked into it. At your prompting, I took a look and saw that DQ inputs fail in the Brent solver (not bracketing) in many places - see the attached image. This PR does not fix that issue (same results with and without the modification), and I suspect that they are unrelated. I can see that this could be an issue and I can try to look into it when I get some more time. Is there an existing issue number that I should track against? |
What does your picture look like after your fix? If complete, I'll merge. Overall your fixes seem reasonable from my looking at it. |
My fix does not resolve this, as the failure for Dmass_Q inputs is unrelated. I will provide some discussion below, but suggest that you merge this change and that the DQ issue be solved in a separate PR. DmassQ fails for densities greater than the critical density. This is illustrated in the plot below where the red points indicate failures to converge. All of these failures indicate that the initial guess of the Brent solver does not bracket. Note that in this plot the consecutive dots form lines of constant quality. Recall that the Brent solver that is used here iterates on temperature and seeks the error in the quality to be reduced to 0. The initial guesses for the Brent solver are the triple point temperature (lower bound) and the critical temperature (upper bound). Initially, these seem like good guesses because a two-phase fluid should obviously be within this temperature range. However, at these temperatures, we do not bracket the desired quality. Instead, we find that both temperatures will give a quality that is too low! The triple point temperature evaluation is reasonable (typically giving a quality in the range [0, 1e-3]), but the critical point is not. The critical point quality is reported as a negative number. We can plot the change in quality with respect to temperature to investigate further. We see that there is a sudden reversal and that the estimated quality goes negative once the density/temperature combination goes outside of the two-phase region. My initial guess of how to correct this is to add another solver loop to provide an upper limit of the temperature guess to be the saturation temperature for the given density, but I have not yet implemented this. |
Ok, let's merge this and then if you want to dig into the DQ that would be great. The DQ routines in REFPROP also fail sometimes. |
Description of the Change
When updating an HEOS abstract state from density and energy, crashes (
ValueError
thrown) can occur when the density is very near the critical density. This PR fixes this behavior and prevents the exception.HSU_D_flash
is called when updating with D and U (as well as other combinations). As one of the first steps in the flash routine, the saturation points are determined at the given density (saturation_D_pure
). However, when near the critical density, instabilities in the solver routines cause an exception. This PR utilizes a few strategies to prevent this:saturation_D_pure
, prevent the calculation of a negative temperature or density by dynamically changing omega (underrelaxation factor). I.e., check what the new values would be and if they were going to be negative, set the omega value to instead cause a bisection.saturation_D_pure
in a try block and repeat the solution with a smaller starting omega value (and more allowed iterations) a few times in an attempt to get even better stability. Cases that were already solving fine will require no further computation.The "hole" in the results is illustrated in the attached image (colormap of temperature).
Benefits
Currently, CoolProp will fail for many fluids when updating from density/energy when the density is near the critical density. Updates from density/energy are a fairly common method in thermal hydraulic code development and crossing the isochor of the critical density is not uncommon. Prior to this PR, CoolProp would throw an exception, usually leading to a crash of the user's program. This allows the solver to continue without impacting calculations at other parts of the domain.
Possible Drawbacks
I foresee two potential drawbacks:
Verification Process
I evaluated several thousand density/energy combinations near the critical point for both hydrogen and helium (see c++ code below). In almost all cases, rather than throwing an exception, a good value was created. A few points fail in the Brent solver. These failures are due to a two-phase state where the temperature is within 0.01 of the critical temperature because HSU_D_flash_twophase applies a 0.01 bracket limit to the temperature. I have not removed this for fear of causing bracketing issues in other parts of the code.
Furthermore, when linked to another code, the code produces good, expected results - both near and removed from the critical point. This indicates that there is likely no regression failure.
Applicable Issues
Closes #2154
Test code
Sample Plot