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

Add isothermal_compressibility and thermal_expansion_coefficient for Redlich-Kwong and Peng-Robinson EOS #1421

Merged
merged 3 commits into from
May 14, 2023

Conversation

corykinney
Copy link
Contributor

@corykinney corykinney commented Jan 3, 2023

Changes proposed in this pull request

Add isothermalCompressibility and thermalExpansionCoeff definitions to the RedlichKwongMFTP and PengRobinson models.
Add corresponding thermo consistency tests betaT_eq_minus_dmv_dP_const_T_div_mv and alphaV_eq_dmv_dT_const_P_div_mv based on finite differences.

Closes Cantera/enhancements#122

Checklist

  • The pull request includes a clear description of this code change
  • Commit messages have short titles and reference relevant issues
  • Build passes (scons build & scons test) and unit tests address code coverage
  • Style & formatting of contributed code follows contributing guidelines
  • The pull request is ready for review

@bryanwweber bryanwweber marked this pull request as draft January 3, 2023 21:14
@codecov
Copy link

codecov bot commented Jan 3, 2023

Codecov Report

Merging #1421 (79da95a) into main (2ce92ea) will decrease coverage by 1.27%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##             main    #1421      +/-   ##
==========================================
- Coverage   70.78%   69.51%   -1.27%     
==========================================
  Files         373      377       +4     
  Lines       53872    57993    +4121     
  Branches    17579    20693    +3114     
==========================================
+ Hits        38131    40313    +2182     
- Misses      13339    14730    +1391     
- Partials     2402     2950     +548     
Impacted Files Coverage Δ
include/cantera/thermo/PengRobinson.h 100.00% <ø> (ø)
include/cantera/thermo/RedlichKwongMFTP.h 100.00% <ø> (ø)
src/thermo/PengRobinson.cpp 84.70% <100.00%> (-0.04%) ⬇️
src/thermo/RedlichKwongMFTP.cpp 84.41% <100.00%> (-0.33%) ⬇️

... and 301 files with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@corykinney
Copy link
Contributor Author

corykinney commented Jan 12, 2023

For reference, the following finite difference script was used to calculate the values in the test and set reasonable tolerances:

import cantera as ct
import numpy as np


def isothermal_compressibility(state, dP):
    T, P = state.TP
    state.TP = T, P - dP
    rho1 = state.density_mass
    state.TP = T, P + dP
    rho2 = state.density_mass
    state.TP = T, P
    return - state.density_mass * (1 / rho2 - 1 / rho1) / (2 * dP)


def thermal_expansion_coeff(state, dT):
    T, P = state.TP
    state.TP = T - dT, P
    rho1 = state.density_mass
    state.TP = T + dT, P
    rho2 = state.density_mass
    state.TP = T, P
    return state.density_mass * (1 / rho2 - 1 / rho1) / (2 * dT)


gas = ct.Solution("thermo-models.yaml", "CO2-PR")
gas.X = "CO2: 1"

betaT_ct = []
betaT_fd = []
alphaV_ct = []
alphaV_fd = []

P = 5e5
for i in range(10):
    T = 300 + 25 * i
    gas.TP = T, P
    betaT_ct.append(gas.isothermal_compressibility)
    alphaV_ct.append(gas.thermal_expansion_coeff)
    betaT_fd.append(isothermal_compressibility(gas, P * 1e-5))
    alphaV_fd.append(thermal_expansion_coeff(gas, T * 1e-5))

betaT_error = np.array(betaT_fd) - np.array(betaT_ct)
alphaV_error = np.array(alphaV_fd) - np.array(alphaV_ct)

print(f"Isothermal compressibility\nCantera: {betaT_ct}")
print(f"Finite difference error: ≤ {max(abs(betaT_error)):.5e}\n")
print(f"Thermal expansion coefficient\nCantera: {alphaV_ct}")
print(f"Finite difference error: ≤ {max(abs(alphaV_error)):.5e}")

which gives the following output for PengRobinson

Isothermal compressibility
Cantera: [2.0565735288179827e-06, 2.043354114849639e-06, 2.0336631623657743e-06, 2.0263828696824072e-06, 2.0208047779848385e-06, 2.0164611841648566e-06, 2.0130329255806156e-06, 2.010296131734592e-06, 2.0080900822010764e-06, 2.0062970726140575e-06]
Finite difference error: ≤ 2.20314e-16

Thermal expansion coefficient
Cantera: [0.0036236953242238055, 0.0032907286131624216, 0.003018150217266326, 0.0027901653673486353, 0.0025961927676142714, 0.0024288429329921953, 0.0022827789874782295, 0.0021540380684750378, 0.0020396082523816846, 0.0019371545393275069]
Finite difference error: ≤ 6.56762e-14

Note: For the PengRobinson functions, in addition to the finite difference check, the values from the functions were also compared to CoolProp output (CoolProp doesn't have RedlichKwong).

It seems that there might be a slight difference in critical parameters used for carbon dioxide, as the latest version of CoolProp gives slightly different values for density than were used in the original PengRobinson test for density. Thus, the derivatives evaluated by CoolProp were also very close but not exact.

@corykinney corykinney marked this pull request as ready for review January 12, 2023 22:20
@corykinney
Copy link
Contributor Author

@ischoegl Sorry to tag you again, but the finite difference tests are now implemented as thermodynamic consistency tests as you suggested using the framework by @speth, which is great because now I don't have to do any extra work for #1423 😄. Everything passes scons test and is ready for review. Let me know if I need to clean up the commits, as I added the tests in the individual classes before removing and switching them.

Copy link
Member

@ischoegl ischoegl left a comment

Choose a reason for hiding this comment

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

@corykinney ... thanks - while I'm not familiar with the theory (and don't have time to catch up at the moment), I looked over the code; the only thing I found was a LaTeX docstring typo. @decaluwe - the actual changes look like they are well aligned with recent work by your group - could you review and approve as appropriate?

Regarding your question about squashing commits: as you introduce tests that are subsequently removed it makes sense to squash corresponding commits.

include/cantera/thermo/PengRobinson.h Outdated Show resolved Hide resolved
@decaluwe
Copy link
Member

Sorry for the delay - taking a look at the derivation, just to get another set of eyes on it... Stay tuned :)

@corykinney
Copy link
Contributor Author

@decaluwe I appreciate you taking the time to review it! The derivations in the original enhancement proposal were a bit outdated, as I didn't originally realize that $d(a\alpha)/dT$ was already implemented, so I compiled the final version used for the implementation which should be easier to review.

@corykinney
Copy link
Contributor Author

corykinney commented Jan 25, 2023

@speth For the consistency tests with finite differences implemented here, I followed the tolerance pattern used for checking the final result of many of the other properties, in this case it looked like:

EXPECT_NEAR(betaT_fd, betaT_mid, max({rtol_fd * betaT_mid, rtol_fd * betaT_fd, atol}));

I would think min would be more appropriate in this case where atol might be relatively large compared to the magnitude of the value being checked, in which case using the product of rtol_fd and the expected value would be desired. But I wanted to check with you since every other test uses max.

@speth
Copy link
Member

speth commented Jan 25, 2023

I would think min would be more appropriate in this case where atol might be relatively large compared to the magnitude of the value being checked, in which case using the product of rtol_fd and the expected value would be desired. But I wanted to check with you since every other test uses max.

@speth For the consistency tests with finite differences implemented here, I followed the tolerance pattern used for checking the final result of many of the other properties, in this case it looked like:

EXPECT_NEAR(betaT_fd, betaT_mid, max({rtol_fd * betaT_mid, rtol_fd * betaT_fd, atol}));

I would think min would be more appropriate in this case where atol might be relatively large compared to the magnitude of the value being checked, in which case using the product of rtol_fd and the expected value would be desired. But I wanted to check with you since every other test uses max.

max is correct. The point of an absolute tolerance is to specify a limit below which we don't care about differences because both values are effectively zero. You need make sure to use an absolute tolerance that corresponds to "effectively zero" for these specific tests, though. The default value of atol used in other tests is 1e-5, where it is used to compare values that are in J/kg, and 1e-5 J/kg is indeed negligible for most purposes. But that's not really the right scale for the compressibility or thermal expansion coefficient.

@corykinney
Copy link
Contributor Author

@speth Thank you for the explanation. For some reason my impression was the opposite, which is that atol might be a required level of precision that would be used when rtol times the actual quantity is larger, but what you said makes a lot of sense!

I tried to select some reasonable numbers for the two quantities with units $1/K$ and $1/Pa$, for isothermal compressibility and thermal expansion coefficient, respectively. Based on the order of magnitudes of the ideal values 1/T and 1/P, considering that the real gas deviation will be one or two orders of magnitude smaller, I selected 1e-8 and 1e-12. However, when setting these values the following tests failed:

Failed tests:
    - thermo: NitrogenPureFluid/TestConsistency.betaT_eq_minus_dmv_dP_const_T_div_mv/1
    - thermo: NitrogenPureFluid/TestConsistency.betaT_eq_minus_dmv_dP_const_T_div_mv/2
    - thermo: LiquidWaterIapws95/TestConsistency.betaT_eq_minus_dmv_dP_const_T_div_mv/0

@speth Do you think these tolerances are reasonable, or are they perhaps too restrictive?

@corykinney
Copy link
Contributor Author

Actually I realized the values were swapped, so now the tests pass and the only question is whether the atol values should be more restrictive or if they are appropriate.

@corykinney
Copy link
Contributor Author

corykinney commented Feb 7, 2023

@decaluwe Have you had a chance to look at the final version (also linked above) of the derivations and the implementation in the last couple weeks?

@corykinney
Copy link
Contributor Author

@ischoegl Any chance this might get reviewed soon?

@ischoegl
Copy link
Member

@corykinney … I was under the impression that @decaluwe would review the theory.

Copy link
Member

@speth speth 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 this PR, @corykinney. I think both the implementation of these properties for the P-R and R-K models and the additional consistency tests for these properties across all phase models are useful contributions.

I had some comments on the derivation of these formulas that I shared in Cantera/enhancements#122 that I think can be used to simplify the calculations here. Other than that, I have just a few minor comments shown below.

Comment on lines 208 to 209
//! Returns the isothermal compressibility. Units: 1/Pa.
virtual double isothermalCompressibility() const;

//! Return the volumetric thermal expansion coefficient. Units: 1/K.
virtual double thermalExpansionCoeff() const;
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 this would be a good place to document the formulas for these properties. Likewise in RedlichKwongMFTP.h

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added the formulas in a9b5945
Unfortunately I'm not certain how to build the documentation to verify, but it should be formatted correctly

double P1 = phase->pressure();
double mv1 = phase->molarVolume();

double P2 = P1 * (1 + 1e-7);
Copy link
Member

Choose a reason for hiding this comment

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

If you use the same relative perturbation of 1e-6 that is used on other finite difference tests, I think you can set much tighter absolute tolerances for these tests. At least locally, with this larger perturbation, I was able to pass the test suite with atol_c = 1e-14 and atol_e = 1e-18.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adjust tolerances in a9b5945 as mentioned

double mv_mid = 0.5 * (mv1 + mv2);
double alphaV_fd = 1 / mv_mid * (mv2 - mv1) / (T2 - T1);

EXPECT_NEAR(alphaV_fd, alphaV_mid, max({rtol_fd * alphaV_mid, rtol_fd * alphaV_fd, atol_e}));
Copy link
Member

Choose a reason for hiding this comment

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

Please wrap long lines (over 88 characters).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wrapped both lines in a9b5945

@corykinney
Copy link
Contributor Author

corykinney commented Apr 26, 2023

@speth Currently working on testing the simplified equations mentiond in your other comment. Regarding the applicability of the inverse function theorem, the Jacobian just needs to be invertible at whichever point we're evaluating the derivatives at in order to use these simplifications, correct?

@corykinney
Copy link
Contributor Author

@speth Switching to the derivative identity approach allowed for leveraging existing pressure derivative functions, so now the implementation is extremely simple. Let me know if everything looks good, and I can squash the changes.

Add finite difference checks for `isothermalCompressibility` and `thermalExpansionCoeff` for thermo models.
Copy link
Member

@speth speth left a comment

Choose a reason for hiding this comment

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

Thanks for the update to this PR, @corykinney. The revised implementation looks good to me -- I wasn't even expecting it to be that simple!

Besides the minor doc update, please add yourself to the AUTHORS file, and then I think this will be good to go.

//! Returns the isothermal compressibility (\f$\beta_T\f$). Units: 1/Pa.
/*!
* \f[
* \beta_T = -\frac{1}{v} \left(\frac{\partial v}{\partial p}\right)_T
* \f]
*/
virtual double isothermalCompressibility() const;
Copy link
Member

Choose a reason for hiding this comment

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

There's no need to repeat documentation for overrides unless there's some specific differences in the documentation, which isn't the case here.

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 didn't realize the equations were already documented in ThermoPhase! The unnecessary comments are now removed.

Add `isothermalCompressibility` and `thermalExpansionCoeff` definitions to the `RedlichKwongMFTP` and `PengRobinson` models
@corykinney
Copy link
Contributor Author

@speth Let me know if there are any other changes :)

@speth speth merged commit 0d449ca into Cantera:main May 14, 2023
@corykinney corykinney deleted the real-gas-derivatives branch June 14, 2023 20:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Add isothermal_compressibility and thermal_expansion_coefficient for Redlich-Kwong and Peng-Robinson EOS
4 participants