19. Additive-Volume Real-Fluid EOS Closure#

This appendix summarizes the equation-of-state closure used for real-fluid mixtures through the Stream EOS interface. The key idea is an Amagat-style additive-volume mixture rule: all species share one pressure and one temperature, each species contributes its pure-fluid specific volume at that common pressure and temperature, and the mixture specific volume is the mass-fraction-weighted sum of those pure-species specific volumes.

This matters numerically because the EOS queries used by the flow solver are not independent black boxes. A query such as (p,\rho,Y) or (p,h,Y) is closed by finding a temperature and a set of pure-species densities that satisfy the additive-volume relation and the requested thermodynamic constraint. If a face state is built from independently reconstructed pressure, density, enthalpy, and composition, those quantities may correspond to different thermodynamic states even though each scalar reconstruction looks reasonable on its own.

19.1. Notation#

Let

  • (Y_k) be the mass fraction of species (k),

  • (\rho) be the mixture density,

  • (v=1/\rho) be the mixture specific volume,

  • (p) be pressure,

  • (T) be the common mixture temperature,

  • (\widehat{\rho}_k(p,T)) be the pure-species density of species (k) at the common pressure and temperature,

  • (e_k) be the species specific internal energy at the converged state,

  • (e) be the mixture specific internal energy,

  • (h=e+p/\rho) be the mixture static enthalpy,

  • (H=h+\frac{1}{2}\lVert \vec{u}\rVert^2) be the mixture total enthalpy.

The notation (\widehat{\rho}_k) is intentionally distinct from the density-weighted species variable

(19.1.1)#\[\rho_k^{\mathrm{part}} = \rho Y_k.\]

In several code paths, arrays named rhoi or rhois appear. In Stream-facing calls, a rhoi input often means the partial-density vector (\rho Y_k). Inside the real-fluid mixture solver, rhois often means the pure-species densities (\widehat{\rho}_k(p,T)). Those are different objects.

19.2. EOS Call Inputs and Work Arrays#

The Stream EOS interface uses a compact C++ API, so some arguments carry more meaning than their names suggest. The table below summarizes the most common inputs used by Stream.

Argument or store

Meaning

Used by

yi, mixture, y, yStar

Species mass fractions (Y_k). These are composition variables and should sum to one after any required normalization or clipping.

State_from_p_h, State_from_p_e, State_from_mixture_p_T

rhoi input

Partial-density vector (\rho_k^{\mathrm{part}}=\rho Y_k). The EOS sums this vector to recover (\rho), then divides by (\rho) to recover (Y_k).

State_from_rho_p, State_from_rho_T, State_from_rho_e, State_from_rho_h

rhois inside the real-fluid EOS

Pure-species densities (\widehat{\rho}_k(p,T)) at the converged common pressure and temperature. These are not partial densities.

solveEOStpm, solveEOSrpm, solveEOSphm, energySolve

mState, ms, eos_mixture_state

Per-species EOS work/storage array populated by the EOS query. For the real-fluid EOS it stores species internal energies, pure-species densities, and derivative data.

EOS queries, speciesEnthalpy, pureSpecificVolumes

eos_state

Mixture thermodynamic state: (T,\rho,p,e,c_v,c_p,\partial\rho/\partial T,\partial\rho/\partial p,a^2).

Flow solver property extraction

hint, hint_n

Nonlinear-solve initial guess and cache. It helps the EOS converge but should not be treated as the state definition.

EOS queries and getHint

The partial-density vector is needed for density-based calls because (\rho) alone does not define the composition, and (Y_k) alone does not define the density. Passing (\rho Y_k) gives the EOS both pieces in one vector:

(19.2.1)#\[\rho = \sum_k \rho_i[k], \qquad Y_k = \frac{\rho_i[k]}{\sum_j \rho_i[j]}.\]

That is why a call such as

State_from_rho_p(rhoi, p, eos_mixture_state, hint)

expects rhoi[k] = rho * Y[k], not the pure-species density (\widehat{\rho}_k). The pure-species densities are computed later by the real-fluid EOS while closing the additive-volume state at the requested conditions.

By contrast, calls such as

State_from_p_h(y, p, h, eos_mixture_state, hint)
State_from_p_e(y, p, e, eos_mixture_state, hint)
State_from_mixture_p_T(y, p, T, eos_mixture_state, hint)

take mass fractions directly. In those calls the mixture density is not supplied by the caller. It is an EOS output obtained from the additive-volume relation after the temperature has either been supplied or found by an inverse solve.

The eos_mixture_state store is easy to underestimate. It is not another independent thermodynamic state selected by the flow solver; it is the per-species state data associated with the current eos_state. For the real-fluid EOS, the first block stores the converged species internal energies (e_k), the second block stores the pure-species densities (\widehat{\rho}_k), and later blocks store derivative data. Functions such as speciesEnthalpy(i,eos_state,eos_mixture_state) and pureSpecificVolumes(...) read this stored pure-species data. Therefore eos_state and eos_mixture_state must come from the same EOS query.

Warning

Naming collision When reading code, do not infer semantics from rhoi by name alone. In Stream-side setup code, rhoi is usually a temporary partial-density vector (\rho Y_k) prepared for a density-based EOS query. In the real-fluid EOS internals and in eos_mixture_state, rhois are usually pure-species densities (\widehat{\rho}_k(p,T)). Mixing those interpretations gives exactly the kind of over-specified face state that can produce surprising temperature and enthalpy values.

19.3. Additive-Volume Mixture Rule#

For a given pressure, temperature, and composition, the real-fluid EOS first solves each pure-species EOS at the common ((p,T)):

(19.3.1)#\[p = p_k\!\left(\widehat{\rho}_k,T\right), \qquad k=1,\dots,N_s.\]

The mixture specific volume is then formed by the additive-volume rule

(19.3.2)#\[v(p,T,Y) = \frac{1}{\rho(p,T,Y)} = \sum_{k=1}^{N_s} \frac{Y_k}{\widehat{\rho}_k(p,T)}.\]

Equivalently,

(19.3.3)#\[\rho(p,T,Y) = \left( \sum_{k=1}^{N_s} \frac{Y_k}{\widehat{\rho}_k(p,T)} \right)^{-1}.\]

The assumptions embedded in this closure are:

  1. all species are in mechanical and thermal equilibrium, so there is one (p) and one (T),

  2. the composition is represented by mass fractions,

  3. the mixture volume is the mass-fraction-weighted sum of pure-fluid specific volumes,

  4. no additional excess-volume model is added on top of the pure-species real-fluid corrections.

For an ideal-gas mixture, this reduces to the usual mixture gas-constant relation. For a real-fluid mixture, each (\widehat{\rho}_k(p,T)) comes from the corresponding pure-fluid EOS, so the volume rule can be highly nonlinear near dense-fluid states.

19.4. Mixture Energy and Enthalpy#

At the converged common state, each species internal energy is assembled as an ideal-gas reference contribution plus a real-fluid correction:

(19.4.1)#\[e_k(p,T) = e^{\mathrm{ig}}_k(T) + e^{\mathrm{corr}}_k\!\left(\widehat{\rho}_k,T,p\right).\]

The mixture internal energy is the mass-fraction-weighted sum

(19.4.2)#\[e = \sum_{k=1}^{N_s} Y_k e_k.\]

The mixture static enthalpy is

(19.4.3)#\[h = e + p v = \sum_{k=1}^{N_s}Y_k e_k + p\sum_{k=1}^{N_s}\frac{Y_k}{\widehat{\rho}_k}.\]

It is therefore also the mass-fraction-weighted sum of species static enthalpies defined with the pure-species densities:

(19.4.4)#\[h = \sum_{k=1}^{N_s}Y_k h_k, \qquad h_k = e_k + \frac{p}{\widehat{\rho}_k}.\]

This is the relation used by the real-fluid speciesEnthalpy(i,state,mixtureState) path: the stored species internal energy is combined with (p/\widehat{\rho}_i), not with (p/\rho) and not with the partial density (\rho Y_i).

The total enthalpy used by the flow equations adds kinetic energy to the static thermodynamic enthalpy:

(19.4.5)#\[H = h + \frac{1}{2}\lVert \vec{u}\rVert^2.\]

Similarly, the total specific energy is

(19.4.6)#\[E = e + \frac{1}{2}\lVert \vec{u}\rVert^2.\]

The EOS thermoState object stores the static thermodynamic state. Its enthalpy() accessor corresponds to (e+p/\rho). Kinetic energy is added by the flow solver when a total enthalpy or total energy variable is needed.

19.5. The (p,T,Y) Query#

The direct pressure-temperature-composition query is the simplest path:

  1. take (p), (T), and (Y_k) as inputs,

  2. solve each pure-species EOS for (\widehat{\rho}_k(p,T)),

  3. compute the mixture density from Eq. (19.3.3),

  4. compute (e_k), (e), (h), (c_v), (c_p), density derivatives, and acoustic data from the converged state.

In the Stream EOS interface, this corresponds to the State_from_mixture_p_T path calling the pressure-temperature mixture solve, then the energy-property assembly.

19.6. The (p,\rho,Y) Query#

The pressure-density-composition query is the path used when the caller supplies a density-weighted composition vector. The input vector is interpreted as

(19.6.1)#\[\rho_k^{\mathrm{part}} = \rho Y_k.\]

The real-fluid wrapper first recovers the mixture density and mass fractions by

(19.6.2)#\[\rho = \sum_{k=1}^{N_s}\rho_k^{\mathrm{part}}, \qquad Y_k = \frac{\rho_k^{\mathrm{part}}}{\rho}.\]

Then the mixture solver finds the temperature that makes the additive-volume density match the requested density at the requested pressure and composition. Written as a scalar residual in (T),

(19.6.3)#\[R_{\rho}(T) = \rho_{\mathrm{target}} - \left( \sum_{k=1}^{N_s} \frac{Y_k}{\widehat{\rho}_k(p,T)} \right)^{-1} =0.\]

For each trial (T), each (\widehat{\rho}_k(p,T)) is recomputed from the pure-species EOS. After convergence, the same energySolve path forms species energies, mixture internal energy, mixture enthalpy, heat capacities, derivatives, and sound speed.

In the Stream EOS interface, this is the State_from_rho_p path calling the pressure-density mixture solve, then the energy-property assembly.

Important

Consistency point for face states For this query, the density and composition are not independent once they are packed into (\rho_k^{\mathrm{part}}). If a face state reconstructs (\rho) and (Y_k) separately, then forms (\rho Y_k) afterward, it is imposing one particular density-composition pair on the EOS. If another rule at the same face uses a separately reconstructed enthalpy or a separately reconstructed set of species volumes, it may be asking the EOS for a different thermodynamic state.

19.7. The (p,h,Y) Query#

The pressure-enthalpy-composition query takes (p), target static enthalpy (h), and (Y_k). It solves for the temperature that makes the additive-volume mixture enthalpy equal the requested enthalpy.

For each trial (T):

  1. solve each pure-species density (\widehat{\rho}_k(p,T)),

  2. form the additive specific volume (v(T)=\sum_k Y_k/\widehat{\rho}_k(p,T)),

  3. compute each species internal energy (e_k(p,T)),

  4. form the mixture internal energy (e(T)=\sum_k Y_k e_k(p,T)),

  5. evaluate the enthalpy residual

(19.7.1)#\[R_h(T) = h_{\mathrm{target}} - \left[ \sum_{k=1}^{N_s}Y_k e_k(p,T) + p\sum_{k=1}^{N_s}\frac{Y_k}{\widehat{\rho}_k(p,T)} \right] =0.\]

After this residual converges, the mixture density is

(19.7.2)#\[\rho = \frac{1}{v(T)}.\]

The remaining thermodynamic data are again computed from the same converged state. In the Stream EOS interface, this is the State_from_p_h path calling the pressure-enthalpy mixture solve, then the energy-property assembly.

19.8. Face Reconstruction Guidance#

The safest way to build a thermodynamic face state is to choose one minimal set of variables that closes the EOS, reconstruct only that set to the face, and derive every other thermodynamic quantity from one EOS query. “Minimal” means enough information to determine the state, but no extra independently reconstructed thermodynamic quantities.

Common choices are:

Face coordinate set

EOS closure

EOS outputs that should be derived, not also imposed

(p_f,T_f,Y_{k,f})

State_from_mixture_p_T

(\rho_f,e_f,h_f,\widehat{\rho}{k,f},h{k,f})

(p_f,h_f,Y_{k,f})

State_from_p_h

(\rho_f,T_f,e_f,\widehat{\rho}{k,f},h{k,f})

(p_f,e_f,Y_{k,f})

State_from_p_e

(\rho_f,T_f,h_f,\widehat{\rho}{k,f},h{k,f})

(p_f,(\rho Y_k)_f)

State_from_rho_p

(Y_{k,f},\rho_f,T_f,e_f,h_f,\widehat{\rho}{k,f},h{k,f})

For conservative species transport, the last form is often the most natural face coordinate set. Define

(19.8.1)#\[q_{k,f} = \left(\rho Y_k\right)_f, \qquad \rho_f = \sum_k q_{k,f}, \qquad Y_{k,f} = \frac{q_{k,f}}{\rho_f}.\]

Then the EOS input for a pressure-density-composition face query is the vector (q_{k,f}), together with (p_f). The face temperature, static enthalpy, species enthalpies, pure-species densities, and pure specific volumes should all come from the resulting face EOS state and face mixture state.

The main rule is:

(19.8.2)#\[\text{one reconstructed face coordinate set} \quad\Longrightarrow\quad \text{one EOS query} \quad\Longrightarrow\quad \text{all derived thermodynamic data}.\]

Avoid combining pieces that came from different closures. For example, do not take a face pressure and a reconstructed ((\rho Y_k)_f) for one EOS query, but then use species pure volumes from neighboring cell eos_mixture_state objects as though they represented the same face state. Those cell pure volumes are useful in pressure-correction linearizations, but they are not the pure-species densities of the face EOS state unless the face state actually equals that cell state.

19.8.1. Why (p,\rho Y) and (p,\rho,Y) Can Differ#

The notation can hide a real numerical difference. If rhoi means the partial-density vector expected by State_from_rho_p, then (p,\rho_i) and (p,\rho Y) describe the same mathematical EOS input:

(19.8.1.1)#\[\rho_i[k] \equiv \rho Y_k.\]

However, there are two different ways one might produce that vector at a face:

(19.8.1.2)#\[q_{k,f}^{(A)} = \mathcal{R}\!\left(\rho Y_k\right),\]

or

(19.8.1.3)#\[q_{k,f}^{(B)} = \mathcal{R}(\rho)\,\mathcal{R}(Y_k).\]

For first-order upwind reconstruction these may collapse to the same cell value. For second-order limited reconstruction, they generally do not:

(19.8.1.4)#\[\mathcal{R}\!\left(\rho Y_k\right) \ne \mathcal{R}(\rho)\,\mathcal{R}(Y_k).\]

The reason is that limiting and projection are nonlinear operations. The limiter applied to (\rho Y_k) sees the conservative species profile, while the limiter applied separately to (\rho) and (Y_k) sees two different profiles and then multiplies their limited face values. Near a material interface, that difference can be large enough to give the EOS a different mixture density and a different composition, even when all reconstructed variables look bounded.

Once those two vectors differ, the real-fluid (p,\rho,Y) inversion solves a different problem:

(19.8.1.5)#\[R_{\rho}^{(A)}(T) \ne R_{\rho}^{(B)}(T),\]

so the resulting temperatures, pure-species densities, species enthalpies, and mixture enthalpy can also differ. The EOS is not necessarily misbehaving in that case; it is answering two different thermodynamic questions.

There is a second possible ambiguity. If rhoi is used to mean the pure-species densities stored in eos_mixture_state, then (p,\rho_i) is not the same as (p,\rho Y). The pure-species densities (\widehat{\rho}_k) are EOS outputs at a particular ((p,T)), not conservative species variables. Reconstructing or mixing those values independently with a new face pressure does not define a complete additive-volume face state unless a common face temperature and composition are also specified consistently.

Important

Practical recommendation For an actual face EOS state, prefer reconstructing a single thermodynamic coordinate set and immediately closing it with the EOS. For conservative species reconstruction, this means reconstructing the density-weighted species vector used by the species equation, deriving (\rho_f) and (Y_{k,f}) from that vector, and then using State_from_rho_p with the same face pressure. Treat eos_mixture_state quantities such as pure specific volumes as derived data tied to that one EOS query.

19.9. Implementation Map#

The relevant real-fluid EOS implementation paths are:

  • State_from_rho_p sums the input partial-density vector, normalizes it to mass fractions, performs the pressure-density mixture solve, then assembles the remaining energy properties.

  • the pressure-density mixture solve iterates on (T) so that the additive-volume density equals the requested (\rho) at fixed (p,Y).

  • State_from_mixture_p_T computes pure-species densities at fixed (p,T,Y), then forms the additive-volume mixture density.

  • State_from_p_h performs the pressure-enthalpy mixture solve, iterating on (T) so that the additive-volume static enthalpy equals the requested (h) at fixed (p,Y).

  • the energy-property assembly stores the corrected species internal energies, forms the mixture internal energy, and computes heat capacities, density derivatives, and acoustic data.

  • speciesEnthalpy(i,state,mixtureState) returns (e_i+p/\widehat{\rho}_i) using the converged mixture state.