Files
CoolProp/Web/scripts/fluid_properties.Superancillary.py
Ian Bell 267d64533a 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
2025-05-17 20:27:19 -04:00

160 lines
5.1 KiB
Python

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)