Computing and comparing the properties of an \(CH_4\) plasma.#

Compute the properties of an \(CH_4\) plasma using minplascalc. Data are compared with reference data from the literature.

Properties calculated include:

  • Density

  • Enthalpy

  • Heat capacity

  • Viscosity

  • Thermal conductivity

  • Electrical conductivity

Species data is loaded from ./data/minplascalc/species folder. These data have been generated using the generate_minplascalc_data.py script.

Reference data are [Wu2016] and [Niu2016].

Import the required libraries.#

import cantera as ct
import matplotlib.pyplot as plt
import minplascalc as mpc
import numpy as np
from adjustText import adjust_text

import rizer.misc.units as u
from rizer.io.thermo_transport_data_reader import ThermoTransportDataReader
from rizer.misc.ct_utils import load_goutier2025_mechanism
from rizer.misc.plt_utils import (
    get_species_color,
    get_species_in_latex,
    get_text,
    set_mpl_style,
)
from rizer.misc.utils import get_path_to_data

set_mpl_style(nb_columns=1)

Create mixture object for the species we’re interested in.#

Next, we create a minplascalc LTE mixture object. Here we use a helper function in minplascalc which creates the object directly from a list of the species names.

species_and_concentrations = {
    "CH4": 1,
    "CH3": 0,
    "CH2": 0,
    "CH": 0,
    "C": 0,
    "C+": 0,
    "C2H6": 0,
    "C2H4": 0,
    "C2H2": 0,
    "C2H": 0,
    "C2": 0,
    "H2": 0,
    "H": 0,
    "H+": 0,
}

SPECIES_PATH = get_path_to_data("minplascalc", "species")
species_list = [
    mpc.species.from_file(SPECIES_PATH / f"{species_name}.json")
    for species_name in species_and_concentrations
]

methane_mixture = mpc.mixture.LTE(
    species=species_list,
    x0=species_and_concentrations.values(),
    T=300,
    P=101325,
    gfe_initial_particles=1e23,
    gfe_rtol=1e-10,
    gfe_max_iter=1000,
)

Set a range of temperatures to calculate the equilibrium compositions at.#

Next, set a range of temperatures to calculate the equilibrium compositions at - in this case we’re going from 300 to 6000 K in 100 K steps. Also initialise a list to store the property values at each temperature.

temperatures = np.arange(300, 6000, 100)
species_names = [sp.name for sp in methane_mixture.species]
ni_list: list[np.ndarray] = []
density: list[float] = []
h: list[float] = []
cp: list[float] = []
viscosity: list[float] = []
thermal_conductivity: list[float] = []
electrical_conductivity: list[float] = []

Perform the composition calculations.#

Now we can perform the property calculations.

Note that execution of this calculation is fairly compute intensive and the following code snippet may take several minute to complete.

for T in temperatures:
    print(f"Calculating properties at T = {T} K...")
    methane_mixture.T = T
    # give back the composition of the species in the mixture?
    ni_list.append(methane_mixture.calculate_composition())

    density.append(methane_mixture.calculate_density())
    h.append(methane_mixture.calculate_enthalpy())
    cp.append(methane_mixture.calculate_heat_capacity())
    viscosity.append(methane_mixture.calculate_viscosity())
    thermal_conductivity.append(methane_mixture.calculate_thermal_conductivity())
    electrical_conductivity.append(methane_mixture.calculate_electrical_conductivity())

ni = np.array(ni_list).transpose()

print("Done!")

Load reference data.#

# Load reference data from Wu2016 and Niu2016 for comparison.
data_ref = ThermoTransportDataReader(
    gas_name="CH4", pressure_atm=1, source="Wu2016_Niu2016", ignore_missing_values=True
)
temperatures_ref = data_ref.temperature
enthalpy_ref = data_ref.enthalpy
thermal_conductivity_ref = data_ref.thermal_conductivity

Plot the results.#

Now we can visualise the properties by plotting them against temperature, to see how they vary.

# Plot enthalpy vs. temperature.
fig, ax = plt.subplots()
ax.plot(temperatures, h, "k", label="minplascalc")
ax.plot(temperatures_ref, enthalpy_ref, "k--", label="Wu2016")
ax.legend()
ax.set_title(r"$\mathregular{CH_4}$ plasma enthalpy at 1 atm")
ax.set_xlabel("T [K]")
ax.set_ylabel(r"$\mathregular{H [J/kg]}$")
ax.set_xlim(300, 6000)
ax.set_ylim(-0.1e8, 2e8)
plt.show()

# Plot thermal conductivity vs. temperature.
fig, ax = plt.subplots()
ax.plot(temperatures, thermal_conductivity, "k", label="minplascalc")
ax.plot(temperatures_ref, thermal_conductivity_ref, "k--", label="Wu2016")
ax.legend()
ax.set_title(r"$\mathregular{CH_4}$ plasma thermal conductivity at 1 atm")
ax.set_xlabel("T [K]")
ax.set_ylabel(r"$\mathregular{kappa [W/(m.K)]}$")
ax.set_xlim(300, 6000)

plt.show()

Compute the equilibrium composition with Cantera.#

gas = load_goutier2025_mechanism("gas")
states = ct.SolutionArray(gas, shape=(len(temperatures),))
states.TPX = temperatures, ct.one_atm, {"CH4": 1}
states.equilibrate("TP")
xi_cantera = states.X.transpose()
species_names_cantera = states.species_names

# ni_cantera = xi_cantera * n_total
# P = n_total * k_b * T
n_total = ct.one_atm / (u.k_b * temperatures)
ni_cantera = xi_cantera * n_total

Plot the species composition.#

fig, ax = plt.subplots()
ax.set_title(r"$\mathregular{CH_4}$ LTE plasma composition with temperature at 1 atm")
ax.set_xlabel("T [K]")
ax.set_ylabel(r"$\mathregular{n_i [m^{-3}]}$")
# ax.set_ylim(1e15, 5e25)
ax.set_ylim(1e-6, 100)
ax.set_xlim(300, 6000)


texts = []
for species_density, species_name in zip(ni, species_names):
    if species_name not in species_names_cantera:
        if species_name == "e":
            species_name = "e-"
        else:
            continue

    n_tot = ct.one_atm / (u.k_b * temperatures) / 100

    ax.semilogy(
        temperatures, species_density / n_tot, color=get_species_color(species_name)
    )
    texts.append(
        get_text(
            x=temperatures[np.argmax(species_density)],
            y=np.max(species_density / n_tot),
            text=get_species_in_latex(species_name),
            ax=ax,
            color=get_species_color(species_name),
        )
    )
    idx_ct = species_names_cantera.index(species_name)
    ax.scatter(
        temperatures[::5],
        ni_cantera[idx_ct, ::5] / n_tot[::5],
        color=get_species_color(species_name),
        marker="x",
        s=100,
    )

adjust_text(texts, avoid_self=False)
plt.show()