Skip to content

Commit 16dea37

Browse files
committed
Fix Redlich-Kister excess enthalpy: standard polynomial form, drop spurious 0.5 factor
1 parent 0cbfa12 commit 16dea37

2 files changed

Lines changed: 40 additions & 23 deletions

File tree

src/pathsim_chem/thermodynamics/enthalpy.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -301,22 +301,24 @@ class ExcessEnthalpyRedlichKister(Function):
301301
Computes the molar excess enthalpy using a Redlich-Kister polynomial
302302
expansion. This is a flexible, empirical model for representing binary
303303
excess properties. For a multi-component mixture, the excess enthalpy
304-
is computed as a sum of binary pair contributions:
304+
is summed over the unique binary pairs supplied:
305305
306306
.. math::
307307
308-
h^E = \frac{1}{2} \sum_i \sum_j h^E_{ij}
308+
h^E = \sum_{(i,j)} h^E_{ij}
309309
310-
Each binary pair contribution:
310+
Each binary pair contribution follows the standard Redlich-Kister form
311311
312312
.. math::
313313
314314
h^E_{ij} = \frac{x_i x_j}{x_i + x_j}
315-
\left(A(T) x_d + B(T) x_d^2 + C(T) x_d^3 + \ldots\right)
315+
\left(A_0(T) + A_1(T) x_d + A_2(T) x_d^2 + \ldots\right)
316316
317-
where :math:`x_d = x_i - x_j` and the coefficients :math:`A(T)`,
318-
:math:`B(T)`, ... are temperature-dependent polynomials:
319-
:math:`A(T) = a_0 + a_1 T + a_2 T^2 + \ldots`
317+
where :math:`x_d = x_i - x_j` and each :math:`A_k(T)` is a temperature
318+
polynomial :math:`A_k(T) = a_{k,0} + a_{k,1} T + a_{k,2} T^2 + \ldots`.
319+
For a true binary system :math:`x_i + x_j = 1` and the prefactor
320+
reduces to :math:`x_i x_j`; in multicomponent mixtures the
321+
:math:`/(x_i+x_j)` term provides the symmetric extension.
320322
321323
**Input port:** ``T`` -- temperature [K].
322324
@@ -328,12 +330,15 @@ class ExcessEnthalpyRedlichKister(Function):
328330
Mole fractions [N].
329331
coeffs : dict
330332
Redlich-Kister coefficients keyed by binary pair ``(i, j)`` as tuples.
331-
Each value is a list of polynomial coefficient arrays, one per
332-
Redlich-Kister term. Example for a single pair (0,1) with 3 terms::
333+
Provide each unordered pair only once (e.g. ``(0, 1)``, not also
334+
``(1, 0)``). Each value is a list of polynomial coefficient arrays,
335+
one per Redlich-Kister term. Example for a single pair (0,1) with
336+
3 terms::
333337
334338
{(0, 1): [[a0, a1], [b0, b1], [c0]]}
335339
336-
This gives A(T)=a0+a1*T, B(T)=b0+b1*T, C(T)=c0.
340+
This gives A_0(T)=a0+a1*T, A_1(T)=b0+b1*T, A_2(T)=c0, where
341+
:math:`A_k` multiplies :math:`x_d^k`.
337342
"""
338343

339344
input_port_labels = {"T": 0}
@@ -358,9 +363,9 @@ def _eval(self, T):
358363

359364
xd = xi - xj
360365

361-
# evaluate each Redlich-Kister term
366+
# evaluate Redlich-Kister polynomial: A_0 + A_1*xd + A_2*xd^2 + ...
362367
pair_hE = 0.0
363-
xd_power = xd
368+
xd_power = 1.0
364369
for poly_coeffs in terms:
365370
# temperature polynomial: c0 + c1*T + c2*T^2 + ...
366371
coeff_val = 0.0
@@ -373,7 +378,7 @@ def _eval(self, T):
373378

374379
hE += xi * xj / x_sum * pair_hE
375380

376-
return 0.5 * hE
381+
return hE
377382

378383

379384
# ENTHALPY DEPARTURE FROM EOS (9.7) =====================================================

tests/thermodynamics/test_enthalpy.py

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -201,28 +201,40 @@ def test_init(self):
201201
)
202202
self.assertEqual(hE.n, 2)
203203

204-
def test_symmetric_binary(self):
205-
# Simplest case: one-term Redlich-Kister with constant A
206-
# h^E_ij = x_i*x_j/(x_i+x_j) * A * (x_i-x_j)
207-
# For x=[0.5, 0.5]: h^E = 0.5 * 0.5 / 1.0 * A * 0 = 0
204+
def test_constant_coeff_only(self):
205+
# One-term Redlich-Kister with constant A_0:
206+
# h^E = x_i*x_j/(x_i+x_j) * A_0
207+
# For x=[0.5, 0.5], A_0=1000: h^E = 0.25/1.0 * 1000 = 250
208208
hE = ExcessEnthalpyRedlichKister(
209209
x=[0.5, 0.5],
210210
coeffs={(0, 1): [[1000.0]]},
211211
)
212212
result = eval_block_T(hE, 300)
213-
self.assertAlmostEqual(result, 0.0, places=10)
213+
self.assertAlmostEqual(result, 250.0, places=10)
214214

215-
def test_asymmetric_composition(self):
216-
# x=[0.3, 0.7], one-term A=2000
217-
# h^E = 0.5 * 0.3*0.7/1.0 * 2000 * (0.3-0.7) = 0.5 * 0.21 * 2000 * (-0.4) = -84
215+
def test_two_term_polynomial(self):
216+
# x=[0.3, 0.7], coeffs A_0=2000, A_1=500
217+
# h^E = 0.21 * (2000 + 500*(-0.4)) = 0.21 * 1800 = 378
218218
hE = ExcessEnthalpyRedlichKister(
219219
x=[0.3, 0.7],
220-
coeffs={(0, 1): [[2000.0]]},
220+
coeffs={(0, 1): [[2000.0], [500.0]]},
221221
)
222222
result = eval_block_T(hE, 300)
223-
expected = 0.5 * 0.3 * 0.7 / 1.0 * 2000.0 * (0.3 - 0.7)
223+
expected = 0.3 * 0.7 / 1.0 * (2000.0 + 500.0 * (0.3 - 0.7))
224224
self.assertAlmostEqual(result, expected, places=5)
225225

226+
def test_antisymmetric_under_pair_swap(self):
227+
# Odd-order RK terms should flip sign when (i,j) <-> (j,i),
228+
# since x_d = x_i - x_j flips. Verify by swapping x values.
229+
hE_a = ExcessEnthalpyRedlichKister(
230+
x=[0.3, 0.7], coeffs={(0, 1): [[0.0], [1000.0]]}, # A_1 only
231+
)
232+
hE_b = ExcessEnthalpyRedlichKister(
233+
x=[0.7, 0.3], coeffs={(0, 1): [[0.0], [1000.0]]},
234+
)
235+
self.assertAlmostEqual(eval_block_T(hE_a, 300),
236+
-eval_block_T(hE_b, 300), places=8)
237+
226238
def test_temperature_dependent_coeffs(self):
227239
# A(T) = 1000 + 2*T
228240
hE = ExcessEnthalpyRedlichKister(

0 commit comments

Comments
 (0)