rizer.cantera_ext
C++ Cantera 1-D plasma extension (custom Domain1D models and solvers)
Loading...
Searching...
No Matches
PlasmaColumnSolver.cpp
Go to the documentation of this file.
3#include "units.h"
4
8
9#include <memory>
10
11namespace rizer {
12
13ColumnResult solveColumn(double R, double electric_field, double T_wall,
14 const PropertyTable& sigma, const PropertyTable& kappa,
15 const PropertyTable& p_rad, const ColumnOptions& opts)
16{
17 using std::make_shared;
18
19 // Optional initial guess T(r), folded into the domain so it is applied at
20 // construction (via initialValue) without the deprecated Sim1D::setProfile.
21 // Requires at least two points and matching lengths for init_r/init_T;
22 // otherwise init_profile is left default-constructed and the domain falls
23 // back to its own parabolic seed built from T_wall and T_center_guess.
24 PropertyTable init_profile;
25 if (opts.init_r.size() >= 2 && opts.init_r.size() == opts.init_T.size()) {
26 init_profile = PropertyTable(opts.init_r, opts.init_T);
27 }
28
29 // The interior domain owns the actual Elenbaas-Heller physics: the radial
30 // conduction/Joule-heating/radiation balance is assembled as residuals on
31 // this domain's grid points; sigma/kappa/p_rad are consulted at each
32 // Newton iterate via their tabulated PropertyTable lookups.
33 auto column = make_shared<ThermalPlasmaColumn1D>(
34 R, opts.n_points, electric_field, T_wall,
35 sigma, kappa, p_rad, opts.T_center_guess, opts.rho_cp, init_profile);
36
37 // Dummy no-op terminators (no Solution required), matching the pattern of
38 // Cantera's BoundaryValueProblem sample. The column owns its own BCs.
39 auto left = make_shared<Cantera::Empty1D>();
40 auto right = make_shared<Cantera::Empty1D>();
41
42 // Assemble the three-domain Sim1D stack [Empty1D | ThermalPlasmaColumn1D |
43 // Empty1D]. Sim1D indexes domains in this vector order, so the column is
44 // always domain index 1 regardless of how many points it has.
45 std::vector<std::shared_ptr<Cantera::Domain1D>> domains{left, column, right};
46 Cantera::Sim1D sim(domains);
47
48 // Refinement applies to the column (domain index 1). These thresholds
49 // (ratio/slope/curve/prune) come straight from ColumnOptions and control
50 // how aggressively Sim1D inserts/removes grid points between Newton
51 // solves when refine_grid is enabled below.
53 opts.refine_curve, opts.refine_prune);
54
55 // Drive the steady solve. Internally, Sim1D::solve() first attempts a
56 // direct Newton solve on the current grid; if Newton fails to converge it
57 // automatically falls back to pseudo-transient time-stepping (using
58 // rho_cp baked into the column's residuals as the effective transient
59 // time constant) to move the iterate closer to the solution before
60 // retrying Newton. When refine_grid is true, this Newton/time-stepping
61 // cycle is repeated on progressively refined grids (per the criteria set
62 // above) until no further refinement is needed.
63 sim.solve(opts.loglevel, opts.refine_grid);
64
65 // Extract the converged profile from the (possibly refined) grid. Note
66 // nPoints()/z(j) reflect the final refined grid, which may differ in size
67 // and spacing from the initial opts.n_points uniform grid.
68 const std::size_t np = column->nPoints();
69 ColumnResult res;
70 res.n_points = np;
71 res.electric_field = electric_field;
72 res.r.reserve(np);
73 res.T.reserve(np);
74 res.sigma.reserve(np);
75 res.kappa.reserve(np);
76
77 // Re-evaluate sigma(T)/kappa(T) at the converged temperatures (rather than
78 // caching values from the last Newton iterate) so the returned arrays are
79 // exactly consistent with the final T profile.
80 std::vector<double> Tvals = column->values("T");
81 for (std::size_t j = 0; j < np; j++) {
82 double r = column->z(j);
83 double T = Tvals[j];
84 res.r.push_back(r);
85 res.T.push_back(T);
86 res.sigma.push_back(column->sigmaAt(T));
87 res.kappa.push_back(column->kappaAt(T));
88 }
89
90 // Integrated current:
91 // @f$I = \int_0^R \sigma(T)\, E\, 2\pi r\, dr@f$ (trapezoidal).
92 double I = 0.0;
93 for (std::size_t j = 0; j + 1 < np; j++) {
94 // Trapezoidal rule:
95 // @f$\int_a^b f(r)\, dr \approx \tfrac{1}{2}(f(a)+f(b))(b-a)@f$
96 double f0 = res.sigma[j] * res.r[j];
97 double f1 = res.sigma[j + 1] * res.r[j + 1];
98 double dr = res.r[j + 1] - res.r[j];
99 I += 0.5 * (f0 + f1) * dr;
100 }
101 // E is uniform along the column (fixed axial field), so it can be pulled
102 // out of the radial integral: @f$I = 2\pi E \int \sigma(T)\, r\, dr@f$.
103 res.current = 2.0 * units::pi * electric_field * I;
104
105 return res;
106}
107
108} // namespace rizer
void solve(int loglevel=0, bool refine_grid=true)
void setRefineCriteria(int dom=-1, double ratio=10.0, double slope=0.8, double curve=0.8, double prune=-0.1)
constexpr double pi
pi (matches numpy's np.pi used in units.py).
Definition units.h:51
ColumnResult solveColumn(double R, double electric_field, double T_wall, const PropertyTable &sigma, const PropertyTable &kappa, const PropertyTable &p_rad, const ColumnOptions &opts)
Build, solve, and extract the radial Elenbaas-Heller column.
Solver configuration. All fields have defaults matching a typical H2 arc.
std::vector< double > init_r
Optional seed profile for the initial guess.
int loglevel
Sim1D solver verbosity [-] (0 = silent, 1 = progress, higher = more detail).
double refine_ratio
Refiner: max allowed ratio of adjacent cell widths [-].
double refine_curve
Refiner: max allowed fractional curvature of T per cell [-].
double rho_cp
Volumetric heat capacity [J/m^3/K] — scales the pseudo-transient term used as a Newton fallback.
double refine_slope
Refiner: max allowed fractional change in T slope between adjacent cells [-].
double T_center_guess
Parabolic-seed centerline temperature [K], used to build the default initial guess.
double refine_prune
Refiner: remove grid points whose contribution falls below this threshold [-].
std::vector< double > init_T
Optional seed temperatures [K] at each init_r location.
std::size_t n_points
Initial uniform grid point count [-] (refined during solve if refine_grid).
bool refine_grid
Enable Cantera adaptive grid refinement [-] (true/false) during the solve.
Output of a completed column solve.
double electric_field
Axial E field [V/m] used for this solve (echo of the input).
std::size_t n_points
Number of grid points after adaptive refinement.
std::vector< double > T
Converged temperature [K] at each grid point.
double current
Total arc current [A], trapezoidal quadrature.
std::vector< double > sigma
Electrical conductivity [S/m] evaluated at T.
std::vector< double > kappa
Thermal conductivity [W/m/K] evaluated at T.
std::vector< double > r
Radial grid [m] after refinement, r[0]=0, r[N-1]=R.
Physical constants and unit conversions, in SI by default.