37 , m_solution(loadMechanism(cfg.mech, cfg.
phase))
38 , m_n_species(m_solution->thermo()->
nSpecies())
39 , m_electron_index(m_solution->thermo()->speciesIndex(
"e-"))
40 , m_outer_radius(cfg.R_max)
42 , m_electric_field(cfg.electric_field)
43 , m_species_diffusivity(cfg.D_species)
44 , m_reacting(cfg.reacting)
45 , m_ambient_temperature(cfg.T_amb)
47 , m_kappa(
PropertyTable::fromTableOrScalar(cfg.kappa_T, cfg.kappa_v))
48 , m_kappa_e(
PropertyTable::fromTableOrScalar(cfg.kappa_e_T, cfg.kappa_e_v))
49 , m_initTg(
PropertyTable::fromTableOrScalar(cfg.initTg_r, cfg.initTg_v))
50 , m_initTe(
PropertyTable::fromTableOrScalar(cfg.initTe_r, cfg.initTe_v))
53 const bool explicit_grid = cfg.
grid.size() >= 2;
54 const std::size_t n_points = explicit_grid ? cfg.
grid.size() : cfg.
n_points;
56 m_outer_radius = cfg.
grid.back();
58 throw std::invalid_argument(
"PlasmaChannel1D: need R_max>0, n_points>=1 "
59 "(or supply an explicit grid of >=2 points).");
62 throw std::invalid_argument(
"PlasmaChannel1D: mechanism has no 'e-' species.");
64 if (m_Y0.size() != m_n_species) {
65 throw std::invalid_argument(
"PlasmaChannel1D: Y0 length != nSpecies.");
86 const bool have_collision = !cfg.
specs.empty();
87 const bool have_legacy_closure = !cfg.
sigma_T.empty() || !cfg.
sigma_v.empty()
89 || cfg.
nu_m > 0.0 || cfg.
nu_E != 0.0;
90 if (have_collision && have_legacy_closure) {
91 throw std::invalid_argument(
92 "PlasmaChannel1D: cfg.specs (composition-resolved collision model) "
93 "cannot be combined with sigma_T/sigma_v, nu_m, nu_m_T/nu_m_v, or "
94 "nu_E (the legacy closure) -- pick one.");
101 if (have_collision) {
105 std::vector<CollisionModel::SpeciesSpec> specs = cfg.
specs;
106 specs.resize(m_n_species);
107 const auto& molecular_weights = m_solution->thermo()->molecularWeights();
108 for (std::size_t k = 0; k < m_n_species; k++) {
110 specs[k].molar_mass = molecular_weights[k] * 1.0e-3;
111 specs[k].is_electron = (k == m_electron_index);
121 std::make_unique<ReactorRHS>(m_solution, std::move(reactor_config));
126 const std::size_t n_radii = cfg.initY_r.size();
127 m_initY.reserve(m_n_species);
128 for (std::size_t k = 0; k < m_n_species; k++) {
129 std::vector<double> column(n_radii);
130 for (std::size_t i = 0; i < n_radii; i++) {
131 column[i] = cfg.initY_v[i * m_n_species + k];
133 m_initY.push_back(PropertyTable::fromTableOrScalar(cfg.initY_r, column));
137 !m_kappa.empty() || !m_kappa_e.empty() || m_species_diffusivity > 0.0;
142 }
else if (cfg.n_points >= 2) {
143 setupUniformGrid(cfg.n_points, cfg.R_max, 0.0);
147 setComponentName(0,
"Tg");
148 setComponentName(1,
"Te");
149 for (std::size_t k = 0; k < m_n_species; k++) {
150 setComponentName(2 + k, m_solution->thermo()->speciesName(k));
159 setBounds(0, 200.0, 1.0e6);
160 setBounds(1, 200.0, 1.0e6);
161 for (std::size_t k = 0; k < m_n_species; k++) {
162 setBounds(2 + k, -1.0e-12, 2.0);
168 setSteadyTolerances(1.0e-4, 1.0e-10);
169 setTransientTolerances(1.0e-4, 1.0e-12);
279 auto x = xg.subspan(
loc(),
size());
280 auto rsd = rg.subspan(
loc(),
size());
281 auto diag = maskg.subspan(
loc(),
size());
283 std::size_t jmin, jmax;
290 std::size_t local_point = (jg == 0) ? 0 : jg -
firstPoint();
291 jmin = std::max<std::size_t>(local_point, 1) - 1;
292 jmax = std::min(local_point + 1,
m_points - 1);
316 auto fvDiv = [&](std::size_t component, std::size_t node,
double ghost,
317 auto coef) ->
double {
318 double value_here = x[
index(component, node)];
319 double radius_east, spacing_east, value_east;
321 value_east = x[
index(component, node + 1)];
322 radius_east = 0.5 * (
z(node) +
z(node + 1));
323 spacing_east =
z(node + 1) -
z(node);
326 double last_spacing =
327 (
m_points >= 2) ?
z(node) -
z(node - 1) : m_outer_radius;
329 radius_east =
z(node) + 0.5 * last_spacing;
330 spacing_east = last_spacing;
332 double coef_east = 0.5 * (coef(value_here) + coef(value_east));
334 radius_east * coef_east * (value_east - value_here) / spacing_east;
335 double flux_west = 0.0, radius_west = 0.0;
337 double value_west = x[
index(component, node - 1)];
338 radius_west = 0.5 * (
z(node - 1) +
z(node));
339 double spacing_west =
z(node) -
z(node - 1);
340 double coef_west = 0.5 * (coef(value_west) + coef(value_here));
342 radius_west * coef_west * (value_here - value_west) / spacing_west;
346 0.5 * (radius_east * radius_east - radius_west * radius_west);
347 return (flux_east - flux_west) / volume;
367 auto centeredGradient = [&](std::size_t component, std::size_t node,
368 double ghost) ->
double {
369 if (node == 0)
return 0.0;
370 double value_here = x[
index(component, node)];
371 double value_west = x[
index(component, node - 1)];
372 double spacing_west =
z(node) -
z(node - 1);
373 double gradient_west = (value_here - value_west) / spacing_west;
375 double gradient_east;
377 double value_east = x[
index(component, node + 1)];
378 double spacing_east =
z(node + 1) -
z(node);
379 gradient_east = (value_east - value_here) / spacing_east;
381 double last_spacing =
382 (
m_points >= 2) ?
z(node) -
z(node - 1) : m_outer_radius;
383 gradient_east = (ghost - value_here) / last_spacing;
385 return 0.5 * (gradient_west + gradient_east);
390 const std::vector<double>& molecular_weights = m_reactor_rhs->molecularWeights();
392 std::vector<double> dYdt(m_n_species);
393 std::vector<double> raw_Y_divergence(m_n_species);
395 for (std::size_t j = jmin; j <= jmax; j++) {
396 double Tg = x[
index(0, j)];
397 double Te = x[
index(1, j)];
398 const double* Y = &x[
index(2, j)];
409 double dTg = 0.0, dTe = 0.0, lhsTg = 1.0, lhsTe = 1.0;
410 std::fill(dYdt.begin(), dYdt.end(), 0.0);
411 m_reactor_rhs->rates(Tg, Te, Y, m_rho, m_electric_field, dTg, dTe,
412 dYdt.data(), lhsTg, lhsTe);
414 if (m_has_transport) {
415 if (!m_kappa.empty()) {
416 dTg += fvDiv(0, j, m_ambient_temperature,
417 [&](
double T) {
return m_kappa.eval(T); }) / lhsTg;
419 if (!m_kappa_e.empty()) {
420 dTe += fvDiv(1, j, m_ambient_temperature,
421 [&](
double T) {
return m_kappa_e.eval(T); }) / lhsTe;
423 if (m_species_diffusivity > 0.0) {
434 for (std::size_t k = 0; k < m_n_species; k++) {
435 raw_Y_divergence[k] =
436 fvDiv(2 + k, j, m_Y0[k], [](
double) {
return 1.0; });
445 for (std::size_t k = 0; k < m_n_species; k++) {
446 dYdt[k] += m_species_diffusivity * raw_Y_divergence[k];
456 const double We = molecular_weights[m_electron_index];
457 dTe += m_rho * m_species_diffusivity * (
GasConstant * Te / We)
458 * raw_Y_divergence[m_electron_index] / lhsTe;
460 double gas_compressibility_sum = 0.0;
461 for (std::size_t k = 0; k < m_n_species; k++) {
462 if (k == m_electron_index)
continue;
463 gas_compressibility_sum +=
464 raw_Y_divergence[k] / molecular_weights[k];
466 dTg += m_rho * m_species_diffusivity *
GasConstant * Tg
467 * gas_compressibility_sum / lhsTg;
478 const std::vector<double>& species_heat_capacity =
479 m_reactor_rhs->speciesCp();
480 const double Tg_gradient =
481 centeredGradient(0, j, m_ambient_temperature);
482 const double Te_gradient =
483 centeredGradient(1, j, m_ambient_temperature);
484 const double Ye_gradient = centeredGradient(
485 2 + m_electron_index, j, m_Y0[m_electron_index]);
486 dTe += m_rho * m_species_diffusivity
487 * species_heat_capacity[m_electron_index] * Ye_gradient
488 * Te_gradient / lhsTe;
490 double gas_enthalpy_diffusion_sum = 0.0;
491 for (std::size_t k = 0; k < m_n_species; k++) {
492 if (k == m_electron_index)
continue;
493 double Yk_gradient = centeredGradient(2 + k, j, m_Y0[k]);
494 gas_enthalpy_diffusion_sum +=
495 species_heat_capacity[k] * Yk_gradient;
497 dTg += m_rho * m_species_diffusivity * Tg_gradient
498 * gas_enthalpy_diffusion_sum / lhsTg;
512 diag[
index(0, j)] = 1;
513 diag[
index(1, j)] = 1;
514 for (std::size_t k = 0; k < m_n_species; k++) {
515 rsd[
index(2 + k, j)] = dYdt[k] - rdt * (Y[k] -
prevSoln(2 + k, j));
516 diag[
index(2 + k, j)] = 1;
vector< double > values(const string &component) const
virtual void resize(size_t nv, size_t np)
double prevSoln(size_t n, size_t j) const
Domain1D(size_t nv=1, size_t points=1, double time=0.0)
size_t index(size_t n, size_t j) const
virtual size_t loc(size_t j=0) const