Transitory functions

This module contains transitory functions which all have a specific physical meaning for modeling the PEM fuel cell.

C_v_sat(T)

This function calculates the saturated vapor concentration for a perfect gas, in mol.m-3, as a function of the temperature.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Saturated vapor concentration for a perfect gas in mol.m-3.

Source code in modules/transitory_functions.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def C_v_sat(T):
    """This function calculates the saturated vapor concentration for a perfect gas, in mol.m-3, as a function of the
    temperature.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Saturated vapor concentration for a perfect gas in mol.m-3.
    """
    return Psat(T) / (R * T)

Cp0(component, T)

This function calculates the specific heat capacity of fluids, in J.kg-1.K-1, as a function of the temperature.

Parameters:
  • component (str) –

    Specifies the gas for which the specific heat capacity is calculated. Must be either 'H2O_l' (liquid water), 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (oxygen), or 'N2' (nitrogen).

  • T (float) –

    Temperature in K.

Returns:
  • float

    Specific heat capacity of the selected fluid in J.kg-1.K-1.

Notes

Source : Chase, M. W. (1998). NIST-JANAF Thermochemical Tables, 4th edition

Source code in modules/transitory_functions.py
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
def Cp0(component, T):
    """This function calculates the specific heat capacity of fluids, in J.kg-1.K-1, as a function of the
    temperature.

    Parameters
    ----------
    component : str
        Specifies the gas for which the specific heat capacity is calculated.
        Must be either 'H2O_l' (liquid water), 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (oxygen), or 'N2' (nitrogen).
    T : float
        Temperature in K.

    Returns
    -------
    float
        Specific heat capacity of the selected fluid in J.kg-1.K-1.

    Notes
    -----
    Source : Chase, M. W. (1998). NIST-JANAF Thermochemical Tables, 4th edition"""

    if component == 'H2O_l':  # For T >= 298 and T <= 500 K.
        return 1/M_H2O * (- 203.6060 + 1523.290 * (T/1000) - 3196.413 * (T/1000)**2 + 2474.455 * (T/1000)**3 +
                         3.855326 / (T/1000)**2)
    elif component == 'H2O_v':  # For T = 350 K. I failed to find a proper equation at the good range of temperature.
        return 1880
    elif component == 'H2':  # For T >= 298 K and T <= 1000 K.
        return 1/M_H2 * (33.066178 - 11.363417 * (T/1000) + 11.432816 * (T/1000)**2 - 2.772874 * (T/1000)**3 -
                         0.158558 / (T/1000)**2)
    elif component == 'O2':  # For T >= 100 K and T <= 700 K.
        return 1/M_O2 * (31.32234 - 20.23531 * (T/1000) + 57.86644 * (T/1000)**2 - 36.50624 * (T/1000)**3 -
                         0.007374 / (T/1000)**2)
    elif component == 'N2':  # For T >= 100 K and T <= 500 K.
        return 1/M_N2 * (28.98641 + 1.853978 * (T/1000) - 9.647459 * (T/1000)**2 + 16.63537 * (T/1000)**3 +
                         0.000117 / (T/1000)**2)
    else:
        raise ValueError("The element should be either 'H2O_l', 'H2O_v', 'H2', 'O2' or 'N2'.")

D(lambdaa)

This function calculates the diffusion coefficient of water in the membrane, in m².s-1.

Parameters:
  • lambdaa (float) –

    Water content in the membrane.

Returns:
  • float

    Diffusion coefficient of water in the membrane in m².s-1.

Source code in modules/transitory_functions.py
606
607
608
609
610
611
612
613
614
615
616
617
618
619
def D(lambdaa):
    """This function calculates the diffusion coefficient of water in the membrane, in m².s-1.

    Parameters
    ----------
    lambdaa : float
        Water content in the membrane.

    Returns
    -------
    float
        Diffusion coefficient of water in the membrane in m².s-1.
    """
    return 4.1e-10 * (lambdaa / 25.0) ** 0.15 * (1.0 + math.tanh((lambdaa - 2.5) / 1.4))

Da(P, T)

This function calculates the diffusion coefficient at the anode, in m².s-1.

Parameters:
  • P (float) –

    Pressure in Pa.

  • T (float) –

    Temperature in K.

Returns:
  • float

    Diffusion coefficient at the anode in m².s-1.

Source code in modules/transitory_functions.py
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
def Da(P, T):
    """This function calculates the diffusion coefficient at the anode, in m².s-1.

    Parameters
    ----------
    P : float
        Pressure in Pa.
    T : float
        Temperature in K.

    Returns
    -------
    float
        Diffusion coefficient at the anode in m².s-1.
    """
    return 1.644e-4 * (T / 333) ** 2.334 * (101325 / P)

Da_eff(element, s, T, P, epsilon, epsilon_c=None)

This function calculates the effective diffusion coefficient at the GDL, TL, MPL or the CL and at the anode, in m².s-1, considering GDL compression.

Parameters:
  • element (str) –

    Specifies the element for which the effective diffusion coefficient is calculated. Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).

  • s (float) –

    Liquid water saturation variable.

  • T (float) –

    Temperature in K.

  • P (float) –

    Pressure in Pa.

  • epsilon (float) –

    Porosity.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

Returns:
  • float

    Effective diffusion coefficient at the anode in m².s-1.

Source code in modules/transitory_functions.py
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
def Da_eff(element, s, T, P, epsilon, epsilon_c=None):
    """This function calculates the effective diffusion coefficient at the GDL, TL, MPL or the CL and at the anode,
     in m².s-1, considering GDL compression.

    Parameters
    ----------
    element : str
        Specifies the element for which the effective diffusion coefficient is calculated.
        Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).
    s : float
        Liquid water saturation variable.
    T : float
        Temperature in K.
    P : float
        Pressure in Pa.
    epsilon : float
        Porosity.
    epsilon_c : float, optional
        Compression ratio of the GDL.

    Returns
    -------
    float
        Effective diffusion coefficient at the anode in m².s-1.
    """

    if element == 'gdl': # The effective diffusion coefficient at the GDL using Tomadakis and Sotirchos model.
        # According to the GDL porosity, the GDL compression effect is different.
        if epsilon < 0.67:
            beta2 = -1.59
        else:
            beta2 = -0.90
        tau_gdl = 1 / (((epsilon - epsilon_p) / (1 - epsilon_p)) ** alpha_p)
        return epsilon / tau_gdl * math.exp(beta2 * epsilon_c) * (1 - s) ** r_s_gdl * Da(P, T)

    elif element == 'mpl': # The effective diffusion coefficient at the MPL using Bruggeman model.
        return epsilon / tau_mpl * (1 - s) ** r_s_mpl * Da(P, T)

    elif element == 'cl': # The effective diffusion coefficient at the CL using Bruggeman model.
        return epsilon / tau_cl * (1 - s) ** r_s_cl * Da(P, T)

    else:
        raise ValueError("The element should be either 'gdl', 'tl', 'mpl' or 'cl'.")

Dc(P, T)

This function calculates the diffusion coefficient at the cathode, in m².s-1.

Parameters:
  • P (float) –

    Pressure in Pa.

  • T (float) –

    Temperature in K.

Returns:
  • float

    Diffusion coefficient at the cathode in m².s-1.

Source code in modules/transitory_functions.py
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
def Dc(P, T):
    """This function calculates the diffusion coefficient at the cathode, in m².s-1.

    Parameters
    ----------
    P : float
        Pressure in Pa.
    T : float
        Temperature in K.

    Returns
    -------
    float
        Diffusion coefficient at the cathode in m².s-1.
    """
    return 3.242e-5 * (T / 333) ** 2.334 * (101325 / P)

Dc_eff(element, s, T, P, epsilon, epsilon_c=None)

This function calculates the effective diffusion coefficient at the GDL, MPL, TL or the CL and at the cathode, in m².s-1, considering GDL compression.

Parameters:
  • element (str) –

    Specifies the element for which the effective diffusion coefficient is calculated. Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).

  • s (float) –

    Liquid water saturation variable.

  • T (float) –

    Temperature in K.

  • P (float) –

    Pressure in Pa.

  • epsilon (float) –

    Porosity.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

Returns:
  • float

    Effective diffusion coefficient at the cathode in m².s-1.

Source code in modules/transitory_functions.py
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
def Dc_eff(element, s, T, P, epsilon, epsilon_c=None):
    """This function calculates the effective diffusion coefficient at the GDL, MPL, TL or the CL and at the cathode,
     in m².s-1, considering GDL compression.

    Parameters
    ----------
    element : str
        Specifies the element for which the effective diffusion coefficient is calculated.
        Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).
    s : float
        Liquid water saturation variable.
    T : float
        Temperature in K.
    P : float
        Pressure in Pa.
    epsilon : float
        Porosity.
    epsilon_c : float, optional
        Compression ratio of the GDL.

    Returns
    -------
    float
        Effective diffusion coefficient at the cathode in m².s-1.
    """

    if element == 'gdl': # The effective diffusion coefficient at the GDL using Tomadakis and Sotirchos model.
        # According to the GDL porosity, the GDL compression effect is different.
        if epsilon < 0.67:
            beta2 = -1.59
        else:
            beta2 = -0.90
        tau_gdl = 1 / (((epsilon - epsilon_p) / (1 - epsilon_p)) ** alpha_p)
        return epsilon / tau_gdl * math.exp(beta2 * epsilon_c) * (1 - s) ** r_s_gdl * Dc(P, T)

    elif element == 'mpl': # The effective diffusion coefficient at the MPL using Bruggeman model.
        return epsilon / tau_mpl * (1 - s) ** r_s_mpl * Dc(P, T)

    elif element == 'cl': # The effective diffusion coefficient at the CL using Bruggeman model.
        return epsilon / tau_cl * (1 - s) ** r_s_cl * Dc(P, T)

    else:
        raise ValueError("The element should be either 'gdl', 'tl', 'mpl' or 'cl'.")

Dcap(element, s, T, epsilon, e, epsilon_c=None)

This function calculates the capillary coefficient at the GDL or the CL and at the anode, in kg.m.s-1, considering GDL compression.

Parameters:
  • element (str) –

    Specifies the element for which the capillary coefficient is calculated. Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).

  • s (float) –

    Liquid water saturation variable.

  • T (float) –

    Temperature in K.

  • epsilon (float) –

    Porosity.

  • e (float) –

    Capillary exponent.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

Source code in modules/transitory_functions.py
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
def Dcap(element, s, T, epsilon, e, epsilon_c=None):
    """ This function calculates the capillary coefficient at the GDL or the CL and at the anode, in kg.m.s-1,
    considering GDL compression.

    Parameters
    ----------
    element : str
        Specifies the element for which the capillary coefficient is calculated.
        Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).
    s : float
        Liquid water saturation variable.
    T : float
        Temperature in K.
    epsilon : float
        Porosity.
    e : float
        Capillary exponent.
    epsilon_c : float, optional
        Compression ratio of the GDL.
    """

    K0_value = K0(element, epsilon, epsilon_c)
    if element == 'gdl':
        theta_c_value = theta_c_gdl
    elif element == 'mpl':
        theta_c_value = theta_c_mpl
    elif element == 'cl':
        theta_c_value = theta_c_cl
    else:
        raise ValueError("The element should be either 'gdl', 'mpl' or 'cl'.")

    return sigma(T) * K0_value / nu_l(T) * abs(math.cos(theta_c_value)) * \
           (epsilon / K0_value) ** 0.5 * (s ** e + 1e-7) * (1.417 - 4.24 * s + 3.789 * s ** 2)

K0(element, epsilon, epsilon_c=None) cached

This function calculates the intrinsic permeability, in m², considering GDL compression.

Parameters:
  • element (str) –

    Specifies the element for which the intrinsic permeability is calculated. Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).

  • epsilon (float) –

    Porosity.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

Returns:
  • float

    Intrinsic permeability in m².

Sources
  1. Qin Chen 2020 - Two-dimensional multi-physics modeling of porous transport layer in polymer electrolyte membrane electrolyzer for water splitting - for the Blake-Kozeny equation.
  2. M.L. Stewart 2005 - A study of pore geometry effects on anisotropy in hydraulic permeability using the lattice-Boltzmann method - for the Blake-Kozeny equation.
Source code in modules/transitory_functions.py
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
@lru_cache(maxsize=None) # Cache the results to optimize performance
def K0(element, epsilon, epsilon_c=None):
    """This function calculates the intrinsic permeability, in m², considering GDL compression.

    Parameters
    ----------
    element : str
        Specifies the element for which the intrinsic permeability is calculated.
        Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).
    epsilon : float
        Porosity.
    epsilon_c : float, optional
        Compression ratio of the GDL.

    Returns
    -------
    float
        Intrinsic permeability in m².

    Sources
    -------
    1. Qin Chen 2020 - Two-dimensional multi-physics modeling of porous transport layer in polymer electrolyte membrane
    electrolyzer for water splitting - for the Blake-Kozeny equation.
    2. M.L. Stewart 2005 - A study of pore geometry effects on anisotropy in hydraulic permeability using the
    lattice-Boltzmann method - for the Blake-Kozeny equation.
    """

    if element == 'gdl':
        # According to the GDL porosity, the GDL compression effect is different.
        if epsilon < 0.67:
            beta1 = -3.60
        else:
            beta1 = -2.60
        return epsilon / (8 * math.log(epsilon) ** 2) * (epsilon - epsilon_p) ** (alpha_p + 2) * \
            4.6e-6 ** 2 / ((1 - epsilon_p) ** alpha_p * ((alpha_p + 1) * epsilon - epsilon_p) ** 2) * math.exp(beta1 * epsilon_c)

    elif element == 'mpl':
        return (Dp_mpl**2 / 150) * (epsilon**3 / ((1-epsilon)**2)) # Using the Blake-Kozeny equation

    elif element == 'cl':
        return (Dp_cl**2 / 150) * (epsilon**3 / ((1-epsilon)**2)) # Using the Blake-Kozeny equation

    else:
        raise ValueError("The element should be either 'gdl', 'mpl' or 'cl'.")

Psat(T)

This function calculates the saturated partial pressure of vapor, in Pa, as a function of the temperature.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Saturated partial pressure of vapor in Pa.

Source code in modules/transitory_functions.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
def Psat(T):
    """This function calculates the saturated partial pressure of vapor, in Pa, as a function of the temperature.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Saturated partial pressure of vapor in Pa.
    """
    Tcelsius = T - 273.15
    return 101325 * 10 ** (-2.1794 + 0.02953 * Tcelsius - 9.1837e-5 * Tcelsius ** 2 + 1.4454e-7 * Tcelsius ** 3)

Svl(element, s, C_v, Ctot, T, epsilon)

This function calculates the phase transfer rate of water condensation or evaporation, in mol.m-3.s-1.

Parameters:
  • element (str) –

    Specifies the element for which the phase transfer rate is calculated.

  • s (float) –

    Liquid water saturation variable.

  • C_v (float) –

    Water concentration variable in mol.m-3.

  • Ctot (float) –

    Total gas concentration in mol.m-3.

  • T (float) –

    Temperature in K.

  • epsilon (float) –

    Porosity.

Returns:
  • float

    Phase transfer rate of water condensation or evaporation in mol.m-3.s-1.

Source code in modules/transitory_functions.py
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
def Svl(element, s, C_v, Ctot, T, epsilon):
    """This function calculates the phase transfer rate of water condensation or evaporation, in mol.m-3.s-1.

    Parameters
    ----------
    element : str
        Specifies the element for which the phase transfer rate is calculated.
    s : float
        Liquid water saturation variable.
    C_v : float
        Water concentration variable in mol.m-3.
    Ctot : float
        Total gas concentration in mol.m-3.
    T : float
        Temperature in K.
    epsilon : float
        Porosity.

    Returns
    -------
    float
        Phase transfer rate of water condensation or evaporation in mol.m-3.s-1.
    """

    # Calculation of the total and partial pressures
    Ptot = Ctot * R * T # Total pressure
    P_v = C_v * R * T # Partial pressure of vapor

    # Determination of the diffusion coefficient at the anode or the cathode
    if element == 'anode':
        D_value = Da(Ptot, T)  # Diffusion coefficient at the anode
    else:  # element == 'cathode'
        D_value = Dc(Ptot, T)  # Diffusion coefficient at the cathode

    Svl_cond = gamma_cond * M_H2O / (R * T) * epsilon * (1 - s) * D_value * Ptot * math.log((Ptot - Psat(T)) / (Ptot - P_v))
    Svl_evap = gamma_evap * M_H2O / (R * T) * epsilon * s * D_value * Ptot * math.log((Ptot - Psat(T)) / (Ptot - P_v))
    w = 0.5 * (1 + math.tanh(K_transition * (C_v_sat(T) - C_v))) # transition function
    return w * Svl_evap + (1 - w) * Svl_cond # interpolation between condensation and evaporation

average(terms, weights=None)

Calculate the weighted arithmetic mean of a list of terms with corresponding weights. It is more efficient to express this function in the code than calling average from numpy.

Parameters:
  • terms

    The terms to calculate the average for.

  • weights

    The weights corresponding to each term. If None, uniform weights are assumed.

Returns:
  • float

    The weighted arithmetic mean.

Source code in modules/transitory_functions.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def average(terms, weights=None):
    """
    Calculate the weighted arithmetic mean of a list of terms with corresponding weights.
    It is more efficient to express this function in the code than calling average from numpy.

    Parameters
    ----------
    terms (list of float):
        The terms to calculate the average for.
    weights (list of float, optional):
        The weights corresponding to each term. If None, uniform weights are assumed.

    Returns
    -------
    float:
        The weighted arithmetic mean.
    """
    n = len(terms)
    if weights is None:
        total_weight = n
        weighted_sum = 0.0
        for t in terms:
            weighted_sum += t
    else:
        if n != len(weights):
            raise ValueError("The length of terms and weights must be the same.")
        total_weight = 0.0
        weighted_sum = 0.0
        for i in range(n):
            w = weights[i]
            total_weight += w
            weighted_sum += w * terms[i]

    if total_weight == 0:
        return float('nan')
    return weighted_sum / total_weight

calculate_rho_Cp0(element, T, C_v=None, s=None, lambdaa=None, C_H2=None, C_O2=None, C_N2=None, epsilon=None, epsilon_mc=None)

This function calculates the volumetric heat capacity, in J.m-3.K-1, in either the GDL, the MPL, the CL or the membrane.

Parameters:
  • element (str) –

    Specifies the element for which the volumetric heat capacity is calculated. Must be either 'agdl' (anode gas diffusion layer), 'cgdl' (cathode gas diffusion layer), 'acl' (anode catalyst layer), 'ccl' (cathode catalyst layer) or 'mem' (membrane).

  • T (float) –

    Temperature in K.

  • C_v (float, default: None ) –

    Water concentration variable in mol.m-3.

  • s (float, default: None ) –

    Liquid water saturation variable.

  • lambdaa (float, default: None ) –

    Water content in the membrane.

  • C_H2 (float, default: None ) –

    Concentration of hydrogen in the AGDL or ACL.

  • C_O2 (float, default: None ) –

    Concentration of oxygen in the CGDL or CCL.

  • C_N2 (float, default: None ) –

    Concentration of nitrogen in the CGDL or CCL.

  • epsilon (float, default: None ) –

    Porosity.

  • epsilon_mc (float, default: None ) –

    Volume fraction of ionomer in the CL.

Returns:
  • float

    Volumetric heat capacity in J.m-3.K-1.

Source code in modules/transitory_functions.py
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
def calculate_rho_Cp0(element, T, C_v=None, s=None, lambdaa=None, C_H2=None, C_O2=None, C_N2=None, epsilon=None,
                      epsilon_mc=None):
    """This function calculates the volumetric heat capacity, in J.m-3.K-1, in either the GDL, the MPL, the CL or
    the membrane.

    Parameters
    ----------
    element : str
        Specifies the element for which the volumetric heat capacity is calculated.
        Must be either 'agdl' (anode gas diffusion layer), 'cgdl' (cathode gas diffusion layer),
        'acl' (anode catalyst layer), 'ccl' (cathode catalyst layer) or 'mem' (membrane).
    T : float
        Temperature in K.
    C_v : float
        Water concentration variable in mol.m-3.
    s : float
        Liquid water saturation variable.
    lambdaa : float
        Water content in the membrane.
    C_H2 : float
        Concentration of hydrogen in the AGDL or ACL.
    C_O2 : float
        Concentration of oxygen in the CGDL or CCL.
    C_N2 : float
        Concentration of nitrogen in the CGDL or CCL.
    epsilon : float
        Porosity.
    epsilon_mc : float
        Volume fraction of ionomer in the CL.

    Returns
    -------
    float
        Volumetric heat capacity in J.m-3.K-1."""

    if element in ('agdl', 'cgdl', 'ampl', 'cmpl'):  # The volumetric heat capacity at the GDL
        if element in ('agdl', 'ampl'):  # In the anode
            sum_C_v_C_H2_C_N2 = C_v + C_H2 + C_N2
            rho_Cp0_gaz = average([M_H2O * C_v * Cp0('H2O_v', T), M_H2 * C_H2 * Cp0('H2', T),
                                     M_N2 * C_N2 * Cp0('N2', T)],
                                    weights=[C_v / sum_C_v_C_H2_C_N2, C_H2 / sum_C_v_C_H2_C_N2, C_N2 / sum_C_v_C_H2_C_N2])
        else:  # In the cathode
            sum_C_v_C_O2_C_N2 = C_v + C_O2 + C_N2
            rho_Cp0_gaz = average([M_H2O * C_v * Cp0('H2O_v', T), M_O2 * C_O2 * Cp0('O2', T),
                                     M_N2 * C_N2 * Cp0('N2', T)],
                                    weights=[C_v / sum_C_v_C_O2_C_N2, C_O2 / sum_C_v_C_O2_C_N2, C_N2 / sum_C_v_C_O2_C_N2])
        if element in ('agdl', 'cgdl'): # In the GDLs
            return average([rho_gdl * Cp_gdl, rho_H2O_l(T) * Cp0('H2O_l', T), rho_Cp0_gaz],
                            weights=[1 - epsilon, epsilon * s, epsilon * (1 - s)])
        else: # In the MPLs
            return average([rho_mpl * Cp_mpl, rho_H2O_l(T) * Cp0('H2O_l', T), rho_Cp0_gaz],
                           weights=[1 - epsilon, epsilon * s, epsilon * (1 - s)])

    elif element == 'acl' or element == 'ccl':  # The volumetric heat capacity at the CL
        if element == 'acl':  # The heat capacity of the gas mixture in the ACL
            sum_C_v_C_H2_C_N2 = C_v + C_H2 + C_N2
            rho_Cp0_gaz = average([M_H2O * C_v * Cp0('H2O_v', T), M_H2 * C_H2 * Cp0('H2', T),
                                     M_N2 * C_N2 * Cp0('N2', T)],
                                    weights=[C_v / sum_C_v_C_H2_C_N2, C_H2 / sum_C_v_C_H2_C_N2, C_N2 / sum_C_v_C_H2_C_N2])
        else:  # The heat capacity of the gas mixture in the CCL
            sum_C_v_C_O2_C_N2 = C_v + C_O2 + C_N2
            rho_Cp0_gaz = average([M_H2O * C_v * Cp0('H2O_v', T), M_O2 * C_O2 * Cp0('O2', T),
                                     M_N2 * C_N2 * Cp0('N2', T)],
                                    weights=[C_v / sum_C_v_C_O2_C_N2, C_O2 / sum_C_v_C_O2_C_N2, C_N2 / sum_C_v_C_O2_C_N2])
        return average([rho_cl * Cp_cl, rho_mem * Cp_mem, rho_H2O_l(T) * Cp0('H2O_l', T), rho_Cp0_gaz],
                          weights=[1 - epsilon - epsilon_mc, epsilon_mc, epsilon * s, epsilon * (1 - s)])

    elif element == 'mem':  # The volumetric heat capacity at the membrane
        fv_val = fv(lambdaa, T)
        return average([rho_mem * Cp_mem, rho_H2O_l(T) * Cp0('H2O_l', T)],
                          weights=[1 - fv_val, fv_val])

    else:
        raise ValueError("The element should be either 'agdl', 'cgdl', 'ampl', 'cmpl', 'acl', 'ccl' or 'mem'.")

d2_dx2(y_minus, y_0, y_plus, dx_minus, dx_plus=None)

Computes the centered second derivative (second order) with different steps to the left and right.

Parameters:
  • y_minus (float) –

    Value at the left point (i-1).

  • y_0 (float) –

    Value at the central point (i).

  • y_plus (float) –

    Value at the right point (i+1).

  • dx_minus (float) –

    Step between (i-1) and i.

  • dx_plus (float, default: None ) –

    Step between i and (i+1).

Returns:
  • float

    Approximation of the second derivative at i.

Source code in modules/transitory_functions.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
def d2_dx2(y_minus, y_0, y_plus, dx_minus, dx_plus = None):
    """
    Computes the centered second derivative (second order) with different steps to the left and right.

    Parameters
    ----------
    y_minus : float
        Value at the left point (i-1).
    y_0 : float
        Value at the central point (i).
    y_plus : float
        Value at the right point (i+1).
    dx_minus : float
        Step between (i-1) and i.
    dx_plus : float
        Step between i and (i+1).

    Returns
    -------
    float
        Approximation of the second derivative at i.
    """

    if dx_plus is None:
        dx = dx_minus
        return (y_plus - 2 * y_0 + y_minus)  / dx**2
    else:
        return 2 * (y_plus * dx_minus - y_0 * (dx_minus + dx_plus) + y_minus * dx_plus)  / \
                   (dx_minus * dx_plus * (dx_minus + dx_plus))

d_dx(y_minus, y_plus, dx=None, dx_minus=None, dx_plus=None)

Computes the centered first derivative (second order) with different steps to the left and right.

Parameters:
  • y_minus (float) –

    Value at the left point (i-1).

  • y_plus (float) –

    Value at the right point (i+1).

  • dx (float, default: None ) –

    Step between (i-1) and (i+1) when dx_minus = dx_plus.

  • dx_minus (float, default: None ) –

    Step between (i-1) and i.

  • dx_plus (float, default: None ) –

    Step between i and (i+1).

Returns:
  • float

    Approximation of the first derivative at i.

Source code in modules/transitory_functions.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
def d_dx(y_minus, y_plus, dx = None, dx_minus = None, dx_plus = None):
    """
    Computes the centered first derivative (second order) with different steps to the left and right.

    Parameters
    ----------
    y_minus : float
        Value at the left point (i-1).
    y_plus : float
        Value at the right point (i+1).
    dx : float
        Step between (i-1) and (i+1) when dx_minus = dx_plus.
    dx_minus : float
        Step between (i-1) and i.
    dx_plus : float
        Step between i and (i+1).

    Returns
    -------
    float
        Approximation of the first derivative at i.
    """

    # Case of uniform grid spacing
    if dx is None:
        if dx_minus is None or dx_plus is None:
            raise ValueError("Either dx or both dx_minus and dx_plus must be provided.")
    else:
        if dx == 0:
            raise ValueError("dx must be non-zero.")
        return (y_plus - y_minus) / (2.0 * dx)

    # Case of non-uniform grid spacing (dx is None and dx_minus and dx_plus are provided)
    if dx_minus <= 0 or dx_plus <= 0:
        raise ValueError("dx_minus and dx_plus must be positive non-zero values.")
    y_0 = interpolate([y_minus, y_plus], [dx_minus, dx_plus])
    return (y_plus * dx_minus**2 + y_0 * (dx_plus**2 - dx_minus**2) - y_minus * dx_plus**2)  / \
           (dx_minus * dx_plus * (dx_minus + dx_plus))

delta_h_abs(T)

This function computes the molar enthalpy of absorption of water at a given temperature, in J.mol-1. This reaction is exothermic.

Parameters
T : float
    Temperature in K.
Returns
delta_h_sorp : float
    Molar enthalpy of absorption in the CL in J.mol-1.
Notes
For Nafion, the enthalpy of absorption is almost equal to that of liquefaction [vetterFreeOpenReference2019].
Source code in modules/transitory_functions.py
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
def delta_h_abs(T):
    """This function computes the molar enthalpy of absorption of water at a given temperature, in J.mol-1.
    This reaction is exothermic.

        Parameters
        ----------
        T : float
            Temperature in K.

        Returns
        -------
        delta_h_sorp : float
            Molar enthalpy of absorption in the CL in J.mol-1.

        Notes
        -----
        For Nafion, the enthalpy of absorption is almost equal to that of liquefaction [vetterFreeOpenReference2019].
    """

    return delta_h_liq(T)

delta_h_liq(T)

This function computes the molar enthalpy of liquefaction of water at a given temperature, in J.mol-1. It is calculated as the difference in molar enthalpy between liquid water (H2O_l) and water vapor (H2O_v).

Parameters

T : float Temperature in K.

Returns

delta_h_liq : float Molar enthalpy of liquefaction in J.mol-1.

Notes

This value should be close to -42 000 J.mol-1 [vetterFreeOpenReference2019].

Source code in modules/transitory_functions.py
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
def delta_h_liq(T):
    """This function computes the molar enthalpy of liquefaction of water at a given temperature, in J.mol-1.
       It is calculated as the difference in molar enthalpy between liquid water (H2O_l) and water vapor (H2O_v).

        Parameters
        ----------
        T : float
            Temperature in K.

        Returns
        -------
        delta_h_liq : float
            Molar enthalpy of liquefaction in J.mol-1.

        Notes
        -----
        This value should be close to -42 000 J.mol-1 [vetterFreeOpenReference2019].
    """

    return h0('H2O_l', T) - h0('H2O_v', T)

fv(lambdaa, T)

This function calculates the water volume fraction of the membrane.

Parameters:
  • lambdaa (float) –

    Water content in the membrane.

Returns:
  • float

    Water volume fraction of the membrane.

Source code in modules/transitory_functions.py
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
def fv(lambdaa, T):
    """This function calculates the water volume fraction of the membrane.

    Parameters
    ----------
    lambdaa : float
        Water content in the membrane.

    Returns
    -------
    float
        Water volume fraction of the membrane.
    """

    return (lambdaa * M_H2O / rho_H2O_l(T)) / (M_eq / rho_mem + lambdaa * M_H2O / rho_H2O_l(T))

gamma_sorp(C_v, s, lambdaa, T, Hcl)

This function calculates the sorption rate of water in the membrane, in s-1.

Parameters:
  • C_v (float) –

    Water concentration variable in mol.m-3.

  • s (float) –

    Liquid water saturation variable.

  • lambdaa (float) –

    Water content in the membrane.

  • T (float) –

    Temperature in K.

  • Hcl (float) –

    Thickness of the CL layer.

Returns:
  • float

    Sorption rate of water in the membrane in s-1.

Source code in modules/transitory_functions.py
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
def gamma_sorp(C_v, s, lambdaa, T, Hcl):
    """This function calculates the sorption rate of water in the membrane, in s-1.

    Parameters
    ----------
    C_v : float
        Water concentration variable in mol.m-3.
    s : float
        Liquid water saturation variable.
    lambdaa : float
        Water content in the membrane.
    T : float
        Temperature in K.
    Hcl : float
        Thickness of the CL layer.

    Returns
    -------
    float
        Sorption rate of water in the membrane in s-1.
    """

    fv_value = fv(lambdaa, T)
    gamma_abs = (1.14e-5 * fv_value) / Hcl * math.exp(2416 * (1 / 303 - 1 / T))
    gamma_des = (4.59e-5 * fv_value) / Hcl * math.exp(2416 * (1 / 303 - 1 / T))
    w = 0.5 * (1 + math.tanh(K_transition * (lambda_eq(C_v, s, T) - lambdaa))) # transition function
    return w * gamma_abs + (1 - w) * gamma_des # interpolation between absorption and desorption

h0(component, T)

This function calculates the standard enthalpy of fluids, in J.mol-1, as a function of the temperature. The variation of the enthalpy of reaction with temperature is given by Kirchhoff's Law of Thermochemistry.

Parameters:
  • component (str) –

    Specifies the gas for which the specific heat capacity is calculated. Must be either 'H2O_l' (liquid water) or 'H2O_v' (vapor).

  • T (float) –

    Temperature in K.

Returns:
  • float

    Standard enthalpy of the selected fluid in J.mol-1.

Notes

Source : Chase, M. W. (1998). NIST-JANAF Thermochemical Tables, 4th edition

Source code in modules/transitory_functions.py
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
def h0(component, T):
    """This function calculates the standard enthalpy of fluids, in J.mol-1, as a function of the temperature.
    The variation of the enthalpy of reaction with temperature is given by Kirchhoff's Law of Thermochemistry.

    Parameters
    ----------
    component : str
        Specifies the gas for which the specific heat capacity is calculated.
        Must be either 'H2O_l' (liquid water) or 'H2O_v' (vapor).
    T : float
        Temperature in K.

    Returns
    -------
    float
        Standard enthalpy of the selected fluid in J.mol-1.

    Notes
    -----
    Source : Chase, M. W. (1998). NIST-JANAF Thermochemical Tables, 4th edition"""

    if component == 'H2O_l':  # For T >= 298 and T <= 500 K.
        return (-285.83 - 203.6060 * (T/1000) + 1523.290 * (T/1000)**2 / 2 - 3196.413 * (T/1000)**3 / 3 +
                2474.455 * (T/1000)**4 / 4 - 3.855326 / (T/1000) - 256.5478 + 285.8304) * 1e3
    elif component == 'H2O_v':  # For T = 298.15 K. I failed to find a proper equation at the good range of temperature.
        return -241.83 * 1e3 + Cp0('H2O_v', T) * M_H2O * (T - 298.15)
    else:
        raise ValueError("The element should be either 'H2O_l' or 'H2O_v'")

h_a(P, T, Wgc, Hgc)

This function calculates the effective convective-conductive mass transfer coefficient at the anode, in m.s-1.

Parameters:
  • P (float) –

    Pressure in Pa.

  • T (float) –

    Temperature in K.

  • Wgc (float) –

    Width of the gas channel in m.

  • Hgc (float) –

    Thickness of the gas channel in m.

Returns:
  • float

    Effective convective-conductive mass transfer coefficient at the anode in m.s-1.

Source code in modules/transitory_functions.py
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
def h_a(P, T, Wgc, Hgc):
    """This function calculates the effective convective-conductive mass transfer coefficient at the anode, in m.s-1.

    Parameters
    ----------
    P : float
        Pressure in Pa.
    T : float
        Temperature in K.
    Wgc : float
        Width of the gas channel in m.
    Hgc : float
        Thickness of the gas channel in m.

    Returns
    -------
    float
        Effective convective-conductive mass transfer coefficient at the anode in m.s-1.
    """
    Sh = 0.9247 * math.log(Wgc / Hgc) + 2.3787  # Sherwood coefficient.
    return Sh * Da(P, T) / Hgc

h_c(P, T, Wgc, Hgc)

This function calculates the effective convective-conductive mass transfer coefficient at the cathode, in m.s-1.

Parameters:
  • P (float) –

    Pressure in Pa.

  • T (float) –

    Temperature in K.

  • Wgc (float) –

    Width of the gas channel in m.

  • Hgc (float) –

    Thickness of the gas channel in m.

Returns:
  • float

    Effective convective-conductive mass transfer coefficient at the cathode in m.s-1.

Source code in modules/transitory_functions.py
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
def h_c(P, T, Wgc, Hgc):
    """This function calculates the effective convective-conductive mass transfer coefficient at the cathode, in m.s-1.

    Parameters
    ----------
    P : float
        Pressure in Pa.
    T : float
        Temperature in K.
    Wgc : float
        Width of the gas channel in m.
    Hgc : float
        Thickness of the gas channel in m.

    Returns
    -------
    float
        Effective convective-conductive mass transfer coefficient at the cathode in m.s-1.
    """
    Sh = 0.9247 * math.log(Wgc / Hgc) + 2.3787  # Sherwood coefficient.
    return Sh * Dc(P, T) / Hgc

hmean(terms, weights=None)

Calculate the weighted harmonic mean of a list of terms with corresponding weights. It is more efficient to express this function in the code than calling hmean from scipy.stats.

Parameters:
  • terms

    The terms to calculate the harmonic mean for.

  • weights

    The weights corresponding to each term. If None, uniform weights are assumed.

Returns:
  • float

    The weighted harmonic mean.

Source code in modules/transitory_functions.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def hmean(terms, weights=None):
    """
    Calculate the weighted harmonic mean of a list of terms with corresponding weights.
    It is more efficient to express this function in the code than calling hmean from scipy.stats.

    Parameters
    ----------
    terms (list of float):
        The terms to calculate the harmonic mean for.
    weights (list of float):
        The weights corresponding to each term. If None, uniform weights are assumed.

    Returns
    -------
    float:
        The weighted harmonic mean.
    """

    n = len(terms)
    if weights is None:
        weights = [1] * n  # Assign equal weights if not provided

    if len(weights) != n:
        raise ValueError("The length of terms and weights must be the same.")

    # Calculate the weighted harmonic mean
    weighted_sum = 0
    total_weight = 0
    for w, t in zip(weights, terms):
        if t != 0:
            weighted_sum += w / t
        total_weight += w

    if weighted_sum == 0:
        return float('inf')  # Avoid division by zero

    return total_weight / weighted_sum

interpolate(terms, distances)

Fast inverse distance interpolation for exactly 2 points.

Parameters:
  • terms (list of float) –

    The values at each node ([y1, y2]).

  • distances (list of float) –

    The distances from each node to the interpolation point ([d1, d2]).

Returns:
  • float

    The interpolated value.

Source code in modules/transitory_functions.py
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
def interpolate(terms, distances):
    """
    Fast inverse distance interpolation for exactly 2 points.

    Parameters
    ----------
    terms : list of float
        The values at each node ([y1, y2]).
    distances : list of float
        The distances from each node to the interpolation point ([d1, d2]).

    Returns
    -------
    float
        The interpolated value.
    """
    if len(terms) != 2 or len(distances) != 2:
        raise ValueError("This function only supports interpolation with 2 points.")
    y1, y2 = terms
    d1, d2 = distances
    if d1 == 0: return y1
    if d2 == 0: return y2
    return (d2 * y1 + d1 * y2) / (d1 + d2)

k_H2(lambdaa, T, kappa_co)

This function calculates the permeability coefficient of the membrane for hydrogen, in mol.m−1.s−1.Pa−1.

Parameters:
  • lambdaa (float) –

    Water content in the membrane.

  • T (float) –

    Temperature in K.

  • kappa_co (float) –

    Crossover correction coefficient in mol.m-1.s-1.Pa-1.

Returns:
  • float

    Permeability coefficient of the membrane for hydrogen in mol.m−1.s−1.Pa−1.

Source code in modules/transitory_functions.py
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
def k_H2(lambdaa, T, kappa_co):
    """This function calculates the permeability coefficient of the membrane for hydrogen, in mol.m−1.s−1.Pa−1.

    Parameters
    ----------
    lambdaa : float
        Water content in the membrane.
    T : float
        Temperature in K.
    kappa_co : float
        Crossover correction coefficient in mol.m-1.s-1.Pa-1.

    Returns
    -------
    float
        Permeability coefficient of the membrane for hydrogen in mol.m−1.s−1.Pa−1.
    """

    # Initialisation of the constants
    E_H2_v = 2.1e4  # J.mol-1. It is the activation energy of H2 for crossover in the under saturated membrane.
    E_H2_l = 1.8e4  # J.mol-1. It is the activation energy of H2 for crossover in the liquid-equilibrated membrane.
    Tref = 303.15  # K.

    # Calculation of the permeability coefficient of the membrane for hydrogen
    k_H2_d = kappa_co * (0.29 + 2.2 * fv(lambdaa, T)) * 1e-14 * math.exp(E_H2_v / R * (1 / Tref - 1 / T))
    k_H2_l = kappa_co * 1.8 * 1e-14 * math.exp(E_H2_l / R * (1 / Tref - 1 / T))
    w = 0.5 * (1 + math.tanh(K_transition * (lambda_l_eq(T) - lambdaa)))  # transition function
    return w * k_H2_d + (1 - w) * k_H2_l  # interpolation between under-saturated and liquid-equilibrated H2 crossover

k_O2(lambdaa, T, kappa_co)

This function calculates the permeability coefficient of the membrane for oxygen, in mol.m−1.s−1.Pa−1.

Parameters:
  • lambdaa (float) –

    Water content in the membrane.

  • T (float) –

    Temperature in K.

  • kappa_co (float) –

    Crossover correction coefficient in mol.m-1.s-1.Pa-1.

Returns:
  • float

    Permeability coefficient of the membrane for oxygen in mol.m−1.s−1.Pa−1.

Source code in modules/transitory_functions.py
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
def k_O2(lambdaa, T, kappa_co):
    """This function calculates the permeability coefficient of the membrane for oxygen, in mol.m−1.s−1.Pa−1.

    Parameters
    ----------
    lambdaa : float
        Water content in the membrane.
    T : float
        Temperature in K.
    kappa_co : float
        Crossover correction coefficient in mol.m-1.s-1.Pa-1.

    Returns
    -------
    float
        Permeability coefficient of the membrane for oxygen in mol.m−1.s−1.Pa−1.
    """

    # Initialisation of the constants
    E_O2_v = 2.2e4  # J.mol-1. It is the activation energy of oxygen for crossover in the under saturated membrane.
    E_O2_l = 2.0e4  # J.mol-1. It is the activation energy of oxygen for crossover in the liquid-equilibrated membrane.
    Tref = 303.15  # K.

    # Calculation of the permeability coefficient of the membrane for oxygen
    k_O2_v = kappa_co * (0.11 + 1.9 * fv(lambdaa, T)) * 1e-14 * math.exp(E_O2_v / R * (1 / Tref - 1 / T))
    k_O2_l = kappa_co * 1.2 * 1e-14 * math.exp(E_O2_l / R * (1 / Tref - 1 / T))
    w = 0.5 * (1 + math.tanh(K_transition * (lambda_l_eq(T) - lambdaa)))  # transition function
    return w * k_O2_v + (1 - w) * k_O2_l  # interpolation between under-saturated and liquid-equilibrated O2 crossover

k_th(component, T) cached

This function calculates the thermal conductivity of fluids, in J.m-1.s-1.K-1, as a function of the temperature.

Parameters:
  • component (str) –

    Specifies the gas for which the thermal conductivity is calculated. Must be either 'H2O_l' (liquid water), 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (hydrogen), or 'N2' (nitrogen).

  • T (float) –

    Temperature in K.

Returns:
  • float

    Thermal conductivity of the selected fluid in J.m-1.s-1.K-1.

Notes

Source : Carl L. Yaws - Manuel 2014 - Transport properties of chemicals and hydrocarbons (https://www.sciencedirect.com/book/9780323286589/transport-properties-of-chemicals-and-hydrocarbons)

Source code in modules/transitory_functions.py
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
@lru_cache(maxsize=None) # Cache the results to optimize performance
def k_th(component, T):
    """This function calculates the thermal conductivity of fluids, in J.m-1.s-1.K-1, as a function of the
    temperature.

    Parameters
    ----------
    component : str
        Specifies the gas for which the thermal conductivity is calculated.
        Must be either 'H2O_l' (liquid water), 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (hydrogen), or 'N2' (nitrogen).
    T : float
        Temperature in K.

    Returns
    -------
    float
        Thermal conductivity of the selected fluid in J.m-1.s-1.K-1.

    Notes
    -----
    Source : Carl L. Yaws - Manuel 2014 - Transport properties of chemicals and hydrocarbons
    (https://www.sciencedirect.com/book/9780323286589/transport-properties-of-chemicals-and-hydrocarbons)"""

    if component == 'H2O_l':  # For T >= 273.16 and T <= 633.15 K.
        return -0.2987 + 4.7054e-3 * T - 5.6209e-6 * T ** 2
    elif component == 'H2O_v': # For T >= 150 K and T <= 1500 k.
        return 5.6199e-3 + 1.5699e-5 * T + 1.0106e-7 * T ** 2 - 2.4282e-11 * T ** 3
    elif component == 'H2': # For T >= 14 K and T <= 1500 K.
        return 1.0979e-2 + 6.6411e-4 * T - 3.4378e-7 * T ** 2 + 9.7283e-11 * T ** 3
    elif component == 'O2': # For T >= 80 K and T <= 2000 K.
        return 1.5475e-4 + 9.4153e-5 * T - 2.7529e-8 * T ** 2 + 5.2069e-12 * T ** 3
    elif component == 'N2': # For T >= 63 K and T <= 1500 K.
        return -2.2678e-4 + 1.0275e-4 * T - 6.0151e-8 * T ** 2 + 2.2332e-11 * T ** 3
    else:
        raise ValueError("The element should be either 'H2O_l', 'H2O_v', 'H2', 'O2' or 'N2'.")

k_th_eff(element, T, C_v=None, s=None, lambdaa=None, C_H2=None, C_O2=None, C_N2=None, epsilon=None, epsilon_mc=None, epsilon_c=None)

This function calculates the effective thermal conductivity, in J.m-1.s-1.K-1, in either the GDL, the MPL, the CL or the membrane. A weighted harmonic average is used for characterizing the conductivity of each material in a layer, instead of a weighted arithmetic average. The physical meaning is that all the heat energy is forced to pass through all the material, as a series resistance network, instead of a parallel one [pharoahEffectiveTransportCoefficients2006].

Parameters:
  • element (str) –

    Specifies the element for which the proton conductivity is calculated. Must be either 'agdl' (anode gas diffusion layer), 'cgdl' (cathode gas diffusion layer), 'acl' (anode catalyst layer), 'ccl' (cathode catalyst layer) or 'mem' (membrane).

  • T (float) –

    Temperature in K.

  • C_v (float, default: None ) –

    Water concentration variable in mol.m-3.

  • s (float, default: None ) –

    Liquid water saturation variable.

  • lambdaa (float, default: None ) –

    Water content in the membrane.

  • C_H2 (float, default: None ) –

    Concentration of hydrogen in the AGDL or ACL.

  • C_O2 (float, default: None ) –

    Concentration of oxygen in the CGDL or CCL.

  • C_N2 (float, default: None ) –

    Concentration of nitrogen in the CGDL or CCL.

  • epsilon (float, default: None ) –

    Porosity.

  • epsilon_mc (float, default: None ) –

    Volume fraction of ionomer in the CL.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

Returns:
  • float

    Effective thermal conductivity in J.m-1.s-1.K-1.

Source code in modules/transitory_functions.py
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
def k_th_eff(element, T, C_v=None, s=None, lambdaa=None, C_H2=None, C_O2=None, C_N2=None, epsilon=None, epsilon_mc=None,
             epsilon_c=None):
    """This function calculates the effective thermal conductivity, in J.m-1.s-1.K-1, in either the GDL, the MPL, the CL
    or the membrane. A weighted harmonic average is used for characterizing the conductivity of each material in a layer,
    instead of a weighted arithmetic average. The physical meaning is that all the heat energy is forced to pass through
    all the material, as a series resistance network, instead of a parallel one
    [pharoahEffectiveTransportCoefficients2006].

    Parameters
    ----------
    element : str
        Specifies the element for which the proton conductivity is calculated.
        Must be either 'agdl' (anode gas diffusion layer), 'cgdl' (cathode gas diffusion layer),
        'acl' (anode catalyst layer), 'ccl' (cathode catalyst layer) or 'mem' (membrane).
    T : float
        Temperature in K.
    C_v : float
        Water concentration variable in mol.m-3.
    s : float
        Liquid water saturation variable.
    lambdaa : float
        Water content in the membrane.
    C_H2 : float
        Concentration of hydrogen in the AGDL or ACL.
    C_O2 : float
        Concentration of oxygen in the CGDL or CCL.
    C_N2 : float
        Concentration of nitrogen in the CGDL or CCL.
    epsilon : float
        Porosity.
    epsilon_mc : float
        Volume fraction of ionomer in the CL.
    epsilon_c : float
        Compression ratio of the GDL.

    Returns
    -------
    float
        Effective thermal conductivity in J.m-1.s-1.K-1."""

    if element in ('agdl', 'cgdl'): # The effective thermal conductivity at the GDL
        # According to the GDL porosity, the GDL compression effect is different.
        if epsilon < 0.67:
            beta3 = 4.04
        else:
            beta3 = 4.40
        if element == 'agdl': # The thermal conductivity of the gas mixture in the AGDL
            sum_C_v_C_H2_C_N2 = C_v + C_H2 + C_N2
            x_v, x_h2, x_n2 = C_v / sum_C_v_C_H2_C_N2, C_H2 / sum_C_v_C_H2_C_N2, C_N2 / sum_C_v_C_H2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('H2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('H2', T), mu_gaz('N2', T)],
                                        [x_v, x_h2, x_n2],
                                        [M_H2O, M_H2, M_N2])
        else:                 # The thermal conductivity of the gas mixture in the CGDL
            sum_C_v_C_O2_C_N2 = C_v + C_O2 + C_N2
            x_v, x_o2, x_n2 = C_v / sum_C_v_C_O2_C_N2, C_O2 / sum_C_v_C_O2_C_N2, C_N2 / sum_C_v_C_O2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('O2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('O2', T), mu_gaz('N2', T)],
                                        [x_v, x_o2, x_n2],
                                        [M_H2O, M_O2, M_N2])
        return hmean([k_th_gdl * math.exp(beta3 * epsilon_c), k_th('H2O_l', T), k_th_gaz],
                     weights=[1 - epsilon, epsilon * s, epsilon * (1 - s)])

    elif element in ('ampl', 'cmpl'): # The effective thermal conductivity at the GDL
        if element == 'ampl': # The thermal conductivity of the gas mixture in the AGDL
            sum_C_v_C_H2_C_N2 = C_v + C_H2 + C_N2
            x_v, x_h2, x_n2 = C_v / sum_C_v_C_H2_C_N2, C_H2 / sum_C_v_C_H2_C_N2, C_N2 / sum_C_v_C_H2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('H2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('H2', T), mu_gaz('N2', T)],
                                        [x_v, x_h2, x_n2],
                                        [M_H2O, M_H2, M_N2])
        else:                 # The thermal conductivity of the gas mixture in the CGDL
            sum_C_v_C_O2_C_N2 = C_v + C_O2 + C_N2
            x_v, x_o2, x_n2 = C_v / sum_C_v_C_O2_C_N2, C_O2 / sum_C_v_C_O2_C_N2, C_N2 / sum_C_v_C_O2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('O2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('O2', T), mu_gaz('N2', T)],
                                        [x_v, x_o2, x_n2],
                                        [M_H2O, M_O2, M_N2])
        return hmean([k_th_mpl, k_th('H2O_l', T), k_th_gaz],
                     weights=[1 - epsilon, epsilon * s, epsilon * (1 - s)])

    elif element in ('acl', 'ccl'): # The effective thermal conductivity at the CL
        fv_val = fv(lambdaa, T)
        k_th_eff_mem = hmean([k_th_mem, k_th('H2O_l', T)],
                             weights=[1 - fv_val, fv_val]) # The effective thermal conductivity at the membrane
        if element == 'acl':  # The thermal conductivity of the gas mixture in the ACL
            sum_C_v_C_H2_C_N2 = C_v + C_H2 + C_N2
            x_v, x_h2, x_n2 = C_v / sum_C_v_C_H2_C_N2, C_H2 / sum_C_v_C_H2_C_N2, C_N2 / sum_C_v_C_H2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('H2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('H2', T), mu_gaz('N2', T)],
                                        [x_v, x_h2, x_n2],
                                        [M_H2O, M_H2, M_N2])
        else:  # The thermal conductivity of the gas mixture in the CCL
            sum_C_v_C_O2_C_N2 = C_v + C_O2 + C_N2
            x_v, x_o2, x_n2 = C_v / sum_C_v_C_O2_C_N2, C_O2 / sum_C_v_C_O2_C_N2, C_N2 / sum_C_v_C_O2_C_N2
            k_th_gaz = k_th_gaz_mixture([k_th('H2O_v', T), k_th('O2', T), k_th('N2', T)],
                                        [mu_gaz('H2O_v', T), mu_gaz('O2', T), mu_gaz('N2', T)],
                                        [x_v, x_o2, x_n2],
                                        [M_H2O, M_O2, M_N2])
        return hmean([k_th_cl, k_th_eff_mem, k_th('H2O_l', T), k_th_gaz],
                     weights=[1 - epsilon - epsilon_mc, epsilon_mc, epsilon * s, epsilon * (1-s)])

    elif element == 'mem': # The effective thermal conductivity at the membrane
        fv_val = fv(lambdaa, T)
        return hmean([k_th_mem, k_th('H2O_l', T)],
                     weights=[1 - fv_val, fv_val])

    else:
        raise ValueError("The element should be either 'agdl', 'cgdl', 'ampl', 'cmpl', 'acl', 'ccl' or 'mem'.")

k_th_gaz_mixture(k_th_g, mu_g, x, M)

This function calculates the thermal conductivity of a gas mixture, in J.m-1.s-1.K-1. The Lindsay–Bromley (Wassiljewa) method is used.

Parameters:
  • k_th_g (list) –

    Thermal conductivities of each pure gas component, in J.m-1.s-1.K-1, at the same temperature.

  • mu_g (list) –

    Viscosity of each pure gas component, in Pa.s, at the same temperature.

  • x (list) –

    Mole fractions of each gas component in the mixture (must sum to 1).

  • M (list) –

    Molar masses of each gas component (in kg.mol-1).

Returns:
  • lambda_mix( float ) –

    Thermal conductivity of the gas mixture, in J.m-1.s-1.K-1.

Notes

Source : [wuMathematicalModelingTransient2009] and [polingPropertiesGasesLiquids2001]

Source code in modules/transitory_functions.py
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
def k_th_gaz_mixture(k_th_g, mu_g, x, M):
    """This function calculates the thermal conductivity of a gas mixture, in J.m-1.s-1.K-1. The Lindsay–Bromley
    (Wassiljewa) method is used.

    Parameters
    ----------
    k_th_g : list
        Thermal conductivities of each pure gas component, in J.m-1.s-1.K-1, at the same temperature.
    mu_g : list
        Viscosity of each pure gas component, in Pa.s, at the same temperature.
    x : list
        Mole fractions of each gas component in the mixture (must sum to 1).
    M : list
        Molar masses of each gas component (in kg.mol-1).

    Returns
    -------
    lambda_mix : float
        Thermal conductivity of the gas mixture, in J.m-1.s-1.K-1.

    Notes
    -----
    Source : [wuMathematicalModelingTransient2009] and [polingPropertiesGasesLiquids2001]"""

    total_x = 0.0
    for xi in x:
        total_x += xi
    if abs(total_x - 1.0) > 1e-6:
        raise ValueError("The sum of the molar fractions should be 1.")

    n = len(k_th_g)
    epsilon_TS = 0.85 # Value suggested by Tandon and Saxena in 1965.

    # Calculation of A_W using Maxon and Saxena suggestion.
    A_W = []
    for i in range(n):
        row = []
        for j in range(n):
            if i == j:
                row.append(1.0)
            else:
                val = (epsilon_TS * (1 + (mu_g[i] / mu_g[j]) ** 0.5 * (M[j] / M[i]) ** 0.25) ** 2) / \
                      (8 * (1 + M[i] / M[j])) ** 0.5
                row.append(val)
        A_W.append(row)

    # Calculation of the thermal conductivity of the gas mixture.
    k_th_gaz_mixture = 0.0
    for i in range(n):
        prod_x_A_w = 0.0
        for j in range(n):
            prod_x_A_w += x[j] * A_W[i][j]
        k_th_gaz_mixture += x[i] * k_th_g[i] / prod_x_A_w

    return k_th_gaz_mixture

lambda_eq(C_v, s, T)

This function calculates the equilibrium water content in the membrane. Hinatsu's expression modified with Bao's formulation has been selected.

Parameters:
  • C_v (float) –

    Water concentration variable in mol.m-3.

  • s (float) –

    Liquid water saturation variable.

  • T (float) –

    Temperature in K.

Returns:
  • float

    Equilibrium water content in the membrane.

Source code in modules/transitory_functions.py
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
def lambda_eq(C_v, s, T):
    """This function calculates the equilibrium water content in the membrane. Hinatsu's expression modified with
    Bao's formulation has been selected.

    Parameters
    ----------
    C_v : float
        Water concentration variable in mol.m-3.
    s : float
        Liquid water saturation variable.
    T : float
        Temperature in K.

    Returns
    -------
    float
        Equilibrium water content in the membrane.
    """
    a_w = C_v / C_v_sat(T) + 2 * s  # water activity
    return 0.5 * lambda_v_eq(a_w)                                          * (1 - math.tanh(100 * (a_w - 1))) + \
           0.5 * (lambda_v_eq(1) + ((lambda_l_eq(T) - lambda_v_eq(1)) / 2) * (1 - math.exp(-Kshape * (a_w - 1)))) * \
                                                                             (1 + math.tanh(100 * (a_w - 1)))

lambda_l_eq(T)

This function calculates the equilibrium water content in the membrane from the liquid phase. Hinatsu's expression has been selected. It is valid for N-form membranes for 25 to 100 °C.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Equilibrium water content in the membrane from the liquid phase.

Source code in modules/transitory_functions.py
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
def lambda_l_eq(T):
    """This function calculates the equilibrium water content in the membrane from the liquid phase.
    Hinatsu's expression has been selected. It is valid for N-form membranes for 25 to 100 °C.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Equilibrium water content in the membrane from the liquid phase.
    """
    return 10.0 * 1.84e-2 * (T - 273.15) + 9.90e-4 * (T - 273.15)**2

lambda_v_eq(a_w)

This function calculates the equilibrium water content in the membrane from the vapor phase. Hinatsu's expression has been selected.

Parameters:
  • a_w

    Water activity.

Returns:
  • float

    Equilibrium water content in the membrane from the vapor phase.

Source code in modules/transitory_functions.py
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
def lambda_v_eq(a_w):
    """This function calculates the equilibrium water content in the membrane from the vapor phase. Hinatsu's expression
     has been selected.

    Parameters
    ----------
    a_w: float
        Water activity.

    Returns
    -------
    float
        Equilibrium water content in the membrane from the vapor phase.
    """
    return 0.300 + 10.8 * a_w - 16.0 * a_w ** 2 + 14.1 * a_w ** 3

mu_gaz(component, T)

This function calculates the dynamic viscosity of different gases, in Pa.s, as a function of the temperature.

Parameters:
  • component (str) –

    Specifies the gas for which the dynamic viscosity is calculated. Must be either 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (hydrogen), or 'N2' (nitrogen).

  • T (float) –

    Temperature in K.

Returns:
  • float

    Dynamic viscosity of the selected gas in Pa.s.

Notes

Source : Carl L. Yaws - Manuel 2014 - Transport properties of chemicals and hydrocarbons (https://www.sciencedirect.com/book/9780323286589/transport-properties-of-chemicals-and-hydrocarbons)

Source code in modules/transitory_functions.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def mu_gaz(component, T):
    """This function calculates the dynamic viscosity of different gases, in Pa.s, as a function of the temperature.

    Parameters
    ----------
    component : str
        Specifies the gas for which the dynamic viscosity is calculated.
        Must be either 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (hydrogen), or 'N2' (nitrogen).
    T : float
        Temperature in K.

    Returns
    -------
    float
        Dynamic viscosity of the selected gas in Pa.s.

    Notes
    -----
    Source : Carl L. Yaws - Manuel 2014 - Transport properties of chemicals and hydrocarbons
    (https://www.sciencedirect.com/book/9780323286589/transport-properties-of-chemicals-and-hydrocarbons)"""

    if component == 'H2O_v': # For T >= 150 K and T <= 1500 k.
        return (22.8211 + 1.7387e-1 * T + 3.2465e-4 * T ** 2 - 1.4334e-7 * T ** 3) * 10**-7
    elif component == 'H2': # For T >= 15 K and T <= 1500 K.
        return (1.7611 + 3.4165e-1 * T - 1.8368e-4 * T ** 2 + 5.1147e-8 * T ** 3) * 10**-7
    elif component == 'O2': # For T >= 54 K and T <= 1500 K.
        return (-4.9433 + 8.0673e-1 * T - 4.0416e-4 * T ** 2 + 1.0111e-7 * T ** 3) * 10**-7
    elif component == 'N2': # For T >= 63 K and T <= 1970 K.
        return (4.4656 + 6.3814e-1 * T - 2.6596e-4 * T ** 2 + 5.4113e-8 * T ** 3) * 10**-7
    else:
        raise ValueError("The element should be either 'H2O_v', 'H2', 'O2' or 'N2'.")

mu_mixture_gases(components, x, T)

This function calculates the dynamic viscosity of a gas mixture, in Pa.s, as a function of the temperature.

Parameters:
  • components (list of str) –

    List of gas components in the mixture. Each component must be either 'H2O_v' (vapor), 'H2' (hydrogen), 'O2' (oxygen), or 'N2' (nitrogen).

  • x (list of float) –

    List of mole fractions corresponding to each gas component in the mixture.

  • T (float) –

    Temperature in K.

Returns:
  • float

    Dynamic viscosity of the gas mixture in Pa.s.

Notes

A simple mixture law is used here to calculate the dynamic viscosity of the gas mixture.

Source code in modules/transitory_functions.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
def mu_mixture_gases(components, x, T):
    """This function calculates the dynamic viscosity of a gas mixture, in Pa.s, as a function of the temperature.

    Parameters
    ----------
    components : list of str
        List of gas components in the mixture. Each component must be either 'H2O_v' (vapor), 'H2' (hydrogen),
        'O2' (oxygen), or 'N2' (nitrogen).
    x : list of float
        List of mole fractions corresponding to each gas component in the mixture.
    T : float
        Temperature in K.

    Returns
    -------
    float
        Dynamic viscosity of the gas mixture in Pa.s.

    Notes
    -----
    A simple mixture law is used here to calculate the dynamic viscosity of the gas mixture.
    """

    # Calculate the dynamic viscosities of each gas component in Pa.s.
    mu_values = [mu_gaz(comp, T) for comp in components]

    # Calculate the molar mass of the gas mixture in kg/mol.
    M_mix = 0.0
    for j in range(len(components)):
        M_j = M_H2O if components[j] == 'H2O_v' else M_H2 if components[j] == 'H2' else M_O2 if components[j] == 'O2' \
              else M_N2 if components[j] == 'N2' else None
        M_mix += M_j * x[j]

    inv_mu_mix = 0.0
    for j, mu_j in enumerate(mu_values):
        M_j = M_H2O if components[j] == 'H2O_v' else M_H2 if components[j] == 'H2' else M_O2 if components[j] == 'O2' else M_N2
        c_j = M_j * x[j] / M_mix
        inv_mu_mix += c_j / mu_j

    return 1 / inv_mu_mix

nu_l(T)

This function calculates the liquid water kinematic viscosity, in m².s-1, as a function of the temperature.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Liquid water kinematic viscosity in m².s-1.

Source code in modules/transitory_functions.py
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def nu_l(T):
    """This function calculates the liquid water kinematic viscosity, in m².s-1, as a function of the temperature.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Liquid water kinematic viscosity in m².s-1.
    """
    mu_l = 2.414 * 10 ** (-5 + 247.8 / (T - 140.0))  # Pa.s. It is the liquid water dynamic viscosity.
    return mu_l / rho_H2O_l(T)

rho_H2O_l(T)

This function calculates the water density, in kg.m-3, as a function of the temperature.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Water density in kg.m-3.

Source code in modules/transitory_functions.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def rho_H2O_l(T):
    """This function calculates the water density, in kg.m-3, as a function of the temperature.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Water density in kg.m-3.
    """
    T_Celsius = T - 273.15
    return ((999.83952 + 16.945176 * T_Celsius - 7.9870401e-3 * T_Celsius ** 2 - 46.170461e-6 * T_Celsius ** 3
             + 105.56302e-9 * T_Celsius ** 4 - 280.54253e-12 * T_Celsius ** 5) / (1 + 16.879850e-3 * T_Celsius))

sigma(T)

This function calculates the water surface tension, in N.m-1, as a function of the temperature.

Parameters:
  • T (float) –

    Temperature in K.

Returns:
  • float

    Water surface tension in N.m-1.

Source code in modules/transitory_functions.py
708
709
710
711
712
713
714
715
716
717
718
719
720
721
def sigma(T):
    """This function calculates the water surface tension, in N.m-1, as a function of the temperature.

    Parameters
    ----------
    T : float
        Temperature in K.

    Returns
    -------
    float
        Water surface tension in N.m-1.
    """
    return 235.8e-3 * ((647.15 - T) / 647.15) ** 1.256 * (1 - 0.625 * (647.15 - T) / 647.15)

sigma_e_eff(element, epsilon, epsilon_c=None, epsilon_mc=None) cached

This function calculates the effective electrical conductivity, in Ω-1.m-1, in either the GDL, the MPL or the CL, considering GDL compression.

Parameters:
  • element (str) –

    Specifies the element for which the proton conductivity is calculated. Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).

  • epsilon (float) –

    Porosity.

  • epsilon_c (float, default: None ) –

    Compression ratio of the GDL.

  • epsilon_mc (float, default: None ) –

    Volume fraction of ionomer in the CL.

Returns:
  • float

    Effective electrical conductivity in Ω-1.m-1.

Source code in modules/transitory_functions.py
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
@lru_cache(maxsize=None) # Cache the results to optimize performance
def sigma_e_eff(element, epsilon, epsilon_c=None, epsilon_mc=None):
    """This function calculates the effective electrical conductivity, in Ω-1.m-1, in either the GDL, the MPL or the CL,
    considering GDL compression.

    Parameters
    ----------
    element : str
        Specifies the element for which the proton conductivity is calculated.
        Must be either 'gdl' (gas diffusion layer) or 'cl' (catalyst layer).
    epsilon : float
        Porosity.
    epsilon_c : float, optional
        Compression ratio of the GDL.
    epsilon_mc : float, optional
        Volume fraction of ionomer in the CL.

    Returns
    -------
    float
        Effective electrical conductivity in Ω-1.m-1.
    """
    if element == 'gdl': # The effective electrical conductivity at the GDL
        # According to the GDL porosity, the GDL compression effect is different.
        if epsilon < 0.67:
            beta3 = 4.04
        else:
            beta3 = 4.40
        return (1 - epsilon) * sigma_e_gdl * math.exp(beta3 * epsilon_c) # Using the volume fraction of conductive material.

    elif element == 'mpl': # The effective electrical conductivity at the MPL
        return (1 - epsilon) * sigma_e_mpl # Using the volume fraction of conductive material.

    elif element == 'cl': # The effective electrical conductivity at the CL
        return (1 - epsilon - epsilon_mc) * sigma_e_cl # Using the volume fraction of conductive material.

    else:
        raise ValueError("The element should be either 'gdl', 'mpl' or 'cl'.")

sigma_p_eff(element, lambdaa, T, epsilon_mc=None)

This function calculates the effective proton conductivity, in Ω-1.m-1, in either the membrane or the CCL.

Parameters:
  • element (str) –

    Specifies the element for which the proton conductivity is calculated. Must be either 'mem' (membrane) or 'ccl' (cathode catalyst layer).

  • lambdaa (float) –

    Water content in the membrane.

  • T (float) –

    Temperature in K.

  • epsilon_mc (float, default: None ) –

    Volume fraction of ionomer in the CCL.

Returns:
  • float

    Proton conductivity in Ω-1.m-1.

Source code in modules/transitory_functions.py
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
def sigma_p_eff(element, lambdaa, T, epsilon_mc=None):
    """This function calculates the effective proton conductivity, in Ω-1.m-1, in either the membrane or the CCL.

    Parameters
    ----------
    element : str
        Specifies the element for which the proton conductivity is calculated.
        Must be either 'mem' (membrane) or 'ccl' (cathode catalyst layer).
    lambdaa : float
        Water content in the membrane.
    T : float
        Temperature in K.
    epsilon_mc : float
        Volume fraction of ionomer in the CCL.

    Returns
    -------
    float
        Proton conductivity in Ω-1.m-1.
    """

    lambda_transition = 1

    if element == 'mem': # The proton conductivity at the membrane
        sigma_p_eff_low = 0.1879 * math.exp(1268 * (1 / 303.15 - 1 / T))
        sigma_p_eff_high = (0.5139 * lambdaa - 0.326) * math.exp(1268 * (1 / 303.15 - 1 / T))
    elif element == 'ccl': # The effective proton conductivity at the cathode catalyst layer
        sigma_p_eff_low = epsilon_mc * 0.1879 * math.exp(1268 * (1 / 303.15 - 1 / T))
        sigma_p_eff_high = epsilon_mc * (0.5139 * lambdaa - 0.326) * math.exp(1268 * (1 / 303.15 - 1 / T))
    else:
        raise ValueError("The element should be either 'mem' or 'ccl'.")

    w = 0.5 * (1 + math.tanh(K_transition * (lambda_transition - lambdaa)))  # transition function
    return w * sigma_p_eff_low + (1 - w) * sigma_p_eff_high  # interpolation between sigma_p_eff value at low and high lambda.