mirror of
https://github.com/CoolProp/CoolProp.git
synced 2026-01-09 05:58:12 -05:00
Superancillaries for pure fluids (#2511)
* Expansions are fully wrapped, looking good. Next step is the set of expansions that is the 1D approximation * Get 1D approx working via cython * Count solutions * SuperAncillary class is working >1000x speedup for water Time for C++! * Superancillaries are working! In C++, speedup is more than 2000x. In Python, more like 150x because of Python <-> C++ overhead * Add pmax check for PQ superancillary calls * Update tests * Allow T limits to be obtained * Implement get_fluid_parameter_double for getting superanc value * Add tests for getting parameters from superanc * Script for testing superancillaries for sphinx * Microoptimizations; don't help speed The limiting factor remains the clear function, which takes about 30 ns * Add R125 superancillary * Use the release from fastchebpure for the files * Drop a .gitignore in the unzipped folder * Update superancillary injection script * Turn on superancillaries by default * Missing header * Many int conversions in superancillary * Another int cast * More annoying solution for boost iter max * Fix warnings * One more warning * Clear up the calculation of rho * Update docs_docker-build.yml Use arm64 since the containers were built on mac * Superfluous ; * Update backend.py * Get the critical points working for superancillaries * Fix wrapping changes of xmin&xmax methods * squelch warnings * Version 0 of jupyter notebook for docs * Try to add the notebook to the docs * Add jupyter notebook for superancillary * Lots of updates to superancillary notebook * More updates to docs * Skip pseudo-pure for superancillary docs * Fix output of superancillary figures * Add superancillary plots to docs for the page for each fluid * Make a placeholder figure for fluids without superancillary * Add superancillary plots to task list * Bump to release fixing m-xylene * Relax the location of the REFPROP stuff * Change default name for R-1336mzz(E) * No need for figures to be so large * Don't need REFPROP setting * Bump to fastchebpure release with methanol * Benchmark caching options * Benchmark more granularly * Add the fast methods to public API for HEOS class * Back to memset - can memset with 0 but no other value * Fix how caching is managed in Helmholtz class * Close to final implementation Perhaps a tiny bit more optimization possible? * Update function name * Make message more accurate * Fix init order * Expose update_QT_pure_superanc to Python * Fix when _reducing is set for pures * Fix the post_update * Indent * Notebook * Notebook * Make ln(p) construction lazy Only really matters for debug builds * Also make reference non-const * Inject superancillary for methanol * Make the superancillary loading entirely lazy in debug * Fix PH bug for Nitrogen Closes #2470 * Force the clear to be called on SatL and SatV To invalidate them at start * Default is non-lazy superancillary loading * Add CMake option to have lazy-loading superancillaries [skip ci] Not a good idea unless doing very narrow testing
This commit is contained in:
2
.github/workflows/docs_docker-build.yml
vendored
2
.github/workflows/docs_docker-build.yml
vendored
@@ -24,7 +24,7 @@ env:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04-arm
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
@@ -306,6 +306,8 @@ list(APPEND APP_INCLUDE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/externals/msgpack-c/include")
|
||||
list(APPEND APP_INCLUDE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/externals/miniz-3.0.2")
|
||||
list(APPEND APP_INCLUDE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/externals/nlohmann-json")
|
||||
list(APPEND APP_INCLUDE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/externals/incbin")
|
||||
list(APPEND APP_INCLUDE_DIRS
|
||||
@@ -2103,6 +2105,10 @@ if(COOLPROP_CATCH_MODULE)
|
||||
set_property(TARGET CatchTestRunner PROPERTY CXX_INCLUDE_WHAT_YOU_USE
|
||||
${iwyu_path})
|
||||
endif()
|
||||
|
||||
if(COOLPROP_LAZY_LOAD_SUPERANCILLARIES)
|
||||
target_compile_definitions(CatchTestRunner PRIVATE LAZY_LOAD_SUPERANCILLARIES)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(COOLPROP_CPP_EXAMPLE_TEST)
|
||||
|
||||
11
Web/conf.py
11
Web/conf.py
@@ -18,6 +18,8 @@
|
||||
# built documents.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import urllib.request
|
||||
import zipfile
|
||||
@@ -76,6 +78,14 @@ else:
|
||||
'cpapi': ('_static/doxygen/CoolPropDoxyLink.tag', 'http://www.coolprop.org/dev/_static/doxygen/html')
|
||||
}
|
||||
|
||||
# Execute all the notebooks
|
||||
for dirpath, dirnames, filenames in Path(__file__).parent.walk():
|
||||
for file in filenames:
|
||||
if file.endswith('.ipynb') and '.ipynb_checkpoints' not in str(dirpath):
|
||||
cmd = f'jupyter nbconvert --allow-errors --to notebook --output "{file}" --execute "{file}"'
|
||||
subprocess.check_output(cmd, shell=True, cwd=dirpath)
|
||||
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
@@ -103,6 +113,7 @@ extensions = ['IPython.sphinxext.ipython_console_highlighting',
|
||||
# 'inheritance_diagram',
|
||||
# 'numpydoc',
|
||||
# 'breathe'
|
||||
"nbsphinx",
|
||||
]
|
||||
|
||||
# set path to issue tracker:
|
||||
|
||||
434
Web/coolprop/SuperAncillary.ipynb
Normal file
434
Web/coolprop/SuperAncillary.ipynb
Normal file
@@ -0,0 +1,434 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ed8d1cd8-0c5b-4f4e-8668-ef653075ec02",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Superancillaries \n",
|
||||
"\n",
|
||||
"## Motivation\n",
|
||||
"\n",
|
||||
"VLE calculations for pure fluids require to solve the system of equations \n",
|
||||
"\n",
|
||||
"$$\n",
|
||||
"p(T,\\rho') = p(T,\\rho'')\n",
|
||||
"$$\n",
|
||||
"$$\n",
|
||||
"g(T,\\rho') = g(T,\\rho'')\n",
|
||||
"$$\n",
|
||||
"which is a complicated non-linear rootfinding problem. For a specified $T$, one must get guess values for $\\rho'(T)$ and $\\rho''(T)$ which are commonly obtained from ancillary functions that give \"good enough\" estimates of the densities from which a nonlinear rootfinding algorithm launches to solve for the co-existing densities.\n",
|
||||
"\n",
|
||||
"These calculations, while not very slow (order of microseconds per call), often, if not usually, represent a computational pinchpoint. So it would be nice to be able to use a numerical function that can represent the results of these iterative calculations so well that the iterative calculation itself can be avoided. The superancillary functions described here satisfy that goal.\n",
|
||||
"\n",
|
||||
"## Theory and Approach\n",
|
||||
"\n",
|
||||
"The development of superancillary functions have been laid out in a series of publications:\n",
|
||||
"\n",
|
||||
"* [Exceptionally reliable density-solving algorithms for multiparameter mixture models from Chebyshev expansion rootfinding](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=WNn0e_4AAAAJ&cstart=20&pagesize=80&sortby=pubdate&citation_for_view=WNn0e_4AAAAJ:hMod-77fHWUC)\n",
|
||||
"* [Efficient and Precise Representation of Pure Fluid Phase Equilibria with Chebyshev Expansions](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=WNn0e_4AAAAJ&cstart=20&pagesize=80&sortby=pubdate&citation_for_view=WNn0e_4AAAAJ:OU6Ihb5iCvQC)\n",
|
||||
"* [Superancillary Equations for the Multiparameter Equations of State in REFPROP 10.0](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=WNn0e_4AAAAJ&sortby=pubdate&citation_for_view=WNn0e_4AAAAJ:AXPGKjj_ei8C)\n",
|
||||
"\n",
|
||||
"The term \"superancillary\" was coined by Ulrich Deiters to differentiate it from the ancillary functions that are commonly provided alongside reference equations of state.\n",
|
||||
"\n",
|
||||
"At their core, a superancillary function is constructed from a series of Chebyshev expansions. When considering the entire set of Chebyshev expansions, they span the entire range of the independent variable. In their current form, they support only 1D function approximation. Chebyshev expansions, and orthogonal polynomials more generally, are a well-studied numerical tool for function approximation and can permit function approximation to the level of numerical precision.\n",
|
||||
"\n",
|
||||
"To build the superancillary equations, one does vapor-liquid equilibrium (VLE) calculations at carefully selected temperatures and *does some math* to construct the Chebyshev expansion. If the expansion is not accurate enough, the domain is subdivided into two halves and the process is then carried out in each section.\n",
|
||||
"\n",
|
||||
"To ensure highly accurate and reliable results in the very near vicinity of the critical point, as well as at very pressures (e.g., near the triple point of propane), it is necessary to do the phase equilibrium calculations in extended precision arithmetic. The ``boost::multiprecision`` library is used in C++ to do the extended precision calculations, in concert with the new ``teqp`` library for the equation of state part. The code used to do this exercise is in a fork at https://github.com/CoolProp/fastchebpure and [the releases](https://github.com/CoolProp/fastchebpure/releases) contain the obtained functions.\n",
|
||||
"\n",
|
||||
"### Caveats:\n",
|
||||
"* When superancillaries are enabled, the \"critical point\" is the numerical one that is obtained by enforcing $\\left(\\frac{\\partial p}{\\partial \\rho}\\right)_T = \\left(\\frac{\\partial^2 p}{\\partial \\rho^2}\\right)_T = 0$, rather than the one reported by the EOS developers. This is because the superancillaries are developed to try to fix the critical region as well as possible. The differences can be sometimes not too small: [An Analysis of the Critical Region of Multiparameter Equations of State](https://scholar.google.com/citations?view_op=view_citation&hl=en&user=WNn0e_4AAAAJ&sortby=pubdate&citation_for_view=WNn0e_4AAAAJ:1qzjygNMrQYC)\n",
|
||||
"* When pressure is provided, the inverse superancillary for $T(p)$ is used, which introduces a very small error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ab07d156-01ad-43c2-902c-6f45ee76dfa6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import timeit\n",
|
||||
"import CoolProp.CoolProp as CP\n",
|
||||
"import numpy as np\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import urllib\n",
|
||||
"from zipfile import ZipFile\n",
|
||||
"from pathlib import Path"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "bad6e729-92f7-42d2-a53e-ff1c11263af1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"AS = CP.AbstractState('HEOS','Water')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0d7a3783-b7fa-471b-902b-539fe3f8769a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# See caveat noted above. The use of superancillary functions necessarily changes the critical point for the fluid\n",
|
||||
"# Not usually by an amount that will be meaningful in practical applications\n",
|
||||
"for superanc in [False, True]:\n",
|
||||
" CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, superanc)\n",
|
||||
" print(superanc, AS.T_critical())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "58913d10-19c7-4b47-858c-60c7e9a66ec8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The JSON data for the expansions can be accessed from CoolProp\n",
|
||||
"jSuper = json.loads(CP.get_fluid_param_string(\"WATER\", \"JSON\"))[0][\"EOS\"][0][\"SUPERANCILLARY\"]\n",
|
||||
"SA = CP.SuperAncillary(json.dumps(jSuper))\n",
|
||||
"\n",
|
||||
"AS = CP.AbstractState('HEOS','Water')\n",
|
||||
"Tt = AS.Ttriple()\n",
|
||||
"Tc = AS.T_critical()\n",
|
||||
"\n",
|
||||
"Ts = np.linspace(Tt, 647.0959999999873, 5*10**5)\n",
|
||||
"ps = np.zeros_like(Ts)\n",
|
||||
"SA.eval_sat_many(Ts, 'P', 0, ps)\n",
|
||||
"plt.plot(1/Ts, ps)\n",
|
||||
"plt.yscale('log'); plt.gca().set(xlabel='$1/T$ / 1/K', ylabel='$p$ / Pa');"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "418b1527-9d80-44ca-9461-5cf1cb5ad35d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# At the lower level, calling with a buffer of points\n",
|
||||
"tic = timeit.default_timer()\n",
|
||||
"SA.eval_sat_many(Ts, 'P', 0, ps)\n",
|
||||
"toc = timeit.default_timer()\n",
|
||||
"print((toc-tic)/len(Ts)*1e6, 'μs/call')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "54f6ad37-0813-4f8a-b7ba-8752b550587e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# With superancillaries disabled\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)\n",
|
||||
"QT_INPUTS = CP.QT_INPUTS\n",
|
||||
"tic = timeit.default_timer()\n",
|
||||
"for T_ in Ts:\n",
|
||||
" AS.update(QT_INPUTS, 0, T_)\n",
|
||||
"toc = timeit.default_timer()\n",
|
||||
"print((toc-tic)/len(Ts)*1e6, 'μs/call')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5d254da7-0080-4549-9222-0f0b276fd6d5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# With superancillaries enabled, three superancillary functions are evaluated\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)\n",
|
||||
"QT_INPUTS = CP.QT_INPUTS\n",
|
||||
"tic = timeit.default_timer()\n",
|
||||
"for T_ in Ts:\n",
|
||||
" AS.update(QT_INPUTS, 0, T_)\n",
|
||||
"toc = timeit.default_timer()\n",
|
||||
"print((toc-tic)/len(Ts)*1e6, 'μs/call')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f8731b27-90f9-413a-8cfc-363d6feea0f1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If pressure is specified, the speedup is even greater because one must also iterate for the pressure of interest:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "72969532-5b98-4c44-adc9-204d16127e9b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pssmall = np.geomspace(ps[0], ps[-1]*(1-1e-10), 10**4) # The full equilibrium is slow\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)\n",
|
||||
"PQ_INPUTS = CP.PQ_INPUTS\n",
|
||||
"tic = timeit.default_timer()\n",
|
||||
"for p_ in pssmall:\n",
|
||||
" AS.update(PQ_INPUTS, p_, 0)\n",
|
||||
"toc = timeit.default_timer()\n",
|
||||
"print((toc-tic)/len(pssmall)*1e6, 'μs/call')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "960463cb-9574-4ab7-b3be-23a932470d30",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# With superancillaries enabled, one evaluates T(p) from an inverse function and then uses that T\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)\n",
|
||||
"PQ_INPUTS = CP.PQ_INPUTS\n",
|
||||
"tic = timeit.default_timer()\n",
|
||||
"for p_ in pssmall:\n",
|
||||
" AS.update(PQ_INPUTS, p_, 0)\n",
|
||||
"toc = timeit.default_timer()\n",
|
||||
"print((toc-tic)/len(pssmall)*1e6, 'μs/call')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "56dee520-1a03-45dc-bd2e-c8343da27e59",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cecc435c-89ad-47f5-a368-3daba910a294",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Other validation checks to confirm accuracy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d7635fe7-94f7-4415-bcb3-403c5efb5d42",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_hfg(T):\n",
|
||||
" \"\"\" Latent heat \"\"\"\n",
|
||||
" AS.update(CP.QT_INPUTS, 0, T)\n",
|
||||
" return AS.saturated_vapor_keyed_output(CP.iHmolar)-AS.saturated_liquid_keyed_output(CP.iHmolar)\n",
|
||||
" \n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)\n",
|
||||
"HFG_SA = np.array([get_hfg(T_) for T_ in Ts])\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)\n",
|
||||
"HFG_NON = np.array([get_hfg(T_) for T_ in Ts])\n",
|
||||
"\n",
|
||||
"plt.plot(Ts, np.abs(HFG_NON/HFG_SA-1))\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)\n",
|
||||
"plt.yscale('log')\n",
|
||||
"plt.ylabel(r'$|\\Delta h_{fg, non}/\\Delta h_{fg, sa} - 1|$')\n",
|
||||
"plt.xlabel('$T$ / K');"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b7eb7d11-f782-46b4-a245-07d05aa038f8",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_sf(T):\n",
|
||||
" \"\"\" liquid entropy \"\"\"\n",
|
||||
" AS.update(CP.QT_INPUTS, 0, T)\n",
|
||||
" return AS.saturated_liquid_keyed_output(CP.iSmolar)\n",
|
||||
" \n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)\n",
|
||||
"SF_SA = np.array([get_sf(T_) for T_ in Ts])\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)\n",
|
||||
"SF_NON = np.array([get_sf(T_) for T_ in Ts])\n",
|
||||
"\n",
|
||||
"plt.plot(Ts, np.abs(SF_NON - SF_SA))\n",
|
||||
"plt.yscale('log')\n",
|
||||
"plt.ylabel(r\"$|s'_{non} - s'_{sa}|$ / J/mol\")\n",
|
||||
"plt.xlabel('$T$ / K');\n",
|
||||
"\n",
|
||||
"plt.figure()\n",
|
||||
"plt.plot(Ts, SF_NON)\n",
|
||||
"plt.title(r'Note value goes towards zero at $T\\to 273.16$ K')\n",
|
||||
"plt.gca().set(ylabel=r\"$s'_{non}$ / J/mol\", xlabel='$T$ / K')\n",
|
||||
"\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f05b9de1-27d7-42ce-a35c-08cbb5b99d25",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Checks against extended precision"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "933cb2e5-d31e-4fac-a1d9-b4aa82e079a0",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"outputversion = '2025.04.27'\n",
|
||||
"if not Path(f'{outputversion}.zip').exists():\n",
|
||||
" print('Downloading the chebyshev output file to ', Path('.').absolute())\n",
|
||||
" urllib.request.urlretrieve(f'https://github.com/CoolProp/fastchebpure/archive/refs/tags/{outputversion}.zip', f'{outputversion}.zip')\n",
|
||||
"\n",
|
||||
" with ZipFile(f'{outputversion}.zip') as z:\n",
|
||||
" print(Path(f'{outputversion}.zip').parent.absolute())\n",
|
||||
" z.extractall(Path(f'{outputversion}.zip').parent.absolute())\n",
|
||||
"\n",
|
||||
" with (Path('.') / f'fastchebpure-{outputversion}' / '.gitignore').open('w') as fp:\n",
|
||||
" fp.write(\"*\")\n",
|
||||
"\n",
|
||||
"outputcheck = (Path('.') / f'fastchebpure-{outputversion}' / 'outputcheck').absolute()\n",
|
||||
"assert outputcheck.exists()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e26150cd-0e6a-477a-90c9-4aa11e076d7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import matplotlib\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import json\n",
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import CoolProp.CoolProp as CP\n",
|
||||
"\n",
|
||||
"fluid = 'Water'\n",
|
||||
"\n",
|
||||
"CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)\n",
|
||||
"\n",
|
||||
"AS = CP.AbstractState('HEOS', f\"{fluid}\")\n",
|
||||
"jSuper = json.loads(CP.get_fluid_param_string(f\"{fluid}\", \"JSON\"))[0]['EOS'][0]['SUPERANCILLARY']\n",
|
||||
"superanc = CP.SuperAncillary(json.dumps(jSuper))\n",
|
||||
"RPname = AS.fluid_param_string(\"REFPROP_name\")\n",
|
||||
"\n",
|
||||
"# Load extended precision calcs from the release on github\n",
|
||||
"chk = json.load(open(f'{outputcheck}/{fluid}_check.json'))\n",
|
||||
"df = pd.DataFrame(chk['data'])\n",
|
||||
"# df.info() # uncomment to see what fields are available\n",
|
||||
"\n",
|
||||
"Tcrit_num = AS.get_fluid_parameter_double(0, \"SUPERANC::Tcrit_num\")\n",
|
||||
"T = df['T / K'].to_numpy()\n",
|
||||
"Theta = (Tcrit_num-T)/Tcrit_num\n",
|
||||
"\n",
|
||||
"fig, axes = plt.subplots(3, 1, sharex=True, figsize=(6,10))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"plt.sca(axes[0])\n",
|
||||
"rhoL_anc = np.zeros_like(T)\n",
|
||||
"superanc.eval_sat_many(T, 'D', 0, rhoL_anc)\n",
|
||||
"err = np.abs(df[\"rho'(mp) / mol/m^3\"]/rhoL_anc-1)\n",
|
||||
"plt.plot(Theta, err, 'o', label=r'$\\Upsilon$:SA')\n",
|
||||
"\n",
|
||||
"errCP = np.abs(df[\"rho'(mp) / mol/m^3\"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 0, f'HEOS::{fluid}')-1)\n",
|
||||
"plt.plot(Theta, errCP, '^', label=r'$\\Upsilon$:HEOS')\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" errRP = np.abs(df[\"rho'(mp) / mol/m^3\"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 0, f'REFPROP::{RPname}')-1)\n",
|
||||
" plt.plot(Theta, errRP, 'x', label=r'$\\Upsilon$:REFPROP')\n",
|
||||
"except BaseException as BE:\n",
|
||||
" print(BE)\n",
|
||||
"\n",
|
||||
"plt.legend(loc='best')\n",
|
||||
"plt.ylabel(r\"$|\\rho_{{\\rm \\Upsilon}}'/\\rho_{{\\rm ep}}'-1|$\")\n",
|
||||
"plt.yscale('log')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"plt.sca(axes[1])\n",
|
||||
"rhoV_anc = np.zeros_like(T)\n",
|
||||
"superanc.eval_sat_many(T, 'D', 1, rhoV_anc)\n",
|
||||
"err = np.abs(df[\"rho''(mp) / mol/m^3\"]/rhoV_anc-1)\n",
|
||||
"plt.plot(Theta, err, 'o', label=r'$\\Upsilon$:SA')\n",
|
||||
"\n",
|
||||
"errCP = np.abs(df[\"rho''(mp) / mol/m^3\"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 1, f'HEOS::{fluid}')-1)\n",
|
||||
"plt.plot(Theta, errCP, '^', label=r'$\\Upsilon$:HEOS')\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" errRP = np.abs(df[\"rho''(mp) / mol/m^3\"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 1, f'REFPROP::{RPname}')-1)\n",
|
||||
" plt.plot(Theta, errRP, 'x', label=r'$\\Upsilon$:REFPROP')\n",
|
||||
"except BaseException as BE:\n",
|
||||
" print(BE)\n",
|
||||
"\n",
|
||||
"plt.legend(loc='best')\n",
|
||||
"plt.ylabel(r\"$|\\rho_{{\\rm \\Upsilon}}''/\\rho_{{\\rm ep}}''-1|$\")\n",
|
||||
"plt.yscale('log')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"plt.sca(axes[2])\n",
|
||||
"p_anc = np.zeros_like(T)\n",
|
||||
"superanc.eval_sat_many(T, 'P', 1, p_anc)\n",
|
||||
"err = np.abs(df[\"p(mp) / Pa\"]/p_anc-1)\n",
|
||||
"plt.plot(Theta, err, 'o', label=r'$\\Upsilon$:SA')\n",
|
||||
"\n",
|
||||
"errCP = np.abs(df[\"p(mp) / Pa\"]/CP.PropsSI('P', 'T', T, 'Q', 1, f'HEOS::{fluid}')-1)\n",
|
||||
"plt.plot(Theta, errCP, '^', label=r'$\\Upsilon$:HEOS')\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" errRP = np.abs(df[\"p(mp) / Pa\"]/CP.PropsSI('P', 'T', T, 'Q', 1, f'REFPROP::{RPname}')-1)\n",
|
||||
" plt.plot(Theta, errRP, 'x', label=r'$\\Upsilon$:REFPROP')\n",
|
||||
"except BaseException as BE:\n",
|
||||
" print(BE)\n",
|
||||
"plt.legend(loc='best')\n",
|
||||
"\n",
|
||||
"# print(CP.PropsSI('gas_constant', 'T', T[0], 'Q', 1, f'HEOS::{fluid}'))\n",
|
||||
"# print(CP.PropsSI('gas_constant', 'T', T[0], 'Q', 1, f'REFPROP::{fluid}'))\n",
|
||||
"\n",
|
||||
"plt.ylabel(r\"$|p_{{\\rm \\Upsilon}}/p_{{\\rm ep}}-1|$\")\n",
|
||||
"plt.yscale('log')\n",
|
||||
"\n",
|
||||
"plt.sca(axes[2])\n",
|
||||
"plt.xlabel(r'$\\Theta=(T_{{\\rm crit,num}}-T)/T_{{\\rm crit,num}}$')\n",
|
||||
"plt.xscale('log')\n",
|
||||
"\n",
|
||||
"for ax in axes:\n",
|
||||
" ax.axhline(1e-12, dashes=[2,2])\n",
|
||||
" ax.axvline(1e-6, dashes=[2,2])\n",
|
||||
"\n",
|
||||
"plt.tight_layout(pad=0.2)\n",
|
||||
"plt.show()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -17,3 +17,4 @@ This section includes information about the CoolProp software, listings of input
|
||||
PCSAFT.rst
|
||||
examples.rst
|
||||
changelog.rst
|
||||
SuperAncillary.ipynb
|
||||
|
||||
@@ -49,6 +49,17 @@ In this figure, we start off with a state point given by T,P and then we calcula
|
||||
|
||||
.. image:: Consistencyplots/{fluid:s}.png
|
||||
|
||||
Superancillary Plots
|
||||
====================
|
||||
|
||||
The following figure shows the accuracy of the superancillary functions relative to extended precision calculations carried out in C++ with the teqp library. The results of the iterative calculations with REFPROP and CoolProp are also shown.
|
||||
|
||||
.. note::
|
||||
|
||||
You can download the script that generated the following figure here: :download:`(link to script)<Superancillaryplots/{fluid:s}.py>`, right-click the link and then save as... or the equivalent in your browser. You can also download this figure :download:`as a PDF<Superancillaryplots/{fluid:s}.pdf>`.
|
||||
|
||||
.. image:: Superancillaryplots/{fluid:s}.png
|
||||
|
||||
"""
|
||||
|
||||
table_template = """ Parameter, Value
|
||||
|
||||
@@ -84,6 +84,7 @@ add_to_task_list("coolprop.tabular.speed.py")
|
||||
add_to_task_list("fluid_properties.phase_envelope.py")
|
||||
add_to_task_list("fluid_properties.PurePseudoPure.py")
|
||||
add_to_task_list("fluid_properties.Mixtures.py")
|
||||
add_to_task_list("fluid_properties.Superancillary.py")
|
||||
add_to_task_list("coolprop.parametric_table.py")
|
||||
add_to_task_list("coolprop.configuration.py")
|
||||
add_to_task_list("logo_2014.py")
|
||||
|
||||
159
Web/scripts/fluid_properties.Superancillary.py
Normal file
159
Web/scripts/fluid_properties.Superancillary.py
Normal file
@@ -0,0 +1,159 @@
|
||||
from __future__ import print_function
|
||||
import os.path
|
||||
import CoolProp
|
||||
import urllib.request
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from zipfile import ZipFile
|
||||
|
||||
web_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
root_dir = os.path.abspath(os.path.join(web_dir, '..'))
|
||||
fluids_path = os.path.join(web_dir, 'fluid_properties', 'fluids')
|
||||
plots_path = os.path.join(web_dir, 'fluid_properties', 'fluids', 'Superancillaryplots')
|
||||
|
||||
outputversion = '2025.04.27'
|
||||
if not Path(f'{outputversion}.zip').exists():
|
||||
print('Downloading the chebyshev output file to ', Path('.').absolute())
|
||||
urllib.request.urlretrieve(f'https://github.com/CoolProp/fastchebpure/archive/refs/tags/{outputversion}.zip', f'{outputversion}.zip')
|
||||
|
||||
with ZipFile(f'{outputversion}.zip') as z:
|
||||
z.extractall('.')
|
||||
|
||||
with (Path('.') / f'fastchebpure-{outputversion}' / '.gitignore').open('w') as fp:
|
||||
fp.write("*")
|
||||
|
||||
outputcheck = (Path('.') / f'fastchebpure-{outputversion}' / 'outputcheck').absolute()
|
||||
|
||||
|
||||
template = r"""
|
||||
import matplotlib
|
||||
matplotlib.use('Agg') # Force mpl to use a non-GUI backend
|
||||
import matplotlib.pyplot as plt
|
||||
import json
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import CoolProp.CoolProp as CP
|
||||
|
||||
CP.set_config_bool(CP.ENABLE_SUPERANCILLARIES, False)
|
||||
|
||||
AS = CP.AbstractState('HEOS', "{fluid}")
|
||||
|
||||
# Skip pseudo-pure fluids, pure fluids only; pseudo-pure do not have superancillaries
|
||||
if AS.fluid_param_string("pure") != "true":
|
||||
quit()
|
||||
|
||||
jEOS = json.loads(CP.get_fluid_param_string("{fluid}", "JSON"))[0]['EOS'][0]
|
||||
if 'SUPERANCILLARY' not in jEOS:
|
||||
fig = plt.figure()
|
||||
fig.text(0.5, 0.5, 'Superancillary not available')
|
||||
plt.savefig('{fluid:s}.png', dpi = 300)
|
||||
plt.savefig('{fluid:s}.pdf')
|
||||
plt.close()
|
||||
quit()
|
||||
else:
|
||||
jSuper = jEOS['SUPERANCILLARY']
|
||||
|
||||
superanc = CP.SuperAncillary(json.dumps(jSuper))
|
||||
RPname = AS.fluid_param_string("REFPROP_name")
|
||||
|
||||
# Load extended precision calcs from the release on github
|
||||
chk = json.load(open('{outputcheck}/{fluid}_check.json'))
|
||||
df = pd.DataFrame(chk['data'])
|
||||
# df.info() # uncomment to see what fields are available
|
||||
|
||||
Tcrit_num = AS.get_fluid_parameter_double(0, "SUPERANC::Tcrit_num")
|
||||
T = df['T / K'].to_numpy()
|
||||
Theta = (Tcrit_num-T)/Tcrit_num
|
||||
|
||||
fig, axes = plt.subplots(3, 1, sharex=True, figsize=(3.5,7))
|
||||
|
||||
|
||||
plt.sca(axes[0])
|
||||
rhoL_anc = np.zeros_like(T)
|
||||
superanc.eval_sat_many(T, 'D', 0, rhoL_anc)
|
||||
err = np.abs(df["rho'(mp) / mol/m^3"]/rhoL_anc-1)
|
||||
plt.plot(Theta, err, 'o', label=r'$\Upsilon$:SA')
|
||||
|
||||
errCP = np.abs(df["rho'(mp) / mol/m^3"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 0, f'HEOS::{fluid}')-1)
|
||||
plt.plot(Theta, errCP, '^', label=r'$\Upsilon$:HEOS')
|
||||
|
||||
try:
|
||||
errRP = np.abs(df["rho'(mp) / mol/m^3"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 0, f'REFPROP::{{RPname}}')-1)
|
||||
plt.plot(Theta, errRP, 'x', label=r'$\Upsilon$:REFPROP')
|
||||
except BaseException as BE:
|
||||
print(BE)
|
||||
|
||||
plt.legend(loc='best')
|
||||
plt.ylabel(r"$|\rho_{{\rm \Upsilon}}'/\rho_{{\rm ep}}'-1|$")
|
||||
plt.yscale('log')
|
||||
|
||||
|
||||
|
||||
plt.sca(axes[1])
|
||||
rhoV_anc = np.zeros_like(T)
|
||||
superanc.eval_sat_many(T, 'D', 1, rhoV_anc)
|
||||
err = np.abs(df["rho''(mp) / mol/m^3"]/rhoV_anc-1)
|
||||
plt.plot(Theta, err, 'o', label=r'$\Upsilon$:SA')
|
||||
|
||||
errCP = np.abs(df["rho''(mp) / mol/m^3"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 1, f'HEOS::{fluid}')-1)
|
||||
plt.plot(Theta, errCP, '^', label=r'$\Upsilon$:HEOS')
|
||||
|
||||
try:
|
||||
errRP = np.abs(df["rho''(mp) / mol/m^3"]/CP.PropsSI('Dmolar', 'T', T, 'Q', 1, f'REFPROP::{{RPname}}')-1)
|
||||
plt.plot(Theta, errRP, 'x', label=r'$\Upsilon$:REFPROP')
|
||||
except BaseException as BE:
|
||||
print(BE)
|
||||
|
||||
plt.legend(loc='best')
|
||||
plt.ylabel(r"$|\rho_{{\rm \Upsilon}}''/\rho_{{\rm ep}}''-1|$")
|
||||
plt.yscale('log')
|
||||
|
||||
|
||||
|
||||
plt.sca(axes[2])
|
||||
p_anc = np.zeros_like(T)
|
||||
superanc.eval_sat_many(T, 'P', 1, p_anc)
|
||||
err = np.abs(df["p(mp) / Pa"]/p_anc-1)
|
||||
plt.plot(Theta, err, 'o', label=r'$\Upsilon$:SA')
|
||||
|
||||
errCP = np.abs(df["p(mp) / Pa"]/CP.PropsSI('P', 'T', T, 'Q', 1, f'HEOS::{fluid}')-1)
|
||||
plt.plot(Theta, errCP, '^', label=r'$\Upsilon$:HEOS')
|
||||
|
||||
try:
|
||||
errRP = np.abs(df["p(mp) / Pa"]/CP.PropsSI('P', 'T', T, 'Q', 1, f'REFPROP::{{RPname}}')-1)
|
||||
plt.plot(Theta, errRP, 'x', label=r'$\Upsilon$:REFPROP')
|
||||
except BaseException as BE:
|
||||
print(BE)
|
||||
plt.legend(loc='best')
|
||||
|
||||
# print(CP.PropsSI('gas_constant', 'T', T[0], 'Q', 1, f'HEOS::{fluid}'))
|
||||
# print(CP.PropsSI('gas_constant', 'T', T[0], 'Q', 1, f'REFPROP::{fluid}'))
|
||||
|
||||
plt.ylabel(r"$|p_{{\rm \Upsilon}}/p_{{\rm ep}}-1|$")
|
||||
plt.yscale('log')
|
||||
|
||||
plt.sca(axes[2])
|
||||
plt.xlabel(r'$\Theta=(T_{{\rm crit,num}}-T)/T_{{\rm crit,num}}$')
|
||||
plt.xscale('log')
|
||||
|
||||
plt.suptitle('Superancillary v. Extended Precision')
|
||||
plt.tight_layout(pad=0.2, rect=(0,0,1,0.95))
|
||||
plt.savefig('{fluid:s}.png', dpi = 300)
|
||||
plt.savefig('{fluid:s}.pdf')
|
||||
plt.close()
|
||||
"""
|
||||
if not os.path.exists(plots_path):
|
||||
os.makedirs(plots_path)
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
for fluid in CoolProp.__fluids__:
|
||||
print('fluid:', fluid)
|
||||
file_string = template.format(fluid=fluid, outputcheck=outputcheck)
|
||||
file_path = os.path.join(plots_path, fluid + '.py')
|
||||
print('Writing to', file_path)
|
||||
with open(file_path, 'w') as fp:
|
||||
fp.write(file_string)
|
||||
print('calling:', 'python "' + fluid + '.py"', 'in', plots_path)
|
||||
subprocess.check_call('python -u "' + fluid + '.py"', cwd=plots_path, stdout=sys.stdout, stderr=sys.stderr, shell=True)
|
||||
@@ -4,10 +4,12 @@ import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
#
|
||||
|
||||
def build_sdist(wheel_directory, config_settings=None):
|
||||
return _orig.build_sdist(wheel_directory, config_settings)
|
||||
|
||||
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
|
||||
sys.path.append(str(Path(__file__).parent))
|
||||
os.chdir('wrappers/Python')
|
||||
return _orig.build_wheel(wheel_directory, config_settings, metadata_directory)
|
||||
return _orig.build_wheel(wheel_directory, config_settings, metadata_directory)
|
||||
|
||||
@@ -6,14 +6,14 @@ FROM ubuntu:24.04
|
||||
RUN apt-get -y -m update && DEBIAN_FRONTEND=noninteractive apt-get install -y cmake g++ git zip wget xz-utils
|
||||
|
||||
RUN mkdir /boost && \
|
||||
wget -c --no-check-certificate https://boostorg.jfrog.io/artifactory/main/release/1.87.0/source/boost_1_87_0_rc1.tar.gz -O - | tar -xz -C /boost && \
|
||||
wget -c --no-check-certificate https://archives.boost.io/release/1.87.0/source/boost_1_87_0.tar.gz -O - | tar -xz -C /boost && \
|
||||
cd /boost/boost_1_87_0/ && \
|
||||
./bootstrap.sh && \
|
||||
./b2 tools/bcp
|
||||
|
||||
WORKDIR /boost/boost_1_87_0/
|
||||
RUN mkdir /boost_CoolProp && \
|
||||
./bin.v2/tools/bcp/gcc-13/debug/x86_64/link-static/threading-multi/visibility-hidden/bcp predef/other/endian.h boost/fusion/sequence/intrinsic/size.hpp boost/fusion/algorithm/iteration/for_each.hpp boost/fusion/include/mpl.hpp boost/fusion/sequence/intrinsic/at.hpp boost/utility/string_ref.hpp boost/utility/string_view.hpp boost/mpl/size.hpp boost/variant.hpp boost/assert.hpp boost/preprocessor.hpp boost/fusion/support/is_sequence.hpp boost/optional.hpp boost/operators.hpp boost/version.hpp /boost_CoolProp && \
|
||||
dist/bin/bcp predef/other/endian.h boost/fusion/sequence/intrinsic/size.hpp boost/fusion/algorithm/iteration/for_each.hpp boost/fusion/include/mpl.hpp boost/fusion/sequence/intrinsic/at.hpp boost/utility/string_ref.hpp boost/utility/string_view.hpp boost/mpl/size.hpp boost/variant.hpp boost/assert.hpp boost/preprocessor.hpp boost/fusion/support/is_sequence.hpp boost/optional.hpp boost/operators.hpp boost/version.hpp math/tools/toms748_solve.hpp /boost_CoolProp && \
|
||||
zip -r /boost_CoolProp.zip /boost_CoolProp && \
|
||||
tar cJf /boost_CoolProp.tar.xz /boost_CoolProp
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
5928
dev/fluids/R125.json
5928
dev/fluids/R125.json
File diff suppressed because it is too large
Load Diff
@@ -3798,7 +3798,8 @@
|
||||
"R1336mzz(E)",
|
||||
"(E)-1,1,1,4,4,4-Hexafluoro-2-butene",
|
||||
"R1336MZZ(E)",
|
||||
"(e)-1,1,1,4,4,4-hexafluoro-2-butene"
|
||||
"(e)-1,1,1,4,4,4-hexafluoro-2-butene",
|
||||
"R1336MZZE"
|
||||
],
|
||||
"CAS": "66711-86-2",
|
||||
"CHEMSPIDER_ID": -1,
|
||||
@@ -3816,7 +3817,7 @@
|
||||
"FORMULA": "C_{4}H_{2}F_{6}",
|
||||
"INCHI_KEY": "NLOLSXYRJFEOTA-OWOJBTEDSA-N",
|
||||
"INCHI_STRING": "InChI=1S/C4H2F6/c5-3(6,7)1-2-4(8,9)10/h1-2H/b2-1+",
|
||||
"NAME": "R1336MZZE",
|
||||
"NAME": "R1336mzz(E)",
|
||||
"REFPROP_NAME": "R1336MZZE",
|
||||
"SMILES": "?"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
FLUIDS = Path(__file__).parent / 'fluids'
|
||||
FLUIDS = Path(__file__).parent.parent / 'fluids'
|
||||
print(FLUIDS)
|
||||
assert(FLUIDS.exists())
|
||||
FASTCHEB_OUT = Path(r"D:\Code\fastchebpure")
|
||||
FASTCHEB_OUT = Path(r"/Users/ianbell/Documents/Code/fastchebpure/output")
|
||||
assert(FASTCHEB_OUT.exists())
|
||||
for path in FASTCHEB_OUT.glob('*.json'):
|
||||
print(path)
|
||||
name = path.stem.replace('_exps','')
|
||||
contents = json.load(path.open())
|
||||
destpath = FLUIDS / f'{name}.json'
|
||||
dest = json.load(destpath.open())
|
||||
dest['EOS'][0]['SUPERANCILLARY'] = contents
|
||||
with destpath.open('w') as fp:
|
||||
fp.write(json.dumps(dest, indent=2))
|
||||
fp.write(json.dumps(dest, indent=2))
|
||||
|
||||
24765
externals/nlohmann-json/nlohmann/json.hpp
vendored
Normal file
24765
externals/nlohmann-json/nlohmann/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -838,6 +838,11 @@ class AbstractState
|
||||
|
||||
/// Update the state using two state variables
|
||||
virtual void update(CoolProp::input_pairs input_pair, double Value1, double Value2) = 0;
|
||||
|
||||
/// Update the state for QT inputs for pure fluids when using the superancillary functions
|
||||
virtual void update_QT_pure_superanc(double Q, double T){
|
||||
throw NotImplementedError("update_QT_pure_superanc is not implemented for this backend");
|
||||
};
|
||||
|
||||
/// Update the state using two state variables and providing guess values
|
||||
/// Some or all of the guesses will be used - this is backend dependent
|
||||
@@ -867,7 +872,6 @@ class AbstractState
|
||||
virtual const double get_fluid_constant(std::size_t i, parameters param) const {
|
||||
throw NotImplementedError("get_fluid_constant is not implemented for this backend");
|
||||
};
|
||||
;
|
||||
|
||||
/// Set binary mixture floating point parameter (EXPERT USE ONLY!!!)
|
||||
virtual void set_binary_interaction_double(const std::string& CAS1, const std::string& CAS2, const std::string& parameter, const double value) {
|
||||
|
||||
@@ -93,8 +93,8 @@ class CacheArrayElement
|
||||
{
|
||||
|
||||
private:
|
||||
bool& is_cached;
|
||||
NumType& value;
|
||||
bool& is_cached;
|
||||
|
||||
public:
|
||||
|
||||
@@ -153,7 +153,7 @@ private:
|
||||
std::array<bool, N> m_cached;
|
||||
public:
|
||||
void clear(){
|
||||
std::fill_n(m_values.data(), m_values.size(), _HUGE);
|
||||
memset(m_values.data(), 0, sizeof(m_values));
|
||||
memset(m_cached.data(), false, sizeof(m_cached));
|
||||
}
|
||||
auto factory(std::size_t i){
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
X(VTPR_ALWAYS_RELOAD_LIBRARY, "VTPR_ALWAYS_RELOAD_LIBRARY", false, \
|
||||
"If true, the library will always be reloaded, no matter what is currently loaded") \
|
||||
X(FLOAT_PUNCTUATION, "FLOAT_PUNCTUATION", ".", "The first character of this string will be used as the separator between the number fraction.") \
|
||||
X(ENABLE_SUPERANCILLARIES, "ENABLE_SUPERANCILLARIES", true, "If true, the superancillary functions will be used for VLE of pure fluids") \
|
||||
X(LIST_STRING_DELIMITER, "LIST_STRING_DELIMITER", ",", "The delimiter to be used when converting a list of strings to a string")
|
||||
|
||||
// Use preprocessor to create the Enum
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include "Eigen/Core"
|
||||
#include "PolyMath.h"
|
||||
#include "Ancillaries.h"
|
||||
#include "superancillary/superancillary.h"
|
||||
|
||||
namespace CoolProp {
|
||||
|
||||
@@ -417,6 +420,13 @@ struct Ancillaries
|
||||
*/
|
||||
class EquationOfState
|
||||
{
|
||||
public:
|
||||
using SuperAncillary_t = superancillary::SuperAncillary<std::vector<double>>;
|
||||
|
||||
private:
|
||||
std::string superancillaries_str;
|
||||
std::optional<SuperAncillary_t> superancillaries; ///< The superancillaries
|
||||
|
||||
public:
|
||||
EquationOfState(){};
|
||||
~EquationOfState(){};
|
||||
@@ -439,6 +449,32 @@ class EquationOfState
|
||||
BibTeX_CP0; ///< The bibtex key for the ideal gas specific heat correlation
|
||||
CriticalRegionSplines
|
||||
critical_region_splines; ///< A cubic spline in the form T = f(rho) for saturated liquid and saturated vapor curves in the near-critical region
|
||||
|
||||
/// Get the optional of the populated superancillary
|
||||
std::optional<SuperAncillary_t> & get_superanc_optional(){
|
||||
|
||||
if (!superancillaries){
|
||||
if (!superancillaries_str.empty()){
|
||||
auto start = std::chrono::high_resolution_clock::now(); // Start time
|
||||
// Now do the parsing pass and replace with the actual superancillary
|
||||
superancillaries.emplace(SuperAncillary_t(superancillaries_str));
|
||||
auto end = std::chrono::high_resolution_clock::now(); // End time
|
||||
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
std::cout << "Execution time: " << duration.count() << " microseconds for " << BibTeX_EOS << std::endl;
|
||||
}
|
||||
}
|
||||
return superancillaries;
|
||||
}
|
||||
/// Set the placeholder string for the superancillaries to allow for lazy construction, particularly important in debug builds
|
||||
void set_superancillaries_str(const std::string &s){
|
||||
superancillaries_str = s;
|
||||
// Do the construction greedily by default, but allow it to be lazy if you want
|
||||
#if !defined(LAZY_LOAD_SUPERANCILLARIES)
|
||||
get_superanc_optional();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Validate the EOS that was just constructed
|
||||
void validate() {
|
||||
|
||||
@@ -741,6 +741,7 @@ class BaseHelmholtzContainer
|
||||
{
|
||||
protected:
|
||||
std::array<double, 16> cache;
|
||||
std::array<bool, 16> is_cached;
|
||||
constexpr static std::size_t i00 = 0, i01 = 1, i02 = 2, i03 = 3, i04 = 4,
|
||||
i10 = 5, i11 = 6, i12 = 7, i13 = 8,
|
||||
i20 = 9, i21 = 10, i22 = 11,
|
||||
@@ -748,12 +749,13 @@ class BaseHelmholtzContainer
|
||||
i40 = 14;
|
||||
|
||||
bool cache_valid(std::size_t i) const {
|
||||
return std::isfinite(cache[i]);
|
||||
return is_cached[i];
|
||||
}
|
||||
|
||||
public:
|
||||
void clear() {
|
||||
std::fill_n(cache.data(), cache.size(), _HUGE);
|
||||
memset(cache.data(), 0, sizeof(cache));
|
||||
memset(is_cached.data(), false, sizeof(is_cached));
|
||||
};
|
||||
|
||||
virtual void empty_the_EOS() = 0;
|
||||
@@ -874,6 +876,7 @@ class ResidualHelmholtzContainer : public BaseHelmholtzContainer
|
||||
cache[i03] = derivs.d3alphar_dtau3;
|
||||
cache[i21] = derivs.d3alphar_ddelta2_dtau;
|
||||
cache[i12] = derivs.d3alphar_ddelta_dtau2;
|
||||
memset(is_cached.data(), true, sizeof(is_cached));
|
||||
}
|
||||
return derivs;
|
||||
};
|
||||
@@ -1456,6 +1459,7 @@ class IdealHelmholtzContainer : public BaseHelmholtzContainer
|
||||
cache[i03] = derivs.d3alphar_dtau3 * _prefactor;
|
||||
cache[i21] = derivs.d3alphar_ddelta2_dtau * _prefactor;
|
||||
cache[i12] = derivs.d3alphar_ddelta_dtau2 * _prefactor;
|
||||
memset(is_cached.data(), true, sizeof(is_cached));
|
||||
}
|
||||
return derivs * _prefactor;
|
||||
};
|
||||
|
||||
1378
include/superancillary/superancillary.h
Normal file
1378
include/superancillary/superancillary.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -176,19 +176,18 @@ std::vector<std::string> AbstractState::fluid_names(void) {
|
||||
bool AbstractState::clear_comp_change() {
|
||||
// Reset all instances of CachedElement and overwrite
|
||||
// the internal double values with -_HUGE
|
||||
this->_gas_constant.clear();
|
||||
this->_molar_mass.clear();
|
||||
this->_critical.fill(_HUGE);
|
||||
this->_reducing.fill(_HUGE);
|
||||
calc_reducing_state();
|
||||
this->_gas_constant = calc_gas_constant();
|
||||
return true;
|
||||
}
|
||||
bool AbstractState::clear() {
|
||||
// Reset all instances of CachedElement and overwrite
|
||||
// the internal double values with -_HUGE
|
||||
// Reset all instances of CachedElement
|
||||
cache.clear();
|
||||
|
||||
this->_critical.fill(_HUGE);
|
||||
this->_reducing.fill(_HUGE);
|
||||
|
||||
/// Bulk values
|
||||
this->_rhomolar = -_HUGE;
|
||||
|
||||
@@ -341,7 +341,31 @@ void FlashRoutines::QS_flash(HelmholtzEOSMixtureBackend& HEOS) {
|
||||
}
|
||||
void FlashRoutines::QT_flash(HelmholtzEOSMixtureBackend& HEOS) {
|
||||
CoolPropDbl T = HEOS._T;
|
||||
CoolPropDbl Q = HEOS._Q;
|
||||
if (HEOS.is_pure_or_pseudopure) {
|
||||
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && HEOS.is_pure()){
|
||||
auto& optsuperanc = HEOS.get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
auto& superanc = optsuperanc.value();
|
||||
|
||||
CoolPropDbl Tcrit_num = superanc.get_Tcrit_num();
|
||||
if (T > Tcrit_num){
|
||||
throw ValueError(format("Temperature to QT_flash [%0.8Lg K] may not be above the numerical critical point of %0.15Lg K", T, Tcrit_num));
|
||||
}
|
||||
auto rhoL = superanc.eval_sat(T, 'D', 0);
|
||||
auto rhoV = superanc.eval_sat(T, 'D', 1);
|
||||
auto p = superanc.eval_sat(T, 'P', 1);
|
||||
HEOS.SatL->update_TDmolarP_unchecked(T, rhoL, p);
|
||||
HEOS.SatV->update_TDmolarP_unchecked(T, rhoV, p);
|
||||
HEOS._p = p;
|
||||
HEOS._rhomolar = 1 / (Q / rhoV + (1 - Q) / rhoL);
|
||||
HEOS._phase = iphase_twophase;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The maximum possible saturation temperature
|
||||
// Critical point for pure fluids, slightly different for pseudo-pure, very different for mixtures
|
||||
CoolPropDbl Tmax_sat = HEOS.calc_Tmax_sat() + 1e-13;
|
||||
@@ -353,9 +377,9 @@ void FlashRoutines::QT_flash(HelmholtzEOSMixtureBackend& HEOS) {
|
||||
|
||||
// Get a reference to keep the code a bit cleaner
|
||||
const CriticalRegionSplines& splines = HEOS.components[0].EOS().critical_region_splines;
|
||||
|
||||
// If exactly(ish) at the critical temperature, liquid and vapor have the critial density
|
||||
|
||||
if ((get_config_bool(CRITICAL_WITHIN_1UK) && std::abs(T - Tmax_sat) < 1e-6) || std::abs(T - Tmax_sat) < 1e-12) {
|
||||
// If exactly(ish) at the critical temperature, liquid and vapor have the critial density
|
||||
HEOS.SatL->update(DmolarT_INPUTS, HEOS.rhomolar_critical(), HEOS._T);
|
||||
HEOS.SatV->update(DmolarT_INPUTS, HEOS.rhomolar_critical(), HEOS._T);
|
||||
HEOS._rhomolar = HEOS.rhomolar_critical();
|
||||
@@ -567,6 +591,29 @@ void get_Henrys_coeffs_FP(const std::string& CAS, double& A, double& B, double&
|
||||
}
|
||||
void FlashRoutines::PQ_flash(HelmholtzEOSMixtureBackend& HEOS) {
|
||||
if (HEOS.is_pure_or_pseudopure) {
|
||||
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && HEOS.is_pure()){
|
||||
auto& optsuperanc = HEOS.get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
auto& superanc = optsuperanc.value();
|
||||
CoolPropDbl pmax_num = superanc.get_pmax();
|
||||
if (HEOS._p > pmax_num){
|
||||
throw ValueError(format("Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa", HEOS._p, pmax_num));
|
||||
}
|
||||
auto T = superanc.get_T_from_p(HEOS._p);
|
||||
auto rhoL = superanc.eval_sat(T, 'D', 0);
|
||||
auto rhoV = superanc.eval_sat(T, 'D', 1);
|
||||
auto p = HEOS._p;
|
||||
HEOS.SatL->update_TDmolarP_unchecked(T, rhoL, p);
|
||||
HEOS.SatV->update_TDmolarP_unchecked(T, rhoV, p);
|
||||
HEOS._T = T;
|
||||
HEOS._p = p;
|
||||
HEOS._rhomolar = 1 / (HEOS._Q / HEOS.SatV->rhomolar() + (1 - HEOS._Q) / HEOS.SatL->rhomolar());
|
||||
HEOS._phase = iphase_twophase;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (HEOS.components[0].EOS().pseudo_pure) {
|
||||
// It is a pseudo-pure mixture
|
||||
|
||||
@@ -1551,6 +1598,19 @@ void FlashRoutines::HSU_P_flash(HelmholtzEOSMixtureBackend& HEOS, parameters oth
|
||||
if (HEOS._p < HEOS.p_triple()) {
|
||||
Tmin = std::max(HEOS.Tmin(), HEOS.Ttriple());
|
||||
} else {
|
||||
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && HEOS.is_pure()){
|
||||
auto& optsuperanc = HEOS.get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
auto& superanc = optsuperanc.value();
|
||||
CoolPropDbl pmax_num = superanc.get_pmax();
|
||||
if (HEOS._p > pmax_num){
|
||||
throw ValueError(format("Pressure to PQ_flash [%0.8Lg Pa] may not be above the numerical critical point of %0.15Lg Pa", HEOS._p, pmax_num));
|
||||
}
|
||||
Tmin = superanc.get_T_from_p(HEOS._p)-1e-12;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (saturation_called) {
|
||||
Tmin = HEOS.SatV->T();
|
||||
} else {
|
||||
|
||||
@@ -391,6 +391,12 @@ class JSONFluidLibrary
|
||||
// BibTex keys
|
||||
EOS.BibTeX_EOS = cpjson::get_string(EOS_json, "BibTeX_EOS");
|
||||
EOS.BibTeX_CP0 = cpjson::get_string(EOS_json, "BibTeX_CP0");
|
||||
|
||||
if (EOS_json.HasMember("SUPERANCILLARY")){
|
||||
// This is inefficient as we do JSON(rapidjson) -> string -> JSON(nlohmann)
|
||||
// which implies two large parsing passes
|
||||
EOS.set_superancillaries_str(cpjson::json2string(EOS_json["SUPERANCILLARY"]));
|
||||
}
|
||||
|
||||
EOS.alphar = parse_alphar(EOS_json["alphar"]);
|
||||
EOS.alpha0 = parse_alpha0(EOS_json["alpha0"]);
|
||||
|
||||
@@ -100,6 +100,8 @@ void HelmholtzEOSMixtureBackend::set_components(const std::vector<CoolPropFluid>
|
||||
mole_fractions = std::vector<CoolPropDbl>(1, 1);
|
||||
std::vector<std::vector<double>> ones(1, std::vector<double>(1, 1));
|
||||
Reducing = shared_ptr<ReducingFunction>(new GERG2008ReducingFunction(components, ones, ones, ones, ones));
|
||||
_reducing = calc_reducing_state_nocache(mole_fractions);
|
||||
_gas_constant = calc_gas_constant();
|
||||
} else {
|
||||
// Set the mixture parameters - binary pair reducing functions, departure functions, F_ij, etc.
|
||||
set_mixture_parameters();
|
||||
@@ -113,8 +115,10 @@ void HelmholtzEOSMixtureBackend::set_components(const std::vector<CoolPropFluid>
|
||||
SatL.reset(get_copy(false));
|
||||
SatL->specify_phase(iphase_liquid);
|
||||
linked_states.push_back(SatL);
|
||||
SatL->clear();
|
||||
SatV.reset(get_copy(false));
|
||||
SatV->specify_phase(iphase_gas);
|
||||
SatV->clear();
|
||||
linked_states.push_back(SatV);
|
||||
}
|
||||
}
|
||||
@@ -257,6 +261,47 @@ std::string HelmholtzEOSMixtureBackend::fluid_param_string(const std::string& Pa
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<EquationOfState::SuperAncillary_t>& HelmholtzEOSMixtureBackend::get_superanc_optional(){
|
||||
if (!is_pure()){
|
||||
throw CoolProp::ValueError("Only available for pure (and not pseudo-pure) fluids");
|
||||
}
|
||||
return components[0].EOS().get_superanc_optional();
|
||||
}
|
||||
|
||||
double HelmholtzEOSMixtureBackend::get_fluid_parameter_double(const size_t i, const std::string& parameter){
|
||||
if (i >= N) {
|
||||
throw ValueError(format("Index i [%d] is out of bounds. Must be between 0 and %d.", i, N-1));
|
||||
}
|
||||
auto& optsuperanc = get_superanc_optional();
|
||||
if (parameter.find("SUPERANC::") == 0){
|
||||
auto& superanc = optsuperanc.value();
|
||||
if (optsuperanc){
|
||||
std::string key = parameter.substr(10);
|
||||
if (key == "pmax"){
|
||||
return superanc.get_pmax();
|
||||
}
|
||||
else if (key == "pmin"){
|
||||
return superanc.get_pmin();
|
||||
}
|
||||
else if (key == "Tmin"){
|
||||
return superanc.get_Tmin();
|
||||
}
|
||||
else if (key == "Tcrit_num"){
|
||||
return superanc.get_Tcrit_num();
|
||||
}
|
||||
else {
|
||||
throw ValueError(format("Superancillary parameter [%s] is invalid", key.c_str()));
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw ValueError(format("Superancillary not available for this fluid"));
|
||||
}
|
||||
} else {
|
||||
throw ValueError(format("fluid parameter [%s] is invalid", parameter.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HelmholtzEOSMixtureBackend::apply_simple_mixing_rule(std::size_t i, std::size_t j, const std::string& model) {
|
||||
// bound-check indices
|
||||
if (i < 0 || i >= N) {
|
||||
@@ -1040,6 +1085,12 @@ CoolPropDbl HelmholtzEOSMixtureBackend::calc_T_critical(void) {
|
||||
throw ValueError(format("critical point finding routine found %d critical points", critpts.size()));
|
||||
}
|
||||
} else {
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && is_pure()){
|
||||
auto& optsuperanc = get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
return optsuperanc.value().get_Tcrit_num(); // from the numerical critical point satisfying dp/drho|T = d2p/drho2|T = 0
|
||||
}
|
||||
}
|
||||
return components[0].crit.T;
|
||||
}
|
||||
}
|
||||
@@ -1053,6 +1104,12 @@ CoolPropDbl HelmholtzEOSMixtureBackend::calc_p_critical(void) {
|
||||
throw ValueError(format("critical point finding routine found %d critical points", critpts.size()));
|
||||
}
|
||||
} else {
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && is_pure()){
|
||||
auto& optsuperanc = get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
return optsuperanc.value().get_pmax(); // from the numerical critical point satisfying dp/drho|T = d2p/drho2|T = 0
|
||||
}
|
||||
}
|
||||
return components[0].crit.p;
|
||||
}
|
||||
}
|
||||
@@ -1066,6 +1123,12 @@ CoolPropDbl HelmholtzEOSMixtureBackend::calc_rhomolar_critical(void) {
|
||||
throw ValueError(format("critical point finding routine found %d critical points", critpts.size()));
|
||||
}
|
||||
} else {
|
||||
if (get_config_bool(ENABLE_SUPERANCILLARIES) && is_pure()){
|
||||
auto& optsuperanc = get_superanc_optional();
|
||||
if (optsuperanc){
|
||||
return optsuperanc.value().get_rhocrit_num(); // from the numerical critical point satisfying dp/drho|T = d2p/drho2|T = 0
|
||||
}
|
||||
}
|
||||
return components[0].crit.rhomolar;
|
||||
}
|
||||
}
|
||||
@@ -1142,6 +1205,54 @@ CoolPropDbl HelmholtzEOSMixtureBackend::calc_pmax(void) {
|
||||
return summer;
|
||||
}
|
||||
|
||||
void HelmholtzEOSMixtureBackend::update_QT_pure_superanc(CoolPropDbl Q, CoolPropDbl T) {
|
||||
clear();
|
||||
_Q = Q;
|
||||
_T = T;
|
||||
if (!is_pure()){
|
||||
throw ValueError(format("Is not a pure fluid"));
|
||||
}
|
||||
if (!get_config_bool(ENABLE_SUPERANCILLARIES)){
|
||||
throw ValueError(format("Superancillaries are not enabled"));
|
||||
}
|
||||
auto& optsuperanc = get_superanc_optional();
|
||||
if (!optsuperanc){
|
||||
throw ValueError(format("Superancillaries not available for this fluid"));
|
||||
}
|
||||
|
||||
auto& superanc = optsuperanc.value();
|
||||
CoolPropDbl Tcrit_num = superanc.get_Tcrit_num();
|
||||
if (T > Tcrit_num){
|
||||
throw ValueError(format("Temperature to QT_flash [%0.8Lg K] may not be above the numerical critical point of %0.15Lg K", T, Tcrit_num));
|
||||
}
|
||||
auto rhoL = superanc.eval_sat(T, 'D', 0);
|
||||
auto rhoV = superanc.eval_sat(T, 'D', 1);
|
||||
auto p = superanc.eval_sat(T, 'P', 1);
|
||||
SatL->update_TDmolarP_unchecked(T, rhoL, p);
|
||||
SatV->update_TDmolarP_unchecked(T, rhoV, p);
|
||||
_p = p;
|
||||
_rhomolar = 1 / (Q / rhoV + (1 - Q) / rhoL);
|
||||
_phase = iphase_twophase;
|
||||
|
||||
post_update(false /*optional_checks*/);
|
||||
}
|
||||
|
||||
|
||||
void HelmholtzEOSMixtureBackend::update_TDmolarP_unchecked(CoolPropDbl T, CoolPropDbl rhomolar, CoolPropDbl p) {
|
||||
|
||||
clear();
|
||||
|
||||
_rhomolar = rhomolar;
|
||||
_T = T;
|
||||
_p = p;
|
||||
|
||||
// Cleanup
|
||||
bool optional_checks = false;
|
||||
post_update(optional_checks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void HelmholtzEOSMixtureBackend::update_DmolarT_direct(CoolPropDbl rhomolar, CoolPropDbl T) {
|
||||
// TODO: This is just a quick fix for #878 - should be done more systematically
|
||||
const CoolPropDbl rhomolar_min = 0;
|
||||
@@ -1411,7 +1522,9 @@ void HelmholtzEOSMixtureBackend::post_update(bool optional_checks) {
|
||||
_delta = _rhomolar / _reducing.rhomolar;
|
||||
|
||||
// Update the terms in the excess contribution
|
||||
residual_helmholtz->Excess.update(_tau, _delta);
|
||||
if (!is_pure_or_pseudopure){
|
||||
residual_helmholtz->Excess.update(_tau, _delta);
|
||||
}
|
||||
}
|
||||
|
||||
CoolPropDbl HelmholtzEOSMixtureBackend::calc_Bvirial() {
|
||||
|
||||
@@ -104,6 +104,8 @@ class HelmholtzEOSMixtureBackend : public AbstractState
|
||||
std::vector<CoolProp::CriticalState> _calc_all_critical_points(bool find_critical_points = true);
|
||||
|
||||
static void set_fluid_enthalpy_entropy_offset(CoolPropFluid& component, double delta_a1, double delta_a2, const std::string& ref);
|
||||
|
||||
std::optional<EquationOfState::SuperAncillary_t>& get_superanc_optional();
|
||||
|
||||
public:
|
||||
HelmholtzEOSMixtureBackend();
|
||||
@@ -194,6 +196,7 @@ class HelmholtzEOSMixtureBackend : public AbstractState
|
||||
virtual void set_fluid_parameter_double(const size_t i, const std::string& parameter, const double value) {
|
||||
throw ValueError("set_fluid_parameter_double only defined for cubic backends");
|
||||
};
|
||||
virtual double get_fluid_parameter_double(const size_t i, const std::string& parameter);
|
||||
|
||||
phases calc_phase(void) {
|
||||
return _phase;
|
||||
@@ -356,6 +359,8 @@ class HelmholtzEOSMixtureBackend : public AbstractState
|
||||
*/
|
||||
void update_TP_guessrho(CoolPropDbl T, CoolPropDbl p, CoolPropDbl rho_guess);
|
||||
void update_DmolarT_direct(CoolPropDbl rhomolar, CoolPropDbl T);
|
||||
void update_TDmolarP_unchecked(CoolPropDbl T, CoolPropDbl rhomolarL, CoolPropDbl p);
|
||||
void update_QT_pure_superanc(CoolPropDbl Q, CoolPropDbl T);
|
||||
void update_HmolarQ_with_guessT(CoolPropDbl hmolar, CoolPropDbl Q, CoolPropDbl Tguess);
|
||||
|
||||
/** \brief Set the components of the mixture
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "DataStructures.h"
|
||||
#include "../Backends/Helmholtz/HelmholtzEOSMixtureBackend.h"
|
||||
#include "../Backends/Helmholtz/HelmholtzEOSBackend.h"
|
||||
#include "superancillary/superancillary.h"
|
||||
|
||||
// ############################################
|
||||
// TESTS
|
||||
// ############################################
|
||||
@@ -2315,7 +2317,14 @@ TEST_CASE("Github issue #2470", "[pureflash]") {
|
||||
auto pressure = 3368965.046; //Pa
|
||||
std::shared_ptr<CoolProp::AbstractState> AS;
|
||||
AS.reset(AbstractState::factory("HEOS", fluide));
|
||||
AS->update(PQ_INPUTS, pressure, 1);
|
||||
auto Ts = AS->T();
|
||||
AS->specify_phase(iphase_gas);
|
||||
CHECK_NOTHROW(AS->update(PT_INPUTS, pressure, Ts));
|
||||
AS->unspecify_phase();
|
||||
CHECK_NOTHROW(AS->update(HmassP_INPUTS, enthalpy, pressure));
|
||||
auto Tfinal = AS->T();
|
||||
CHECK(Tfinal > AS->T_critical());
|
||||
}
|
||||
|
||||
TEST_CASE("Github issue #2467", "[pureflash]") {
|
||||
@@ -2411,6 +2420,214 @@ TEST_CASE("Check that indexes for mixtures are assigned correctly, especially fo
|
||||
CHECK(abs((p_extra - p)/ p * 100) < 1e-1);
|
||||
}
|
||||
|
||||
/// A fixture class to enable superancillaries just for a given test
|
||||
class SuperAncillaryOnFixture{
|
||||
private:
|
||||
const configuration_keys m_key = ENABLE_SUPERANCILLARIES;
|
||||
const bool initial_value;
|
||||
public:
|
||||
SuperAncillaryOnFixture() : initial_value(CoolProp::get_config_bool(m_key)) {
|
||||
CoolProp::set_config_bool(m_key, true);
|
||||
}
|
||||
~SuperAncillaryOnFixture(){
|
||||
CoolProp::set_config_bool(m_key, initial_value);
|
||||
}
|
||||
};
|
||||
|
||||
/// A fixture class to enable superancillaries just for a given test
|
||||
class SuperAncillaryOffFixture{
|
||||
private:
|
||||
const configuration_keys m_key = ENABLE_SUPERANCILLARIES;
|
||||
const bool initial_value;
|
||||
public:
|
||||
SuperAncillaryOffFixture() : initial_value(CoolProp::get_config_bool(m_key)) {
|
||||
CoolProp::set_config_bool(m_key, false);
|
||||
}
|
||||
~SuperAncillaryOffFixture(){
|
||||
CoolProp::set_config_bool(m_key, initial_value);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check superancillary for water", "[superanc]") {
|
||||
|
||||
auto json = nlohmann::json::parse(get_fluid_param_string("WATER", "JSON"))[0].at("EOS")[0].at("SUPERANCILLARY");
|
||||
superancillary::SuperAncillary<std::vector<double>> anc{json};
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
|
||||
shared_ptr<CoolProp::AbstractState> IF97(CoolProp::AbstractState::factory("IF97", "Water"));
|
||||
auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
|
||||
BENCHMARK("HEOS.clear()"){
|
||||
return rHEOS.clear();
|
||||
};
|
||||
BENCHMARK("HEOS rho(T)"){
|
||||
return AS->update(QT_INPUTS, 1.0, 300.0);
|
||||
};
|
||||
BENCHMARK("HEOS update_QT_pure_superanc(Q,T)"){
|
||||
return rHEOS.update_QT_pure_superanc(1.0, 300.0);
|
||||
};
|
||||
BENCHMARK("superanc rho(T)"){
|
||||
return anc.eval_sat(300.0, 'D', 1);
|
||||
};
|
||||
BENCHMARK("IF97 rho(T)"){
|
||||
return IF97->update(QT_INPUTS, 1.0, 300.0);
|
||||
};
|
||||
|
||||
double Tmin = AS->get_fluid_parameter_double(0, "SUPERANC::Tmin");
|
||||
double Tc = AS->get_fluid_parameter_double(0, "SUPERANC::Tcrit_num");
|
||||
double pmin = AS->get_fluid_parameter_double(0, "SUPERANC::pmin");
|
||||
double pmax = AS->get_fluid_parameter_double(0, "SUPERANC::pmax");
|
||||
|
||||
CHECK_THROWS(AS->get_fluid_parameter_double(1, "SUPERANC::pmax"));
|
||||
|
||||
BENCHMARK("HEOS rho(p)"){
|
||||
return AS->update(PQ_INPUTS, 101325, 1.0);
|
||||
};
|
||||
BENCHMARK("superanc T(p)"){
|
||||
return anc.get_T_from_p(101325);
|
||||
};
|
||||
BENCHMARK("IF97 rho(p)"){
|
||||
return IF97->update(PQ_INPUTS, 101325, 1.0);
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOffFixture, "Check superancillary-like calculations with superancillary disabled for water", "[superanc]") {
|
||||
|
||||
auto json = nlohmann::json::parse(get_fluid_param_string("WATER", "JSON"))[0].at("EOS")[0].at("SUPERANCILLARY");
|
||||
superancillary::SuperAncillary<std::vector<double>> anc{json};
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
|
||||
shared_ptr<CoolProp::AbstractState> IF97(CoolProp::AbstractState::factory("IF97", "Water"));
|
||||
auto& approxrhoL = anc.get_approx1d('D', 0);
|
||||
|
||||
BENCHMARK("HEOS rho(T)"){
|
||||
return AS->update(QT_INPUTS, 1.0, 300.0);
|
||||
};
|
||||
BENCHMARK("superanc rho(T)"){
|
||||
return anc.eval_sat(300.0, 'D', 1);
|
||||
};
|
||||
BENCHMARK("superanc rho(T) with expansion directly"){
|
||||
return approxrhoL.eval(300.0);
|
||||
};
|
||||
BENCHMARK("superanc get_index rho(T)"){
|
||||
return approxrhoL.get_index(300.0);
|
||||
};
|
||||
BENCHMARK("IF97 rho(T)"){
|
||||
return IF97->update(QT_INPUTS, 1.0, 300.0);
|
||||
};
|
||||
|
||||
BENCHMARK("HEOS rho(p)"){
|
||||
return AS->update(PQ_INPUTS, 101325, 1.0);
|
||||
};
|
||||
BENCHMARK("superanc T(p)"){
|
||||
return anc.get_T_from_p(101325);
|
||||
};
|
||||
BENCHMARK("IF97 rho(p)"){
|
||||
return IF97->update(PQ_INPUTS, 101325, 1.0);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check superancillary functions are available for all pure fluids", "[ancillary]") {
|
||||
for (auto & fluid : strsplit(CoolProp::get_global_param_string("fluids_list"), ',')){
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", fluid));
|
||||
auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
|
||||
if (rHEOS.is_pure()){
|
||||
CAPTURE(fluid);
|
||||
CHECK_NOTHROW(rHEOS.update_QT_pure_superanc(1, rHEOS.T_critical()*0.9999));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check out of bound for superancillary", "[superanc]") {
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
|
||||
CHECK_THROWS(AS->update(PQ_INPUTS, 100000000001325, 1.0));
|
||||
CHECK_THROWS(AS->update(QT_INPUTS, 1.0, 1000000));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check throws for R410A", "[superanc]") {
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "R410A"));
|
||||
auto& rHEOS = *dynamic_cast<HelmholtzEOSMixtureBackend*>(AS.get());
|
||||
CHECK_THROWS(rHEOS.update_QT_pure_superanc(1.0, 300.0));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check throws for REFPROP", "[superanc]") {
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("REFPROP", "WATER"));
|
||||
CHECK_THROWS(AS->update_QT_pure_superanc(1.0, 300.0));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check Tc & pc", "[superanccrit]") {
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
|
||||
set_config_bool(ENABLE_SUPERANCILLARIES, true);
|
||||
auto TcSA = AS->T_critical();
|
||||
auto pcSA = AS->p_critical();
|
||||
auto rhocSA = AS->rhomolar_critical();
|
||||
set_config_bool(ENABLE_SUPERANCILLARIES, false);
|
||||
auto TcnonSA = AS->T_critical();
|
||||
auto pcnonSA = AS->p_critical();
|
||||
auto rhocnonSA = AS->rhomolar_critical();
|
||||
CHECK(TcSA != TcnonSA);
|
||||
CHECK(pcSA != pcnonSA);
|
||||
CHECK(rhocSA != rhocnonSA);
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Check h_fg", "[superanc]") {
|
||||
shared_ptr<CoolProp::AbstractState> AS(CoolProp::AbstractState::factory("HEOS", "Water"));
|
||||
CHECK_THROWS(AS->saturated_vapor_keyed_output(iHmolar) - AS->saturated_liquid_keyed_output(iHmolar));
|
||||
AS->update_QT_pure_superanc(1, 300);
|
||||
CHECK_NOTHROW(AS->saturated_vapor_keyed_output(iHmolar) - AS->saturated_liquid_keyed_output(iHmolar));
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(SuperAncillaryOnFixture, "Benchmarking caching options", "[caching]") {
|
||||
std::array<double, 16> buf15; buf15.fill(0.0);
|
||||
std::array<double, 100> buf100; buf100.fill(0.0);
|
||||
std::array<bool, 100> bool100; bool100.fill(false);
|
||||
std::vector<CachedElement> cache100(100);
|
||||
for (auto i = 0; i < cache100.size(); ++i){ cache100[i] = _HUGE; }
|
||||
|
||||
std::vector<std::optional<double>> opt100(100);
|
||||
for (auto i = 0; i < opt100.size(); ++i){ opt100[i] = _HUGE; }
|
||||
|
||||
BENCHMARK("memset array15 w/ 0"){
|
||||
std::memset(buf15.data(), 0, sizeof(buf15));
|
||||
return buf15;
|
||||
};
|
||||
BENCHMARK("std::fill_n array15"){
|
||||
std::fill_n(buf15.data(), 15, _HUGE);
|
||||
return buf15;
|
||||
};
|
||||
BENCHMARK("std::fill array15"){
|
||||
std::fill(buf15.begin(), buf15.end(), _HUGE);
|
||||
return buf15;
|
||||
};
|
||||
BENCHMARK("array15.fill()"){
|
||||
buf15.fill(_HUGE);
|
||||
return buf15;
|
||||
};
|
||||
BENCHMARK("memset array100 w/ 0"){
|
||||
memset(buf100.data(), 0, sizeof(buf100));
|
||||
return buf100;
|
||||
};
|
||||
BENCHMARK("memset bool100 w/ 0"){
|
||||
memset(bool100.data(), false, sizeof(bool100));
|
||||
return buf100;
|
||||
};
|
||||
BENCHMARK("std::fill_n array100"){
|
||||
std::fill_n(buf100.data(), 100, _HUGE);
|
||||
return buf100;
|
||||
};
|
||||
BENCHMARK("fill array100"){
|
||||
buf100.fill(_HUGE);
|
||||
return buf100;
|
||||
};
|
||||
BENCHMARK("fill cache100"){
|
||||
for (auto i = 0; i < cache100.size(); ++i){ cache100[i] = _HUGE; }
|
||||
return cache100;
|
||||
};
|
||||
BENCHMARK("fill opt100"){
|
||||
for (auto i = 0; i < opt100.size(); ++i){ opt100[i] = _HUGE; }
|
||||
return opt100;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
TEST_CASE("Test that HS solver works for a few fluids", "[HS_solver]")
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ cdef class PySpinodalData:
|
||||
cdef class AbstractState:
|
||||
cdef cAbstractState.AbstractState *thisptr # hold a C++ instance which we're wrapping
|
||||
cpdef update(self, constants_header.input_pairs iInput1, double Value1, double Value2)
|
||||
cpdef update_QT_pure_superanc(self, double Q, double T)
|
||||
cpdef update_with_guesses(self, constants_header.input_pairs iInput1, double Value1, double Value2, PyGuessesStructure guesses)
|
||||
cpdef set_mole_fractions(self, vector[double] z)
|
||||
cpdef set_mass_fractions(self, vector[double] z)
|
||||
|
||||
@@ -106,6 +106,9 @@ cdef class AbstractState:
|
||||
cpdef update(self, constants_header.input_pairs ipair, double Value1, double Value2):
|
||||
""" Update function - wrapper of c++ function :cpapi:`CoolProp::AbstractState::update` """
|
||||
self.thisptr.update(ipair, Value1, Value2)
|
||||
cpdef update_QT_pure_superanc(self, double Q, double T):
|
||||
""" Update function - wrapper of c++ function :cpapi:`CoolProp::AbstractState::update` """
|
||||
self.thisptr.update_QT_pure_superanc(Q, T)
|
||||
cpdef update_with_guesses(self, constants_header.input_pairs ipair, double Value1, double Value2, PyGuessesStructure guesses):
|
||||
""" Update function - wrapper of c++ function :cpapi:`CoolProp::AbstractState::update` """
|
||||
cdef cAbstractState.GuessesStructure _guesses
|
||||
|
||||
@@ -108,6 +108,103 @@ from .constants import *
|
||||
from .constants_header cimport *
|
||||
from . cimport constants_header
|
||||
|
||||
from . cimport superancillary as supanc
|
||||
from cpython cimport array
|
||||
|
||||
from libcpp.utility cimport move
|
||||
|
||||
ctypedef vector[double] ArrayType
|
||||
ctypedef supanc.ChebyshevExpansion[ArrayType] ChebExp
|
||||
|
||||
cdef class ChebyshevExpansion:
|
||||
cdef ChebExp* m_exp
|
||||
|
||||
def __cinit__(self, double xmin, double xmax, vector[double] coef):
|
||||
self.m_exp = new ChebExp(xmin, xmax, coef)
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.m_exp
|
||||
|
||||
def xmin(self):
|
||||
return self.m_exp.xmin()
|
||||
|
||||
def xmax(self):
|
||||
return self.m_exp.xmax()
|
||||
|
||||
def coeff(self):
|
||||
np_array = np.asarray(self.m_exp.coeff() )
|
||||
cdef double[:] view = np_array
|
||||
return view
|
||||
|
||||
def eval_many(self, double[::1] x, double[::1] y):
|
||||
assert x.size == y.size
|
||||
self.m_exp.eval_manyC(&x[0], &y[0], x.shape[0])
|
||||
|
||||
def solve_for_x(self, double y, double a, double b, unsigned int bits, size_t max_iter, double boundstytol):
|
||||
return self.m_exp.solve_for_x(y, a, b, bits, max_iter, boundstytol)
|
||||
|
||||
def solve_for_x_many(self, double[::1] y, double a, double b, unsigned int bits, size_t max_iter, double boundstytol, double[::1] x, double [::1] counts):
|
||||
cdef size_t N = y.shape[0]
|
||||
return self.m_exp.solve_for_x_manyC(&y[0], N, a, b, bits, max_iter, boundstytol, &x[0], &counts[0])
|
||||
|
||||
ctypedef supanc.ChebyshevApproximation1D[ArrayType] ChebApprox1D
|
||||
from cython.operator cimport dereference as deref
|
||||
|
||||
cdef class ChebyshevApproximation1D:
|
||||
cdef supanc.ChebyshevApproximation1D[vector[double]]* thisptr
|
||||
|
||||
def __cinit__(self, expansions):
|
||||
cdef vector[supanc.ChebyshevExpansion[vector[double]]] expansions_copy
|
||||
cdef ChebyshevExpansion expansion
|
||||
for expansion in expansions:
|
||||
expansions_copy.push_back(deref(expansion.m_exp))
|
||||
self.thisptr = new ChebApprox1D(move(expansions_copy))
|
||||
|
||||
def xmin(self):
|
||||
return self.thisptr.xmin()
|
||||
|
||||
def xmax(self):
|
||||
return self.thisptr.xmax()
|
||||
|
||||
def is_monotonic(self):
|
||||
return self.thisptr.is_monotonic()
|
||||
|
||||
def eval_many(self, double[::1] x, double[::1] y):
|
||||
assert x.size == y.size
|
||||
self.thisptr.eval_manyC(&x[0], &y[0], x.shape[0])
|
||||
|
||||
def get_x_for_y(self, double y, unsigned int bits, size_t max_iter, double boundstytol):
|
||||
return self.thisptr.get_x_for_y(y, bits, max_iter, boundstytol)
|
||||
|
||||
def count_x_for_y_many(self, double[::1] y, unsigned int bits, size_t max_iter, double boundstytol, double[::1] counts):
|
||||
assert y.shape[0] == counts.shape[0]
|
||||
cdef size_t N = y.shape[0]
|
||||
return self.thisptr.count_x_for_y_manyC(&y[0], N, bits, max_iter, boundstytol, &counts[0])
|
||||
|
||||
def monotonic_intervals(self):
|
||||
return self.thisptr.get_monotonic_intervals()
|
||||
|
||||
def __dealloc__(self):
|
||||
del self.thisptr
|
||||
|
||||
ctypedef supanc.SuperAncillary[ArrayType] SuperAncillary_t
|
||||
|
||||
cdef class SuperAncillary:
|
||||
cdef SuperAncillary_t* thisptr
|
||||
|
||||
def __cinit__(self, str json_as_string):
|
||||
self.thisptr = new SuperAncillary_t(json_as_string)
|
||||
|
||||
def eval_sat(self, double T, str prop, short Q):
|
||||
cdef char prop_ = prop[0]
|
||||
return self.thisptr.eval_sat(T, prop_, Q)
|
||||
|
||||
def eval_sat_many(self, double[::1] T, str prop, short Q, double[::1] y):
|
||||
assert T.shape[0] == y.shape[0]
|
||||
cdef char prop_ = prop[0]
|
||||
cdef size_t N = y.shape[0]
|
||||
return self.thisptr.eval_sat_manyC(&T[0], N, prop_, Q, &y[0])
|
||||
|
||||
cdef bint iterable(object a):
|
||||
"""
|
||||
If numpy is supported, this function returns true if the argument is a
|
||||
@@ -127,6 +224,9 @@ cdef ndarray_or_iterable(object input):
|
||||
include "HumidAirProp.pyx"
|
||||
include "AbstractState.pyx"
|
||||
|
||||
|
||||
|
||||
|
||||
def set_reference_state(FluidName, *args):
|
||||
"""
|
||||
Accepts one of two signatures:
|
||||
|
||||
@@ -103,6 +103,8 @@ cdef extern from "AbstractState.h" namespace "CoolProp":
|
||||
## Property updater
|
||||
## Uses the indices in CoolProp for the input parameters
|
||||
void update(constants_header.input_pairs iInput1, double Value1, double Value2) except +ValueError
|
||||
## QT inputs for pure fluids with the superancillary functions
|
||||
void update_QT_pure_superanc(double Q, double T) except +ValueError
|
||||
## Uses the indices in CoolProp for the input parameters
|
||||
void update_with_guesses(constants_header.input_pairs iInput1, double Value1, double Value2, GuessesStructure) except +ValueError
|
||||
|
||||
|
||||
48
wrappers/Python/CoolProp/superancillary.pxd
Normal file
48
wrappers/Python/CoolProp/superancillary.pxd
Normal file
@@ -0,0 +1,48 @@
|
||||
from libcpp cimport bool
|
||||
from libcpp.vector cimport vector
|
||||
from libcpp.pair cimport pair
|
||||
from libcpp.string cimport string
|
||||
|
||||
|
||||
cdef extern from "superancillary/superancillary.h" namespace "CoolProp::superancillary":
|
||||
cdef struct MonotonicExpansionMatch:
|
||||
const size_t idx # The index of the expansion that has been matched
|
||||
const double ymin, ymax, xmin, xmax
|
||||
# Check if a value of the dependent variable is within this match
|
||||
# bool contains_y (double) const
|
||||
|
||||
# Data associated with a monotonic interval
|
||||
cdef struct IntervalMatch:
|
||||
const vector[MonotonicExpansionMatch] expansioninfo # The information about the expansions for this interval
|
||||
const double xmin, xmax, ymin, ymax
|
||||
# Check if a value of the dependent variable is within this interval
|
||||
# bool contains_y (double) const
|
||||
|
||||
cdef cppclass ChebyshevExpansion[ArrayType]:
|
||||
ChebyshevExpansion() except +ValueError
|
||||
ChebyshevExpansion(double, double, ArrayType) except +ValueError
|
||||
const double xmin() const
|
||||
const double xmax() const
|
||||
const ArrayType& coeff() const
|
||||
U eval[U](const U& x) const
|
||||
void eval_manyC[U](const U x[], U v[], size_t) const
|
||||
double solve_for_x(double, double, double, unsigned int, size_t, double) const
|
||||
void solve_for_x_manyC[U](const U[], size_t, double, double, unsigned int, size_t, double, U[], U[]) const
|
||||
|
||||
cdef cppclass ChebyshevApproximation1D[ArrayType]:
|
||||
ChebyshevApproximation1D(vector[ChebyshevExpansion[ArrayType]]&&) except +ValueError
|
||||
bool is_monotonic() const
|
||||
double xmin()
|
||||
double xmax()
|
||||
void eval_manyC[U](const U x[], U v[], size_t) const
|
||||
const vector[ChebyshevExpansion[ArrayType]] m_expansions # The collection of expansions forming the approximation
|
||||
const vector[double] m_x_at_extrema # The values of the independent variable at the extrema of the expansions
|
||||
const vector[IntervalMatch] get_monotonic_intervals() # The intervals that are monotonic
|
||||
vector[pair[double, int]] get_x_for_y(double, unsigned int, size_t, double) const
|
||||
void count_x_for_y_manyC[U](const U[], size_t, unsigned int, size_t, double, U[]) const
|
||||
|
||||
cdef cppclass SuperAncillary[ArrayType]:
|
||||
SuperAncillary(const string&) except +ValueError
|
||||
double eval_sat(double, char, short) except +ValueError
|
||||
void eval_sat_manyC[U](const U[], size_t, char, short, U[]) except +ValueError
|
||||
|
||||
@@ -438,6 +438,7 @@ if __name__ == '__main__':
|
||||
os.path.join(CProot, 'externals', 'fmtlib', 'include'),
|
||||
os.path.join(CProot, 'boost_CoolProp'),
|
||||
os.path.join(CProot, 'externals', 'incbin'),
|
||||
os.path.join(CProot, 'externals', 'nlohmann-json'),
|
||||
os.path.join(CProot, 'externals', 'miniz-3.0.2'),
|
||||
os.path.join(CProot, 'externals', 'msgpack-c', 'include')]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user