45 class ChannelRHS :
public Cantera::FuncEval
64 ChannelRHS(PlasmaChannel1D *channel, Cantera::OneDim *sim,
65 const std::vector<double> &state, std::size_t n_vars,
66 std::size_t channel_offset, std::size_t n_points,
67 std::function<
double(
double)> electricFieldAt)
68 : m_channel(channel), m_sim(sim), m_state(&state), m_n_vars(n_vars),
69 m_channel_offset(channel_offset), m_n_points(n_points),
70 m_field(std::move(electricFieldAt)) {}
72 std::size_t neq()
const override {
return m_state->size(); }
80 void getState(Cantera::span<double> y)
override
82 std::copy(m_state->begin(), m_state->end(), y.begin());
91 Cantera::span<const double> y,
92 Cantera::span<double> ydot,
93 Cantera::span<const double> )
override
95 m_channel->setElectricField(m_field(t));
115 void getConstraints(Cantera::span<double> constraints)
override
117 std::fill(constraints.begin(), constraints.end(), 0.0);
118 for (std::size_t j = 0; j < m_n_points; j++)
120 for (std::size_t comp = 2; comp < m_n_vars; comp++)
123 constraints[m_channel_offset + m_n_vars * j + comp] = 1.0;
130 PlasmaChannel1D *m_channel;
132 Cantera::OneDim *m_sim;
135 const std::vector<double> *m_state;
137 std::size_t m_n_vars;
139 std::size_t m_channel_offset;
141 std::size_t m_n_points;
143 std::function<double(
double)> m_field;
150 class NodeRHS :
public Cantera::FuncEval
162 NodeRHS(PlasmaChannel1D *channel,
const std::vector<double> &state,
163 std::size_t n_vars, std::size_t offset, std::size_t node,
164 std::function<
double(
double)> electricFieldAt)
165 : m_channel(channel), m_state(&state), m_n_vars(n_vars),
166 m_offset(
offset), m_node(node), m_field(std::move(electricFieldAt)) {}
168 std::size_t neq()
const override {
return m_n_vars; }
170 void getState(Cantera::span<double> y)
override
172 std::copy(m_state->begin() +
static_cast<std::ptrdiff_t
>(m_offset),
173 m_state->begin() +
static_cast<std::ptrdiff_t
>(m_offset + m_n_vars),
177 void eval(
double t, Cantera::span<const double> y,
178 Cantera::span<double> ydot,
179 Cantera::span<const double> )
override
181 m_channel->nodeRates(m_node, y[0], y[1], &y[2], m_field(t),
189 void getConstraints(Cantera::span<double> constraints)
override
191 std::fill(constraints.begin(), constraints.end(), 0.0);
192 for (std::size_t comp = 2; comp < m_n_vars; comp++)
194 constraints[comp] = 1.0;
199 PlasmaChannel1D *m_channel;
200 const std::vector<double> *m_state;
201 std::size_t m_n_vars;
202 std::size_t m_offset;
204 std::function<double(
double)> m_field;
220 const std::size_t n_points = channel.nPoints();
221 std::vector<double> r_face(n_points + 1);
222 for (std::size_t j = 0; j <= n_points; j++)
224 r_face[j] = channel.faceRadius(j);
231 std::vector<double> cellVolumes(
const std::vector<double> &r_face)
233 std::vector<double> V_cell(r_face.size() - 1);
234 for (std::size_t j = 0; j < V_cell.size(); j++)
260 const std::size_t n_points = channel.nPoints();
262 cfg.
r_node.resize(n_points);
263 for (std::size_t j = 0; j < n_points; j++)
264 cfg.r_node[j] = channel.z(j);
265 cfg.r_face = faceRadii(channel);
266 cfg.V_cell = cellVolumes(cfg.r_face);
268 cfg.cylindrical_source =
true;
269 cfg.outer_bc_mode = channel.outerBcMode();
270 cfg.p_amb = opts.cfg.p_amb;
271 cfg.T_amb = opts.cfg.T_amb;
272 cfg.Y_amb = channel.ambientMassFractions();
273 cfg.cfl = opts.cfg.cfl;
274 if (opts.cfg.hydro_substep > 0.0)
276 const double count = 0.5 * opts.dt / opts.cfg.hydro_substep;
277 const double whole = std::round(count);
278 if (whole < 1.0 || std::abs(count - whole) > 1.0e-9 * count)
280 throw std::invalid_argument(
281 "solveChannelTransient: the half-step dt/2 = " +
283 " s is not an integer multiple of hydro_substep = " +
284 significant(opts.cfg.hydro_substep) +
" s; it comes to " +
287 cfg.n_subcycles =
static_cast<std::size_t
>(whole);
288 cfg.refuse_above_cfl =
true;
290 cfg.electron_index =
static_cast<int>(channel.electronIndex());
291 cfg.thermo = &thermo;
300 std::vector<double> &x, std::size_t channel_offset,
301 std::size_t n_vars,
double t0,
double dt,
304 const std::size_t n_points = channel.nPoints();
305 const std::size_t n_species = channel.nSpecies();
309 state.rho = channel.density();
310 state.u_r = channel.velocity();
311 state.Tg.resize(n_points);
312 state.Te.resize(n_points);
313 state.Y.resize(n_points * n_species);
314 for (std::size_t j = 0; j < n_points; j++)
316 const std::size_t base = channel_offset + n_vars * j;
317 state.Tg[j] = x[base + 0];
318 state.Te[j] = x[base + 1];
319 for (std::size_t k = 0; k < n_species; k++)
321 state.Y[j * n_species + k] = x[base + 2 + k];
330 history.boundary_mass_out += result.boundary_mass_out;
331 for (std::size_t k = 0; k < n_species; k++)
333 history.boundary_species_mass_out[k] +=
334 result.boundary_species_mass_out[k];
336 history.boundary_total_energy_out += result.boundary_total_energy_out;
337 history.boundary_electron_energy_out += result.boundary_electron_energy_out;
338 history.pressure_work_electron += result.pressure_work_electron;
340 channel.setHydroState(state.rho, state.u_r);
341 for (std::size_t j = 0; j < n_points; j++)
343 const std::size_t base = channel_offset + n_vars * j;
344 x[base + 0] = state.Tg[j];
345 x[base + 1] = state.Te[j];
346 for (std::size_t k = 0; k < n_species; k++)
348 x[base + 2 + k] = state.Y[j * n_species + k];
370 std::string decodeSolverFailure(
const std::string &message,
371 std::size_t failed_node,
373 std::size_t channel_offset,
375 std::size_t n_points)
379 double t_fail = -1.0;
381 std::size_t t_pos = message.find(
"At t = ");
382 if (t_pos != std::string::npos)
386 t_fail = std::stod(message.substr(t_pos + 7));
392 std::ostringstream out;
393 out <<
"PlasmaChannel1D BDF solve failed to converge";
395 out <<
" near t=" << t_fail <<
" s";
396 out <<
".\nThe implicit corrector could not resolve the local "
397 "dynamics and the step size hit its floor. The dominant "
398 "error contributions are:\n";
399 std::istringstream in(message);
401 bool in_error_block =
false, found_any =
false;
402 while (std::getline(in, line))
404 if (line.find(
"Components with largest") != std::string::npos)
406 in_error_block =
true;
411 std::size_t colon = line.find(
':');
412 if (colon == std::string::npos)
418 global_index = std::stol(line.substr(0, colon));
419 error_value = std::stod(line.substr(colon + 1));
430 ?
static_cast<long>(channel_offset)
432 long local_index = global_index - base;
436 ?
static_cast<std::size_t
>(local_index) / n_vars
438 std::size_t comp =
static_cast<std::size_t
>(local_index) % n_vars;
441 out <<
" node " << j <<
" (r=" << channel.z(j) <<
" m) "
442 << channel.componentName(comp)
443 <<
" (weighted error " << error_value <<
")\n";
448 out << message <<
"\n";
456 const std::string caught_marker =
"Exceptions caught during RHS evaluation";
457 const std::size_t caught = message.find(caught_marker);
458 if (caught != std::string::npos)
460 const std::size_t table =
461 message.find(
"Components with largest", caught);
462 out << message.substr(caught, table == std::string::npos
468 out <<
"Remedies: reduce dt, loosen tolerances, soften the initial "
469 "profiles or the field ramp, or verify the initial state is "
470 "physically consistent at the flagged nodes.";
482 using std::make_shared;
497 auto channel = make_shared<PlasmaChannel1D>(opts.
cfg);
499 auto left = make_shared<Cantera::Empty1D>();
500 auto right = make_shared<Cantera::Empty1D>();
501 std::vector<std::shared_ptr<Cantera::Domain1D>> domains{left, channel, right};
505 const std::size_t n_species = channel->nSpecies();
506 const std::size_t n_points = channel->nPoints();
507 const std::size_t n_vars = 2 + n_species;
508 const std::size_t channel_offset =
516 std::vector<double> x(sim.
size(), 0.0);
522 for (std::size_t comp = 0; comp < n_vars; comp++)
524 std::vector<double> vals = channel->values(channel->componentName(comp));
525 for (std::size_t j = 0; j < n_points; j++)
527 x[channel_offset + n_vars * j + comp] = vals[j];
535 history.
npts = n_points;
536 history.
nsp = n_species;
538 for (std::size_t j = 0; j < n_points; j++)
539 history.
r.push_back(channel->z(j));
540 for (std::size_t k = 0; k < n_species; k++)
542 history.
species.push_back(channel->componentName(2 + k));
548 auto record = [&](
double t)
550 history.
t.push_back(t);
551 for (std::size_t j = 0; j < n_points; j++)
553 const double Tg = x[channel_offset + n_vars * j + 0];
554 const double Te = x[channel_offset + n_vars * j + 1];
555 history.
Tg.push_back(Tg);
556 history.
Te.push_back(Te);
557 double pressure, electron_density;
558 channel->nodeDiagnostics(Tg, Te,
559 &x[channel_offset + n_vars * j + 2], j,
560 pressure, electron_density);
561 history.
ne.push_back(electron_density);
562 for (std::size_t k = 0; k < n_species; k++)
564 history.
Y.push_back(x[channel_offset + n_vars * j + 2 + k]);
566 history.
rho.push_back(channel->density()[j]);
567 history.
p.push_back(pressure);
568 history.
u_r.push_back(channel->velocity()[j]);
581 const double schedule_tolerance =
582 1.0e-9 * opts.
dt *
static_cast<double>(opts.
n_steps + 1);
583 if (!opts.
Et_t.empty() || !opts.
Et_v.empty())
585 if (opts.
Et_t.size() < 2 || opts.
Et_t.size() != opts.
Et_v.size())
587 throw std::invalid_argument(
588 "solveChannel: the E(t) schedule needs at least 2 points and "
589 "Et_t and Et_v of equal length; got " +
590 std::to_string(opts.
Et_t.size()) +
" times and " +
591 std::to_string(opts.
Et_v.size()) +
" values.");
596 const double run_end = opts.
dt *
static_cast<double>(opts.
n_steps);
597 if (opts.
Et_t.front() > 0.0 ||
598 opts.
Et_t.back() < run_end + opts.
dt - schedule_tolerance)
600 throw std::invalid_argument(
601 "solveChannel: the E(t) schedule Et_t covers [" +
605 "] s: the run's n_steps * dt plus one step dt, the farthest "
606 "CVODE evaluates the field.");
615 channel_offset, n_vars);
619 auto electricFieldAt = [&](
double t)
629 if (t < Efield.
first() || t > Efield.
last() + schedule_tolerance)
631 throw std::runtime_error(
632 "solveChannel: the field is evaluated at t = " +
significant(t) +
636 return Efield.
eval(t);
640 auto recordCircuit = [&]()
645 history.
Rp.push_back(circuit_sample.
Rp);
647 history.
Vp.push_back(circuit_sample.
Vp);
648 history.
I.push_back(circuit_sample.
current);
666 throw std::runtime_error(
667 "solveChannelTransient: independent_nodes solves each node as its "
668 "own ODE system, which is only equivalent to the coupled solve "
669 "when no transport term couples the nodes. This channel has "
670 "conduction or species diffusion enabled, so its nodes are not "
671 "independent. Leave independent_nodes off.");
676 throw std::invalid_argument(
677 "solveChannelTransient: hydro_substep must be >= 0 s and is used only "
679 " s with hydrodynamics " +
685 throw std::invalid_argument(
686 "solveChannelTransient: plasma_voltage sets the field from the circuit, "
687 "so a prescribed electric_field or Et_t/Et_v schedule would be ignored. "
688 "Give one or the other.");
692 throw std::runtime_error(
693 "solveChannelTransient: plasma_voltage is evaluated once per "
694 "global step from the solver's own thread and would have to "
695 "re-acquire the GIL from each worker otherwise. Use n_threads=1 "
696 "with a circuit callback.");
700 throw std::runtime_error(
701 "solveChannelTransient: n_threads must be at least 1.");
705 throw std::runtime_error(
706 "solveChannelTransient: n_threads=" +
708 " needs independent_nodes: the coupled solve is one banded "
709 "system with no per-node loop to spread across threads. "
710 "Rejected rather than silently ignored.");
714 std::ostringstream message;
715 message <<
"solveChannelTransient: the bdf tolerances must all be "
718 <<
", atol_Y=" << opts.
atol_Y <<
".";
719 throw std::runtime_error(message.str());
730 throw std::runtime_error(
731 "solveChannelTransient: hydrodynamics and plasma_voltage advance "
732 "one global step at a time, so they need n_steps > 0 and "
733 "record_every to divide it exactly; n_steps=" +
734 std::to_string(opts.
n_steps) +
" and record_every=" +
741 const std::unique_ptr<CanteraThermo> hydro_thermo =
747 ? buildHydroConfig(*channel, opts, *hydro_thermo)
749 const std::size_t steps_per_frame = stepped ? opts.
record_every : 1;
760 const double t_total =
761 opts.
dt *
static_cast<double>(opts.
n_steps);
762 const std::size_t n_intervals =
763 std::max<std::size_t>(
773 std::unique_ptr<Cantera::FuncEval> rhs;
774 std::unique_ptr<Cantera::Integrator> cvode;
775 std::size_t offset = 0;
776 std::size_t length = 0;
786 const int threads = independent
787 ?
static_cast<int>(std::min(opts.
n_threads, n_points))
791 channel->setEvaluatorPoolSize(
static_cast<std::size_t
>(threads));
793 std::vector<SubSystem> systems;
794 systems.reserve(independent ? n_points : 1);
797 for (std::size_t j = 0; j < n_points; j++)
800 system.offset = channel_offset + n_vars * j;
801 system.length = n_vars;
803 system.rhs = std::make_unique<NodeRHS>(
804 channel.get(), x, n_vars, system.offset, j,
806 systems.push_back(std::move(system));
813 system.length = x.size();
814 system.rhs = std::make_unique<ChannelRHS>(
815 channel.get(), &sim, x, n_vars, channel_offset, n_points,
817 systems.push_back(std::move(system));
820 for (SubSystem &system : systems)
838 const int bandwidth =
static_cast<int>(
839 channel->hasTransport() ? 2 * n_vars : n_vars - 1);
840 cvode->setBandwidth(bandwidth, bandwidth);
843 std::vector<double> atol(system.length);
844 for (std::size_t j = 0; j < (independent ? 1 : n_points); j++)
846 atol[n_vars * j + 0] = opts.
atol_T;
847 atol[n_vars * j + 1] = opts.
atol_T;
848 for (std::size_t k = 0; k < n_species; k++)
850 atol[n_vars * j + 2 + k] = opts.
atol_Y;
871 system.rhs->suppressErrors(
true);
890 auto integrateTo = [&](
double t1)
895 const int count =
static_cast<int>(systems.size());
896 const int thread_count = threads;
897 std::vector<std::string> failures(systems.size());
901 std::vector<char> failed(systems.size(), 0);
903#pragma omp parallel for schedule(dynamic) num_threads(thread_count) if (thread_count > 1)
905 for (
int i = 0; i < count; i++)
907 SubSystem &system = systems[
static_cast<std::size_t
>(i)];
910 system.cvode->integrate(t1);
911 auto solution = system.cvode->solution();
913 solution.begin(), solution.end(),
914 x.begin() +
static_cast<std::ptrdiff_t
>(system.offset));
916 catch (
const std::exception &err)
918 failed[
static_cast<std::size_t
>(i)] = 1;
922 failures[
static_cast<std::size_t
>(i)] =
923 decodeSolverFailure(err.what(), system.node, *channel,
924 channel_offset, n_vars,
935 failed[
static_cast<std::size_t
>(i)] = 1;
938 for (std::size_t i = 0; i < failed.size(); i++)
945 throw std::runtime_error(
946 "solveChannelTransient: " +
948 ?
"the reaction sub-step failed and the error could "
961 double t_advanced = 0.0;
962 auto advanceTo = [&](
double t1)
964 if (!channel->hasTransport())
972 double conduction_start, diffusion_start;
973 std::vector<double> species_start, species_end;
974 channel->wallEnergyOutflow(x_channel, conduction_start, diffusion_start,
977 double conduction_end, diffusion_end;
978 channel->wallEnergyOutflow(x_channel, conduction_end, diffusion_end,
980 const double half_interval = 0.5 * (t1 - t_advanced);
982 half_interval * (conduction_start + conduction_end);
984 half_interval * (diffusion_start + diffusion_end);
985 for (std::size_t k = 0; k < n_species; k++)
988 half_interval * (species_start[k] + species_end[k]);
1019 const double first_target =
1020 stepped ? opts.
dt : t_total /
static_cast<double>(n_intervals);
1021 bool circuit_seeded =
false;
1027 circuit_seeded =
true;
1029 for (
double t_warm :
1030 {1.0e-15, 1.0e-14, 1.0e-13, 1.0e-12, 1.0e-11, 1.0e-10})
1032 if (t_warm >= first_target)
1042 struct SolverCounters
1044 double steps = 0.0, rhs_evals = 0.0, jac_evals = 0.0;
1045 double err_test_fails = 0.0, nonlin_conv_fails = 0.0;
1046 double lin_rhs_evals = 0.0, lin_setups = 0.0;
1047 double last_order = 0.0;
1049 auto readSolverCounters = [&]()
1051 SolverCounters counters;
1052 counters.last_order = std::numeric_limits<double>::max();
1053 for (
const SubSystem &system : systems)
1056 counters.steps +=
static_cast<double>(stats.
getInt(
"steps", 0));
1057 counters.rhs_evals +=
1058 static_cast<double>(stats.
getInt(
"rhs_evals", 0));
1059 counters.jac_evals +=
1060 static_cast<double>(stats.
getInt(
"jac_evals", 0));
1061 counters.err_test_fails +=
1062 static_cast<double>(stats.
getInt(
"err_test_fails", 0));
1063 counters.nonlin_conv_fails +=
static_cast<double>(
1064 stats.
getInt(
"nonlinear_conv_fails", 0));
1065 counters.lin_rhs_evals +=
1066 static_cast<double>(stats.
getInt(
"lin_rhs_evals", 0));
1067 counters.lin_setups +=
1068 static_cast<double>(stats.
getInt(
"lin_solve_setups", 0));
1069 counters.last_order = std::min(
1070 counters.last_order,
1071 static_cast<double>(stats.
getInt(
"last_order", 0)));
1075 auto recordSolverCounters = [&](
const SolverCounters &counters)
1088 recordSolverCounters(SolverCounters{});
1091 for (std::size_t frame = 1; frame <= n_intervals; frame++)
1093 const double t_rec = t_total *
1094 static_cast<double>(frame) /
1095 static_cast<double>(n_intervals);
1098 SolverCounters frame_counters;
1106 const std::size_t n_parts =
1107 channel->hasTransport()
1108 ? std::max<std::size_t>(
1109 1,
static_cast<std::size_t
>(
1110 std::ceil((t_rec - t_advanced) / opts.
dt)))
1112 const double t_frame_start = t_advanced;
1113 for (std::size_t part = 1; part <= n_parts; part++)
1115 advanceTo(part == n_parts
1117 : t_frame_start + (t_rec - t_frame_start) *
1118 static_cast<double>(part) /
1119 static_cast<double>(n_parts));
1121 frame_counters = readSolverCounters();
1131 for (std::size_t sub = 0; sub < steps_per_frame; sub++)
1136 const std::size_t step = (frame - 1) * steps_per_frame + sub;
1137 const double t0 =
static_cast<double>(step) * opts.
dt;
1138 const double t1 =
static_cast<double>(step + 1) * opts.
dt;
1141 hydroHalfStep(*channel, hydro_cfg, x, channel_offset,
1142 n_vars, t0, 0.5 * opts.
dt, history);
1147 if (!(step == 0 && circuit_seeded))
1156 for (SubSystem &system : systems)
1158 system.cvode->reinitialize(t0, *system.rhs);
1161 const SolverCounters step_counters = readSolverCounters();
1162 frame_counters.steps += step_counters.steps;
1163 frame_counters.rhs_evals += step_counters.rhs_evals;
1164 frame_counters.jac_evals += step_counters.jac_evals;
1165 frame_counters.err_test_fails += step_counters.err_test_fails;
1166 frame_counters.nonlin_conv_fails +=
1167 step_counters.nonlin_conv_fails;
1168 frame_counters.lin_rhs_evals += step_counters.lin_rhs_evals;
1169 frame_counters.lin_setups += step_counters.lin_setups;
1170 frame_counters.last_order = step_counters.last_order;
1171 hydroHalfStep(*channel, hydro_cfg, x, channel_offset,
1172 n_vars, t0 + 0.5 * opts.
dt, 0.5 * opts.
dt,
1182 frame_counters = readSolverCounters();
1190 ?
static_cast<double>(frame * steps_per_frame) * opts.
dt
1192 recordSolverCounters(frame_counters);
1196 std::fprintf(stderr,
"[bdf] frame %zu/%zu t=%.3e s\n",
1197 frame, n_intervals, t_rec);
1198 std::fflush(stderr);
1201 history.
nframes = history.
t.size();
The production ThermoAccessor for the hydro sub-step: Cantera's two-temperature PlasmaPhase behind Hy...
Closes an external electrical circuit on the channel's own resistance.
Pure-arithmetic primitives for a 1D cylindrical (radial) finite-volume discretization of the diffusiv...
Standalone explicit hydrodynamic sub-stepper for the 1D radial two-temperature plasma channel: the "A...
The transient two-temperature radial channel as a Cantera Domain1D: per-node source terms from Plasma...
Drives PlasmaChannel1D in time and records what it did.
The equation-of-state seam of the hydrodynamic sub-step: the abstract accessor hydroStep() closes its...
long int getInt(const string &key, long int default_) const
virtual void setMaxStepSize(double hmax)
virtual void setMaxSteps(int nmax)
virtual void initialize(double t0, FuncEval &func)
virtual void setTolerances(double reltol, span< const double > abstol)
virtual void setLinearSolverType(const string &linSolverType)
virtual void setMethod(MethodType t)
The channel side of the circuit coupling.
void seedResistance(const std::vector< double > &x)
Set the resistance from the current state without asking the circuit for a voltage.
double electricField(double t) const
Applied field over the step closeOver() last covered [V/m].
CircuitSample sample(const std::vector< double > &x) const
void closeOver(double t0, double t1, const std::vector< double > &x)
Close the circuit over one step: freeze at the state the step starts from and ask the callback for ...
Transient, two-temperature (Te != Tg), finite-rate reacting 1-D radial plasma channel (custom Cantera...
double first() const
First abscissa of a non-empty table, in the table's own units.
double last() const
Last abscissa of a non-empty table, in the table's own units.
double eval(double T) const
Linearly interpolated value at T; clamped to the table endpoints.
bool empty() const
True for a default-constructed (empty) table; eval() returns 0.0.
Equation-of-state and caloric accessor injected into hydroStep().
Integrator * newIntegrator(const string &itype)
double annularVolume(double r_east, double r_west)
Annular cell volume per unit axial length between two face radii, 0.5 * (r_east^2 - r_west^2) [m^2].
ChannelHistory solveChannelTransient(const ChannelOptions &opts)
Build a PlasmaChannel1D from opts.cfg, advance it from t=0 to [s] with the stiff BDF backend,...
HydroStepResult hydroStep(const HydroStepConfig &cfg, HydroStepState &state)
Advance state over one Strang half-step of cfg.dt_total.
std::string significant(double value)
value to 6 significant digits, for exception messages.
std::vector< double > I
Total current V_p/R_p [A].
std::vector< double > Vp
Plasma terminal voltage [V].
std::vector< double > ne
Species mass fractions [-], row-major [nframes, npts, nsp].
std::vector< double > E
Field V_p/gap [V/m].
std::vector< double > Rp_state
Resistance of the recorded state [Ohm].
std::size_t npts
Number of radial grid points [-].
std::vector< double > boundary_species_mass_out
The same, per species [kg/m/rad], length nsp, with what species diffusion carries through the wall gh...
std::vector< double > u_r
Species names, length nsp (order matches the Y axis).
std::vector< double > t
< Recorded times [s], length nframes
std::vector< double > solver_err_test_fails
std::vector< double > solver_last_order
double boundary_conduction_energy_out
What conduction (both pools) and species diffusion moved out through the wall ghost face over the run...
std::vector< double > p
Radial velocity [m/s], row-major [nframes, npts].
std::vector< std::string > species
std::size_t nsp
Number of species [-].
std::vector< double > solver_jac_evals
std::vector< double > Y
Mass density [kg/m^3], row-major [nframes, npts].
std::vector< double > solver_rhs_evals
std::vector< double > r
Gas temperature [K], row-major [nframes, npts].
std::vector< double > solver_lin_setups
std::size_t nframes
Number of recorded time frames [-].
std::vector< double > solver_steps
Per-frame CVODE counters, length nframes; the t=0 entry is zero, since nothing has been integrated ye...
std::vector< double > Rp
Circuit history, length nframes when ChannelOptions::plasma_voltage is set and empty otherwise.
std::vector< double > Tg
Electron temperature [K], row-major [nframes, npts].
std::vector< double > Te
Electron number density [1/m^3], row-major [nframes, npts].
std::vector< double > rho
Total pressure [Pa], row-major [nframes, npts].
std::vector< double > solver_nonlin_conv_fails
double boundary_diffusion_energy_out
std::vector< double > solver_lin_rhs_evals
RHS calls made by the linear solver itself, forming the banded Jacobian by difference quotients.
double rtol
Error tolerances for the stiff BDF backend, which sub-steps internally until it meets them,...
std::function< double(double, double)> plasma_voltage
Closes the electrical circuit on the channel's own resistance, given and returning the plasma termin...
int loglevel
Diagnostic verbosity [-] (0 = silent; >0 prints progress).
std::size_t n_threads
Threads advancing the per-node systems of independent_nodes.
double atol_T
Absolute tolerance on Tg and Te [K].
double gap
Inter-electrode gap [m].
std::size_t n_steps
Number of output steps [-] (dt*n_steps = total time [s]).
PlasmaChannelConfig cfg
Domain physics + initial/boundary state.
std::vector< double > Et_t
Field-vs-time schedule : Et_t [s] strictly increasing, Et_v [V/m] the field at those times.
bool independent_nodes
How the reaction sub-step is partitioned.
std::vector< double > Et_v
double atol_Y
Absolute tolerance on each Y_k [-].
double dt
Output cadence / initial step [s].
std::size_t record_every
Record a frame every N output steps [-].
The circuit columns of one recorded frame.
double current
Total current [A].
double Vp
Plasma terminal voltage at the end of the step [V].
double electric_field
Applied field [V/m].
double Rp_state
Resistance OF this frame's own recorded fields, which is what a reader recomputing it from Tg,...
double Rp
Resistance the circuit was DRIVEN with over the step ending at this frame, which is what current was ...
Geometry, boundary treatment and stepping controls for one call.
std::vector< double > r_node
Node radii [m], length N, ascending.
Diagnostics from one call, enough to assert NUMERICS.md section "Positivity under a strong rarefactio...
Per-node primitive state, advanced in place.
std::size_t n_species
K [-].
double electric_field
Applied electric field E [V/m], held at every node until the driver changes it through setElectricFie...
std::string mech
Cantera mechanism (YAML) file path.
bool hydrodynamics
Radial hydrodynamics (NUMERICS.md section 4.2's Strang split).
double hydro_substep
Pinned hydro sub-step [s]; 0 lets the CFL bound choose it.
std::string phase
Phase name within mech; must be a PlasmaPhase carrying "e-".