rizer.cantera_ext.plasma_channel#

Transient 1D two-temperature reacting plasma channel (Cantera-based).

PlasmaChannel is the 1-D radial, transient counterpart of rizer’s 0-D rizer.plasma.constant_mass_reactor.ConstantMassPlasmaReactorOde: it solves finite-rate two-temperature plasma chemistry together with radial heat conduction, and exposes the discharge-channel diameter expansion D(t) for comparison with rizer’s engineering prediction (rizer.hybrid.engineering_model.model.diffusion) and experiment.

The heavy solve lives in the compiled rizer.cantera_ext._plasma1d; this wrapper builds the initial state, calls it, and post-processes the radial temperature history into a channel radius.

Notes

The native-rate air_plasma_Laux2000 mechanism loads in C++; the Goutier CH4 mechanism has Python-only ExtensibleRate reactions and needs C++ ports for a fully-kinetic CH4 run (see ARCHITECTURE.md). For a thermal (LTE-conduction) CH4 expansion, run with reacting=False and an injected CH4 kappa(T).

Classes#

PlasmaChannel

Transient radial two-temperature reacting plasma channel.

Module Contents#

class rizer.cantera_ext.plasma_channel.PlasmaChannel(mech: str, phase: str, R_max: float, rho: float, Y0: numpy.ndarray | list[float], grid: numpy.ndarray | list[float] | None = None, Tg_profile: tuple[numpy.ndarray, numpy.ndarray] | None = None, Te_profile: tuple[numpy.ndarray, numpy.ndarray] | None = None, Y0_profile: tuple[numpy.ndarray, numpy.ndarray] | None = None, T_amb: float = 300.0, kappa: tuple[numpy.ndarray, numpy.ndarray] | float | None = None, kappa_e: tuple[numpy.ndarray, numpy.ndarray] | float | None = None, D_species: float = 0.0, sigma: tuple[numpy.ndarray, numpy.ndarray] | float = 0.0, nu_E: float = 0.0, nu_m: float | tuple[numpy.ndarray, numpy.ndarray] = 0.0, mtcf: list[rizer.plasma.collision_frequency.MomentumTransferCollisionFrequencyModel] | None = None, Te_min: float = 300.0, Te_max: float = 100000.0, Te_n: int = 400, spitzer: bool = True, electric_field: float | tuple[numpy.ndarray, numpy.ndarray] = 0.0, reacting: bool = True, dt: float = 1e-08, n_steps: int = 100, record_every: int = 1, n_points: int = 81, integrator: str = 'newton', loglevel: int = 0, gap: float | None = None)#

Transient radial two-temperature reacting plasma channel.

Assumptions and domain of validity

This model is a 1-D radial, axisymmetric, constant-volume generalisation of rizer’s 0-D ConstantMassPlasmaReactorOde. The state per node is only (Tg, Te, Y_k) — there is no flow. Read these before interpreting results:

  • Fixed mass density rho (constant volume). There is no continuity or momentum equation, so rho is a fixed parameter, not a state variable. Consequence: pressure is not held constant — a hot core at fixed rho sits at an elevated local pressure (a constant-volume artefact), and the recorded n_e is the model’s own value at that (T, rho) state. Valid when the channel evolves by conduction/diffusion + chemistry; not valid for pressure-driven (shock / Braginskii) gas-dynamic expansion, which would require a compressible flow solve.

  • Prescribed electric field E(t); no self-consistent field. Poisson is not solved and the plasma is treated quasi-neutral, so the dielectric-relaxation timescale never enters. Valid for a given/measured applied field; not valid for self-consistent sheath/streamer dynamics (use a drift-diffusion-Poisson solver for those).

  • Two-temperature (Te != Tg): separate electron and heavy-species energy equations coupled by elastic/inelastic exchange, assuming a Maxwellian EEDF at Te.

  • Heat conduction: two-temperature Fourier — radial conduction for both Tg (kappa(Tg)) and Te (kappa_e(Te)), each active only if its conductivity table is supplied.

  • Species transport: Fickian with a single constant scalar D_species. No thermal diffusion (Soret) or Dufour, not mixture-averaged / multicomponent (one D for every species), and no ambipolar diffusion for charged species (electrons and ions share D, no field coupling). Species diffusion is frozen when D_species == 0 (the default). Species diffusion also feeds back into both energy equations: a compressibility term (from the two-temperature ideal-gas EOS) and an enthalpy-diffusion (species heat transport) term, each nonzero whenever a composition gradient coincides with species of different heat capacity – not just the mass-fraction equations (see ARCHITECTURE.md).

  • Joule heating sigma*E^2. Two mutually exclusive closures for sigma (and, for the electron energy equation, the elastic electron-heavy exchange frequency): (a) mtcf, a composition-resolved collision model evaluated live from the evolving composition every step (the same model the 0-D ConstantMassPlasmaReactorOdeCpp uses) – preferred, since sigma then tracks the reacting mixture instead of a frozen snapshot; or (b) the legacy closure: sigma from a sigma(Te) table, or – if nu_m > 0sigma = n_e e^2/(m_e nu_m) from the local n_e, plus a separately-supplied nu_E. Passing both raises ValueError.

  • Geometry/BC: cylindrical 1-D radial, axisymmetric; r=0 symmetry; r=R_max Dirichlet wall at T_amb (and ambient composition Y0 for diffusion).

  • Numerics (integrator): "newton" is adaptive implicit Backward-Euler (Cantera Sim1D damped-Newton; robust for gentle / near-equilibrium states). "bdf" is a stiff adaptive-order BDF integrator (Cantera CVODE over the method-of-lines RHS) for field-driven plasma chemistry that the Newton solver cannot converge. The stiff limit is the electron-energy equation (tiny electron heat capacity -> sub-ns Te dynamics): start gently (Te=Tg) and use a modest field, as rizer’s 0D NRP reactor does. Both air and the CH4 Goutier2025 mechanism run this way.

Parameters:
  • mech (str) – Cantera mechanism file and phase name (a PlasmaPhase with e-).

  • phase (str) – Cantera mechanism file and phase name (a PlasmaPhase with e-).

  • R_max (float) – Outer radius of the computational domain [m] (ignored if grid is given).

  • rho (float) – (Fixed) mass density [kg/m^3].

  • Y0 (array_like) – Ambient / wall mass fractions, and the initial composition where no Y0_profile is supplied (length = number of species).

  • grid (array_like or None) – Explicit, increasing radial grid [m] (grid[0] == 0). Overrides n_points/R_max; installed directly (Cantera-flame style). If None, a uniform n_points grid on [0, R_max] is built.

  • Tg_profile (tuple(array, array) or None) – (r, T) initial radial temperature profiles [m, K]. If None, uniform T_amb.

  • Te_profile (tuple(array, array) or None) – (r, T) initial radial temperature profiles [m, K]. If None, uniform T_amb.

  • Y0_profile (tuple(array, 2d-array) or None) – (r, Y) initial radial composition, Y shaped (len(r), nsp). If None, the uniform Y0 is used everywhere.

  • T_amb (float) – Wall/ambient temperature [K].

  • kappa (tuple(array, array) or float or None) – Gas / electron thermal conductivity, as (T, value) tables or a constant [W/m/K]. None disables that conduction.

  • kappa_e (tuple(array, array) or float or None) – Gas / electron thermal conductivity, as (T, value) tables or a constant [W/m/K]. None disables that conduction.

  • D_species (float) – Constant species mass diffusivity [m^2/s] (0 = frozen transport).

  • sigma (tuple(array, array) or float) – Electrical conductivity sigma(Te) [S/m] for Joule heating (legacy closure; mutually exclusive with mtcf).

  • nu_E (float) – Effective electron-heavy elastic energy-exchange frequency [1/s] (legacy closure; mutually exclusive with mtcf).

  • nu_m (float or tuple(array, array)) – Electron momentum-transfer frequency [1/s]; if > 0, sigma = n_e e^2/(m_e nu_m) from the local n_e (overrides the sigma table). Legacy closure; mutually exclusive with mtcf.

  • mtcf (list of MomentumTransferCollisionFrequencyModel or None) – Per-species momentum-transfer collision-frequency models (as built by rizer.misc.simulation.simulation.get_momentum_transfer_collision_frequencies_list()), one per species in mechanism order. When given, sigma and the electron-heavy elastic exchange are evaluated live from the evolving composition every step via the same composition-resolved collision model ConstantMassPlasmaReactorOdeCpp uses, instead of the frozen sigma/nu_m/nu_E closure below. Mutually exclusive with sigma, nu_m, and nu_E: passing a non-default value of any of those together with mtcf raises ValueError.

  • Te_min (float, float, int) – Log-spaced electron-temperature grid [K],[K],[-] the collision model (mtcf) precomputes its per-species cross-section tables on. Only used when mtcf is given.

  • Te_max (float, float, int) – Log-spaced electron-temperature grid [K],[K],[-] the collision model (mtcf) precomputes its per-species cross-section tables on. Only used when mtcf is given.

  • Te_n (float, float, int) – Log-spaced electron-temperature grid [K],[K],[-] the collision model (mtcf) precomputes its per-species cross-section tables on. Only used when mtcf is given.

  • spitzer (bool) – Apply the Spitzer e-e correction to the collision model. Only used when mtcf is given.

  • electric_field (float or tuple(array, array)) – Constant field E [V/m], or an arbitrary schedule (t, E) [s, V/m] (linearly interpolated, e.g. a pulse) supplied from Python.

  • reacting (bool) – Include finite-rate chemistry.

  • dt (float, int, int) – Output cadence / initial step [s] (dt*n_steps = total time), number of output steps, recording cadence.

  • n_steps (float, int, int) – Output cadence / initial step [s] (dt*n_steps = total time), number of output steps, recording cadence.

  • record_every (float, int, int) – Output cadence / initial step [s] (dt*n_steps = total time), number of output steps, recording cadence.

  • n_points (int) – Radial grid points (ignored if grid is given).

  • integrator (str) – "newton" (adaptive Backward-Euler) or "bdf" (stiff CVODE).

T_amb#
property t: numpy.ndarray#

Recorded times [s].

property r: numpy.ndarray#

Radial grid [m].

property species: list[str]#

Species names, in the mechanism order used by mass_fractions().

temperature_profiles() tuple[numpy.ndarray, numpy.ndarray]#

Return the recorded gas- and electron-temperature histories.

Returns:

  • Tg (numpy.ndarray) – Gas (heavy-species) temperature [K], shape (nframes, npts).

  • Te (numpy.ndarray) – Electron temperature [K], shape (nframes, npts).

electron_density() numpy.ndarray#

Electron number density n_e(r, t), shape (nframes, npts) [1/m^3].

Computed inside the solver as X_e * P / (k_B * Tmean) per node (the same n_e used in the electron energy balance), so it is the model’s own value rather than a post-hoc reconstruction.

mass_fractions() numpy.ndarray#

Species mass fractions Y_k(r, t), shape (nframes, npts, nsp) [-].

field() numpy.ndarray#

Return the applied electric field E(t) [V/m] at the recorded output times.

Reconstructs the same field the C++ solver evaluated internally at every internal (sub-dt) step: a constant, or linear interpolation of the (t, E) schedule supplied at construction (see ChannelOptions::Efield_t/Efield_v in PlasmaChannel1D.h).

Returns:

Field [V/m] at each recorded time in t.

Return type:

numpy.ndarray

total_current() numpy.ndarray#

Total current I(t) [A] through the channel cross-section.

I = E(t) \int_0^R \sigma(r) 2\pi r\,dr. \sigma follows the same fallback as the solver’s own Joule-heating term: \sigma = n_e e^2/(m_e \nu_m) (from the local n_e) when nu_m > 0 or a nu_m(Te) table is given; otherwise the sigma(Te) conductivity table supplied at construction (matching ReactorRHS::transport, so Joule heating configured purely via sigma is still diagnosable). Assumes the recorded radial profile is uniform along the inter-electrode gap.

Raises:

NotImplementedError – If the channel was built with mtcf (the composition-resolved collision model): reconstructing sigma(r, t) after the fact would need the live collision-model evaluation at each recorded state, which isn’t exposed from Python yet – reconstructing it from a frozen table here would silently misrepresent the solve.

joule_power(gap: float | None = None) numpy.ndarray#

Total Joule power dissipated in the channel cross-section.

P(t) = E(t) * gap * I(t) = U(t) * I(t) [W], i.e. the 1-D radial current profile from total_current() extruded uniformly over the inter-electrode gap (the same constant-along-the-gap assumption as total_current()).

Parameters:

gap (float or None, optional) – Inter-electrode gap [m]. If None (default), uses the gap passed at construction.

Returns:

Joule power [W] at each recorded time in t.

Return type:

numpy.ndarray

Raises:

ValueError – If no gap was supplied here or at construction.

deposited_energy(gap: float | None = None) float#

Total deposited (Joule) energy over the run.

E_dep = \int_0^{t_{end}} E(t) * gap * I(t)\,dt [J], the time integral of joule_power() (trapezoidal rule over the recorded output times).

Parameters:

gap (float or None, optional) – Inter-electrode gap [m]. If None (default), uses the gap passed at construction.

Returns:

Total deposited energy [J].

Return type:

float

channel_radius(method: str = 'half_max') numpy.ndarray#

Channel radius R(t) [m] from the gas-temperature profile.

Parameters:

method ({"half_max", "sigma_weighted"}) –

  • half_max: radius where Tg - T_amb drops to half its axis value.

  • sigma_weighted: sqrt(<r^2>) weighted by Tg - T_amb.

Returns:

Channel radius [m] at each recorded time in t.

Return type:

numpy.ndarray

channel_diameter(method: str = 'half_max') numpy.ndarray#

Channel diameter D(t) = 2 R(t) [m].

See channel_radius() for the method options; this is the quantity directly comparable to rizer’s engineering diffusion model (rizer.hybrid.engineering_model.model.diffusion) and experiment.

Parameters:

method ({"half_max", "sigma_weighted"}) – Passed through to channel_radius().

Returns:

Channel diameter [m] at each recorded time in t.

Return type:

numpy.ndarray