Example 5 from RP-1311ΒΆ

Note

The python script for this example is available in the source/bind/python/cea/samples directory of the CEA repository.

Here we describe how to run example 5 from RP-1311 [1] using the Python API. This is an HP equilibrium problem for a solid propellant blend with five reactants, including one custom reactant (CHOS-Binder) that is not present in thermo.lib.

First import the required libraries:

import numpy as np
import cea

Define the pressure schedule and reactant composition by weight fraction:

pressures = np.array([34.473652, 17.236826, 8.618413, 3.447365, 0.344737])
weights = np.array([0.7206, 0.1858, 0.09, 0.002, 0.0016], dtype=np.float64)
T_reac = np.array([298.15, 298.15, 298.15, 298.15, 298.15], dtype=np.float64)

Create a Reactant for CHOS-Binder using SI values. In the Python API, custom reactant enthalpy is in J/kg and temperature is in K. Use cea.units helpers for pre-conversion as needed.

chos_binder_mw_kg_per_mol = 14.6652984484e-3
chos_binder_h_si = cea.units.cal_to_joule(-2999.082) / chos_binder_mw_kg_per_mol

reactants = [
    "NH4CLO4(I)",
    cea.Reactant(
        name="CHOS-Binder",
        formula={"C": 1.0, "H": 1.86955, "O": 0.031256, "S": 0.008415},
        molecular_weight=14.6652984484,
        enthalpy=chos_binder_h_si,
        temperature=298.15,
    ),
    "AL(cr)",
    "MgO(cr)",
    "H2O(L)",
]

Then build reactant/product mixtures, solve the HP problem, and print the full table output:

import numpy as np
import cea

"""
Example 5 from RP-1311
- HP equilibrium for a solid propellant blend
- Includes one custom reactant (CHOS-Binder) not present in thermo.lib
"""

pressures = np.array([34.473652, 17.236826, 8.618413, 3.447365, 0.344737])
weights = np.array([0.7206, 0.1858, 0.09, 0.002, 0.0016], dtype=np.float64)  # wt fractions
T_reac = np.array([298.15, 298.15, 298.15, 298.15, 298.15], dtype=np.float64)

omit_names = [
    "COOH", "C2", "C2H", "CHCO,ketyl", "C2H2,vinylidene", "CH2CO,ketene", "C2H3,vinyl",
    "CH3CO,acetyl", "C2H4O,ethylen-o", "CH3CHO,ethanal", "CH3COOH", "(HCOOH)2",
    "C2H5", "C2H6", "CH3N2CH3", "CH3OCH3", "C2H5OH", "CCN", "CNC", "C2N2",
    "C2O", "C3", "C3H3,propargyl", "C3H4,allene", "C3H4,propyne", "C3H4,cyclo-",
    "C3H5,allyl", "C3H6,propylene", "C3H6,cyclo-", "C3H6O", "C3H7,n-propyl",
    "C3H7,i-propyl", "C3H8", "C3H8O,1propanol", "C3H8O,2propanol", "C3O2",
    "C4", "C4H2", "C4H4,1,3-cyclo-", "C4H6,butadiene", "C4H6,2-butyne", "C4H6,cyclo-",
    "C4H8,1-butene", "C4H8,cis2-buten", "C4H8,tr2-butene", "C4H8,isobutene", "C4H8,cyclo-",
    "(CH3COOH)2", "C4H9,n-butyl", "C4H9,i-butyl", "C4H9,s-butyl", "C4H9,t-butyl",
    "C4H10,isobutane", "C4H10,n-butane", "C4N2", "C5", "C5H6,1,3cyclo-", "C5H8,cyclo-",
    "C5H10,1-pentene", "C5H10,cyclo-", "C5H11,pentyl", "C5H11,t-pentyl", "C5H12,n-pentane",
    "C5H12,i-pentane", "CH3C(CH3)2CH3", "C6H2", "C6H5,phenyl", "C6H5O,phenoxy",
    "C6H6", "C6H5OH,phenol", "C6H10,cyclo-", "C6H12,1-hexene", "C6H12,cyclo-",
    "C6H13,n-hexyl", "C7H7,benzyl", "C7H8", "C7H8O,cresol-mx", "C7H14,1-heptene",
    "C7H15,n-heptyl", "C7H16,n-heptane", "C8H8,styrene", "C8H10,ethylbenz",
    "C8H16,1-octene", "C8H17,n-octyl", "C8H18,isooctane", "C8H18,n-octane",
    "C9H19,n-nonyl", "C10H8,naphthale", "C10H21,n-decyl", "C12H9,o-bipheny", "C12H10,biphenyl",
    "Jet-A(g)", "HNCO", "HNO", "HNO2", "HNO3", "HCCN", "HCHO,formaldehy", "HCOOH",
    "NH", "NH2", "NH2OH", "NCN", "N2H2", "NH2NO2", "N2H4", "H2O2",
    "(HCOOH)2", "C6H6(L)", "C7H8(L)", "C8H18(L),n-octa", "Jet-A(L)", "H2O(s)", "H2O(L)",
]

# Convert h,cal = -2999.082 cal/mol to SI J/kg for Python Reactant input.
chos_binder_mw_kg_per_mol = 14.6652984484e-3
chos_binder_h_si = cea.units.cal_to_joule(-2999.082) / chos_binder_mw_kg_per_mol

reactants = [
    "NH4CLO4(I)",
    cea.Reactant(
        name="CHOS-Binder",
        formula={"C": 1.0, "H": 1.86955, "O": 0.031256, "S": 0.008415},
        molecular_weight=14.6652984484,
        enthalpy=chos_binder_h_si,
        temperature=298.15,
    ),
    "AL(cr)",
    "MgO(cr)",
    "H2O(L)",
]

reac = cea.Mixture(reactants)
prod = cea.Mixture(reactants, products_from_reactants=True, omit=omit_names)
solver = cea.EqSolver(prod, reactants=reac)
solution = cea.EqSolution(solver)

h0 = reac.calc_property(cea.ENTHALPY, weights, T_reac)
n = len(pressures)
of_ratio_out = np.zeros(n)
P_out = np.zeros(n)
T_out = np.zeros(n)
rho = np.zeros(n)
volume = np.zeros(n)
enthalpy = np.zeros(n)
energy = np.zeros(n)
gibbs = np.zeros(n)
entropy = np.zeros(n)
molecular_weight_M = np.zeros(n)
molecular_weight_MW = np.zeros(n)
gamma_s = np.zeros(n)
cp_eq = np.zeros(n)
cp_fr = np.zeros(n)
cv_eq = np.zeros(n)
cv_fr = np.zeros(n)
mole_fractions = {}
i = 0

for p in pressures:
    solver.solve(solution, cea.HP, h0 / cea.R, p, weights)

    of_ratio_out[i] = 0.0
    P_out[i] = cea.units.bar_to_atm(p)
    if solution.converged:
        T_out[i] = solution.T
        rho[i] = solution.density * 1.0e-3
        volume[i] = solution.volume * 1.0e3
        enthalpy[i] = cea.units.joule_to_cal(solution.enthalpy)
        energy[i] = cea.units.joule_to_cal(solution.energy)
        gibbs[i] = cea.units.joule_to_cal(solution.gibbs_energy)
        entropy[i] = cea.units.joule_to_cal(solution.entropy)
        molecular_weight_M[i] = solution.M
        molecular_weight_MW[i] = solution.MW
        gamma_s[i] = solution.gamma_s
        cp_eq[i] = cea.units.joule_to_cal(solution.cp_eq)
        cp_fr[i] = cea.units.joule_to_cal(solution.cp_fr)
        cv_eq[i] = cea.units.joule_to_cal(solution.cv_eq)
        cv_fr[i] = cea.units.joule_to_cal(solution.cv_fr)

    if i == 0:
        for species in solution.mole_fractions:
            mole_fractions[species] = np.array([solution.mole_fractions[species]])
    else:
        for species in mole_fractions:
            mole_fractions[species] = np.append(mole_fractions[species], solution.mole_fractions[species])
    i += 1

print("o/f             ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(of_ratio_out[i]), end=" ")
    else:
        print("{0:10.3f}".format(of_ratio_out[i]))

print("P, atm          ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(P_out[i]), end=" ")
    else:
        print("{0:10.3f}".format(P_out[i]))

print("T, K            ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(T_out[i]), end=" ")
    else:
        print("{0:10.3f}".format(T_out[i]))

print("Density, g/cc   ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3e}".format(rho[i]), end=" ")
    else:
        print("{0:10.3e}".format(rho[i]))

print("Volume, cc/g    ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3e}".format(volume[i]), end=" ")
    else:
        print("{0:10.3e}".format(volume[i]))

print("H, cal/g        ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(enthalpy[i]), end=" ")
    else:
        print("{0:10.3f}".format(enthalpy[i]))

print("U, cal/g        ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(energy[i]), end=" ")
    else:
        print("{0:10.3f}".format(energy[i]))

print("G, cal/g        ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.1f}".format(gibbs[i]), end=" ")
    else:
        print("{0:10.1f}".format(gibbs[i]))

print("S, cal/g-K      ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(entropy[i]), end=" ")
    else:
        print("{0:10.3f}".format(entropy[i]))

print("M, (1/n)        ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(molecular_weight_M[i]), end=" ")
    else:
        print("{0:10.3f}".format(molecular_weight_M[i]))

print("MW              ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.3f}".format(molecular_weight_MW[i]), end=" ")
    else:
        print("{0:10.3f}".format(molecular_weight_MW[i]))

print("Gamma_s         ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.4f}".format(gamma_s[i]), end=" ")
    else:
        print("{0:10.4f}".format(gamma_s[i]))

print("Cp_eq, cal/g-K  ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.4f}".format(cp_eq[i]), end=" ")
    else:
        print("{0:10.4f}".format(cp_eq[i]))

print("Cp_fr, cal/g-K  ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.4f}".format(cp_fr[i]), end=" ")
    else:
        print("{0:10.4f}".format(cp_fr[i]))

print("Cv_eq, cal/g-K  ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.4f}".format(cv_eq[i]), end=" ")
    else:
        print("{0:10.4f}".format(cv_eq[i]))

print("Cv_fr, cal/g-K  ", end="")
for i in range(n):
    if i < n - 1:
        print("{0:10.4f}".format(cv_fr[i]), end=" ")
    else:
        print("{0:10.4f}".format(cv_fr[i]))

print()
print("MOLE FRACTIONS")
print("")
trace_species = []
for species in mole_fractions:
    if np.any(mole_fractions[species] > 5e-6):
        print("{0:15s}".format(species), end=" ")
        for j in range(n):
            if j < n - 1:
                print("{0:10.5g}".format(mole_fractions[species][j]), end=" ")
            else:
                print("{0:10.5g}".format(mole_fractions[species][j]))
    else:
        trace_species.append(species)

print()
print("TRACE SPECIES:")
max_cols = 10
nrows = (len(trace_species) + max_cols - 1) // max_cols
for i in range(nrows):
    print(" ".join("{0:10s}".format(trace_species[j]) for j in range(i * max_cols, min((i + 1) * max_cols, len(trace_species)))))

References