% luacoolprop.tex -- generic TeX interface for LuaCoolProp
% Copyright (C) 2026 Christophe Jorssen.
% Maintained by Christophe Jorssen <christophe.jorssen@gmail.com>.
% Distributed under LPPL 1.3c or later.

\ifx\luacoolproptexloaded\undefined
\else
  \expandafter\endinput
\fi
\def\luacoolproptexloaded{1.1.0}

\chardef\LCPSavedAtCatcode=\catcode`\@
\catcode`\@=11

% ConTeXt uses \unexpanded as a protected-definition prefix and keeps the
% e-TeX primitive under \normalunexpanded. Normalize that one primitive here
% so Lua string serialization behaves identically in every supported format.
\ifx\normalunexpanded\undefined
  \let\LCP@unexpanded\unexpanded
\else
  \let\LCP@unexpanded\normalunexpanded
\fi

\ifx\directlua\undefined
  \errmessage{luacoolprop requires LuaTeX or LuaHBTeX}
\fi

% Load pgfplots in a generic-friendly way if it is not already available.
\ifx\pgfplotsset\undefined
  \input pgfplots.tex
\fi

% Every generated thermodynamic curve receives a stable TikZ `name path` so
% documents can construct intersections and recover derived states.  This is
% therefore a runtime dependency of the public API, not a document-wide style
% choice.  Loading the library defines the path machinery without changing
% PGFPlots compatibility or the user's axis styles.
\usetikzlibrary{intersections}

% Autonomous label-placement library. LuaCoolProp delegates automatic labels to
% the bundled PGFPlots library instead of implementing placement itself. The
% library only defines its keys.  LuaCoolProp enables its hooks
% locally on the high-level axes it creates; unrelated PGFPlots axes retain the
% user's settings.
\expandafter\ifx\csname pgfplotsautonode@loaded\endcsname\relax
  \usepgfplotslibrary{autonode}%
\fi

\ifx\PackageWarning\undefined
  \def\LCP@warning#1#2{\message{#1 warning: #2}}%
\else
  \def\LCP@warning#1#2{\PackageWarning{#1}{#2}}%
\fi

% Format-independent fallbacks.  The LaTeX wrapper predefines these private
% hooks with siunitx's \num, \qty, and \unit commands.  Plain TeX and ConTeXt
% retain the traditional math-mode output and never acquire a LaTeX dependency.
% The second quantity/unit argument is siunitx syntax; the final argument is
% its generic-TeX representation.
\ifx\LCP@format@number\undefined
  \def\LCP@format@number#1{#1}%
\fi
\ifx\LCP@format@quantity\undefined
  \def\LCP@format@quantity#1#2#3{#1\,#3}%
\fi
\ifx\LCP@format@unit\undefined
  \def\LCP@format@unit#1#2{#2}%
\fi

% --------------------------------------------------------------------------
% Global package state and pgf/tikz integration
% --------------------------------------------------------------------------

\def\LCP@global@fluid{R134a}
\def\LCP@global@library{}
\def\LCP@global@hscale{0.001}% J/kg -> kJ/kg
\def\LCP@global@pscale{0.00001}% Pa -> bar
\def\LCP@global@vscale{1}% m^3/kg -> m^3/kg
\def\LCP@global@sscale{0.001}% J/(kg K) -> kJ/(kg K)
\def\LCP@global@temperaturescale{1}% K -> K
\def\LCP@global@temperatureoffset{0}% plotted T=(T_SI-offset)*scale
\def\LCP@global@haxisunit{kjkg}
\def\LCP@global@paxisunit{bar}
\def\LCP@global@vaxisunit{m3kg}
\def\LCP@global@saxisunit{kjkgk}
\def\LCP@global@temperatureaxisunit{kelvin}
\def\LCP@global@referencestate{DEF}

\def\LCPFluidName{R134a}
\def\LCPFluidReferenceState{DEF}
\def\LCPFluidPcritSI{}
\def\LCPFluidPcritBar{}
\def\LCPFluidPcritPlot{}
\def\LCPFluidPtripleSI{}
\def\LCPFluidPtripleBar{}
\def\LCPFluidTcritK{}
\def\LCPFluidTcritC{}
\def\LCPFluidTtripleK{}
\def\LCPFluidTtripleC{}
\def\LCPFluidRhocritSI{}

\pgfkeys{/luacoolprop/.is family}
\pgfkeys{%
  /luacoolprop/fluid/.code={\LCPSetFluid{#1}},
  /luacoolprop/library/.code={\def\LCP@global@library{#1}},
  /luacoolprop/reference state/.code={\def\LCP@global@referencestate{#1}},
  /luacoolprop/enthalpy scale/.code={\def\LCP@global@hscale{#1}\def\LCP@global@haxisunit{custom}},
  /luacoolprop/h scale/.style={enthalpy scale={#1}},
  /luacoolprop/pressure scale/.code={\def\LCP@global@pscale{#1}\def\LCP@global@paxisunit{custom}},
  /luacoolprop/p scale/.style={pressure scale={#1}},
  /luacoolprop/specific volume scale/.code={\def\LCP@global@vscale{#1}\def\LCP@global@vaxisunit{custom}},
  /luacoolprop/v scale/.style={specific volume scale={#1}},
  /luacoolprop/entropy scale/.code={\def\LCP@global@sscale{#1}\def\LCP@global@saxisunit{custom}},
  /luacoolprop/s scale/.style={entropy scale={#1}},
  /luacoolprop/temperature scale/.code={\def\LCP@global@temperaturescale{#1}\def\LCP@global@temperatureoffset{0}\def\LCP@global@temperatureaxisunit{custom}},
  /luacoolprop/t scale/.style={temperature scale={#1}},
  /luacoolprop/enthalpy axis unit/.is choice,
  /luacoolprop/enthalpy axis unit/jkg/.code={\def\LCP@global@hscale{1}\def\LCP@global@haxisunit{jkg}},
  /luacoolprop/enthalpy axis unit/kjkg/.code={\def\LCP@global@hscale{0.001}\def\LCP@global@haxisunit{kjkg}},
  /luacoolprop/pressure axis unit/.is choice,
  /luacoolprop/pressure axis unit/pa/.code={\def\LCP@global@pscale{1}\def\LCP@global@paxisunit{pa}},
  /luacoolprop/pressure axis unit/kpa/.code={\def\LCP@global@pscale{0.001}\def\LCP@global@paxisunit{kpa}},
  /luacoolprop/pressure axis unit/mpa/.code={\def\LCP@global@pscale{0.000001}\def\LCP@global@paxisunit{mpa}},
  /luacoolprop/pressure axis unit/bar/.code={\def\LCP@global@pscale{0.00001}\def\LCP@global@paxisunit{bar}},
  /luacoolprop/specific volume axis unit/.is choice,
  /luacoolprop/specific volume axis unit/m3kg/.code={\def\LCP@global@vscale{1}\def\LCP@global@vaxisunit{m3kg}},
  /luacoolprop/specific volume axis unit/lkg/.code={\def\LCP@global@vscale{1000}\def\LCP@global@vaxisunit{lkg}},
  /luacoolprop/entropy axis unit/.is choice,
  /luacoolprop/entropy axis unit/jkgk/.code={\def\LCP@global@sscale{1}\def\LCP@global@saxisunit{jkgk}},
  /luacoolprop/entropy axis unit/kjkgk/.code={\def\LCP@global@sscale{0.001}\def\LCP@global@saxisunit{kjkgk}},
  /luacoolprop/temperature axis unit/.is choice,
  /luacoolprop/temperature axis unit/kelvin/.code={\def\LCP@global@temperaturescale{1}\def\LCP@global@temperatureoffset{0}\def\LCP@global@temperatureaxisunit{kelvin}},
  /luacoolprop/temperature axis unit/celsius/.code={\def\LCP@global@temperaturescale{1}\def\LCP@global@temperatureoffset{273.15}\def\LCP@global@temperatureaxisunit{celsius}},
  /luacoolprop/.unknown/.code={%
    \LCP@warning{luacoolprop}{Unknown key `/luacoolprop/\pgfkeyscurrentname`}%
  }%
}

\def\LCPSetup#1{\pgfkeys{/luacoolprop/.cd,#1}}
\def\LCPSetFluid#1{\def\LCP@global@fluid{#1}}
\def\LCPSetLibrary#1{\def\LCP@global@library{#1}}
\def\LCPSetReferenceState#1{%
  \def\LCP@global@referencestate{#1}%
  \directlua{require("luacoolprop").set_reference_state(
    "\luaescapestring{\LCP@global@fluid}",
    "\luaescapestring{\LCP@global@referencestate}",
    "\luaescapestring{\LCP@global@library}")}%
}

\def\LCPUpdateFluidConstants{%
  \directlua{require("luacoolprop").tex.print_fluid_constants({%
    fluid="\luaescapestring{\LCP@global@fluid}",%
    library="\luaescapestring{\LCP@global@library}",%
    reference_state="\luaescapestring{\LCP@global@referencestate}",%
    p_scale="\luaescapestring{\LCP@global@pscale}"%
  })}%
}
\def\LCPDeclareFluid#1{\LCPSetFluid{#1}\LCPUpdateFluidConstants}

\def\LCP@curveoverrides{}
\long\def\LCP@append@curveoverride#1#2#3{%
  \begingroup
    \toks0=\expandafter{\LCP@curveoverrides}%
    \xdef\LCP@curveoverrides{\the\toks0\LCP@unexpanded{#1|#2|#3^^J}}%
  \endgroup
}

% These styles are TikZ node styles, not pgfplots styles.  Keeping font macros
% here avoids passing commands such as \scriptsize through Lua strings.
\def\LCP@label@font{\ifx\scriptsize\undefined\relax\else\scriptsize\fi}
\tikzset{%
  luacoolprop label node/.style={font=\LCP@label@font,fill=white,fill opacity=0.75,text opacity=1,inner sep=1pt},
  luacoolprop quality label node/.style={luacoolprop label node},
  luacoolprop isotherm label node/.style={luacoolprop label node},
  luacoolprop isentrope label node/.style={luacoolprop label node},
  luacoolprop isochore label node/.style={luacoolprop label node},
  luacoolprop isenthalp label node/.style={luacoolprop label node},
  luacoolprop isobar label node/.style={luacoolprop label node},
  luacoolprop phase envelope label node/.style={luacoolprop label node},
  luacoolprop process label node/.style={luacoolprop label node}
}

\pgfplotsset{%
  luacoolprop fluid/.code={\LCPSetFluid{#1}},
  lcp fluid/.style={luacoolprop fluid={#1}}
}


% PGFPlots runs in a survey phase followed by a visualization phase.
% LuaCoolProp emits each automatic label through pgfplots-autonode as a trailing
% command immediately after the corresponding \addplot.  This keeps plot data
% available during the survey phase and delegates collision avoidance to
% pgfplots-autonode.

% Axis labels and numerical coordinates are governed by the same local unit
% selector.  A raw scale remains available for advanced use, but is labelled
% explicitly as a scaled SI coordinate so it can never masquerade as a named
% physical unit.
\def\LCP@unit@jkg{jkg}\def\LCP@unit@kjkg{kjkg}
\def\LCP@unit@pa{pa}\def\LCP@unit@kpa{kpa}
\def\LCP@unit@mpa{mpa}\def\LCP@unit@bar{bar}
\def\LCP@unit@mthreekg{m3kg}\def\LCP@unit@lkg{lkg}
\def\LCP@unit@jkgk{jkgk}\def\LCP@unit@kjkgk{kjkgk}
\def\LCP@unit@kelvin{kelvin}\def\LCP@unit@celsius{celsius}

\def\LCP@enthalpy@axislabel{%
  \ifx\LCP@haxisunit\LCP@unit@jkg
    $\LCP@enthalpysymbol/\LCP@format@unit{\joule\per\kilogram}{{\rm J\,kg^{-1}}}$%
  \else\ifx\LCP@haxisunit\LCP@unit@kjkg
    $\LCP@enthalpysymbol/\LCP@format@unit{\kilo\joule\per\kilogram}{{\rm kJ\,kg^{-1}}}$%
  \else $\LCP@enthalpysymbol_{\rm plot}=\LCP@format@number{\LCP@hscale}\,\LCP@enthalpysymbol_{\rm SI}$%
  \fi\fi}
\def\LCP@pressure@axislabel{%
  \ifx\LCP@paxisunit\LCP@unit@pa
    $\LCP@pressuresymbol/\LCP@format@unit{\pascal}{{\rm Pa}}$%
  \else\ifx\LCP@paxisunit\LCP@unit@kpa
    $\LCP@pressuresymbol/\LCP@format@unit{\kilo\pascal}{{\rm kPa}}$%
  \else\ifx\LCP@paxisunit\LCP@unit@mpa
    $\LCP@pressuresymbol/\LCP@format@unit{\mega\pascal}{{\rm MPa}}$%
  \else\ifx\LCP@paxisunit\LCP@unit@bar
    $\LCP@pressuresymbol/\LCP@format@unit{\bar}{{\rm bar}}$%
  \else $\LCP@pressuresymbol_{\rm plot}=\LCP@format@number{\LCP@pscale}\,\LCP@pressuresymbol_{\rm SI}$%
  \fi\fi\fi\fi}
\def\LCP@volume@axislabel{%
  \ifx\LCP@vaxisunit\LCP@unit@mthreekg
    $\LCP@specificvolumesymbol/\LCP@format@unit{\cubic\metre\per\kilogram}{{\rm m^3\,kg^{-1}}}$%
  \else\ifx\LCP@vaxisunit\LCP@unit@lkg
    $\LCP@specificvolumesymbol/\LCP@format@unit{\litre\per\kilogram}{{\rm L\,kg^{-1}}}$%
  \else $\LCP@specificvolumesymbol_{\rm plot}=\LCP@format@number{\LCP@vscale}\,\LCP@specificvolumesymbol_{\rm SI}$%
  \fi\fi}
\def\LCP@entropy@axislabel{%
  \ifx\LCP@saxisunit\LCP@unit@jkgk
    $\LCP@entropysymbol/\LCP@format@unit{\joule\per\kilogram\per\kelvin}{{\rm J\,kg^{-1}\,K^{-1}}}$%
  \else\ifx\LCP@saxisunit\LCP@unit@kjkgk
    $\LCP@entropysymbol/\LCP@format@unit{\kilo\joule\per\kilogram\per\kelvin}{{\rm kJ\,kg^{-1}\,K^{-1}}}$%
  \else $\LCP@entropysymbol_{\rm plot}=\LCP@format@number{\LCP@sscale}\,\LCP@entropysymbol_{\rm SI}$%
  \fi\fi}
\def\LCP@temperature@axislabel{%
  \ifx\LCP@temperatureaxisunit\LCP@unit@kelvin
    $\LCP@temperaturesymbol/\LCP@format@unit{\kelvin}{{\rm K}}$%
  \else\ifx\LCP@temperatureaxisunit\LCP@unit@celsius
    $\LCP@temperaturesymbol/\LCP@format@unit{\celsius}{{}^{\circ}{\rm C}}$%
  \else $\LCP@temperaturesymbol_{\rm plot}=\LCP@format@number{\LCP@temperaturescale}\,\LCP@temperaturesymbol_{\rm SI}$%
  \fi\fi}


\def\LCP@ph@applyaxisdefaults{%
  \pgfplotsset{every axis/.append style={%
    auto node placement,
    width=13cm,
    height=8cm,
    xlabel={\LCP@enthalpy@axislabel},
    ylabel={\LCP@pressure@axislabel},
    ymode=log,
    grid=both,
    minor grid style={draw=gray!10},
    major grid style={draw=gray!30},
    tick align=outside,
    enlargelimits=false%
  }}%
}

\def\LCP@pv@applyaxisdefaults{%
  \pgfplotsset{every axis/.append style={%
    auto node placement,
    width=13cm,
    height=8cm,
    xlabel={\LCP@volume@axislabel},
    ylabel={\LCP@pressure@axislabel},
    xmode=log,
    ymode=log,
    grid=both,
    minor grid style={draw=gray!10},
    major grid style={draw=gray!30},
    tick align=outside,
    enlargelimits=false%
  }}%
}

\def\LCP@ts@applyaxisdefaults{%
  \pgfplotsset{every axis/.append style={%
    auto node placement,
    width=13cm,
    height=8cm,
    xlabel={\LCP@entropy@axislabel},
    ylabel={\LCP@temperature@axislabel},
    grid=both,
    minor grid style={draw=gray!10},
    major grid style={draw=gray!30},
    tick align=outside,
    enlargelimits=false%
  }}%
}

\def\LCP@hs@applyaxisdefaults{%
  \pgfplotsset{every axis/.append style={%
    auto node placement,
    width=13cm,
    height=8cm,
    xlabel={\LCP@entropy@axislabel},
    ylabel={\LCP@enthalpy@axislabel},
    grid=both,
    minor grid style={draw=gray!10},
    major grid style={draw=gray!30},
    tick align=outside,
    enlargelimits=false%
  }}%
}

\def\LCP@pt@applyaxisdefaults{%
  \pgfplotsset{every axis/.append style={%
    auto node placement,
    width=13cm,
    height=8cm,
    xlabel={\LCP@temperature@axislabel},
    ylabel={\LCP@pressure@axislabel},
    ymode=log,
    grid=both,
    minor grid style={draw=gray!10},
    major grid style={draw=gray!30},
    tick align=outside,
    enlargelimits=false%
  }}%
}


% --------------------------------------------------------------------------
% Diagram keys
%
% Every type owns /luacoolprop/diagram/<TYPE>.  Format-independent mechanics
% live in /luacoolprop/diagram/common; no coordinate system is the template or
% fallback for another one.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/diagram/common/.is family}
\pgfkeys{/luacoolprop/diagram/PH/.is family}
\pgfkeys{/luacoolprop/diagram/PH/.search also={/luacoolprop/diagram/common}}
% Alternate PH namespace accepted by the public interface.
\pgfkeys{/luacoolprop/ph diagram/.is family}
\pgfkeys{/luacoolprop/ph diagram/.search also={/luacoolprop/diagram/PH}}
\def\LCP@unsupported@family#1#2{%
  \errmessage{luacoolprop: diagram type `#1' does not provide the `#2' background family}%
}
\pgfkeys{%
  /luacoolprop/diagram/PH/enthalpy axis unit/.is choice,
  /luacoolprop/diagram/PH/enthalpy axis unit/jkg/.code={\def\LCP@hscale{1}\def\LCP@haxisunit{jkg}},
  /luacoolprop/diagram/PH/enthalpy axis unit/kjkg/.code={\def\LCP@hscale{0.001}\def\LCP@haxisunit{kjkg}},
  /luacoolprop/diagram/PH/pressure axis unit/.is choice,
  /luacoolprop/diagram/PH/pressure axis unit/pa/.code={\def\LCP@pscale{1}\def\LCP@paxisunit{pa}},
  /luacoolprop/diagram/PH/pressure axis unit/kpa/.code={\def\LCP@pscale{0.001}\def\LCP@paxisunit{kpa}},
  /luacoolprop/diagram/PH/pressure axis unit/mpa/.code={\def\LCP@pscale{0.000001}\def\LCP@paxisunit{mpa}},
  /luacoolprop/diagram/PH/pressure axis unit/bar/.code={\def\LCP@pscale{0.00001}\def\LCP@paxisunit{bar}},
  /luacoolprop/diagram/PH/isenthalp/.code={\LCP@unsupported@family{PH}{isenthalp}},
  /luacoolprop/diagram/PH/isenthalps/.style={isenthalp={#1}},
  /luacoolprop/diagram/PH/isobar/.code={\LCP@unsupported@family{PH}{isobar}},
  /luacoolprop/diagram/PH/isobars/.style={isobar={#1}}
}

\def\LCP@diagram@setdefaults{%
  \edef\LCP@fluid{\LCP@global@fluid}%
  \edef\LCP@library{\LCP@global@library}%
  \edef\LCP@referencestate{\LCP@global@referencestate}%
  \def\LCP@isoquality{true}%
  \def\LCP@isotherms{false}%
  \def\LCP@isentropes{false}%
  \def\LCP@isochores{false}%
  \def\LCP@enthalpysymbol{h}%
  \def\LCP@pressuresymbol{p}%
  \def\LCP@qualitysymbol{Q}%
  \def\LCP@temperaturesymbol{T}%
  \def\LCP@entropysymbol{s}%
  \def\LCP@specificvolumesymbol{v}%
  \def\LCP@qmin{0}%
  \def\LCP@qmax{1}%
  \def\LCP@qstep{0.1}%
  \def\LCP@qmode{linear}%
  \def\LCP@qcount{auto}%
  \def\LCP@qvalues{}%
  \def\LCP@qpreset{default}%
  \def\LCP@pressuremin{auto}%
  \def\LCP@pressuremax{auto}%
  \def\LCP@pressuremaxfactor{1.10}%
  \edef\LCP@hscale{\LCP@global@hscale}%
  \edef\LCP@pscale{\LCP@global@pscale}%
  \edef\LCP@haxisunit{\LCP@global@haxisunit}%
  \edef\LCP@paxisunit{\LCP@global@paxisunit}%
  \edef\LCP@vaxisunit{\LCP@global@vaxisunit}%
  \edef\LCP@saxisunit{\LCP@global@saxisunit}%
  \edef\LCP@temperatureaxisunit{\LCP@global@temperatureaxisunit}%
  \edef\LCP@temperatureoffset{\LCP@global@temperatureoffset}%
  \def\LCP@initialintervals{18}%
  \def\LCP@maxdepth{8}%
  \def\LCP@tolerance{0.25}%
  \def\LCP@logweight{30}%
  \def\LCP@coorddigits{6}%
  \def\LCP@domainpolicy{ignore}%
  \def\LCP@labels{false}%
  \def\LCP@labelplacement{autonode}%
  \def\LCP@labelcollisionpolicy{keep}%
  \def\LCP@labelhideoverlapthreshold{0.06}%
  \def\LCP@labelhideedgethreshold{0.025}%
  \def\LCP@labelcandidatecount{21}%
  \def\LCP@labelminpos{0}%
  \def\LCP@labelmaxpos{1}%
  \def\LCP@autonodecandidates{51}%
  \def\LCP@autonodecandidatestrategy{around-preferred}%
  \def\LCP@autonodeclearance{1pt}%
  \def\LCP@autonodeinnersep{1.5pt}%
  \def\LCP@autonodenormalshift{0pt}%
  \def\LCP@autonodepreferredweight{8}%
  \def\LCP@autonodeoverlapweight{1000}%
  \def\LCP@labelseparation{0.018}%
  \def\LCP@labeloverlappenalty{1000}%
  \def\LCP@labeldistancepenalty{1}%
  \def\LCP@labeledgepenalty{5}%
  \def\LCP@labelboxbasewidth{0.045}%
  \def\LCP@labelboxcharwidth{0.0060}%
  \def\LCP@labelboxheight{0.060}%
  \def\LCP@labelboxslopedmultiplier{1.35}%
  \def\LCP@curveoverrides{}%
  \def\LCP@qualitylabels{auto}%
  \def\LCP@isothermlabels{auto}%
  \def\LCP@isentropelabels{auto}%
  \def\LCP@isochorelabels{auto}%
  \def\LCP@labelpos{0.35}%
  \def\LCP@qualitylabelpos{0.35}%
  \def\LCP@isothermlabelpos{0.20}%
  \def\LCP@isentropelabelpos{0.45}%
  \def\LCP@isochorelabelpos{0.58}%
  \def\LCP@labelevery{2}%
  \def\LCP@qualitylabelevery{2}%
  \def\LCP@isothermlabelevery{2}%
  \def\LCP@isentropelabelevery{2}%
  \def\LCP@isochorelabelevery{2}%
  \def\LCP@labelsloped{false}%
  \def\LCP@qualitylabelsloped{auto}%
  \def\LCP@isothermlabelsloped{auto}%
  \def\LCP@isentropelabelsloped{auto}%
  \def\LCP@isochorelabelsloped{auto}%
  \def\LCP@labelallowupsidedown{false}%
  \def\LCP@qualitylabelallowupsidedown{auto}%
  \def\LCP@isothermlabelallowupsidedown{auto}%
  \def\LCP@isentropelabelallowupsidedown{auto}%
  \def\LCP@isochorelabelallowupsidedown{auto}%
  \def\LCP@labelstyle{luacoolprop label node}%
  \def\LCP@qualitylabelstyle{luacoolprop quality label node}%
  \def\LCP@isothermlabelstyle{luacoolprop isotherm label node}%
  \def\LCP@isentropelabelstyle{luacoolprop isentrope label node}%
  \def\LCP@isochorelabelstyle{luacoolprop isochore label node}%
  \def\LCP@legend{false}%
  \def\LCP@forgetplot{true}%
  \def\LCP@curvestyle{}%
  \def\LCP@qualitycolor{blue!60!black}%
  \def\LCP@qualityboundarycolor{blue!80!black}%
  \def\LCP@isothermcolor{red!65!black}%
  \def\LCP@isentropecolor{green!50!black}%
  \def\LCP@isochorecolor{orange!80!black}%
  \def\LCP@interiorstyle{line width=0.25pt}%
  \def\LCP@boundarystyle{line width=0.9pt}%
  \def\LCP@isothermstyle{line width=0.25pt}%
  \def\LCP@isentropeunit{kjkgk}%
  \def\LCP@smin{auto}%
  \def\LCP@smax{auto}%
  \def\LCP@sstep{0.1}%
  \def\LCP@smode{linear}%
  \def\LCP@scount{auto}%
  \def\LCP@spreset{default}%
  \def\LCP@svalues{}%
  \def\LCP@isentropestyle{line width=0.25pt,densely dotted}%
  \def\LCP@specificvolumeunit{m3kg}%
  \def\LCP@vmin{auto}%
  \def\LCP@vmax{auto}%
  \def\LCP@vstep{auto}%
  \def\LCP@vmode{log}%
  \def\LCP@vcount{8}%
  \def\LCP@vpreset{default}%
  \def\LCP@vvalues{}%
  \def\LCP@isochorestyle{line width=0.25pt,dash pattern=on 2pt off 1.4pt}%
  \def\LCP@temperatureunit{celsius}%
  \def\LCP@tmin{auto}%
  \def\LCP@tmax{auto}%
  \def\LCP@tstep{10}%
  \def\LCP@tmode{linear}%
  \def\LCP@tcount{auto}%
  \def\LCP@tpreset{default}%
  \def\LCP@tvalues{}%
  \def\LCP@isotherminitialintervals{14}%
  \def\LCP@isothermmaxdepth{7}%
  \def\LCP@isothermtolerance{0.35}%
  \def\LCP@isentropeinitialintervals{14}%
  \def\LCP@isentropemaxdepth{7}%
  \def\LCP@isentropetolerance{0.30}%
  \def\LCP@isochoreinitialintervals{16}%
  \def\LCP@isochoremaxdepth{8}%
  \def\LCP@isochoretolerance{0.28}%
  \def\LCP@saturationpressureepsilon{0.00001}%
}

\pgfkeys{%
  /luacoolprop/diagram/common/fluid/.store in=\LCP@fluid,
  /luacoolprop/diagram/common/library/.store in=\LCP@library,
  /luacoolprop/diagram/common/reference state/.store in=\LCP@referencestate,
  /luacoolprop/diagram/common/quality/.store in=\LCP@isoquality,
  /luacoolprop/diagram/common/iso quality/.style={quality={#1}},
  /luacoolprop/diagram/common/isoquality/.style={quality={#1}},
  /luacoolprop/diagram/common/quality curves/.style={quality={#1}},
  /luacoolprop/diagram/common/isotherm/.store in=\LCP@isotherms,
  /luacoolprop/diagram/common/isotherms/.style={isotherm={#1}},
  /luacoolprop/diagram/common/isentrope/.store in=\LCP@isentropes,
  /luacoolprop/diagram/common/isentropes/.style={isentrope={#1}},
  /luacoolprop/diagram/common/isochore/.store in=\LCP@isochores,
  /luacoolprop/diagram/common/isochores/.style={isochore={#1}},
  /luacoolprop/diagram/common/enthalpy symbol/.store in=\LCP@enthalpysymbol,
  /luacoolprop/diagram/common/pressure symbol/.store in=\LCP@pressuresymbol,
  /luacoolprop/diagram/common/quality symbol/.store in=\LCP@qualitysymbol,
  /luacoolprop/diagram/common/temperature symbol/.store in=\LCP@temperaturesymbol,
  /luacoolprop/diagram/common/entropy symbol/.store in=\LCP@entropysymbol,
  /luacoolprop/diagram/common/specific volume symbol/.store in=\LCP@specificvolumesymbol,
  /luacoolprop/diagram/common/quality min/.store in=\LCP@qmin,
  /luacoolprop/diagram/common/quality max/.store in=\LCP@qmax,
  /luacoolprop/diagram/common/quality step/.store in=\LCP@qstep,
  /luacoolprop/diagram/common/quality mode/.store in=\LCP@qmode,
  /luacoolprop/diagram/common/quality count/.store in=\LCP@qcount,
  /luacoolprop/diagram/common/quality values/.store in=\LCP@qvalues,
  /luacoolprop/diagram/common/quality preset/.store in=\LCP@qpreset,
  /luacoolprop/diagram/common/q min/.style={quality min={#1}},
  /luacoolprop/diagram/common/q max/.style={quality max={#1}},
  /luacoolprop/diagram/common/q step/.style={quality step={#1}},
  /luacoolprop/diagram/common/q mode/.style={quality mode={#1}},
  /luacoolprop/diagram/common/q count/.style={quality count={#1}},
  /luacoolprop/diagram/common/q values/.style={quality values={#1}},
  /luacoolprop/diagram/common/q preset/.style={quality preset={#1}},
  /luacoolprop/diagram/common/pressure min/.store in=\LCP@pressuremin,
  /luacoolprop/diagram/common/pressure max/.store in=\LCP@pressuremax,
  /luacoolprop/diagram/common/p min/.style={pressure min={#1}},
  /luacoolprop/diagram/common/p max/.style={pressure max={#1}},
  /luacoolprop/diagram/common/pressure max factor/.store in=\LCP@pressuremaxfactor,
  /luacoolprop/diagram/common/p max factor/.style={pressure max factor={#1}},
  /luacoolprop/diagram/common/enthalpy scale/.code={\def\LCP@hscale{#1}\def\LCP@haxisunit{custom}},
  /luacoolprop/diagram/common/h scale/.style={enthalpy scale={#1}},
  /luacoolprop/diagram/common/pressure scale/.code={\def\LCP@pscale{#1}\def\LCP@paxisunit{custom}},
  /luacoolprop/diagram/common/p scale/.style={pressure scale={#1}},
  /luacoolprop/diagram/common/initial intervals/.store in=\LCP@initialintervals,
  /luacoolprop/diagram/common/max depth/.store in=\LCP@maxdepth,
  /luacoolprop/diagram/common/tolerance/.store in=\LCP@tolerance,
  /luacoolprop/diagram/common/log weight/.store in=\LCP@logweight,
  /luacoolprop/diagram/common/coord digits/.store in=\LCP@coorddigits,
  /luacoolprop/diagram/common/domain policy/.store in=\LCP@domainpolicy,
  /luacoolprop/diagram/common/discontinuity policy/.style={domain policy={#1}},
  /luacoolprop/diagram/common/labels/.store in=\LCP@labels,
  /luacoolprop/diagram/common/label placement/.store in=\LCP@labelplacement,
  /luacoolprop/diagram/common/label collision policy/.store in=\LCP@labelcollisionpolicy,
  /luacoolprop/diagram/common/label collision/.style={label collision policy={#1}},
  /luacoolprop/diagram/common/label hide overlap threshold/.store in=\LCP@labelhideoverlapthreshold,
  /luacoolprop/diagram/common/label hide edge threshold/.store in=\LCP@labelhideedgethreshold,
  /luacoolprop/diagram/common/smart labels/.style={label placement=autonode},
  /luacoolprop/diagram/common/automatic labels/.style={label placement=autonode},
  /luacoolprop/diagram/common/autonode labels/.style={label placement=autonode},
  /luacoolprop/diagram/common/pgfplots autonode labels/.style={label placement=autonode},
  /luacoolprop/diagram/common/label candidate count/.store in=\LCP@labelcandidatecount,
  /luacoolprop/diagram/common/autonode candidates/.store in=\LCP@autonodecandidates,
  /luacoolprop/diagram/common/autonode candidate strategy/.store in=\LCP@autonodecandidatestrategy,
  /luacoolprop/diagram/common/autonode clearance/.store in=\LCP@autonodeclearance,
  /luacoolprop/diagram/common/autonode inner sep/.store in=\LCP@autonodeinnersep,
  /luacoolprop/diagram/common/autonode normal shift/.store in=\LCP@autonodenormalshift,
  /luacoolprop/diagram/common/autonode preferred weight/.store in=\LCP@autonodepreferredweight,
  /luacoolprop/diagram/common/autonode overlap weight/.store in=\LCP@autonodeoverlapweight,
  /luacoolprop/diagram/common/label min pos/.store in=\LCP@labelminpos,
  /luacoolprop/diagram/common/label max pos/.store in=\LCP@labelmaxpos,
  /luacoolprop/diagram/common/label separation/.store in=\LCP@labelseparation,
  /luacoolprop/diagram/common/label overlap penalty/.store in=\LCP@labeloverlappenalty,
  /luacoolprop/diagram/common/label distance penalty/.store in=\LCP@labeldistancepenalty,
  /luacoolprop/diagram/common/label edge penalty/.store in=\LCP@labeledgepenalty,
  /luacoolprop/diagram/common/label box base width/.store in=\LCP@labelboxbasewidth,
  /luacoolprop/diagram/common/label box char width/.store in=\LCP@labelboxcharwidth,
  /luacoolprop/diagram/common/label box height/.store in=\LCP@labelboxheight,
  /luacoolprop/diagram/common/label box sloped multiplier/.store in=\LCP@labelboxslopedmultiplier,
  /luacoolprop/diagram/common/label pos for/.code 2 args={\LCP@append@curveoverride{#1}{label_pos}{#2}},
  /luacoolprop/diagram/common/label style for/.code 2 args={\LCP@append@curveoverride{#1}{label_style}{#2}},
  /luacoolprop/diagram/common/label text for/.code 2 args={\LCP@append@curveoverride{#1}{label_text}{#2}},
  /luacoolprop/diagram/common/label show for/.code 2 args={\LCP@append@curveoverride{#1}{label_show}{#2}},
  /luacoolprop/diagram/common/label fixed for/.code 2 args={\LCP@append@curveoverride{#1}{label_fixed}{#2}},
  /luacoolprop/diagram/common/label sloped for/.code 2 args={\LCP@append@curveoverride{#1}{label_sloped}{#2}},
  /luacoolprop/diagram/common/label allow upside down for/.code 2 args={\LCP@append@curveoverride{#1}{label_allow_upside_down}{#2}},
  /luacoolprop/diagram/common/curve style for/.code 2 args={\LCP@append@curveoverride{#1}{curve_style}{#2}},
  /luacoolprop/diagram/common/quality labels/.store in=\LCP@qualitylabels,
  /luacoolprop/diagram/common/isotherm labels/.store in=\LCP@isothermlabels,
  /luacoolprop/diagram/common/isentrope labels/.store in=\LCP@isentropelabels,
  /luacoolprop/diagram/common/isochore labels/.store in=\LCP@isochorelabels,
  /luacoolprop/diagram/common/label pos/.store in=\LCP@labelpos,
  /luacoolprop/diagram/common/quality label pos/.store in=\LCP@qualitylabelpos,
  /luacoolprop/diagram/common/isotherm label pos/.store in=\LCP@isothermlabelpos,
  /luacoolprop/diagram/common/isentrope label pos/.store in=\LCP@isentropelabelpos,
  /luacoolprop/diagram/common/isochore label pos/.store in=\LCP@isochorelabelpos,
  /luacoolprop/diagram/common/label every/.store in=\LCP@labelevery,
  /luacoolprop/diagram/common/quality label every/.store in=\LCP@qualitylabelevery,
  /luacoolprop/diagram/common/isotherm label every/.store in=\LCP@isothermlabelevery,
  /luacoolprop/diagram/common/isentrope label every/.store in=\LCP@isentropelabelevery,
  /luacoolprop/diagram/common/isochore label every/.store in=\LCP@isochorelabelevery,
  /luacoolprop/diagram/common/sloped labels/.store in=\LCP@labelsloped,
  /luacoolprop/diagram/common/label sloped/.store in=\LCP@labelsloped,
  /luacoolprop/diagram/common/quality label sloped/.store in=\LCP@qualitylabelsloped,
  /luacoolprop/diagram/common/isotherm label sloped/.store in=\LCP@isothermlabelsloped,
  /luacoolprop/diagram/common/isentrope label sloped/.store in=\LCP@isentropelabelsloped,
  /luacoolprop/diagram/common/isochore label sloped/.store in=\LCP@isochorelabelsloped,
  /luacoolprop/diagram/common/label allow upside down/.store in=\LCP@labelallowupsidedown,
  /luacoolprop/diagram/common/quality label allow upside down/.store in=\LCP@qualitylabelallowupsidedown,
  /luacoolprop/diagram/common/isotherm label allow upside down/.store in=\LCP@isothermlabelallowupsidedown,
  /luacoolprop/diagram/common/isentrope label allow upside down/.store in=\LCP@isentropelabelallowupsidedown,
  /luacoolprop/diagram/common/isochore label allow upside down/.store in=\LCP@isochorelabelallowupsidedown,
  /luacoolprop/diagram/common/label style/.store in=\LCP@labelstyle,
  /luacoolprop/diagram/common/quality label style/.store in=\LCP@qualitylabelstyle,
  /luacoolprop/diagram/common/isotherm label style/.store in=\LCP@isothermlabelstyle,
  /luacoolprop/diagram/common/isentrope label style/.store in=\LCP@isentropelabelstyle,
  /luacoolprop/diagram/common/isochore label style/.store in=\LCP@isochorelabelstyle,
  /luacoolprop/diagram/common/label node style/.code={\tikzset{luacoolprop label node/.style={#1}}},
  /luacoolprop/diagram/common/quality label node style/.code={\tikzset{luacoolprop quality label node/.style={#1}}},
  /luacoolprop/diagram/common/isotherm label node style/.code={\tikzset{luacoolprop isotherm label node/.style={#1}}},
  /luacoolprop/diagram/common/isentrope label node style/.code={\tikzset{luacoolprop isentrope label node/.style={#1}}},
  /luacoolprop/diagram/common/isochore label node style/.code={\tikzset{luacoolprop isochore label node/.style={#1}}},
  /luacoolprop/diagram/common/legend/.store in=\LCP@legend,
  /luacoolprop/diagram/common/forget plot/.store in=\LCP@forgetplot,
  /luacoolprop/diagram/common/curve style/.store in=\LCP@curvestyle,
  /luacoolprop/diagram/common/quality color/.store in=\LCP@qualitycolor,
  /luacoolprop/diagram/common/quality boundary color/.store in=\LCP@qualityboundarycolor,
  /luacoolprop/diagram/common/boundary color/.style={quality boundary color={#1}},
  /luacoolprop/diagram/common/isotherm color/.store in=\LCP@isothermcolor,
  /luacoolprop/diagram/common/isentrope color/.store in=\LCP@isentropecolor,
  /luacoolprop/diagram/common/isochore color/.store in=\LCP@isochorecolor,
  /luacoolprop/diagram/common/interior style/.store in=\LCP@interiorstyle,
  /luacoolprop/diagram/common/boundary style/.store in=\LCP@boundarystyle,
  /luacoolprop/diagram/common/isotherm style/.store in=\LCP@isothermstyle,
  /luacoolprop/diagram/common/isentrope style/.store in=\LCP@isentropestyle,
  /luacoolprop/diagram/common/isochore style/.store in=\LCP@isochorestyle,
  /luacoolprop/diagram/common/specific volume unit/.store in=\LCP@specificvolumeunit,
  /luacoolprop/diagram/common/volume unit/.style={specific volume unit={#1}},
  /luacoolprop/diagram/common/v unit/.style={specific volume unit={#1}},
  /luacoolprop/diagram/common/specific volume min/.store in=\LCP@vmin,
  /luacoolprop/diagram/common/specific volume max/.store in=\LCP@vmax,
  /luacoolprop/diagram/common/specific volume step/.store in=\LCP@vstep,
  /luacoolprop/diagram/common/specific volume mode/.store in=\LCP@vmode,
  /luacoolprop/diagram/common/specific volume count/.store in=\LCP@vcount,
  /luacoolprop/diagram/common/specific volume preset/.store in=\LCP@vpreset,
  /luacoolprop/diagram/common/specific volume values/.store in=\LCP@vvalues,
  /luacoolprop/diagram/common/v min/.style={specific volume min={#1}},
  /luacoolprop/diagram/common/v max/.style={specific volume max={#1}},
  /luacoolprop/diagram/common/v step/.style={specific volume step={#1}},
  /luacoolprop/diagram/common/v mode/.style={specific volume mode={#1}},
  /luacoolprop/diagram/common/v count/.style={specific volume count={#1}},
  /luacoolprop/diagram/common/v preset/.style={specific volume preset={#1}},
  /luacoolprop/diagram/common/v values/.style={specific volume values={#1}},
  /luacoolprop/diagram/common/entropy unit/.store in=\LCP@isentropeunit,
  /luacoolprop/diagram/common/entropy min/.store in=\LCP@smin,
  /luacoolprop/diagram/common/entropy max/.store in=\LCP@smax,
  /luacoolprop/diagram/common/entropy step/.store in=\LCP@sstep,
  /luacoolprop/diagram/common/entropy mode/.store in=\LCP@smode,
  /luacoolprop/diagram/common/entropy count/.store in=\LCP@scount,
  /luacoolprop/diagram/common/entropy preset/.store in=\LCP@spreset,
  /luacoolprop/diagram/common/entropy values/.store in=\LCP@svalues,
  /luacoolprop/diagram/common/s min/.style={entropy min={#1}},
  /luacoolprop/diagram/common/s max/.style={entropy max={#1}},
  /luacoolprop/diagram/common/s step/.style={entropy step={#1}},
  /luacoolprop/diagram/common/s mode/.style={entropy mode={#1}},
  /luacoolprop/diagram/common/s count/.style={entropy count={#1}},
  /luacoolprop/diagram/common/s preset/.style={entropy preset={#1}},
  /luacoolprop/diagram/common/s values/.style={entropy values={#1}},
  /luacoolprop/diagram/common/temperature unit/.store in=\LCP@temperatureunit,
  /luacoolprop/diagram/common/temperature min/.store in=\LCP@tmin,
  /luacoolprop/diagram/common/temperature max/.store in=\LCP@tmax,
  /luacoolprop/diagram/common/temperature step/.store in=\LCP@tstep,
  /luacoolprop/diagram/common/temperature mode/.store in=\LCP@tmode,
  /luacoolprop/diagram/common/temperature count/.store in=\LCP@tcount,
  /luacoolprop/diagram/common/temperature preset/.store in=\LCP@tpreset,
  /luacoolprop/diagram/common/temperature values/.store in=\LCP@tvalues,
  /luacoolprop/diagram/common/t min/.style={temperature min={#1}},
  /luacoolprop/diagram/common/t max/.style={temperature max={#1}},
  /luacoolprop/diagram/common/t step/.style={temperature step={#1}},
  /luacoolprop/diagram/common/t mode/.style={temperature mode={#1}},
  /luacoolprop/diagram/common/t count/.style={temperature count={#1}},
  /luacoolprop/diagram/common/t preset/.style={temperature preset={#1}},
  /luacoolprop/diagram/common/t values/.style={temperature values={#1}},
  /luacoolprop/diagram/common/isotherm initial intervals/.store in=\LCP@isotherminitialintervals,
  /luacoolprop/diagram/common/isotherm max depth/.store in=\LCP@isothermmaxdepth,
  /luacoolprop/diagram/common/isotherm tolerance/.store in=\LCP@isothermtolerance,
  /luacoolprop/diagram/common/isentrope initial intervals/.store in=\LCP@isentropeinitialintervals,
  /luacoolprop/diagram/common/isentrope max depth/.store in=\LCP@isentropemaxdepth,
  /luacoolprop/diagram/common/isentrope tolerance/.store in=\LCP@isentropetolerance,
  /luacoolprop/diagram/common/isochore initial intervals/.store in=\LCP@isochoreinitialintervals,
  /luacoolprop/diagram/common/isochore max depth/.store in=\LCP@isochoremaxdepth,
  /luacoolprop/diagram/common/isochore tolerance/.store in=\LCP@isochoretolerance,
  /luacoolprop/diagram/common/saturation pressure epsilon/.store in=\LCP@saturationpressureepsilon,
  /luacoolprop/diagram/common/axis options/.code={\pgfplotsset{every axis/.append style={#1}}},
  /luacoolprop/diagram/common/.unknown/.code={%
    \LCP@warning{luacoolprop}{Unknown key `/luacoolprop/diagram/common/\pgfkeyscurrentname`}%
  }%
}

\def\LCP@lua@diagramopts{%
  fluid="\luaescapestring{\LCP@fluid}",%
  library="\luaescapestring{\LCP@library}",%
  reference_state="\luaescapestring{\LCP@referencestate}",%
  isoquality="\luaescapestring{\LCP@isoquality}",%
  isotherms="\luaescapestring{\LCP@isotherms}",%
  isentropes="\luaescapestring{\LCP@isentropes}",%
  isochores="\luaescapestring{\LCP@isochores}",%
  enthalpy_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@enthalpysymbol}}",%
  pressure_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@pressuresymbol}}",%
  quality_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@qualitysymbol}}",%
  temperature_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@temperaturesymbol}}",%
  entropy_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@entropysymbol}}",%
  specific_volume_symbol="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@specificvolumesymbol}}",%
  q_min="\luaescapestring{\LCP@qmin}",%
  q_max="\luaescapestring{\LCP@qmax}",%
  q_step="\luaescapestring{\LCP@qstep}",%
  q_mode="\luaescapestring{\LCP@qmode}",%
  q_count="\luaescapestring{\LCP@qcount}",%
  q_values="\luaescapestring{\LCP@qvalues}",%
  q_preset="\luaescapestring{\LCP@qpreset}",%
  pressure_min="\luaescapestring{\LCP@pressuremin}",%
  pressure_max="\luaescapestring{\LCP@pressuremax}",%
  pressure_max_factor="\luaescapestring{\LCP@pressuremaxfactor}",%
  h_scale="\luaescapestring{\LCP@hscale}",%
  p_scale="\luaescapestring{\LCP@pscale}",%
  initial_intervals="\luaescapestring{\LCP@initialintervals}",%
  max_depth="\luaescapestring{\LCP@maxdepth}",%
  tolerance="\luaescapestring{\LCP@tolerance}",%
  log_weight="\luaescapestring{\LCP@logweight}",%
  coord_digits="\luaescapestring{\LCP@coorddigits}",%
  domain_policy="\luaescapestring{\LCP@domainpolicy}",%
  labels="\luaescapestring{\LCP@labels}",%
  label_placement="\luaescapestring{\LCP@labelplacement}",%
  label_collision_policy="\luaescapestring{\LCP@labelcollisionpolicy}",%
  label_hide_overlap_threshold="\luaescapestring{\LCP@labelhideoverlapthreshold}",%
  label_hide_edge_threshold="\luaescapestring{\LCP@labelhideedgethreshold}",%
  label_candidate_count="\luaescapestring{\LCP@labelcandidatecount}",%
  label_min_pos="\luaescapestring{\LCP@labelminpos}",%
  label_max_pos="\luaescapestring{\LCP@labelmaxpos}",%
  autonode_candidates="\luaescapestring{\LCP@autonodecandidates}",%
  autonode_candidate_strategy="\luaescapestring{\LCP@autonodecandidatestrategy}",%
  autonode_clearance="\luaescapestring{\LCP@autonodeclearance}",%
  autonode_inner_sep="\luaescapestring{\LCP@autonodeinnersep}",%
  autonode_normal_shift="\luaescapestring{\LCP@autonodenormalshift}",%
  autonode_preferred_weight="\luaescapestring{\LCP@autonodepreferredweight}",%
  autonode_overlap_weight="\luaescapestring{\LCP@autonodeoverlapweight}",%
  label_separation="\luaescapestring{\LCP@labelseparation}",%
  label_overlap_penalty="\luaescapestring{\LCP@labeloverlappenalty}",%
  label_distance_penalty="\luaescapestring{\LCP@labeldistancepenalty}",%
  label_edge_penalty="\luaescapestring{\LCP@labeledgepenalty}",%
  label_box_base_width="\luaescapestring{\LCP@labelboxbasewidth}",%
  label_box_char_width="\luaescapestring{\LCP@labelboxcharwidth}",%
  label_box_height="\luaescapestring{\LCP@labelboxheight}",%
  label_box_sloped_multiplier="\luaescapestring{\LCP@labelboxslopedmultiplier}",%
  curve_overrides="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@curveoverrides}}",%
  quality_labels="\luaescapestring{\LCP@qualitylabels}",%
  isotherm_labels="\luaescapestring{\LCP@isothermlabels}",%
  isentrope_labels="\luaescapestring{\LCP@isentropelabels}",%
  isochore_labels="\luaescapestring{\LCP@isochorelabels}",%
  label_pos="\luaescapestring{\LCP@labelpos}",%
  quality_label_pos="\luaescapestring{\LCP@qualitylabelpos}",%
  isotherm_label_pos="\luaescapestring{\LCP@isothermlabelpos}",%
  isentrope_label_pos="\luaescapestring{\LCP@isentropelabelpos}",%
  isochore_label_pos="\luaescapestring{\LCP@isochorelabelpos}",%
  label_every="\luaescapestring{\LCP@labelevery}",%
  quality_label_every="\luaescapestring{\LCP@qualitylabelevery}",%
  isotherm_label_every="\luaescapestring{\LCP@isothermlabelevery}",%
  isentrope_label_every="\luaescapestring{\LCP@isentropelabelevery}",%
  isochore_label_every="\luaescapestring{\LCP@isochorelabelevery}",%
  label_sloped="\luaescapestring{\LCP@labelsloped}",%
  quality_label_sloped="\luaescapestring{\LCP@qualitylabelsloped}",%
  isotherm_label_sloped="\luaescapestring{\LCP@isothermlabelsloped}",%
  isentrope_label_sloped="\luaescapestring{\LCP@isentropelabelsloped}",%
  isochore_label_sloped="\luaescapestring{\LCP@isochorelabelsloped}",%
  label_allow_upside_down="\luaescapestring{\LCP@labelallowupsidedown}",%
  quality_label_allow_upside_down="\luaescapestring{\LCP@qualitylabelallowupsidedown}",%
  isotherm_label_allow_upside_down="\luaescapestring{\LCP@isothermlabelallowupsidedown}",%
  isentrope_label_allow_upside_down="\luaescapestring{\LCP@isentropelabelallowupsidedown}",%
  isochore_label_allow_upside_down="\luaescapestring{\LCP@isochorelabelallowupsidedown}",%
  label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@labelstyle}}",%
  quality_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@qualitylabelstyle}}",%
  isotherm_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isothermlabelstyle}}",%
  isentrope_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isentropelabelstyle}}",%
  isochore_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isochorelabelstyle}}",%
  legend="\luaescapestring{\LCP@legend}",%
  forget_plot="\luaescapestring{\LCP@forgetplot}",%
  curve_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@curvestyle}}",%
  quality_color="\luaescapestring{\LCP@qualitycolor}",%
  quality_boundary_color="\luaescapestring{\LCP@qualityboundarycolor}",%
  isotherm_color="\luaescapestring{\LCP@isothermcolor}",%
  isentrope_color="\luaescapestring{\LCP@isentropecolor}",%
  isochore_color="\luaescapestring{\LCP@isochorecolor}",%
  interior_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@interiorstyle}}",%
  boundary_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@boundarystyle}}",%
  isotherm_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isothermstyle}}",%
  entropy_unit="\luaescapestring{\LCP@isentropeunit}",%
  s_min="\luaescapestring{\LCP@smin}",%
  s_max="\luaescapestring{\LCP@smax}",%
  s_step="\luaescapestring{\LCP@sstep}",%
  s_mode="\luaescapestring{\LCP@smode}",%
  s_count="\luaescapestring{\LCP@scount}",%
  s_preset="\luaescapestring{\LCP@spreset}",%
  s_values="\luaescapestring{\LCP@svalues}",%
  isentrope_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isentropestyle}}",%
  specific_volume_unit="\luaescapestring{\LCP@specificvolumeunit}",%
  v_min="\luaescapestring{\LCP@vmin}",%
  v_max="\luaescapestring{\LCP@vmax}",%
  v_step="\luaescapestring{\LCP@vstep}",%
  v_mode="\luaescapestring{\LCP@vmode}",%
  v_count="\luaescapestring{\LCP@vcount}",%
  v_preset="\luaescapestring{\LCP@vpreset}",%
  v_values="\luaescapestring{\LCP@vvalues}",%
  isochore_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isochorestyle}}",%
  temperature_unit="\luaescapestring{\LCP@temperatureunit}",%
  t_min="\luaescapestring{\LCP@tmin}",%
  t_max="\luaescapestring{\LCP@tmax}",%
  t_step="\luaescapestring{\LCP@tstep}",%
  t_mode="\luaescapestring{\LCP@tmode}",%
  t_count="\luaescapestring{\LCP@tcount}",%
  t_preset="\luaescapestring{\LCP@tpreset}",%
  t_values="\luaescapestring{\LCP@tvalues}",%
  isotherm_initial_intervals="\luaescapestring{\LCP@isotherminitialintervals}",%
  isotherm_max_depth="\luaescapestring{\LCP@isothermmaxdepth}",%
  isotherm_tolerance="\luaescapestring{\LCP@isothermtolerance}",%
  isentrope_initial_intervals="\luaescapestring{\LCP@isentropeinitialintervals}",%
  isentrope_max_depth="\luaescapestring{\LCP@isentropemaxdepth}",%
  isentrope_tolerance="\luaescapestring{\LCP@isentropetolerance}",%
  isochore_initial_intervals="\luaescapestring{\LCP@isochoreinitialintervals}",%
  isochore_max_depth="\luaescapestring{\LCP@isochoremaxdepth}",%
  isochore_tolerance="\luaescapestring{\LCP@isochoretolerance}",%
  saturation_pressure_epsilon="\luaescapestring{\LCP@saturationpressureepsilon}"%
}

\def\LCP@lua@phisoquality{%
  \directlua{require("luacoolprop").tex.print_ph_isoquality({\LCP@lua@diagramopts})}%
}

\def\LCP@lua@phisotherms{%
  \directlua{require("luacoolprop").tex.print_ph_isotherms({\LCP@lua@diagramopts})}%
}

\def\LCP@lua@phisentropes{%
  \directlua{require("luacoolprop").tex.print_ph_isentropes({\LCP@lua@diagramopts})}%
}

\def\LCP@lua@phisochores{%
  \directlua{require("luacoolprop").tex.print_ph_isochores({\LCP@lua@diagramopts})}%
}

\def\LCP@lua@phplots{%
  \directlua{require("luacoolprop").tex.print_diagram("PH","plots",{\LCP@lua@diagramopts})}%
}

\def\LCP@lua@phaxisstyle{%
  \directlua{require("luacoolprop").tex.print_diagram("PH","axis_style",{\LCP@lua@diagramopts})}%
}


\def\LCP@lua@phfamilyautonode#1{%
  \directlua{require("luacoolprop").tex.print_diagram("PH","family",{\LCP@lua@diagramopts},"#1")}%
}

% --------------------------------------------------------------------------
% PV diagram keys
%
% PV combines the shared diagram controls with its declared quality,
% isotherm, and isentrope capabilities.  Its horizontal scale and
% logarithmic-x sampling weight are projection-specific.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/pv diagram/.is family}
\pgfkeys{/luacoolprop/diagram/PV/.is family}
\pgfkeys{/luacoolprop/diagram/PV/.search also={/luacoolprop/pv diagram,/luacoolprop/diagram/common}}

\pgfkeys{%
  /luacoolprop/pv diagram/specific volume scale/.code={\def\LCP@vscale{#1}\def\LCP@vaxisunit{custom}},
  /luacoolprop/pv diagram/v scale/.style={specific volume scale={#1}},
  /luacoolprop/pv diagram/log x weight/.store in=\LCP@logxweight,
  /luacoolprop/diagram/PV/specific volume axis unit/.is choice,
  /luacoolprop/diagram/PV/specific volume axis unit/m3kg/.code={\def\LCP@vscale{1}\def\LCP@vaxisunit{m3kg}},
  /luacoolprop/diagram/PV/specific volume axis unit/lkg/.code={\def\LCP@vscale{1000}\def\LCP@vaxisunit{lkg}},
  /luacoolprop/diagram/PV/pressure axis unit/.is choice,
  /luacoolprop/diagram/PV/pressure axis unit/pa/.code={\def\LCP@pscale{1}\def\LCP@paxisunit{pa}},
  /luacoolprop/diagram/PV/pressure axis unit/kpa/.code={\def\LCP@pscale{0.001}\def\LCP@paxisunit{kpa}},
  /luacoolprop/diagram/PV/pressure axis unit/mpa/.code={\def\LCP@pscale{0.000001}\def\LCP@paxisunit{mpa}},
  /luacoolprop/diagram/PV/pressure axis unit/bar/.code={\def\LCP@pscale{0.00001}\def\LCP@paxisunit{bar}},
  /luacoolprop/diagram/PV/isochore/.code={\LCP@unsupported@family{PV}{isochore}},
  /luacoolprop/diagram/PV/isochores/.style={isochore={#1}},
  /luacoolprop/diagram/PV/isenthalp/.code={\LCP@unsupported@family{PV}{isenthalp}},
  /luacoolprop/diagram/PV/isenthalps/.style={isenthalp={#1}},
  /luacoolprop/diagram/PV/isobar/.code={\LCP@unsupported@family{PV}{isobar}},
  /luacoolprop/diagram/PV/isobars/.style={isobar={#1}}
}

\def\LCP@pv@setdefaults{%
  \LCP@diagram@setdefaults
  \edef\LCP@vscale{\LCP@global@vscale}%
  \edef\LCP@vaxisunit{\LCP@global@vaxisunit}%
  \def\LCP@isochores{false}%
  \def\LCP@tolerance{0.18}%
  \def\LCP@isothermtolerance{0.18}%
  \def\LCP@isentropetolerance{0.18}%
  \def\LCP@logxweight{30}%
}

\def\LCP@lua@pvopts{%
  \LCP@lua@diagramopts,%
  v_scale="\luaescapestring{\LCP@vscale}",%
  log_x_weight="\luaescapestring{\LCP@logxweight}"%
}

\def\LCP@lua@pvplots{%
  \directlua{require("luacoolprop").tex.print_diagram("PV","plots",{\LCP@lua@pvopts})}%
}

\def\LCP@lua@pvaxisstyle{%
  \directlua{require("luacoolprop").tex.print_diagram("PV","axis_style",{\LCP@lua@pvopts})}%
}

\def\LCP@lua@pvfamilyautonode#1{%
  \directlua{require("luacoolprop").tex.print_diagram("PV","family",{\LCP@lua@pvopts},"#1")}%
}

% --------------------------------------------------------------------------
% TS diagram keys
%
% TS combines the shared diagram controls with quality and constant-specific-
% enthalpy families, plus the coordinate scales of this projection.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/ts diagram/.is family}
\pgfkeys{/luacoolprop/diagram/TS/.is family}
\pgfkeys{/luacoolprop/diagram/TS/.search also={/luacoolprop/ts diagram,/luacoolprop/diagram/common}}

\pgfkeys{%
  /luacoolprop/ts diagram/isenthalp/.store in=\LCP@isenthalps,
  /luacoolprop/ts diagram/isenthalps/.style={isenthalp={#1}},
  /luacoolprop/ts diagram/enthalpy curves/.style={isenthalp={#1}},
  /luacoolprop/ts diagram/entropy scale/.code={\def\LCP@sscale{#1}\def\LCP@saxisunit{custom}},
  /luacoolprop/ts diagram/s scale/.style={entropy scale={#1}},
  /luacoolprop/ts diagram/temperature scale/.code={\def\LCP@temperaturescale{#1}\def\LCP@temperatureoffset{0}\def\LCP@temperatureaxisunit{custom}},
  /luacoolprop/ts diagram/t scale/.style={temperature scale={#1}},
  /luacoolprop/ts diagram/enthalpy unit/.store in=\LCP@enthalpyunit,
  /luacoolprop/ts diagram/h unit/.style={enthalpy unit={#1}},
  /luacoolprop/ts diagram/enthalpy min/.store in=\LCP@hmin,
  /luacoolprop/ts diagram/enthalpy max/.store in=\LCP@hmax,
  /luacoolprop/ts diagram/enthalpy step/.store in=\LCP@hstep,
  /luacoolprop/ts diagram/enthalpy mode/.store in=\LCP@hmode,
  /luacoolprop/ts diagram/enthalpy count/.store in=\LCP@hcount,
  /luacoolprop/ts diagram/enthalpy values/.store in=\LCP@hvalues,
  /luacoolprop/ts diagram/h min/.style={enthalpy min={#1}},
  /luacoolprop/ts diagram/h max/.style={enthalpy max={#1}},
  /luacoolprop/ts diagram/h step/.style={enthalpy step={#1}},
  /luacoolprop/ts diagram/h mode/.style={enthalpy mode={#1}},
  /luacoolprop/ts diagram/h count/.style={enthalpy count={#1}},
  /luacoolprop/ts diagram/h values/.style={enthalpy values={#1}},
  /luacoolprop/ts diagram/isenthalp labels/.store in=\LCP@isenthalplabels,
  /luacoolprop/ts diagram/isenthalp label pos/.store in=\LCP@isenthalplabelpos,
  /luacoolprop/ts diagram/isenthalp label every/.store in=\LCP@isenthalplabelevery,
  /luacoolprop/ts diagram/isenthalp label sloped/.store in=\LCP@isenthalplabelsloped,
  /luacoolprop/ts diagram/isenthalp label allow upside down/.store in=\LCP@isenthalplabelallowupsidedown,
  /luacoolprop/ts diagram/isenthalp label style/.store in=\LCP@isenthalplabelstyle,
  /luacoolprop/ts diagram/isenthalp label node style/.code={\tikzset{luacoolprop isenthalp label node/.style={#1}}},
  /luacoolprop/ts diagram/isenthalp color/.store in=\LCP@isenthalpcolor,
  /luacoolprop/ts diagram/isenthalp style/.store in=\LCP@isenthalpstyle,
  /luacoolprop/ts diagram/isenthalp initial intervals/.store in=\LCP@isenthalpinitialintervals,
  /luacoolprop/ts diagram/isenthalp max depth/.store in=\LCP@isenthalpmaxdepth,
  /luacoolprop/ts diagram/isenthalp tolerance/.store in=\LCP@isenthalptolerance,
  /luacoolprop/ts diagram/temperature weight/.store in=\LCP@temperatureweight,
  /luacoolprop/ts diagram/entropy axis min/.store in=\LCP@entropyaxismin,
  /luacoolprop/ts diagram/entropy axis max/.store in=\LCP@entropyaxismax,
  /luacoolprop/ts diagram/temperature axis min/.store in=\LCP@temperatureaxismin,
  /luacoolprop/ts diagram/temperature axis max/.store in=\LCP@temperatureaxismax,
  /luacoolprop/diagram/TS/entropy axis unit/.is choice,
  /luacoolprop/diagram/TS/entropy axis unit/jkgk/.code={\def\LCP@sscale{1}\def\LCP@saxisunit{jkgk}},
  /luacoolprop/diagram/TS/entropy axis unit/kjkgk/.code={\def\LCP@sscale{0.001}\def\LCP@saxisunit{kjkgk}},
  /luacoolprop/diagram/TS/temperature axis unit/.is choice,
  /luacoolprop/diagram/TS/temperature axis unit/kelvin/.code={\def\LCP@temperaturescale{1}\def\LCP@temperatureoffset{0}\def\LCP@temperatureaxisunit{kelvin}},
  /luacoolprop/diagram/TS/temperature axis unit/celsius/.code={\def\LCP@temperaturescale{1}\def\LCP@temperatureoffset{273.15}\def\LCP@temperatureaxisunit{celsius}},
  /luacoolprop/diagram/TS/isotherm/.code={\LCP@unsupported@family{TS}{isotherm}},
  /luacoolprop/diagram/TS/isotherms/.style={isotherm={#1}},
  /luacoolprop/diagram/TS/isentrope/.code={\LCP@unsupported@family{TS}{isentrope}},
  /luacoolprop/diagram/TS/isentropes/.style={isentrope={#1}},
  /luacoolprop/diagram/TS/isochore/.code={\LCP@unsupported@family{TS}{isochore}},
  /luacoolprop/diagram/TS/isochores/.style={isochore={#1}},
  /luacoolprop/diagram/TS/isobar/.code={\LCP@unsupported@family{TS}{isobar}},
  /luacoolprop/diagram/TS/isobars/.style={isobar={#1}}
}

\def\LCP@ts@setdefaults{%
  \LCP@diagram@setdefaults
  \def\LCP@isenthalps{false}%
  \edef\LCP@sscale{\LCP@global@sscale}%
  \edef\LCP@temperaturescale{\LCP@global@temperaturescale}%
  \edef\LCP@saxisunit{\LCP@global@saxisunit}%
  \edef\LCP@temperatureaxisunit{\LCP@global@temperatureaxisunit}%
  \edef\LCP@temperatureoffset{\LCP@global@temperatureoffset}%
  \def\LCP@enthalpyunit{kjkg}%
  \def\LCP@hmin{auto}%
  \def\LCP@hmax{auto}%
  \def\LCP@hstep{auto}%
  \def\LCP@hmode{linear}%
  \def\LCP@hcount{8}%
  \def\LCP@hvalues{}%
  \def\LCP@isenthalplabels{auto}%
  \def\LCP@isenthalplabelpos{0.65}%
  \def\LCP@isenthalplabelevery{2}%
  \def\LCP@isenthalplabelsloped{auto}%
  \def\LCP@isenthalplabelallowupsidedown{auto}%
  \def\LCP@isenthalplabelstyle{luacoolprop isenthalp label node}%
  \def\LCP@isenthalpcolor{red!65!black}%
  \def\LCP@isenthalpstyle{line width=0.25pt,densely dashed}%
  \def\LCP@isenthalpinitialintervals{16}%
  \def\LCP@isenthalpmaxdepth{8}%
  \def\LCP@isenthalptolerance{0.10}%
  \def\LCP@temperatureweight{0.01}%
  \def\LCP@entropyaxismin{auto}%
  \def\LCP@entropyaxismax{auto}%
  \def\LCP@temperatureaxismin{auto}%
  \def\LCP@temperatureaxismax{auto}%
  \def\LCP@tolerance{0.10}%
  \def\LCP@logweight{8}%
}

\def\LCP@lua@tsopts{%
  \LCP@lua@diagramopts,%
  isenthalps="\luaescapestring{\LCP@isenthalps}",%
  s_scale="\luaescapestring{\LCP@sscale}",%
  temperature_scale="\luaescapestring{\LCP@temperaturescale}",%
  temperature_offset="\luaescapestring{\LCP@temperatureoffset}",%
  enthalpy_unit="\luaescapestring{\LCP@enthalpyunit}",%
  h_min="\luaescapestring{\LCP@hmin}",%
  h_max="\luaescapestring{\LCP@hmax}",%
  h_step="\luaescapestring{\LCP@hstep}",%
  h_mode="\luaescapestring{\LCP@hmode}",%
  h_count="\luaescapestring{\LCP@hcount}",%
  h_values="\luaescapestring{\LCP@hvalues}",%
  isenthalp_labels="\luaescapestring{\LCP@isenthalplabels}",%
  isenthalp_label_pos="\luaescapestring{\LCP@isenthalplabelpos}",%
  isenthalp_label_every="\luaescapestring{\LCP@isenthalplabelevery}",%
  isenthalp_label_sloped="\luaescapestring{\LCP@isenthalplabelsloped}",%
  isenthalp_label_allow_upside_down="\luaescapestring{\LCP@isenthalplabelallowupsidedown}",%
  isenthalp_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isenthalplabelstyle}}",%
  isenthalp_color="\luaescapestring{\LCP@isenthalpcolor}",%
  isenthalp_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isenthalpstyle}}",%
  isenthalp_initial_intervals="\luaescapestring{\LCP@isenthalpinitialintervals}",%
  isenthalp_max_depth="\luaescapestring{\LCP@isenthalpmaxdepth}",%
  isenthalp_tolerance="\luaescapestring{\LCP@isenthalptolerance}",%
  temperature_weight="\luaescapestring{\LCP@temperatureweight}",%
  entropy_axis_min="\luaescapestring{\LCP@entropyaxismin}",%
  entropy_axis_max="\luaescapestring{\LCP@entropyaxismax}",%
  temperature_axis_min="\luaescapestring{\LCP@temperatureaxismin}",%
  temperature_axis_max="\luaescapestring{\LCP@temperatureaxismax}"%
}

\def\LCP@lua@tsplots{%
  \directlua{require("luacoolprop").tex.print_diagram("TS","plots",{\LCP@lua@tsopts})}%
}

\def\LCP@lua@tsaxisstyle{%
  \directlua{require("luacoolprop").tex.print_diagram("TS","axis_style",{\LCP@lua@tsopts})}%
}

\def\LCP@lua@tsfamilyautonode#1{%
  \directlua{require("luacoolprop").tex.print_diagram("TS","family",{\LCP@lua@tsopts},"#1")}%
}

% --------------------------------------------------------------------------
% HS diagram keys
%
% HS is mass-specific enthalpy versus mass-specific entropy.  It combines the
% shared controls with quality, isochore, isotherm, and isobar families.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/hs diagram/.is family}
\pgfkeys{/luacoolprop/diagram/HS/.is family}
\pgfkeys{/luacoolprop/diagram/HS/.search also={/luacoolprop/hs diagram,/luacoolprop/diagram/common}}

\pgfkeys{%
  /luacoolprop/hs diagram/isobar/.store in=\LCP@isobars,
  /luacoolprop/hs diagram/isobars/.style={isobar={#1}},
  /luacoolprop/hs diagram/entropy scale/.code={\def\LCP@sscale{#1}\def\LCP@saxisunit{custom}},
  /luacoolprop/hs diagram/s scale/.style={entropy scale={#1}},
  /luacoolprop/hs diagram/enthalpy weight/.store in=\LCP@enthalpyweight,
  /luacoolprop/hs diagram/isobar unit/.store in=\LCP@isobarunit,
  /luacoolprop/hs diagram/isobar min/.store in=\LCP@isobarmin,
  /luacoolprop/hs diagram/isobar max/.store in=\LCP@isobarmax,
  /luacoolprop/hs diagram/isobar step/.store in=\LCP@isobarstep,
  /luacoolprop/hs diagram/isobar mode/.store in=\LCP@isobarmode,
  /luacoolprop/hs diagram/isobar count/.store in=\LCP@isobarcount,
  /luacoolprop/hs diagram/isobar values/.store in=\LCP@isobarvalues,
  /luacoolprop/hs diagram/isobar temperature min/.store in=\LCP@isobartemperaturemin,
  /luacoolprop/hs diagram/isobar temperature max/.store in=\LCP@isobartemperaturemax,
  /luacoolprop/hs diagram/isobar labels/.store in=\LCP@isobarlabels,
  /luacoolprop/hs diagram/isobar label pos/.store in=\LCP@isobarlabelpos,
  /luacoolprop/hs diagram/isobar label every/.store in=\LCP@isobarlabelevery,
  /luacoolprop/hs diagram/isobar label sloped/.store in=\LCP@isobarlabelsloped,
  /luacoolprop/hs diagram/isobar label allow upside down/.store in=\LCP@isobarlabelallowupsidedown,
  /luacoolprop/hs diagram/isobar label style/.store in=\LCP@isobarlabelstyle,
  /luacoolprop/hs diagram/isobar label node style/.code={\tikzset{luacoolprop isobar label node/.style={#1}}},
  /luacoolprop/hs diagram/isobar color/.store in=\LCP@isobarcolor,
  /luacoolprop/hs diagram/isobar style/.store in=\LCP@isobarstyle,
  /luacoolprop/hs diagram/isobar initial intervals/.store in=\LCP@isobarinitialintervals,
  /luacoolprop/hs diagram/isobar max depth/.store in=\LCP@isobarmaxdepth,
  /luacoolprop/hs diagram/isobar tolerance/.store in=\LCP@isobartolerance,
  /luacoolprop/hs diagram/entropy axis min/.store in=\LCP@entropyaxismin,
  /luacoolprop/hs diagram/entropy axis max/.store in=\LCP@entropyaxismax,
  /luacoolprop/hs diagram/enthalpy axis min/.store in=\LCP@enthalpyaxismin,
  /luacoolprop/hs diagram/enthalpy axis max/.store in=\LCP@enthalpyaxismax,
  /luacoolprop/diagram/HS/entropy axis unit/.is choice,
  /luacoolprop/diagram/HS/entropy axis unit/jkgk/.code={\def\LCP@sscale{1}\def\LCP@saxisunit{jkgk}},
  /luacoolprop/diagram/HS/entropy axis unit/kjkgk/.code={\def\LCP@sscale{0.001}\def\LCP@saxisunit{kjkgk}},
  /luacoolprop/diagram/HS/enthalpy axis unit/.is choice,
  /luacoolprop/diagram/HS/enthalpy axis unit/jkg/.code={\def\LCP@hscale{1}\def\LCP@haxisunit{jkg}},
  /luacoolprop/diagram/HS/enthalpy axis unit/kjkg/.code={\def\LCP@hscale{0.001}\def\LCP@haxisunit{kjkg}},
  /luacoolprop/diagram/HS/isentrope/.code={\LCP@unsupported@family{HS}{isentrope}},
  /luacoolprop/diagram/HS/isentropes/.style={isentrope={#1}},
  /luacoolprop/diagram/HS/isenthalp/.code={\LCP@unsupported@family{HS}{isenthalp}},
  /luacoolprop/diagram/HS/isenthalps/.style={isenthalp={#1}}
}

\def\LCP@hs@setdefaults{%
  \LCP@diagram@setdefaults
  \def\LCP@isobars{false}%
  \edef\LCP@sscale{\LCP@global@sscale}%
  \edef\LCP@saxisunit{\LCP@global@saxisunit}%
  \def\LCP@enthalpyweight{0.01}%
  \def\LCP@isobarunit{bar}%
  \def\LCP@isobarmin{auto}%
  \def\LCP@isobarmax{auto}%
  \def\LCP@isobarstep{auto}%
  \def\LCP@isobarmode{log}%
  \def\LCP@isobarcount{7}%
  \def\LCP@isobarvalues{}%
  \def\LCP@isobartemperaturemin{auto}%
  \def\LCP@isobartemperaturemax{auto}%
  \def\LCP@isobarlabels{auto}%
  \def\LCP@isobarlabelpos{0.68}%
  \def\LCP@isobarlabelevery{2}%
  \def\LCP@isobarlabelsloped{auto}%
  \def\LCP@isobarlabelallowupsidedown{auto}%
  \def\LCP@isobarlabelstyle{luacoolprop isobar label node}%
  \def\LCP@isobarcolor{purple!70!black}%
  \def\LCP@isobarstyle{line width=0.25pt,dash pattern=on 3pt off 1.5pt}%
  \def\LCP@isobarinitialintervals{16}%
  \def\LCP@isobarmaxdepth{8}%
  \def\LCP@isobartolerance{0.10}%
  \def\LCP@entropyaxismin{auto}%
  \def\LCP@entropyaxismax{auto}%
  \def\LCP@enthalpyaxismin{auto}%
  \def\LCP@enthalpyaxismax{auto}%
  \def\LCP@tolerance{0.10}%
  \def\LCP@isothermtolerance{0.10}%
  \def\LCP@isochoretolerance{0.10}%
  \def\LCP@logweight{8}%
}

\def\LCP@lua@hsopts{%
  \LCP@lua@diagramopts,%
  isobars="\luaescapestring{\LCP@isobars}",%
  s_scale="\luaescapestring{\LCP@sscale}",%
  enthalpy_weight="\luaescapestring{\LCP@enthalpyweight}",%
  isobar_unit="\luaescapestring{\LCP@isobarunit}",%
  isobar_min="\luaescapestring{\LCP@isobarmin}",%
  isobar_max="\luaescapestring{\LCP@isobarmax}",%
  isobar_step="\luaescapestring{\LCP@isobarstep}",%
  isobar_mode="\luaescapestring{\LCP@isobarmode}",%
  isobar_count="\luaescapestring{\LCP@isobarcount}",%
  isobar_values="\luaescapestring{\LCP@isobarvalues}",%
  isobar_temperature_min="\luaescapestring{\LCP@isobartemperaturemin}",%
  isobar_temperature_max="\luaescapestring{\LCP@isobartemperaturemax}",%
  isobar_labels="\luaescapestring{\LCP@isobarlabels}",%
  isobar_label_pos="\luaescapestring{\LCP@isobarlabelpos}",%
  isobar_label_every="\luaescapestring{\LCP@isobarlabelevery}",%
  isobar_label_sloped="\luaescapestring{\LCP@isobarlabelsloped}",%
  isobar_label_allow_upside_down="\luaescapestring{\LCP@isobarlabelallowupsidedown}",%
  isobar_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isobarlabelstyle}}",%
  isobar_color="\luaescapestring{\LCP@isobarcolor}",%
  isobar_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isobarstyle}}",%
  isobar_initial_intervals="\luaescapestring{\LCP@isobarinitialintervals}",%
  isobar_max_depth="\luaescapestring{\LCP@isobarmaxdepth}",%
  isobar_tolerance="\luaescapestring{\LCP@isobartolerance}",%
  entropy_axis_min="\luaescapestring{\LCP@entropyaxismin}",%
  entropy_axis_max="\luaescapestring{\LCP@entropyaxismax}",%
  enthalpy_axis_min="\luaescapestring{\LCP@enthalpyaxismin}",%
  enthalpy_axis_max="\luaescapestring{\LCP@enthalpyaxismax}"%
}

\def\LCP@lua@hsplots{%
  \directlua{require("luacoolprop").tex.print_diagram("HS","plots",{\LCP@lua@hsopts})}%
}

\def\LCP@lua@hsaxisstyle{%
  \directlua{require("luacoolprop").tex.print_diagram("HS","axis_style",{\LCP@lua@hsopts})}%
}

\def\LCP@lua@hsfamilyautonode#1{%
  \directlua{require("luacoolprop").tex.print_diagram("HS","family",{\LCP@lua@hsopts},"#1")}%
}

% --------------------------------------------------------------------------
% PT diagram keys
%
% PT is pressure versus temperature.  Its coexistence locus is represented by
% one phase-envelope curve because saturated liquid and saturated vapour have
% identical PT coordinates.  Isentropes, isenthalps, and isochores are
% independent optional background families.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/pt diagram/.is family}
\pgfkeys{/luacoolprop/diagram/PT/.is family}
\pgfkeys{/luacoolprop/diagram/PT/.search also={/luacoolprop/pt diagram,/luacoolprop/diagram/common}}

\pgfkeys{%
  /luacoolprop/pt diagram/phase envelope/.store in=\LCP@phaseenvelope,
  /luacoolprop/pt diagram/saturation/.style={phase envelope={#1}},
  /luacoolprop/pt diagram/equilibrium curve/.style={phase envelope={#1}},
  /luacoolprop/pt diagram/isenthalp/.store in=\LCP@isenthalps,
  /luacoolprop/pt diagram/isenthalps/.style={isenthalp={#1}},
  /luacoolprop/pt diagram/enthalpy curves/.style={isenthalp={#1}},
  /luacoolprop/pt diagram/temperature scale/.code={\def\LCP@temperaturescale{#1}\def\LCP@temperatureoffset{0}\def\LCP@temperatureaxisunit{custom}},
  /luacoolprop/pt diagram/t scale/.style={temperature scale={#1}},
  /luacoolprop/pt diagram/enthalpy unit/.store in=\LCP@enthalpyunit,
  /luacoolprop/pt diagram/h unit/.style={enthalpy unit={#1}},
  /luacoolprop/pt diagram/enthalpy min/.store in=\LCP@hmin,
  /luacoolprop/pt diagram/enthalpy max/.store in=\LCP@hmax,
  /luacoolprop/pt diagram/enthalpy step/.store in=\LCP@hstep,
  /luacoolprop/pt diagram/enthalpy mode/.store in=\LCP@hmode,
  /luacoolprop/pt diagram/enthalpy count/.store in=\LCP@hcount,
  /luacoolprop/pt diagram/enthalpy values/.store in=\LCP@hvalues,
  /luacoolprop/pt diagram/h min/.style={enthalpy min={#1}},
  /luacoolprop/pt diagram/h max/.style={enthalpy max={#1}},
  /luacoolprop/pt diagram/h step/.style={enthalpy step={#1}},
  /luacoolprop/pt diagram/h mode/.style={enthalpy mode={#1}},
  /luacoolprop/pt diagram/h count/.style={enthalpy count={#1}},
  /luacoolprop/pt diagram/h values/.style={enthalpy values={#1}},
  /luacoolprop/pt diagram/isenthalp labels/.store in=\LCP@isenthalplabels,
  /luacoolprop/pt diagram/isenthalp label pos/.store in=\LCP@isenthalplabelpos,
  /luacoolprop/pt diagram/isenthalp label every/.store in=\LCP@isenthalplabelevery,
  /luacoolprop/pt diagram/isenthalp label sloped/.store in=\LCP@isenthalplabelsloped,
  /luacoolprop/pt diagram/isenthalp label allow upside down/.store in=\LCP@isenthalplabelallowupsidedown,
  /luacoolprop/pt diagram/isenthalp label style/.store in=\LCP@isenthalplabelstyle,
  /luacoolprop/pt diagram/isenthalp label node style/.code={\tikzset{luacoolprop isenthalp label node/.style={#1}}},
  /luacoolprop/pt diagram/isenthalp color/.store in=\LCP@isenthalpcolor,
  /luacoolprop/pt diagram/isenthalp style/.store in=\LCP@isenthalpstyle,
  /luacoolprop/pt diagram/isenthalp initial intervals/.store in=\LCP@isenthalpinitialintervals,
  /luacoolprop/pt diagram/isenthalp max depth/.store in=\LCP@isenthalpmaxdepth,
  /luacoolprop/pt diagram/isenthalp tolerance/.store in=\LCP@isenthalptolerance,
  /luacoolprop/pt diagram/phase envelope labels/.store in=\LCP@phaseenvelopelabels,
  /luacoolprop/pt diagram/phase envelope label text/.store in=\LCP@phaseenvelopelabeltext,
  /luacoolprop/pt diagram/phase envelope label pos/.store in=\LCP@phaseenvelopelabelpos,
  /luacoolprop/pt diagram/phase envelope label sloped/.store in=\LCP@phaseenvelopelabelsloped,
  /luacoolprop/pt diagram/phase envelope label allow upside down/.store in=\LCP@phaseenvelopelabelallowupsidedown,
  /luacoolprop/pt diagram/phase envelope label style/.store in=\LCP@phaseenvelopelabelstyle,
  /luacoolprop/pt diagram/phase envelope label node style/.code={\tikzset{luacoolprop phase envelope label node/.style={#1}}},
  /luacoolprop/pt diagram/phase envelope color/.store in=\LCP@phaseenvelopecolor,
  /luacoolprop/pt diagram/phase envelope style/.store in=\LCP@phaseenvelopestyle,
  /luacoolprop/pt diagram/phase envelope initial intervals/.store in=\LCP@phaseenvelopeinitialintervals,
  /luacoolprop/pt diagram/phase envelope max depth/.store in=\LCP@phaseenvelopemaxdepth,
  /luacoolprop/pt diagram/phase envelope tolerance/.store in=\LCP@phaseenvelopetolerance,
  /luacoolprop/pt diagram/temperature axis min/.store in=\LCP@temperatureaxismin,
  /luacoolprop/pt diagram/temperature axis max/.store in=\LCP@temperatureaxismax,
  /luacoolprop/diagram/PT/temperature axis unit/.is choice,
  /luacoolprop/diagram/PT/temperature axis unit/kelvin/.code={\def\LCP@temperaturescale{1}\def\LCP@temperatureoffset{0}\def\LCP@temperatureaxisunit{kelvin}},
  /luacoolprop/diagram/PT/temperature axis unit/celsius/.code={\def\LCP@temperaturescale{1}\def\LCP@temperatureoffset{273.15}\def\LCP@temperatureaxisunit{celsius}},
  /luacoolprop/diagram/PT/pressure axis unit/.is choice,
  /luacoolprop/diagram/PT/pressure axis unit/pa/.code={\def\LCP@pscale{1}\def\LCP@paxisunit{pa}},
  /luacoolprop/diagram/PT/pressure axis unit/kpa/.code={\def\LCP@pscale{0.001}\def\LCP@paxisunit{kpa}},
  /luacoolprop/diagram/PT/pressure axis unit/mpa/.code={\def\LCP@pscale{0.000001}\def\LCP@paxisunit{mpa}},
  /luacoolprop/diagram/PT/pressure axis unit/bar/.code={\def\LCP@pscale{0.00001}\def\LCP@paxisunit{bar}},
  /luacoolprop/diagram/PT/quality/.code={\LCP@unsupported@family{PT}{quality}},
  /luacoolprop/diagram/PT/isoquality/.style={quality={#1}},
  /luacoolprop/diagram/PT/isotherm/.code={\LCP@unsupported@family{PT}{isotherm}},
  /luacoolprop/diagram/PT/isotherms/.style={isotherm={#1}},
  /luacoolprop/diagram/PT/isobar/.code={\LCP@unsupported@family{PT}{isobar}},
  /luacoolprop/diagram/PT/isobars/.style={isobar={#1}}
}

\def\LCP@pt@setdefaults{%
  \LCP@diagram@setdefaults
  \def\LCP@isoquality{false}%
  \def\LCP@phaseenvelope{true}%
  \def\LCP@isenthalps{false}%
  \edef\LCP@temperaturescale{\LCP@global@temperaturescale}%
  \edef\LCP@temperatureoffset{\LCP@global@temperatureoffset}%
  \edef\LCP@temperatureaxisunit{\LCP@global@temperatureaxisunit}%
  \def\LCP@enthalpyunit{kjkg}%
  \def\LCP@hmin{auto}\def\LCP@hmax{auto}\def\LCP@hstep{auto}%
  \def\LCP@hmode{linear}\def\LCP@hcount{8}\def\LCP@hvalues{}%
  \def\LCP@isenthalplabels{auto}\def\LCP@isenthalplabelpos{0.62}%
  \def\LCP@isenthalplabelevery{2}\def\LCP@isenthalplabelsloped{auto}%
  \def\LCP@isenthalplabelallowupsidedown{auto}%
  \def\LCP@isenthalplabelstyle{luacoolprop isenthalp label node}%
  \def\LCP@isenthalpcolor{red!65!black}%
  \def\LCP@isenthalpstyle{line width=0.25pt,densely dashed}%
  \def\LCP@isenthalpinitialintervals{18}\def\LCP@isenthalpmaxdepth{8}%
  \def\LCP@isenthalptolerance{0.10}%
  \def\LCP@phaseenvelopelabels{auto}%
  \def\LCP@phaseenvelopelabeltext{liquid--vapour equilibrium}%
  \def\LCP@phaseenvelopelabelpos{0.45}%
  \def\LCP@phaseenvelopelabelsloped{auto}%
  \def\LCP@phaseenvelopelabelallowupsidedown{auto}%
  \def\LCP@phaseenvelopelabelstyle{luacoolprop phase envelope label node}%
  \def\LCP@phaseenvelopecolor{blue!80!black}%
  \def\LCP@phaseenvelopestyle{line width=0.9pt}%
  \def\LCP@phaseenvelopeinitialintervals{20}%
  \def\LCP@phaseenvelopemaxdepth{9}%
  \def\LCP@phaseenvelopetolerance{0.08}%
  \def\LCP@temperatureaxismin{auto}\def\LCP@temperatureaxismax{auto}%
  \def\LCP@tolerance{0.10}\def\LCP@logweight{30}%
  \def\LCP@isentropetolerance{0.10}\def\LCP@isochoretolerance{0.10}%
}

\def\LCP@lua@ptopts{%
  \LCP@lua@diagramopts,%
  phase_envelope="\luaescapestring{\LCP@phaseenvelope}",%
  isenthalps="\luaescapestring{\LCP@isenthalps}",%
  temperature_scale="\luaescapestring{\LCP@temperaturescale}",%
  temperature_offset="\luaescapestring{\LCP@temperatureoffset}",%
  enthalpy_unit="\luaescapestring{\LCP@enthalpyunit}",%
  h_min="\luaescapestring{\LCP@hmin}",h_max="\luaescapestring{\LCP@hmax}",%
  h_step="\luaescapestring{\LCP@hstep}",h_mode="\luaescapestring{\LCP@hmode}",%
  h_count="\luaescapestring{\LCP@hcount}",h_values="\luaescapestring{\LCP@hvalues}",%
  isenthalp_labels="\luaescapestring{\LCP@isenthalplabels}",%
  isenthalp_label_pos="\luaescapestring{\LCP@isenthalplabelpos}",%
  isenthalp_label_every="\luaescapestring{\LCP@isenthalplabelevery}",%
  isenthalp_label_sloped="\luaescapestring{\LCP@isenthalplabelsloped}",%
  isenthalp_label_allow_upside_down="\luaescapestring{\LCP@isenthalplabelallowupsidedown}",%
  isenthalp_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isenthalplabelstyle}}",%
  isenthalp_color="\luaescapestring{\LCP@isenthalpcolor}",%
  isenthalp_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@isenthalpstyle}}",%
  isenthalp_initial_intervals="\luaescapestring{\LCP@isenthalpinitialintervals}",%
  isenthalp_max_depth="\luaescapestring{\LCP@isenthalpmaxdepth}",%
  isenthalp_tolerance="\luaescapestring{\LCP@isenthalptolerance}",%
  phase_envelope_labels="\luaescapestring{\LCP@phaseenvelopelabels}",%
  phase_envelope_label_text="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@phaseenvelopelabeltext}}",%
  phase_envelope_label_pos="\luaescapestring{\LCP@phaseenvelopelabelpos}",%
  phase_envelope_label_sloped="\luaescapestring{\LCP@phaseenvelopelabelsloped}",%
  phase_envelope_label_allow_upside_down="\luaescapestring{\LCP@phaseenvelopelabelallowupsidedown}",%
  phase_envelope_label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@phaseenvelopelabelstyle}}",%
  phase_envelope_color="\luaescapestring{\LCP@phaseenvelopecolor}",%
  phase_envelope_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@phaseenvelopestyle}}",%
  phase_envelope_initial_intervals="\luaescapestring{\LCP@phaseenvelopeinitialintervals}",%
  phase_envelope_max_depth="\luaescapestring{\LCP@phaseenvelopemaxdepth}",%
  phase_envelope_tolerance="\luaescapestring{\LCP@phaseenvelopetolerance}",%
  temperature_axis_min="\luaescapestring{\LCP@temperatureaxismin}",%
  temperature_axis_max="\luaescapestring{\LCP@temperatureaxismax}"%
}

\def\LCP@lua@ptplots{%
  \directlua{require("luacoolprop").tex.print_diagram("PT","plots",{\LCP@lua@ptopts})}%
}
\def\LCP@lua@ptaxisstyle{%
  \directlua{require("luacoolprop").tex.print_diagram("PT","axis_style",{\LCP@lua@ptopts})}%
}
\def\LCP@lua@ptfamilyautonode#1{%
  \directlua{require("luacoolprop").tex.print_diagram("PT","family",{\LCP@lua@ptopts},"#1")}%
}


% --------------------------------------------------------------------------
% Process segment keys shared by every registered coordinate projection.
% --------------------------------------------------------------------------

\pgfkeys{/luacoolprop/process/common/.is family}
\pgfkeys{/luacoolprop/process/PH/.is family}
\pgfkeys{/luacoolprop/process/PH/.search also={/luacoolprop/process/common}}
\pgfkeys{/luacoolprop/ph process/.is family}
\pgfkeys{/luacoolprop/ph process/.search also={/luacoolprop/process/PH}}
\pgfkeys{%
  /luacoolprop/process/PH/enthalpy axis unit/.is choice,
  /luacoolprop/process/PH/enthalpy axis unit/jkg/.code={\def\LCP@processhscale{1}},
  /luacoolprop/process/PH/enthalpy axis unit/kjkg/.code={\def\LCP@processhscale{0.001}},
  /luacoolprop/process/PH/pressure axis unit/.is choice,
  /luacoolprop/process/PH/pressure axis unit/pa/.code={\def\LCP@processpscale{1}},
  /luacoolprop/process/PH/pressure axis unit/kpa/.code={\def\LCP@processpscale{0.001}},
  /luacoolprop/process/PH/pressure axis unit/mpa/.code={\def\LCP@processpscale{0.000001}},
  /luacoolprop/process/PH/pressure axis unit/bar/.code={\def\LCP@processpscale{0.00001}}
}

\def\LCP@process@setdefaults{%
  \edef\LCP@processfluid{\LCP@global@fluid}%
  \edef\LCP@processlibrary{\LCP@global@library}%
  \edef\LCP@processreferencestate{\LCP@global@referencestate}%
  \edef\LCP@processhscale{\LCP@global@hscale}%
  \edef\LCP@processpscale{\LCP@global@pscale}%
  \def\LCP@processtype{isobar}%
  \def\LCP@processfrom{}%
  \def\LCP@processto{}%
  \def\LCP@processvalue{}%
  \def\LCP@processp{}%
  \def\LCP@processT{}%
  \def\LCP@processh{}%
  \def\LCP@processs{}%
  \def\LCP@processq{}%
  \def\LCP@processv{}%
  \def\LCP@processname{}%
  \def\LCP@processcolor{black}%
  \def\LCP@processstyle{line width=1pt}%
  \def\LCP@processlabel{}%
  \def\LCP@processlabelpos{0.5}%
  \def\LCP@processlabelstyle{luacoolprop process label node}%
  \def\LCP@processlabelsloped{true}%
  \def\LCP@processlabelallowupsidedown{false}%
  \def\LCP@processmarkendpoints{false}%
  \def\LCP@processmarkerstyle{only marks,mark=*,mark size=1.2pt}%
  \def\LCP@processinitialintervals{12}%
  \def\LCP@processmaxdepth{7}%
  \def\LCP@processtolerance{0.25}%
  \def\LCP@processlogweight{30}%
  \def\LCP@processcoorddigits{6}%
  \def\LCP@processdomainpolicy{ignore}%
  \def\LCP@processexportcoordinates{}%
  \def\LCP@processlogcoordinates{false}%
}

\pgfkeys{%
  /luacoolprop/process/common/fluid/.store in=\LCP@processfluid,
  /luacoolprop/process/common/library/.store in=\LCP@processlibrary,
  /luacoolprop/process/common/reference state/.store in=\LCP@processreferencestate,
  /luacoolprop/process/common/type/.store in=\LCP@processtype,
  /luacoolprop/process/common/kind/.style={type={#1}},
  /luacoolprop/process/common/process/.style={type={#1}},
  /luacoolprop/process/common/from/.store in=\LCP@processfrom,
  /luacoolprop/process/common/start/.style={from={#1}},
  /luacoolprop/process/common/to/.store in=\LCP@processto,
  /luacoolprop/process/common/stop/.style={to={#1}},
  /luacoolprop/process/common/value/.store in=\LCP@processvalue,
  /luacoolprop/process/common/pressure/.store in=\LCP@processp,
  /luacoolprop/process/common/p/.style={pressure={#1}},
  /luacoolprop/process/common/temperature/.store in=\LCP@processT,
  /luacoolprop/process/common/T/.style={temperature={#1}},
  /luacoolprop/process/common/t/.style={temperature={#1}},
  /luacoolprop/process/common/enthalpy/.store in=\LCP@processh,
  /luacoolprop/process/common/h/.style={enthalpy={#1}},
  /luacoolprop/process/common/entropy/.store in=\LCP@processs,
  /luacoolprop/process/common/s/.style={entropy={#1}},
  /luacoolprop/process/common/quality/.store in=\LCP@processq,
  /luacoolprop/process/common/q/.style={quality={#1}},
  /luacoolprop/process/common/Q/.style={quality={#1}},
  /luacoolprop/process/common/specific volume/.store in=\LCP@processv,
  /luacoolprop/process/common/v/.style={specific volume={#1}},
  /luacoolprop/process/common/name/.store in=\LCP@processname,
  /luacoolprop/process/common/id/.style={name={#1}},
  /luacoolprop/process/common/color/.store in=\LCP@processcolor,
  /luacoolprop/process/common/process color/.style={color={#1}},
  /luacoolprop/process/common/style/.store in=\LCP@processstyle,
  /luacoolprop/process/common/process style/.style={style={#1}},
  /luacoolprop/process/common/label/.store in=\LCP@processlabel,
  /luacoolprop/process/common/label text/.style={label={#1}},
  /luacoolprop/process/common/label pos/.store in=\LCP@processlabelpos,
  /luacoolprop/process/common/label style/.store in=\LCP@processlabelstyle,
  /luacoolprop/process/common/label node style/.code={\tikzset{luacoolprop process label node/.style={#1}}},
  /luacoolprop/process/common/label sloped/.store in=\LCP@processlabelsloped,
  /luacoolprop/process/common/sloped label/.style={label sloped={#1}},
  /luacoolprop/process/common/label allow upside down/.store in=\LCP@processlabelallowupsidedown,
  /luacoolprop/process/common/mark endpoints/.store in=\LCP@processmarkendpoints,
  /luacoolprop/process/common/markers/.style={mark endpoints={#1}},
  /luacoolprop/process/common/marker style/.store in=\LCP@processmarkerstyle,
  /luacoolprop/process/common/enthalpy scale/.store in=\LCP@processhscale,
  /luacoolprop/process/common/h scale/.style={enthalpy scale={#1}},
  /luacoolprop/process/common/pressure scale/.store in=\LCP@processpscale,
  /luacoolprop/process/common/p scale/.style={pressure scale={#1}},
  /luacoolprop/process/common/initial intervals/.store in=\LCP@processinitialintervals,
  /luacoolprop/process/common/max depth/.store in=\LCP@processmaxdepth,
  /luacoolprop/process/common/tolerance/.store in=\LCP@processtolerance,
  /luacoolprop/process/common/log weight/.store in=\LCP@processlogweight,
  /luacoolprop/process/common/coord digits/.store in=\LCP@processcoorddigits,
  /luacoolprop/process/common/domain policy/.store in=\LCP@processdomainpolicy,
  /luacoolprop/process/common/discontinuity policy/.style={domain policy={#1}},
  /luacoolprop/process/common/export coordinates/.store in=\LCP@processexportcoordinates,
  /luacoolprop/process/common/log coordinates/.store in=\LCP@processlogcoordinates,
  /luacoolprop/process/common/.unknown/.code={%
    \LCP@warning{luacoolprop}{Unknown key `/luacoolprop/process/common/\pgfkeyscurrentname`}%
  }%
}

\def\LCP@lua@processopts{%
  fluid="\luaescapestring{\LCP@processfluid}",%
  library="\luaescapestring{\LCP@processlibrary}",%
  reference_state="\luaescapestring{\LCP@processreferencestate}",%
  type="\luaescapestring{\LCP@processtype}",%
  % State specifications are numeric data. Expand earlier endpoint exports so
  % one process can feed the next; styles and labels remain unexpanded below.
  from="\luaescapestring{\LCP@processfrom}",%
  to="\luaescapestring{\LCP@processto}",%
  value="\luaescapestring{\LCP@processvalue}",%
  p="\luaescapestring{\LCP@processp}",%
  T="\luaescapestring{\LCP@processT}",%
  h="\luaescapestring{\LCP@processh}",%
  s="\luaescapestring{\LCP@processs}",%
  q="\luaescapestring{\LCP@processq}",%
  v="\luaescapestring{\LCP@processv}",%
  name="\luaescapestring{\LCP@processname}",%
  color="\luaescapestring{\LCP@processcolor}",%
  style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@processstyle}}",%
  label="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@processlabel}}",%
  label_pos="\luaescapestring{\LCP@processlabelpos}",%
  label_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@processlabelstyle}}",%
  label_sloped="\luaescapestring{\LCP@processlabelsloped}",%
  label_allow_upside_down="\luaescapestring{\LCP@processlabelallowupsidedown}",%
  mark_endpoints="\luaescapestring{\LCP@processmarkendpoints}",%
  marker_style="\luaescapestring{\LCP@unexpanded\expandafter{\LCP@processmarkerstyle}}",%
  h_scale="\luaescapestring{\LCP@processhscale}",%
  p_scale="\luaescapestring{\LCP@processpscale}",%
  process_initial_intervals="\luaescapestring{\LCP@processinitialintervals}",%
  process_max_depth="\luaescapestring{\LCP@processmaxdepth}",%
  process_tolerance="\luaescapestring{\LCP@processtolerance}",%
  log_weight="\luaescapestring{\LCP@processlogweight}",%
  coord_digits="\luaescapestring{\LCP@processcoorddigits}",%
  domain_policy="\luaescapestring{\LCP@processdomainpolicy}",%
  export_coordinates="\luaescapestring{\LCP@processexportcoordinates}",%
  log_coordinates="\luaescapestring{\LCP@processlogcoordinates}"%
}

\def\LCP@lua@phprocess{%
  \directlua{require("luacoolprop").tex.print_diagram("PH","process",{\LCP@lua@processopts})}%
}

% PV adds the scale of its horizontal specific-volume coordinate.
\pgfkeys{/luacoolprop/pv process/.is family}
\pgfkeys{/luacoolprop/process/PV/.is family}
\pgfkeys{/luacoolprop/process/PV/.search also={/luacoolprop/pv process,/luacoolprop/process/common}}

\pgfkeys{%
  /luacoolprop/pv process/specific volume scale/.store in=\LCP@processvscale,
  /luacoolprop/pv process/v scale/.style={specific volume scale={#1}},
  /luacoolprop/pv process/log x weight/.store in=\LCP@processlogxweight,
  /luacoolprop/process/PV/specific volume axis unit/.is choice,
  /luacoolprop/process/PV/specific volume axis unit/m3kg/.code={\def\LCP@processvscale{1}},
  /luacoolprop/process/PV/specific volume axis unit/lkg/.code={\def\LCP@processvscale{1000}},
  /luacoolprop/process/PV/pressure axis unit/.is choice,
  /luacoolprop/process/PV/pressure axis unit/pa/.code={\def\LCP@processpscale{1}},
  /luacoolprop/process/PV/pressure axis unit/kpa/.code={\def\LCP@processpscale{0.001}},
  /luacoolprop/process/PV/pressure axis unit/mpa/.code={\def\LCP@processpscale{0.000001}},
  /luacoolprop/process/PV/pressure axis unit/bar/.code={\def\LCP@processpscale{0.00001}}
}

\def\LCP@pv@process@setdefaults{%
  \LCP@process@setdefaults
  \edef\LCP@processvscale{\LCP@global@vscale}%
  \def\LCP@processtolerance{0.18}%
  \def\LCP@processlogxweight{30}%
}

\def\LCP@lua@pvprocessopts{%
  \LCP@lua@processopts,%
  v_scale="\luaescapestring{\LCP@processvscale}",%
  log_x_weight="\luaescapestring{\LCP@processlogxweight}"%
}

\def\LCP@lua@pvprocess{%
  \directlua{require("luacoolprop").tex.print_diagram("PV","process",{\LCP@lua@pvprocessopts})}%
}

% TS adds the scales of its plotted entropy and temperature coordinates.
\pgfkeys{/luacoolprop/ts process/.is family}
\pgfkeys{/luacoolprop/process/TS/.is family}
\pgfkeys{/luacoolprop/process/TS/.search also={/luacoolprop/ts process,/luacoolprop/process/common}}

\pgfkeys{%
  /luacoolprop/ts process/entropy scale/.store in=\LCP@processsscale,
  /luacoolprop/ts process/s scale/.style={entropy scale={#1}},
  /luacoolprop/ts process/temperature scale/.store in=\LCP@processtemperaturescale,
  /luacoolprop/ts process/t scale/.style={temperature scale={#1}},
  /luacoolprop/ts process/temperature weight/.store in=\LCP@processtemperatureweight,
  /luacoolprop/process/TS/entropy axis unit/.is choice,
  /luacoolprop/process/TS/entropy axis unit/jkgk/.code={\def\LCP@processsscale{1}},
  /luacoolprop/process/TS/entropy axis unit/kjkgk/.code={\def\LCP@processsscale{0.001}},
  /luacoolprop/process/TS/temperature axis unit/.is choice,
  /luacoolprop/process/TS/temperature axis unit/kelvin/.code={\def\LCP@processtemperaturescale{1}\def\LCP@processtemperatureoffset{0}},
  /luacoolprop/process/TS/temperature axis unit/celsius/.code={\def\LCP@processtemperaturescale{1}\def\LCP@processtemperatureoffset{273.15}}
}

\def\LCP@ts@process@setdefaults{%
  \LCP@process@setdefaults
  \edef\LCP@processsscale{\LCP@global@sscale}%
  \edef\LCP@processtemperaturescale{\LCP@global@temperaturescale}%
  \edef\LCP@processtemperatureoffset{\LCP@global@temperatureoffset}%
  \def\LCP@processtolerance{0.10}%
  \def\LCP@processlogweight{8}%
  \def\LCP@processtemperatureweight{0.01}%
}

\def\LCP@lua@tsprocessopts{%
  \LCP@lua@processopts,%
  s_scale="\luaescapestring{\LCP@processsscale}",%
  temperature_scale="\luaescapestring{\LCP@processtemperaturescale}",%
  temperature_offset="\luaescapestring{\LCP@processtemperatureoffset}",%
  temperature_weight="\luaescapestring{\LCP@processtemperatureweight}"%
}

\def\LCP@lua@tsprocess{%
  \directlua{require("luacoolprop").tex.print_diagram("TS","process",{\LCP@lua@tsprocessopts})}%
}

% HS adds its entropy coordinate and adaptive enthalpy error weight.
\pgfkeys{/luacoolprop/hs process/.is family}
\pgfkeys{/luacoolprop/process/HS/.is family}
\pgfkeys{/luacoolprop/process/HS/.search also={/luacoolprop/hs process,/luacoolprop/process/common}}

\pgfkeys{%
  /luacoolprop/hs process/entropy scale/.store in=\LCP@processsscale,
  /luacoolprop/hs process/s scale/.style={entropy scale={#1}},
  /luacoolprop/hs process/enthalpy weight/.store in=\LCP@processenthalpyweight,
  /luacoolprop/process/HS/entropy axis unit/.is choice,
  /luacoolprop/process/HS/entropy axis unit/jkgk/.code={\def\LCP@processsscale{1}},
  /luacoolprop/process/HS/entropy axis unit/kjkgk/.code={\def\LCP@processsscale{0.001}},
  /luacoolprop/process/HS/enthalpy axis unit/.is choice,
  /luacoolprop/process/HS/enthalpy axis unit/jkg/.code={\def\LCP@processhscale{1}},
  /luacoolprop/process/HS/enthalpy axis unit/kjkg/.code={\def\LCP@processhscale{0.001}}
}

\def\LCP@hs@process@setdefaults{%
  \LCP@process@setdefaults
  \edef\LCP@processsscale{\LCP@global@sscale}%
  \def\LCP@processtolerance{0.10}%
  \def\LCP@processlogweight{8}%
  \def\LCP@processenthalpyweight{0.01}%
}

\def\LCP@lua@hsprocessopts{%
  \LCP@lua@processopts,%
  s_scale="\luaescapestring{\LCP@processsscale}",%
  enthalpy_weight="\luaescapestring{\LCP@processenthalpyweight}"%
}

\def\LCP@lua@hsprocess{%
  \directlua{require("luacoolprop").tex.print_diagram("HS","process",{\LCP@lua@hsprocessopts})}%
}

% PT adds its plotted temperature coordinate to the common pressure scale.
\pgfkeys{/luacoolprop/pt process/.is family}
\pgfkeys{/luacoolprop/process/PT/.is family}
\pgfkeys{/luacoolprop/process/PT/.search also={/luacoolprop/pt process,/luacoolprop/process/common}}
\pgfkeys{%
  /luacoolprop/pt process/temperature scale/.store in=\LCP@processtemperaturescale,
  /luacoolprop/pt process/t scale/.style={temperature scale={#1}},
  /luacoolprop/process/PT/temperature axis unit/.is choice,
  /luacoolprop/process/PT/temperature axis unit/kelvin/.code={\def\LCP@processtemperaturescale{1}\def\LCP@processtemperatureoffset{0}},
  /luacoolprop/process/PT/temperature axis unit/celsius/.code={\def\LCP@processtemperaturescale{1}\def\LCP@processtemperatureoffset{273.15}},
  /luacoolprop/process/PT/pressure axis unit/.is choice,
  /luacoolprop/process/PT/pressure axis unit/pa/.code={\def\LCP@processpscale{1}},
  /luacoolprop/process/PT/pressure axis unit/kpa/.code={\def\LCP@processpscale{0.001}},
  /luacoolprop/process/PT/pressure axis unit/mpa/.code={\def\LCP@processpscale{0.000001}},
  /luacoolprop/process/PT/pressure axis unit/bar/.code={\def\LCP@processpscale{0.00001}}
}

\def\LCP@pt@process@setdefaults{%
  \LCP@process@setdefaults
  \edef\LCP@processtemperaturescale{\LCP@global@temperaturescale}%
  \edef\LCP@processtemperatureoffset{\LCP@global@temperatureoffset}%
  \def\LCP@processtolerance{0.10}%
  \def\LCP@processlogweight{30}%
}

\def\LCP@lua@ptprocessopts{%
  \LCP@lua@processopts,%
  temperature_scale="\luaescapestring{\LCP@processtemperaturescale}",%
  temperature_offset="\luaescapestring{\LCP@processtemperatureoffset}"%
}

\def\LCP@lua@ptprocess{%
  \directlua{require("luacoolprop").tex.print_diagram("PT","process",{\LCP@lua@ptprocessopts})}%
}

% --------------------------------------------------------------------------
% Diagram dispatch API
%
% Public diagram identifiers are uppercase coordinate pairs (PH, PV, TS, HS,
% and PT).
% Each registered type provides render,
% plots, family, and process handlers with the same calling convention.
% --------------------------------------------------------------------------

\long\def\LCP@register@diagram#1#2#3#4{%
  \expandafter\def\csname LCP@diagram@#1@render\endcsname##1{#2[##1]}%
  \expandafter\def\csname LCP@diagram@#1@plots\endcsname##1{#3[##1]}%
  \expandafter\def\csname LCP@diagram@#1@process\endcsname##1{#4[##1]}%
}

\long\def\LCP@register@diagram@family#1#2#3{%
  \expandafter\def\csname LCP@diagram@#1@family@#2\endcsname##1{#3[##1]}%
}

\def\LCP@diagram@dispatch#1#2#3{%
  \expandafter\ifx\csname LCP@diagram@#1@#2\endcsname\relax
    \errmessage{luacoolprop: diagram type `#1' does not implement `#2'}%
  \else
    \csname LCP@diagram@#1@#2\endcsname{#3}%
  \fi
}

\def\LCPDiagram#1{%
  \pgfutil@ifnextchar[{\LCP@Diagram{#1}}{\LCP@Diagram{#1}[]}%
}
\def\LCP@Diagram#1[#2]{\LCP@diagram@dispatch{#1}{render}{#2}}

\def\LCPAddDiagramPlots#1{%
  \pgfutil@ifnextchar[{\LCP@AddDiagramPlots{#1}}{\LCP@AddDiagramPlots{#1}[]}%
}
\def\LCP@AddDiagramPlots#1[#2]{\LCP@diagram@dispatch{#1}{plots}{#2}}

\def\LCPAddDiagramFamily#1#2{%
  \pgfutil@ifnextchar[{\LCP@AddDiagramFamily{#1}{#2}}{\LCP@AddDiagramFamily{#1}{#2}[]}%
}
\def\LCP@AddDiagramFamily#1#2[#3]{%
  \expandafter\ifx\csname LCP@diagram@#1@family@#2\endcsname\relax
    \errmessage{luacoolprop: diagram type `#1' has no `#2' family}%
  \else
    \csname LCP@diagram@#1@family@#2\endcsname{#3}%
  \fi
}

\def\LCPAddDiagramProcess#1{%
  \pgfutil@ifnextchar[{\LCP@AddDiagramProcess{#1}}{\LCP@AddDiagramProcess{#1}[]}%
}
\def\LCP@AddDiagramProcess#1[#2]{\LCP@diagram@dispatch{#1}{process}{#2}}

% PH registration uses the same dispatch API as PV, TS, and HS.
\def\LCP@AddPHProcess[#1]{%
  \begingroup
    \LCP@process@setdefaults
    \pgfkeys{/luacoolprop/process/PH/.cd,#1}%
    \LCP@lua@phprocess
  \endgroup
}

% Low-level macros: use inside an existing pgfplots axis.
\def\LCP@AddPHQuality[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \def\LCP@isoquality{true}%
    \def\LCP@isotherms{false}%
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phfamilyautonode{quality}
  \endgroup
}

\def\LCP@AddPHIsotherms[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isotherms{true}%
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phfamilyautonode{isotherm}
  \endgroup
}

\def\LCP@AddPHIsentropes[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isentropes{true}%
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phfamilyautonode{isentrope}
  \endgroup
}

\def\LCP@AddPHIsochores[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isochores{true}%
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phfamilyautonode{isochore}
  \endgroup
}

\def\LCP@AddPHPlots[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phplots
  \endgroup
}

% High-level macro: creates the TikZ picture and the pgfplots axis.
\def\LCP@PHDiagram[#1]{%
  \begingroup
    \LCP@diagram@setdefaults
    \LCP@ph@applyaxisdefaults
    \pgfkeys{/luacoolprop/diagram/PH/.cd,#1}%
    \LCP@lua@phaxisstyle
    \ifx\begin\undefined
      \tikzpicture
        \axis
          \LCP@lua@phplots
        \endaxis
      \endtikzpicture
    \else
      \begin{tikzpicture}%
        \begin{axis}%
          \LCP@lua@phplots
        \end{axis}%
      \end{tikzpicture}%
    \fi
  \endgroup
}

% PV handlers implement the generic dispatch contract.
\def\LCP@AddPVProcess[#1]{%
  \begingroup
    \LCP@pv@process@setdefaults
    \pgfkeys{/luacoolprop/process/PV/.cd,#1}%
    \LCP@lua@pvprocess
  \endgroup
}

\def\LCP@AddPVQuality[#1]{%
  \begingroup
    \LCP@pv@setdefaults
    \def\LCP@isoquality{true}%
    \def\LCP@isotherms{false}%
    \def\LCP@isentropes{false}%
    \pgfkeys{/luacoolprop/diagram/PV/.cd,#1}%
    \LCP@lua@pvfamilyautonode{quality}%
  \endgroup
}

\def\LCP@AddPVIsotherms[#1]{%
  \begingroup
    \LCP@pv@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isotherms{true}%
    \def\LCP@isentropes{false}%
    \pgfkeys{/luacoolprop/diagram/PV/.cd,#1}%
    \LCP@lua@pvfamilyautonode{isotherm}%
  \endgroup
}

\def\LCP@AddPVIsentropes[#1]{%
  \begingroup
    \LCP@pv@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isotherms{false}%
    \def\LCP@isentropes{true}%
    \pgfkeys{/luacoolprop/diagram/PV/.cd,#1}%
    \LCP@lua@pvfamilyautonode{isentrope}%
  \endgroup
}

\def\LCP@AddPVPlots[#1]{%
  \begingroup
    \LCP@pv@setdefaults
    \pgfkeys{/luacoolprop/diagram/PV/.cd,#1}%
    \LCP@lua@pvplots
  \endgroup
}

\def\LCP@PVDiagram[#1]{%
  \begingroup
    \LCP@pv@setdefaults
    \LCP@pv@applyaxisdefaults
    \pgfkeys{/luacoolprop/diagram/PV/.cd,#1}%
    \LCP@lua@pvaxisstyle
    \ifx\begin\undefined
      \tikzpicture
        \axis
          \LCP@lua@pvplots
        \endaxis
      \endtikzpicture
    \else
      \begin{tikzpicture}%
        \begin{axis}%
          \LCP@lua@pvplots
        \end{axis}%
      \end{tikzpicture}%
    \fi
  \endgroup
}

% TS handlers implement the generic dispatch contract.
\def\LCP@AddTSProcess[#1]{%
  \begingroup
    \LCP@ts@process@setdefaults
    \pgfkeys{/luacoolprop/process/TS/.cd,#1}%
    \LCP@lua@tsprocess
  \endgroup
}

\def\LCP@AddTSQuality[#1]{%
  \begingroup
    \LCP@ts@setdefaults
    \def\LCP@isoquality{true}%
    \def\LCP@isenthalps{false}%
    \pgfkeys{/luacoolprop/diagram/TS/.cd,#1}%
    \LCP@lua@tsfamilyautonode{quality}%
  \endgroup
}

\def\LCP@AddTSIsenthalps[#1]{%
  \begingroup
    \LCP@ts@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isenthalps{true}%
    \pgfkeys{/luacoolprop/diagram/TS/.cd,#1}%
    \LCP@lua@tsfamilyautonode{isenthalp}%
  \endgroup
}

\def\LCP@AddTSPlots[#1]{%
  \begingroup
    \LCP@ts@setdefaults
    \pgfkeys{/luacoolprop/diagram/TS/.cd,#1}%
    \LCP@lua@tsplots
  \endgroup
}

\def\LCP@TSDiagram[#1]{%
  \begingroup
    \LCP@ts@setdefaults
    \LCP@ts@applyaxisdefaults
    \pgfkeys{/luacoolprop/diagram/TS/.cd,#1}%
    \LCP@lua@tsaxisstyle
    \ifx\begin\undefined
      \tikzpicture
        \axis
          \LCP@lua@tsplots
        \endaxis
      \endtikzpicture
    \else
      \begin{tikzpicture}%
        \begin{axis}%
          \LCP@lua@tsplots
        \end{axis}%
      \end{tikzpicture}%
    \fi
  \endgroup
}

% HS handlers mirror the other projections and expose all four requested
% background families independently inside an existing axis.
\def\LCP@AddHSProcess[#1]{%
  \begingroup
    \LCP@hs@process@setdefaults
    \pgfkeys{/luacoolprop/process/HS/.cd,#1}%
    \LCP@lua@hsprocess
  \endgroup
}

\def\LCP@AddHSQuality[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \def\LCP@isoquality{true}%
    \def\LCP@isochores{false}%
    \def\LCP@isotherms{false}%
    \def\LCP@isobars{false}%
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsfamilyautonode{quality}%
  \endgroup
}

\def\LCP@AddHSIsochores[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isochores{true}%
    \def\LCP@isotherms{false}%
    \def\LCP@isobars{false}%
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsfamilyautonode{isochore}%
  \endgroup
}

\def\LCP@AddHSIsotherms[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isochores{false}%
    \def\LCP@isotherms{true}%
    \def\LCP@isobars{false}%
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsfamilyautonode{isotherm}%
  \endgroup
}

\def\LCP@AddHSIsobars[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \def\LCP@isoquality{false}%
    \def\LCP@isochores{false}%
    \def\LCP@isotherms{false}%
    \def\LCP@isobars{true}%
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsfamilyautonode{isobar}%
  \endgroup
}

\def\LCP@AddHSPlots[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsplots
  \endgroup
}

\def\LCP@HSDiagram[#1]{%
  \begingroup
    \LCP@hs@setdefaults
    \LCP@hs@applyaxisdefaults
    \pgfkeys{/luacoolprop/diagram/HS/.cd,#1}%
    \LCP@lua@hsaxisstyle
    \ifx\begin\undefined
      \tikzpicture
        \axis
          \LCP@lua@hsplots
        \endaxis
      \endtikzpicture
    \else
      \begin{tikzpicture}%
        \begin{axis}%
          \LCP@lua@hsplots
        \end{axis}%
      \end{tikzpicture}%
    \fi
  \endgroup
}

% PT handlers expose the coexistence curve and all three optional isoline
% families independently inside an existing axis.
\def\LCP@AddPTProcess[#1]{%
  \begingroup
    \LCP@pt@process@setdefaults
    \pgfkeys{/luacoolprop/process/PT/.cd,#1}%
    \LCP@lua@ptprocess
  \endgroup
}

\def\LCP@AddPTPhaseEnvelope[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \def\LCP@phaseenvelope{true}%
    \def\LCP@isentropes{false}\def\LCP@isenthalps{false}%
    \def\LCP@isochores{false}%
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptfamilyautonode{phase_envelope}%
  \endgroup
}

\def\LCP@AddPTIsentropes[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \def\LCP@phaseenvelope{false}%
    \def\LCP@isentropes{true}\def\LCP@isenthalps{false}%
    \def\LCP@isochores{false}%
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptfamilyautonode{isentrope}%
  \endgroup
}

\def\LCP@AddPTIsenthalps[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \def\LCP@phaseenvelope{false}%
    \def\LCP@isentropes{false}\def\LCP@isenthalps{true}%
    \def\LCP@isochores{false}%
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptfamilyautonode{isenthalp}%
  \endgroup
}

\def\LCP@AddPTIsochores[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \def\LCP@phaseenvelope{false}%
    \def\LCP@isentropes{false}\def\LCP@isenthalps{false}%
    \def\LCP@isochores{true}%
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptfamilyautonode{isochore}%
  \endgroup
}

\def\LCP@AddPTPlots[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptplots
  \endgroup
}

\def\LCP@PTDiagram[#1]{%
  \begingroup
    \LCP@pt@setdefaults
    \LCP@pt@applyaxisdefaults
    \pgfkeys{/luacoolprop/diagram/PT/.cd,#1}%
    \LCP@lua@ptaxisstyle
    \ifx\begin\undefined
      \tikzpicture
        \axis
          \LCP@lua@ptplots
        \endaxis
      \endtikzpicture
    \else
      \begin{tikzpicture}%
        \begin{axis}%
          \LCP@lua@ptplots
        \end{axis}%
      \end{tikzpicture}%
    \fi
  \endgroup
}

% Register the built-in operations and curve families. Custom diagram types
% provide the same internal handlers and their own capability key families.
\LCP@register@diagram{PH}{\LCP@PHDiagram}{\LCP@AddPHPlots}{\LCP@AddPHProcess}
\LCP@register@diagram@family{PH}{quality}{\LCP@AddPHQuality}
\LCP@register@diagram@family{PH}{isotherm}{\LCP@AddPHIsotherms}
\LCP@register@diagram@family{PH}{isentrope}{\LCP@AddPHIsentropes}
\LCP@register@diagram@family{PH}{isochore}{\LCP@AddPHIsochores}

\LCP@register@diagram{PV}{\LCP@PVDiagram}{\LCP@AddPVPlots}{\LCP@AddPVProcess}
\LCP@register@diagram@family{PV}{quality}{\LCP@AddPVQuality}
\LCP@register@diagram@family{PV}{isotherm}{\LCP@AddPVIsotherms}
\LCP@register@diagram@family{PV}{isentrope}{\LCP@AddPVIsentropes}

\LCP@register@diagram{TS}{\LCP@TSDiagram}{\LCP@AddTSPlots}{\LCP@AddTSProcess}
\LCP@register@diagram@family{TS}{quality}{\LCP@AddTSQuality}
\LCP@register@diagram@family{TS}{isenthalp}{\LCP@AddTSIsenthalps}

\LCP@register@diagram{HS}{\LCP@HSDiagram}{\LCP@AddHSPlots}{\LCP@AddHSProcess}
\LCP@register@diagram@family{HS}{quality}{\LCP@AddHSQuality}
\LCP@register@diagram@family{HS}{isochore}{\LCP@AddHSIsochores}
\LCP@register@diagram@family{HS}{isotherm}{\LCP@AddHSIsotherms}
\LCP@register@diagram@family{HS}{isobar}{\LCP@AddHSIsobars}

\LCP@register@diagram{PT}{\LCP@PTDiagram}{\LCP@AddPTPlots}{\LCP@AddPTProcess}
\LCP@register@diagram@family{PT}{phase_envelope}{\LCP@AddPTPhaseEnvelope}
\LCP@register@diagram@family{PT}{isentrope}{\LCP@AddPTIsentropes}
\LCP@register@diagram@family{PT}{isenthalp}{\LCP@AddPTIsenthalps}
\LCP@register@diagram@family{PT}{isochore}{\LCP@AddPTIsochores}

% Canonical PH convenience API. The uppercase diagram code is deliberately
% stable so that every coordinate system follows the same convenience pattern.
\def\LCPPHDiagram{\LCPDiagram{PH}}
\def\LCPAddPHPlots{\LCPAddDiagramPlots{PH}}
\def\LCPAddPHQuality{\LCPAddDiagramFamily{PH}{quality}}
\def\LCPAddPHIsotherms{\LCPAddDiagramFamily{PH}{isotherm}}
\def\LCPAddPHIsentropes{\LCPAddDiagramFamily{PH}{isentrope}}
\def\LCPAddPHIsochores{\LCPAddDiagramFamily{PH}{isochore}}
\def\LCPAddPHProcess{\LCPAddDiagramProcess{PH}}

% Canonical PV convenience API.  PV is pressure versus mass-specific volume;
% the default high-level axis is logarithmic in both coordinates.
\def\LCPPVDiagram{\LCPDiagram{PV}}
\def\LCPAddPVPlots{\LCPAddDiagramPlots{PV}}
\def\LCPAddPVQuality{\LCPAddDiagramFamily{PV}{quality}}
\def\LCPAddPVIsotherms{\LCPAddDiagramFamily{PV}{isotherm}}
\def\LCPAddPVIsentropes{\LCPAddDiagramFamily{PV}{isentrope}}
\def\LCPAddPVProcess{\LCPAddDiagramProcess{PV}}

% Canonical TS convenience API.  TS is absolute temperature versus
% mass-specific entropy; both axes are linear by default.
\def\LCPTSDiagram{\LCPDiagram{TS}}
\def\LCPAddTSPlots{\LCPAddDiagramPlots{TS}}
\def\LCPAddTSQuality{\LCPAddDiagramFamily{TS}{quality}}
\def\LCPAddTSIsenthalps{\LCPAddDiagramFamily{TS}{isenthalp}}
\def\LCPAddTSProcess{\LCPAddDiagramProcess{TS}}

% Canonical HS convenience API.  HS is mass-specific enthalpy versus
% mass-specific entropy; both axes are linear by default.
\def\LCPHSDiagram{\LCPDiagram{HS}}
\def\LCPAddHSPlots{\LCPAddDiagramPlots{HS}}
\def\LCPAddHSQuality{\LCPAddDiagramFamily{HS}{quality}}
\def\LCPAddHSIsochores{\LCPAddDiagramFamily{HS}{isochore}}
\def\LCPAddHSIsotherms{\LCPAddDiagramFamily{HS}{isotherm}}
\def\LCPAddHSIsobars{\LCPAddDiagramFamily{HS}{isobar}}
\def\LCPAddHSProcess{\LCPAddDiagramProcess{HS}}

% Canonical PT convenience API.  PT is pressure versus temperature; the
% default high-level axis is logarithmic only in pressure.
\def\LCPPTDiagram{\LCPDiagram{PT}}
\def\LCPAddPTPlots{\LCPAddDiagramPlots{PT}}
\def\LCPAddPTPhaseEnvelope{\LCPAddDiagramFamily{PT}{phase_envelope}}
\def\LCPAddPTIsentropes{\LCPAddDiagramFamily{PT}{isentrope}}
\def\LCPAddPTIsenthalps{\LCPAddDiagramFamily{PT}{isenthalp}}
\def\LCPAddPTIsochores{\LCPAddDiagramFamily{PT}{isochore}}
\def\LCPAddPTProcess{\LCPAddDiagramProcess{PT}}

% Alternate spellings. The canonical API above is recommended for documents.
\let\LCPPhDiagram\LCPPHDiagram
\let\LCPAddPHIsoQuality\LCPAddPHQuality
\let\LCPAddPhIsoQuality\LCPAddPHQuality
\let\LCPAddPhIsotherms\LCPAddPHIsotherms
\let\LCPAddPhIsentropes\LCPAddPHIsentropes
\let\LCPAddPhIsochores\LCPAddPHIsochores
\let\LCPAddPhPlots\LCPAddPHPlots
\let\LCPAddPhProcess\LCPAddPHProcess
\let\LCPPhIsoQualityDiagram\LCPPHDiagram
\let\LCPAddTSIsoQuality\LCPAddTSQuality
\let\LCPAddHSIsoQuality\LCPAddHSQuality
\let\LCPAddPTSaturation\LCPAddPTPhaseEnvelope

\catcode`\@=\LCPSavedAtCatcode\relax
\endinput
