38 , m_solution(loadMechanism(cfg.mech, cfg.
phase))
39 , m_n_species(m_solution->thermo()->
nSpecies())
40 , m_electron_index(m_solution->thermo()->speciesIndex(
"e-"))
41 , m_outer_radius(cfg.R_max)
43 , m_electric_field(cfg.electric_field)
44 , m_species_diffusivity(cfg.D_species)
45 , m_reacting(cfg.reacting)
46 , m_ambient_temperature(cfg.T_amb)
48 , m_kappa(
PropertyTable::fromTableOrScalar(cfg.kappa_T, cfg.kappa_v))
49 , m_kappa_e(
PropertyTable::fromTableOrScalar(cfg.kappa_e_T, cfg.kappa_e_v))
50 , m_initTg(
PropertyTable::fromTableOrScalar(cfg.initTg_r, cfg.initTg_v))
51 , m_initTe(
PropertyTable::fromTableOrScalar(cfg.initTe_r, cfg.initTe_v))
54 const bool explicit_grid = cfg.
grid.size() >= 2;
55 const std::size_t n_points = explicit_grid ? cfg.
grid.size() : cfg.
n_points;
57 m_outer_radius = cfg.
grid.back();
59 throw std::invalid_argument(
"PlasmaChannel1D: need R_max>0, n_points>=1 "
60 "(or supply an explicit grid of >=2 points).");
63 throw std::invalid_argument(
"PlasmaChannel1D: mechanism has no 'e-' species.");
65 if (m_Y0.size() != m_n_species) {
66 throw std::invalid_argument(
"PlasmaChannel1D: Y0 length != nSpecies.");
87 const bool have_collision = !cfg.
specs.empty();
88 const bool have_legacy_closure = !cfg.
sigma_T.empty() || !cfg.
sigma_v.empty()
90 || cfg.
nu_m > 0.0 || cfg.
nu_E != 0.0;
91 if (have_collision && have_legacy_closure) {
92 throw std::invalid_argument(
93 "PlasmaChannel1D: cfg.specs (composition-resolved collision model) "
94 "cannot be combined with sigma_T/sigma_v, nu_m, nu_m_T/nu_m_v, or "
95 "nu_E (the legacy closure) -- pick one.");
103 if (have_collision) {
107 std::vector<CollisionModel::SpeciesSpec> specs = cfg.
specs;
108 specs.resize(m_n_species);
109 const auto& molecular_weights = m_solution->thermo()->molecularWeights();
110 for (std::size_t k = 0; k < m_n_species; k++) {
112 specs[k].molar_mass = molecular_weights[k] * 1.0e-3;
113 specs[k].is_electron = (k == m_electron_index);
123 std::make_unique<ReactorRHS>(m_solution, std::move(reactor_config));
128 const std::size_t n_radii = cfg.initY_r.size();
129 m_initY.reserve(m_n_species);
130 for (std::size_t k = 0; k < m_n_species; k++) {
131 std::vector<double> column(n_radii);
132 for (std::size_t i = 0; i < n_radii; i++) {
133 column[i] = cfg.initY_v[i * m_n_species + k];
135 m_initY.push_back(PropertyTable::fromTableOrScalar(cfg.initY_r, column));
139 !m_kappa.empty() || !m_kappa_e.empty() || m_species_diffusivity > 0.0;
144 }
else if (cfg.n_points >= 2) {
145 setupUniformGrid(cfg.n_points, cfg.R_max, 0.0);
149 setComponentName(0,
"Tg");
150 setComponentName(1,
"Te");
151 for (std::size_t k = 0; k < m_n_species; k++) {
152 setComponentName(2 + k, m_solution->thermo()->speciesName(k));
161 setBounds(0, 200.0, 1.0e6);
162 setBounds(1, 200.0, 1.0e6);
163 for (std::size_t k = 0; k < m_n_species; k++) {
164 setBounds(2 + k, -1.0e-12, 2.0);
170 setSteadyTolerances(1.0e-4, 1.0e-10);
171 setTransientTolerances(1.0e-4, 1.0e-12);
281 auto x = xg.subspan(
loc(),
size());
282 auto rsd = rg.subspan(
loc(),
size());
283 auto diag = maskg.subspan(
loc(),
size());
285 std::size_t jmin, jmax;
292 std::size_t local_point = (jg == 0) ? 0 : jg -
firstPoint();
293 jmin = std::max<std::size_t>(local_point, 1) - 1;
294 jmax = std::min(local_point + 1,
m_points - 1);
318 auto fvDiv = [&](std::size_t component, std::size_t node,
double ghost,
319 auto coef) ->
double {
320 double value_here = x[
index(component, node)];
321 double radius_east, spacing_east, value_east;
323 value_east = x[
index(component, node + 1)];
324 radius_east = 0.5 * (
z(node) +
z(node + 1));
325 spacing_east =
z(node + 1) -
z(node);
328 double last_spacing =
329 (
m_points >= 2) ?
z(node) -
z(node - 1) : m_outer_radius;
331 radius_east =
z(node) + 0.5 * last_spacing;
332 spacing_east = last_spacing;
334 double coef_east = 0.5 * (coef(value_here) + coef(value_east));
336 value_east, spacing_east);
337 double flux_west = 0.0, radius_west = 0.0;
339 double value_west = x[
index(component, node - 1)];
340 radius_west = 0.5 * (
z(node - 1) +
z(node));
341 double spacing_west =
z(node) -
z(node - 1);
342 double coef_west = 0.5 * (coef(value_west) + coef(value_here));
344 value_here, spacing_west);
348 return (flux_east - flux_west) / volume;
368 auto centeredGradient = [&](std::size_t component, std::size_t node,
369 double ghost) ->
double {
370 if (node == 0)
return 0.0;
371 double value_here = x[
index(component, node)];
372 double value_west = x[
index(component, node - 1)];
373 double spacing_west =
z(node) -
z(node - 1);
374 double gradient_west = (value_here - value_west) / spacing_west;
376 double gradient_east;
378 double value_east = x[
index(component, node + 1)];
379 double spacing_east =
z(node + 1) -
z(node);
380 gradient_east = (value_east - value_here) / spacing_east;
382 double last_spacing =
383 (
m_points >= 2) ?
z(node) -
z(node - 1) : m_outer_radius;
384 gradient_east = (ghost - value_here) / last_spacing;
386 return 0.5 * (gradient_west + gradient_east);
391 const std::vector<double>& molecular_weights = m_reactor_rhs->molecularWeights();
393 std::vector<double> dYdt(m_n_species);
394 std::vector<double> raw_Y_divergence(m_n_species);
396 for (std::size_t j = jmin; j <= jmax; j++) {
397 double Tg = x[
index(0, j)];
398 double Te = x[
index(1, j)];
399 const double* Y = &x[
index(2, j)];
410 double dTg = 0.0, dTe = 0.0, lhsTg = 1.0, lhsTe = 1.0;
411 std::fill(dYdt.begin(), dYdt.end(), 0.0);
412 m_reactor_rhs->rates(Tg, Te, Y, m_rho, m_electric_field, dTg, dTe,
413 dYdt.data(), lhsTg, lhsTe);
415 if (m_has_transport) {
416 if (!m_kappa.empty()) {
417 dTg += fvDiv(0, j, m_ambient_temperature,
418 [&](
double T) {
return m_kappa.eval(T); }) / lhsTg;
420 if (!m_kappa_e.empty()) {
421 dTe += fvDiv(1, j, m_ambient_temperature,
422 [&](
double T) {
return m_kappa_e.eval(T); }) / lhsTe;
424 if (m_species_diffusivity > 0.0) {
435 for (std::size_t k = 0; k < m_n_species; k++) {
436 raw_Y_divergence[k] =
437 fvDiv(2 + k, j, m_Y0[k], [](
double) {
return 1.0; });
446 for (std::size_t k = 0; k < m_n_species; k++) {
447 dYdt[k] += m_species_diffusivity * raw_Y_divergence[k];
457 const double We = molecular_weights[m_electron_index];
458 dTe += m_rho * m_species_diffusivity * (
GasConstant * Te / We)
459 * raw_Y_divergence[m_electron_index] / lhsTe;
461 double gas_compressibility_sum = 0.0;
462 for (std::size_t k = 0; k < m_n_species; k++) {
463 if (k == m_electron_index)
continue;
464 gas_compressibility_sum +=
465 raw_Y_divergence[k] / molecular_weights[k];
467 dTg += m_rho * m_species_diffusivity *
GasConstant * Tg
468 * gas_compressibility_sum / lhsTg;
479 const std::vector<double>& species_heat_capacity =
480 m_reactor_rhs->speciesCp();
481 const double Tg_gradient =
482 centeredGradient(0, j, m_ambient_temperature);
483 const double Te_gradient =
484 centeredGradient(1, j, m_ambient_temperature);
485 const double Ye_gradient = centeredGradient(
486 2 + m_electron_index, j, m_Y0[m_electron_index]);
487 dTe += m_rho * m_species_diffusivity
488 * species_heat_capacity[m_electron_index] * Ye_gradient
489 * Te_gradient / lhsTe;
491 double gas_enthalpy_diffusion_sum = 0.0;
492 for (std::size_t k = 0; k < m_n_species; k++) {
493 if (k == m_electron_index)
continue;
494 double Yk_gradient = centeredGradient(2 + k, j, m_Y0[k]);
495 gas_enthalpy_diffusion_sum +=
496 species_heat_capacity[k] * Yk_gradient;
498 dTg += m_rho * m_species_diffusivity * Tg_gradient
499 * gas_enthalpy_diffusion_sum / lhsTg;
513 diag[
index(0, j)] = 1;
514 diag[
index(1, j)] = 1;
515 for (std::size_t k = 0; k < m_n_species; k++) {
516 rsd[
index(2 + k, j)] = dYdt[k] - rdt * (Y[k] -
prevSoln(2 + k, j));
517 diag[
index(2 + k, j)] = 1;
vector< double > values(const string &component) const
Domain1D(size_t nv=1, size_t points=1, double time=0.0)