\documentclass[a4paper,11pt]{article} \usepackage[T1]{fontenc} \usepackage[english]{babel} \usepackage{lmodern} \usepackage{microtype} \usepackage{geometry} \usepackage{xcolor} \usepackage{booktabs} \usepackage{array} \usepackage{longtable} \usepackage{tabularx} \usepackage{graphicx} \usepackage{hyperref} \usepackage[noautomatic,quiet]{imakeidx} \usepackage{listings} \usepackage[most]{tcolorbox} \usepackage{luacoolprop} \geometry{margin=24mm} \hypersetup{ colorlinks=true, linkcolor=blue!55!black, urlcolor=blue!55!black, pdftitle={LuaCoolProp Reference Manual}, pdfauthor={Christophe Jorssen}, pdfsubject={Thermodynamic diagrams and CoolProp calculations for generic LuaTeX}, pdfkeywords={LuaTeX, CoolProp, thermodynamics, PGFPlots, PH, PV, TS, HS, PT} } \pgfplotsset{compat=1.18} % The reference contains deliberately dense labelled diagrams. Keep every % label candidate available at the plot boundary and hide low-priority labels % if a showcase has no collision-free complete assignment. \pgfplotsset{ auto node allow outside=true, auto node failure mode=hide-low-priority } \definecolor{LCPBlue}{RGB}{28,70,140} \definecolor{LCPGreen}{RGB}{0,110,95} \definecolor{LCPOrange}{RGB}{190,95,20} \definecolor{LCPGray}{RGB}{246,247,249} \definecolor{LCPLightBlue}{RGB}{235,243,255} \lstdefinestyle{lcptex}{basicstyle=\ttfamily\small,columns=fullflexible,keepspaces=true,breaklines=true} \newtcblisting{LCPExample}[2][]{enhanced,breakable,colback=LCPGray,colframe=LCPBlue!70!black,listing only,listing engine=listings,listing options={style=lcptex},title={#2},fonttitle=\bfseries,left=1mm,right=1mm,top=1mm,bottom=1mm,#1} \newtcblisting{LCPShowcase}[2][]{enhanced,colback=LCPGray,colframe=LCPBlue!70!black, listing side text,sidebyside align=top seam,sidebyside gap=4mm, lefthand ratio=.56,listing engine=listings,listing options={style=lcptex}, title={#2},fonttitle=\bfseries,left=1mm,right=1mm,top=1mm,bottom=1mm,#1} \newtcblisting{LCPShowcaseWide}[2][]{enhanced,breakable,colback=LCPGray, colframe=LCPBlue!70!black,listing and text,listing engine=listings, listing options={style=lcptex},title={#2},fonttitle=\bfseries, left=1mm,right=1mm,top=1mm,bottom=1mm,#1} \newtcolorbox{LCPNote}[1][]{enhanced,breakable,colback=LCPLightBlue,colframe=LCPBlue!70!black,title={#1},fonttitle=\bfseries} \newtcolorbox{LCPWarning}[1][]{enhanced,breakable,colback=orange!8,colframe=LCPOrange!85!black,title={#1},fonttitle=\bfseries} \newcommand{\pkg}[1]{\texttt{#1}} \newcommand{\key}[1]{\texttt{#1}} \newcommand{\macro}[1]{\texttt{\string#1}} \newcommand{\meta}[1]{\ensuremath{\langle\textit{#1}\rangle}} \newcommand{\marg}[1]{\texttt{\char`\{}\meta{#1}\texttt{\char`\}}} \newcommand{\oarg}[1]{\texttt{[}\meta{#1}\texttt{]}} \makeindex[name=commands,title={Command Index},columns=2] \makeindex[name=keys,title={Key and Value Index},columns=2] \newenvironment{LCPCommandRef}[2]{% \index[commands]{#1@\protect\texttt{\protect\textbackslash#1}}% \begin{tcolorbox}[enhanced,breakable,colback=LCPLightBlue, colframe=LCPBlue!75!black,fonttitle=\bfseries, title={\texttt{\textbackslash#1}#2}]% }{\end{tcolorbox}} \newenvironment{LCPKeyRef}[4]{% \index[keys]{#2@\protect\texttt{#2}}% \begin{tcolorbox}[enhanced,breakable,colback=white, colframe=LCPGreen!70!black,fonttitle=\bfseries, title={\texttt{#2}=\meta{#3}\hfill\normalfont\small Default: \texttt{#4}}]% }{\end{tcolorbox}} \newcommand{\LCPKeyAlias}[3]{% \index[keys]{#2@\protect\texttt{#2} (alias)}% \par\smallskip\noindent\textit{Accepted alias: } \texttt{#2} forwards to \texttt{#3}.\par} \newcommand{\LCPChoice}[4]{% \index[keys]{#2=#3@\protect\texttt{#2=#3}}% \item[\texttt{#3}] #4} \newcommand{\LCPIndexValue}[2]{% \index[keys]{#1=#2@\protect\texttt{#1=#2}}} \newcommand{\LCPKeyUse}[3]{% \par\smallskip\noindent\textit{Example: } \texttt{#2=\detokenize{#3}}.\par} \newcommand{\LCPCodeUse}[1]{% \par\smallskip\noindent\textit{Example: } \texttt{\detokenize{#1}}.\par} \newcommand{\LCPCommandAlias}[2]{% \index[commands]{#1@\protect\texttt{\protect\textbackslash#1} (alias)}% \par\smallskip\noindent\textit{Accepted alias: } \texttt{\textbackslash#1}.\par \noindent Target: \texttt{\textbackslash#2}.\par \noindent Example: \texttt{\textbackslash#1}\allowbreak \texttt{[fluid=R134a]}.\par} \newcommand{\LCPKeyDoc}[6]{% \begin{LCPKeyRef}{#1}{#2}{#3}{#4}% #5\LCPKeyUse{#1}{#2}{#6}% \end{LCPKeyRef}} \title{\pkg{luacoolprop}\\[1mm]\large LuaTeX access to CoolProp for PGFPlots thermodynamic diagrams} \author{Christophe Jorssen\\\texttt{christophe.jorssen@gmail.com}} \date{Version 1.1.1 --- 2026-09-20} \begin{document} \maketitle \tableofcontents \clearpage \part{Tutorials} \input{docs/r717-teaching-tutorial.tex} \clearpage \input{docs/r134a-air-conditioner-tutorial.tex} \clearpage \input{docs/butane-pv-tutorial.tex} \clearpage \input{docs/r1234yf-heat-pump-tutorial.tex} \clearpage \input{docs/methane-linde-tutorial.tex} \part{User manual} \section{Purpose and scope} \pkg{luacoolprop} connects LuaTeX to the CoolProp shared library through LuaTeX FFI. Thermodynamic states and isolines are computed in Lua; graphical output is emitted as PGFPlots code. The package provides PH (pressure--enthalpy), PV (pressure--specific-volume), TS (temperature--entropy), HS (enthalpy--entropy), and PT (pressure--temperature) diagrams for pure fluids. PH uses a linear enthalpy axis and logarithmic pressure axis; PV uses logarithmic axes for both specific volume and pressure; TS uses linear mass-specific entropy and absolute temperature axes; HS uses linear mass-specific entropy and enthalpy axes; PT uses temperature and logarithmic pressure axes. The package supports quality curves, isotherms, isentropes, isochores, TS isenthalps, HS isobars, thermodynamic process paths, named curves, per-curve overrides, and automatic label placement delegated to \pkg{pgfplots-autonode}. \begin{LCPWarning}[AI-assisted ``vibe coding''] This package has been \emph{vibe-coded} with ChatGPT, an artificial-intelligence system from OpenAI. Substantial parts of its design, implementation, documentation, and tests have been generated or revised with AI assistance. AI-generated work can be convincing while still containing incorrect code, incorrect thermodynamic assumptions, incomplete tests, or misleading documentation. Publication on CTAN is not a safety certification. Inspect the source and independently validate every result before relying on it, especially in industrial, regulated, safety-critical, or high-consequence work. The maintainers remain responsible for reviewing releases and welcome reproducible issue reports. \end{LCPWarning} Copyright \textcopyright\ 2026 Christophe Jorssen. LuaCoolProp is distributed under the LaTeX Project Public License 1.3c or later. The maintenance status is \emph{maintained}, and the Current Maintainer is Christophe Jorssen (\href{mailto:christophe.jorssen@gmail.com}{\nolinkurl{christophe.jorssen@gmail.com}}). CoolProp is an external, MIT-licensed dependency and is not included in this work. \subsection{How to cite CoolProp} CoolProp asks users to cite its published reference and the project website when reporting work based on its calculations. The citation is: \begin{quote} Ian H. Bell, Jorrit Wronski, Sylvain Quoilin, and Vincent Lemort, ``Pure and Pseudo-pure Fluid Thermophysical Property Evaluation and the Open-Source Thermophysical Property Library CoolProp,'' \emph{Industrial \& Engineering Chemistry Research} \textbf{53}(6), 2498--2508 (2014). DOI: \href{https://doi.org/10.1021/ie4033999}{10.1021/ie4033999}. Project website: \url{https://coolprop.org/}. \end{quote} The project's \href{https://coolprop.org/citation.html}{citation page} provides a BibTeX entry. Cite the CoolProp version used for reproducibility as well as this article; LuaCoolProp is an independent TeX interface, not an official CoolProp component. \begin{LCPNote}[Design principle] LuaCoolProp deliberately separates thermodynamics from label placement. Thermodynamic states, isolines, and process paths are computed in Lua; automatic label placement is delegated to \pkg{pgfplots-autonode}. This keeps the package compatible with PGFPlots' survey/render lifecycle and avoids duplicating axis geometry in Lua. \end{LCPNote} \section{Installation and runtime requirements} A working document needs LuaTeX or LuaHBTeX with FFI enabled, PGF/TikZ, PGFPlots 1.18 or later, the independent \pkg{pgfplots-autonode} package, and a CoolProp shared library. Both \pkg{pgfplots-autonode} and CoolProp are external runtime dependencies; neither is embedded in the LuaCoolProp distribution. LuaCoolProp does not set PGFPlots' \texttt{compat} key. Select the compatibility level required by the complete document in its preamble. Automatic label hooks are local to the axes created by \macro{\LCPDiagram}; a manually constructed axis using low-level family commands must add \texttt{auto node placement} when automatic labels are requested. \subsection{Install the TeX files} After publication, a TeX distribution can install the package through its normal package manager: use \texttt{tlmgr install luacoolprop pgfplots-autonode} with TeX Live, or select both packages in the MiKTeX Console. A distribution normally installs the declared TeX dependency automatically. CoolProp remains a separate native dependency in both cases. For a manual installation, unpack both \texttt{luacoolprop.tds.zip} and \texttt{pgfplots-autonode.tds.zip} at the root of a personal TEXMF tree. TeX Live normally reports that directory with \texttt{kpsewhich -var-value=TEXMFHOME}. Refresh its filename database with \texttt{mktexlsr} if the local installation uses one. The browsing archive \texttt{luacoolprop.zip} is intended for inspection and must not be substituted for the TDS archive. The shared library is typically named \texttt{libCoolProp.so} on Linux, \texttt{libCoolProp.dylib} on macOS, and \texttt{CoolProp.dll} on Windows. LuaCoolProp does not vendor CoolProp, and installing the CTAN package does not install that library. Obtain it separately from the official CoolProp project: \begin{itemize} \item source repository: \url{https://github.com/CoolProp/CoolProp}; \item shared-library instructions and release downloads: \url{https://coolprop.org/coolprop/wrappers/SharedLibrary/}. \end{itemize} \subsection{Security boundary and \texttt{--shell-escape}} \begin{LCPWarning}[Compile trusted TeX sources only] The required \texttt{--shell-escape} option is dangerous. It permits a TeX document, and every class, package, module, or file that it loads, to run external programs with the permissions of the current user. A malicious TeX source can therefore read, modify, or disclose user-accessible data. Compile only sources and dependencies that you trust, preferably in an isolated build directory or sandbox, without elevated privileges or unnecessary secrets. Review downloaded examples before compiling them. Never run the TeX engine as an administrator or as \texttt{root}. \end{LCPWarning} LuaCoolProp requires full shell escape because the LuaTeX distribution exposes its Lua FFI module only in a shell-escape-enabled typesetting process. That FFI module loads \texttt{libCoolProp} as native executable code and calls its C API. LuaCoolProp itself does not invoke a compiler, download CoolProp, or issue shell commands while typesetting; nevertheless, enabling the option enlarges the capabilities of the \emph{entire} document. Restricted shell escape is not sufficient for this FFI mechanism. The shared library is another trust boundary: load only a CoolProp binary that you built from a reviewed revision or obtained from an official release. Use an absolute \texttt{LUACOOLPROP\_LIB} path so an unrelated library with the same name cannot be selected earlier in a platform search path. \subsection{Choose a compatible CoolProp binary} The TeX engine process and the CoolProp library must use the same architecture: normally \texttt{arm64} or \texttt{x86\_64} on macOS, \texttt{x86\_64} on 64-bit Linux, and \texttt{x64} on 64-bit Windows. An architecture mismatch usually appears as a dynamic-loader error before the first diagram is computed. There are two supported approaches: \begin{enumerate} \item download a precompiled library for the required operating system and architecture from CoolProp's official shared-library page; or \item build a pinned CoolProp tag or commit from source, as described below. \end{enumerate} For reproducible documents, record the exact CoolProp release or commit beside the document sources. Do not copy the CoolProp checkout, build tree, or binary into the LuaCoolProp package tree or a CTAN archive. Building requires Git, CMake, Ninja, Python, a C++ compiler, and the tools used by CoolProp's submodules. Clone outside the LuaCoolProp tree and initialize all submodules. The following example pins CoolProp \texttt{v8.0.0}; replace that tag deliberately when adopting another reviewed release. \begin{LCPExample}{Common source checkout on macOS or Linux} git clone https://github.com/CoolProp/CoolProp.git ../coolprop-src git -C ../coolprop-src fetch --tags git -C ../coolprop-src checkout --detach v8.0.0 git -C ../coolprop-src submodule update --init --recursive \end{LCPExample} If \texttt{../coolprop-src} already exists, verify that it is the intended Git checkout, fetch the requested revision, check it out, and repeat the recursive submodule update. A detached checkout is intentional here: it makes the exact dependency visible and prevents an accidental branch update. \subsection{macOS: download or compile the \texttt{.dylib}} Install Apple's command-line developer tools and make Git, CMake, and Ninja available. For a reproducible build, first perform the pinned common checkout above. From a LuaCoolProp source or CTAN tree, the supplied script then reuses that checkout, initializes its submodules, and performs the CMake and Ninja build in sibling directories. If the checkout does not yet exist, the script can clone CoolProp's current default branch for exploratory use: \begin{LCPExample}{Native macOS build} ./scripts/build-coolprop-macos.sh \end{LCPExample} On Apple Silicon the default is \texttt{arm64}, including when the script is started from a translated shell. Override it when the TeX engine has a different architecture, or request a universal library explicitly: \begin{LCPExample}{macOS architecture overrides} COOLPROP_MACOS_ARCH=x86_64 ./scripts/build-coolprop-macos.sh COOLPROP_MACOS_ARCH='arm64;x86_64' ./scripts/build-coolprop-macos.sh \end{LCPExample} The script prints the absolute path to \texttt{libCoolProp.dylib} as its final line. It deliberately leaves both CoolProp source and build products outside the LuaCoolProp directory. Package maintainers who intentionally adopt a new CoolProp API may use \texttt{./scripts/update-coolprop.sh v8.0.0}; unlike the build-only script, that maintainer tool also regenerates the marked FFI declaration block in \pkg{luacoolprop.lua}, so end users should not run it as a routine installation step. A release tag may be written with or without its leading \texttt{v}. The equivalent explicit configuration, useful when the helper script is not available, is: \begin{LCPExample}{Manual arm64 macOS build} cmake -S ../coolprop-src -B ../coolprop-build-macos-arm64 -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ -DCOOLPROP_SHARED_LIBRARY=ON \ -DCOOLPROP_STATIC_LIBRARY=OFF \ -DCOOLPROP_OBJECT_LIBRARY=OFF cmake --build ../coolprop-build-macos-arm64 --target CoolProp export LUACOOLPROP_LIB="$PWD/../coolprop-build-macos-arm64/libCoolProp.dylib" \end{LCPExample} No system-wide installation is necessary. Keeping an absolute environment variable beside a pinned build makes it clear which thermodynamic library a document used. \subsection{Linux: download or compile the \texttt{.so}} Install Git, CMake, Ninja, Python, a C++ build toolchain, and 7-Zip using the distribution's package manager. For example, Debian and Ubuntu package the usual prerequisites as \texttt{git}, \texttt{cmake}, \texttt{ninja-build}, \texttt{build-essential}, \texttt{python3}, and \texttt{p7zip-full}. After the common source checkout above, configure and build the shared-library target: \begin{LCPExample}{Linux shared-library build} cmake -S ../coolprop-src -B ../coolprop-build-linux -G Ninja \ -DCMAKE_BUILD_TYPE=Release \ -DCOOLPROP_SHARED_LIBRARY=ON \ -DCOOLPROP_STATIC_LIBRARY=OFF \ -DCOOLPROP_OBJECT_LIBRARY=OFF cmake --build ../coolprop-build-linux --target CoolProp export LUACOOLPROP_LIB="$PWD/../coolprop-build-linux/libCoolProp.so" \end{LCPExample} Again, copying the library to \texttt{/usr/local/lib} is optional and normally unnecessary. An unprivileged, versioned build directory plus the absolute \texttt{LUACOOLPROP\_LIB} path is easier to audit and does not require \texttt{sudo}. \subsection{Windows: download or compile \texttt{CoolProp.dll}} The simplest Windows installation is an official precompiled x64 DLL. Extract it to a stable directory outside the LuaCoolProp tree and set the environment variable to its absolute path. A source build needs Git, CMake, Python, and either Ninja with a compatible C++ compiler or Visual Studio. In PowerShell, the Ninja workflow is: \begin{LCPExample}{Windows PowerShell checkout and Ninja build} git clone https://github.com/CoolProp/CoolProp.git ..\coolprop-src git -C ..\coolprop-src fetch --tags git -C ..\coolprop-src checkout --detach v8.0.0 git -C ..\coolprop-src submodule update --init --recursive cmake -S ..\coolprop-src -B ..\coolprop-build-windows -G Ninja ` -DCMAKE_BUILD_TYPE=Release ` -DCOOLPROP_SHARED_LIBRARY=ON ` -DCOOLPROP_STATIC_LIBRARY=OFF ` -DCOOLPROP_OBJECT_LIBRARY=OFF cmake --build ..\coolprop-build-windows --target CoolProp $env:LUACOOLPROP_LIB = (Resolve-Path ..\coolprop-build-windows\CoolProp.dll).Path \end{LCPExample} With a Visual Studio generator, select the same architecture as TeX Live and remember that it is a multi-configuration build: \begin{LCPExample}{Windows PowerShell and Visual Studio 2022} cmake -S ..\coolprop-src -B ..\coolprop-build-vs ` -G "Visual Studio 17 2022" -A x64 ` -DCOOLPROP_SHARED_LIBRARY=ON ` -DCOOLPROP_STATIC_LIBRARY=OFF ` -DCOOLPROP_OBJECT_LIBRARY=OFF cmake --build ..\coolprop-build-vs --config Release --target CoolProp $env:LUACOOLPROP_LIB = (Resolve-Path ..\coolprop-build-vs\Release\CoolProp.dll).Path \end{LCPExample} CMake versions and generators can place the DLL in a slightly different configuration subdirectory. If the shown path does not exist, locate the newly built \texttt{CoolProp.dll} inside the selected build directory rather than copying an unrelated DLL into it. The PowerShell assignment lasts for the current terminal. In \texttt{cmd.exe}, use \texttt{set "LUACOOLPROP\_LIB=C:\string\absolute\string\path\string\CoolProp.dll"}. \subsection{Verify the runtime installation} After setting \texttt{LUACOOLPROP\_LIB}, compile a minimal trusted document. Use the command appropriate to the format, always in the same terminal in which the environment variable was set: \begin{LCPExample}{Minimal installation check} % check-luacoolprop.tex \documentclass{article} \usepackage{luacoolprop} \begin{document} \LCPPHDiagram[fluid=R134a,quality values={0,0.5,1}] \end{document} \end{LCPExample} \begin{LCPExample}{Run the LaTeX installation check} lualatex --shell-escape check-luacoolprop.tex \end{LCPExample} The library path is consumed when the Lua module first loads. If it changes, start a fresh TeX process. Loader errors should first be checked for an incorrect absolute path, an architecture mismatch, or a missing dependent runtime library. The package is format-generic. Its public files have distinct roles: \begin{itemize} \item \pkg{luacoolprop.tex} is the canonical generic TeX implementation; \item \pkg{luacoolprop.sty} is the LaTeX wrapper; \item \pkg{t-luacoolprop.tex} is the ConTeXt wrapper; and \item \pkg{p-luacoolprop.tex} is the plain TeX wrapper. \end{itemize} \subsection{LaTeX, plain TeX, and ConTeXt} Each format uses its native wrapper and engine. A LaTeX document loads \pkg{luacoolprop.sty} and is compiled with LuaLaTeX: \begin{LCPExample}{LaTeX wrapper} \documentclass{article} \usepackage{luacoolprop} \begin{document} \LCPPHDiagram[fluid=R134a] \end{document} \end{LCPExample} \begin{LCPExample}{LaTeX command line} lualatex --shell-escape myfile.tex \end{LCPExample} A plain TeX document loads \pkg{p-luacoolprop.tex} directly and is compiled with LuaTeX, not with a LaTeX executable: \begin{LCPExample}{Plain TeX wrapper} \input p-luacoolprop.tex \pgfplotsset{compat=1.18} \LCPPHDiagram[fluid=R134a] \bye \end{LCPExample} \begin{LCPExample}{Plain TeX command line} luatex --shell-escape myfile.tex \end{LCPExample} A ConTeXt document selects the \texttt{t-} module explicitly. This avoids an ambiguous module search in a directory that also contains the \texttt{p-} plain TeX wrapper. \begin{LCPExample}{ConTeXt wrapper} \usemodule[t][luacoolprop] \pgfplotsset{compat=1.18} \starttext \LCPPHDiagram[fluid=R134a] \stoptext \end{LCPExample} \begin{LCPExample}{ConTeXt command line} context --luatex --shell-escape myfile.tex \end{LCPExample} The \texttt{--luatex} switch selects ConTeXt MkIV. It is required because LuaCoolProp uses LuaTeX FFI; LuaMetaTeX/LMTX does not provide that module. The ConTeXt wrapper loads the ConTeXt PGFPlots frontend before the generic layer. \subsection{LaTeX number and unit formatting with \pkg{siunitx}} The LaTeX wrapper formats generated unit-bearing values with \macro{\qty}, unit-only axis labels with \macro{\unit}, and dimensionless numbers such as vapour quality with \macro{\num}. If \pkg{siunitx} is not yet loaded, \pkg{luacoolprop} loads it with its standard defaults. If it is already loaded, the wrapper neither reloads it with options nor calls \macro{\sisetup}; the document's locale, decimal marker, grouping, rounding, font, and per-mode choices are therefore preserved. The user may also call \macro{\sisetup} after loading \pkg{luacoolprop}. \begin{LCPExample}{Configure siunitx before LuaCoolProp} \documentclass[border=3mm]{standalone} \usepackage[ output-decimal-marker={,}, group-separator={\,}, group-minimum-digits=4, per-mode=symbol ]{siunitx} \usepackage{luacoolprop} \begin{document} \LCPPHDiagram[ quality values={0,0.5,1}, isotherm=true,temperature values={-20,0,20}, labels=true] \end{document} \end{LCPExample} The complete executable version is \texttt{examples/siunitx-formatting.tex}. Its title contains \texttt{\string\num\{12345.6\}} and its generated curve labels demonstrate that the same pre-existing decimal and unit policy is retained: \IfFileExists{examples/siunitx-formatting.pdf}{% \begin{center} \includegraphics[width=.82\textwidth]{examples/siunitx-formatting.pdf} \end{center} }{\begin{LCPWarning}[siunitx example PDF not built] Run \texttt{scripts/build-examples.sh} before compiling the manual. \end{LCPWarning}} Numerical process-export macros intentionally remain plain decimal tokens so that PGFPlots and TeX arithmetic can consume them. When displaying such a dimensionless macro in LaTeX, wrap it in \macro{\num}; for example, \texttt{\string\num\{\string\CycleABToQuality\}}. For a value with a unit, use \macro{\qty}, for example \texttt{\string\qty\{\string\CycleABToTemperatureK\}\{\string\kelvin\}}. Plain TeX and ConTeXt keep the generic math-mode formatting and do not depend on \pkg{siunitx}. The input grammar is deliberately independent of this output formatting. LuaCoolProp accepts only a full stop as an input decimal separator, even when \pkg{siunitx} displays a comma. Decimal floating-point and scientific notation are supported, including a directly attached unit: \texttt{pressure=1.2E5Pa}. Thus \texttt{1,2bar} is an error, not another spelling of \texttt{1.2bar}. This fixed convention keeps the same source unambiguous under LaTeX, plain TeX, ConTeXt, and every operating-system locale. \subsection{Repository build workflow} The reproducible build script recognizes the format from each example name: \texttt{plain-*.tex} uses \texttt{luatex}, \texttt{context-*.tex} uses ConTeXt MkIV, and every other example uses \texttt{lualatex}. The manual uses three \texttt{lualatex} passes so its table of contents, cross-references, and two indexes are stable. \begin{LCPExample}{Build every bundled document} LUACOOLPROP_LIB=/path/to/libCoolProp.dylib ./scripts/build-examples.sh \end{LCPExample} Example logs are written to \texttt{examples/logs/}; manual logs are written to \texttt{build-logs/}; each completed PDF is installed next to its source. The script stops at the first engine failure or actionable TeX diagnostic. Sandboxed builds may set \texttt{LUACOOLPROP\_TEX\_CACHE} to a writable directory for the LuaLaTeX and plain LuaTeX font cache. \section{Basic users: quick start} \subsection{A fully automatic diagram} The high-level command creates the complete TikZ picture and the PGFPlots axis. The default units are \(h\) in \(\mathrm{kJ\,kg^{-1}}\) and \(p\) in bar. \begin{LCPExample}{Minimal PH diagram} \documentclass[border=3mm]{standalone} \usepackage{luacoolprop} \begin{document} \LCPPHDiagram[fluid=R134a] \end{document} \end{LCPExample} A more informative chart is obtained by enabling the main isoline families. Label placement is delegated to \pkg{pgfplots-autonode}; \key{label placement=autonode} is the default, but it is shown explicitly below. \begin{LCPExample}{Automatic refrigeration-style diagram} \LCPPHDiagram[ fluid=R134a, isotherm=true, isentrope=true, isochore=true, quality step=0.1, temperature mode=preset, temperature preset=refrigeration, entropy mode=preset, entropy preset=refrigeration, specific volume mode=preset, specific volume preset=refrigeration, labels=true, sloped labels=true, label placement=autonode, axis options={ auto node algorithm=repair, auto node bbox mode=oriented } ] \end{LCPExample} \subsection{Another fluid} CoolProp fluid names can be used directly. The useful isoline range depends on the fluid and on the intended engineering application. \begin{LCPExample}{Propane quick diagram} \LCPPHDiagram[ fluid=Propane, isotherm=true, isentrope=true, quality step=0.2, temperature values={-40,-20,0,20,40,60,80}, entropy values={1.4,1.6,1.8,2.0,2.2}, labels=true, sloped labels=true, label placement=autonode ] \end{LCPExample} \IfFileExists{examples/ph-propane-quick.pdf}{\begin{center}\includegraphics[width=.9\textwidth]{examples/ph-propane-quick.pdf}\end{center}}{} \section{Diagram identifiers and stable API} Diagram identifiers are uppercase coordinate pairs. This release implements \texttt{PH}, \texttt{PV}, and \texttt{TS}. The generic dispatcher keeps the calling convention independent of the coordinate pair: \begin{LCPExample}{Generic PH dispatcher} \LCPDiagram{PH}[fluid=R134a, isotherm=true] \end{LCPExample} \begin{LCPExample}{Generic PV dispatcher} \LCPDiagram{PV}[fluid=R134a, isotherm=true] \end{LCPExample} \begin{LCPExample}{Generic TS dispatcher} \LCPDiagram{TS}[fluid=R134a, isenthalp=true] \end{LCPExample} Inside an existing PGFPlots axis, use one of these generic forms: \begin{itemize} \item \texttt{\string\LCPAddDiagramPlots\{PH\}} or \texttt{\string\LCPAddDiagramPlots\{PV\}} or \texttt{\string\LCPAddDiagramPlots\{TS\}}; \item \texttt{\string\LCPAddDiagramFamily\{PV\}\{isotherm\}}; or \item \texttt{\string\LCPAddDiagramProcess\{PV\}}. \end{itemize} Each accepts its options in brackets after the mandatory diagram and family identifiers. Convenience commands put the same uppercase identifier between the \texttt{LCP} prefix and the operation name. Thus \macro{\LCPPHDiagram} and \macro{\LCPPVDiagram}, \macro{\LCPTSDiagram}, and \macro{\LCPHSDiagram} are equivalent to the generic calls with \texttt{PH}, \texttt{PV}, \texttt{TS}, and \texttt{HS}. Additional implementations can add other identifiers without changing the generic dispatch commands. The canonical family names are \key{quality}, \key{isotherm}, \key{isentrope}, \key{isochore}, \key{isenthalp}, and \key{isobar}. Each diagram exposes only the families it can calculate. Public option names use complete English property names. Examples include \key{quality step}, \key{temperature values}, and \key{specific volume unit}. The canonical advanced key roots pair \texttt{/luacoolprop/diagram} and \texttt{/luacoolprop/process} with an uppercase \texttt{PH}, \texttt{PV}, \texttt{TS}, \texttt{HS}, or \texttt{PT} branch. \begin{LCPNote}[Accepted aliases] The public aliases include: \begin{itemize} \item \macro{\LCPPhDiagram}, \macro{\LCPAddPHIsoQuality}, and the mixed-case \texttt{Ph} family commands; \item the alternate \texttt{/luacoolprop/ph diagram} and \texttt{/luacoolprop/ph process} paths; and \item plural family switches and abbreviated property keys. \end{itemize} The canonical names are recommended. Their vocabulary is shared by all registered diagram types. \end{LCPNote} \section{Advanced users: manual PGFPlots axes} Advanced users can write the PGFPlots axis by hand and add each isoline family separately. The fluid can be declared at the axis level with \key{lcp fluid}. Each family macro emits its \macro{\addplot} data immediately, followed by the corresponding \macro{\pgfplotsautonode} label command when labels are enabled. PGFPlots therefore sees the data during its survey phase and can still compute axis limits natively. Because this is a manually created axis, the \key{auto node placement} lifecycle key is required whenever automatic labels are requested; omitting it produces an explicit warning. \begin{LCPExample}{Manual construction in an axis} \begin{tikzpicture} \begin{axis}[ lcp fluid=R134a, xlabel={$h/{\rm kJ\,kg^{-1}}$}, ylabel={$p/{\rm bar}$}, ymode=log, grid=both, auto node placement, width=14cm, height=9cm, auto node algorithm=repair, auto node bbox mode=oriented ] \LCPAddPHQuality[ quality values={0,0.1,0.3,0.5,0.7,0.9,1}, labels=true, sloped labels=true ] \LCPAddPHIsotherms[ temperature values={-30,-10,10,30,50,70,90}, labels=true, sloped labels=true ] \LCPAddPHIsentropes[ entropy values={1.0,1.2,1.4,1.6,1.8}, labels=true, sloped labels=true ] \LCPAddPHIsochores[ specific volume values={0.001,0.002,0.005,0.01,0.02,0.05,0.1}, labels=true, sloped labels=true ] \end{axis} \end{tikzpicture} \end{LCPExample} \subsection{Curve-family macros} \begin{center} \begin{tabular}{ll} \toprule Macro & Purpose \\ \midrule \macro{\LCPAddPHQuality} & add \(Q=\mathrm{constant}\) curves \\ \macro{\LCPAddPHIsotherms} & add \(T=\mathrm{constant}\) curves \\ \macro{\LCPAddPHIsentropes} & add \(s=\mathrm{constant}\) curves \\ \macro{\LCPAddPHIsochores} & add \(v=\mathrm{constant}\) curves \\ \macro{\LCPAddPHProcess} & add one thermodynamic process segment \\ \bottomrule \end{tabular} \end{center} \subsection{Value grids} All isoline families accept preset, linear, logarithmic, and explicit value lists. Select a preset with both \key{temperature mode=preset} (or the corresponding family mode) and the desired \key{... preset} name. Explicit lists are often best for publication-quality diagrams and take precedence over the mode. \begin{LCPExample}{Explicit value lists} \LCPAddPHQuality[quality values={0,0.25,0.5,0.75,1}] \LCPAddPHIsotherms[temperature values={-20,0,20,40,60,80}] \LCPAddPHIsentropes[entropy values={1.0,1.2,1.4,1.6,1.8}] \LCPAddPHIsochores[specific volume values={0.001,0.002,0.005,0.01,0.02}] \end{LCPExample} Isochores use density internally because CoolProp's high-level interface accepts mass density directly. LuaCoolProp converts \(v\) to \(\rho=1/v\). \section{Automatic labels with pgfplots-autonode} \pkg{pgfplots-autonode} is LuaCoolProp's only automatic label-placement backend and is an independent required package. LuaCoolProp loads its public PGFPlots library interface automatically in LaTeX, plain LuaTeX, and ConTeXt; it does not embed a private copy or implement a competing solver. A missing \pkg{pgfplots-autonode} installation therefore stops package loading instead of silently degrading label placement. The standalone \pkg{pgfplots-autonode} manual is the normative reference for \macro{\pgfplotsautonode}, its candidate strategies and assignment algorithms, measured node geometry, boundary constraints, failure policies, and debug facilities. Its examples use ordinary affine PGFPlots networks and require neither LuaCoolProp nor CoolProp. For each labelled thermodynamic curve, LuaCoolProp emits the same structure: \begin{LCPExample}{Generated structure} \addplot[] coordinates {} \pgfplotsautonode[]{