rizer.cantera_ext
C++ Cantera 1-D plasma extension (custom Domain1D models and solvers)
Loading...
Searching...
No Matches
CircuitCoupling.cpp
Go to the documentation of this file.
1#include "CircuitCoupling.h"
2
4
5#include <algorithm>
6#include <cmath>
7#include <limits>
8#include <stdexcept>
9#include <string>
10
11namespace rizer
12{
13
14 // ------------------------------------------------------------------------------ //
15 // Constructor //
16 // ------------------------------------------------------------------------------ //
17
19 const PlasmaChannel1D &channel, double gap,
20 const std::function<double(double, double)> &plasma_voltage,
21 std::size_t channel_offset, std::size_t n_vars)
22 : m_channel(&channel), m_gap(gap),
23 m_plasma_voltage(&plasma_voltage),
24 m_channel_offset(channel_offset), m_n_vars(n_vars),
25 m_n_points(channel.nPoints()),
26 m_Rp(std::numeric_limits<double>::infinity())
27 {
28 if (!active())
29 {
30 return;
31 }
32 if (!(m_gap > 0.0))
33 {
34 throw std::runtime_error(
35 "ChannelCircuit: the circuit closes on "
36 "R_p = gap / integral(sigma dA) and sets the field to "
37 "V_p / gap, so gap must be positive; got " +
38 std::to_string(m_gap) + " m.");
39 }
40 // A single node has no spacing to mirror, so its outer face is a
41 // stand-in built from the domain radius rather than a cell edge, and a
42 // conductance over it would describe no cross-section in particular.
43 if (m_n_points < 2)
44 {
45 throw std::runtime_error(
46 "ChannelCircuit: a circuit closes on integral(sigma dA) over "
47 "the cross-section, which needs at least 2 radial nodes to "
48 "have an area; got " +
49 std::to_string(m_n_points) + ".");
50 }
51
52 // The same cells the hydro sub-step, the conduction stencil and the
53 // Joule source act on, out to the wall face half a spacing beyond the
54 // last node (NUMERICS.md section 2). Any other cells would put the
55 // circuit's power V^2/R_p and the deposited Joule power out of balance.
56 m_cell_area.resize(m_n_points);
57 for (std::size_t j = 0; j < m_n_points; j++)
58 {
59 const double inner = channel.faceRadius(j);
60 const double outer = channel.faceRadius(j + 1);
61 m_cell_area[j] = Cantera::Pi * (outer * outer - inner * inner);
62 }
63 }
64
65 // ------------------------------------------------------------------------------ //
66 // Public methods //
67 // ------------------------------------------------------------------------------ //
68
69 void ChannelCircuit::seedResistance(const std::vector<double> &x)
70 {
71 if (!active())
72 {
73 return;
74 }
75 m_Rp = resistanceFrom(conductance(x));
76 }
77
78 void ChannelCircuit::closeOver(double t0, double t1,
79 const std::vector<double> &x)
80 {
81 if (!active())
82 {
83 return;
84 }
85 m_Rp = resistanceFrom(conductance(x));
86 m_t0 = t0;
87 m_dt = t1 - t0;
88 m_Vp0 = (*m_plasma_voltage)(t0, m_Rp);
89 m_Vp1 = (*m_plasma_voltage)(t1, m_Rp);
90 }
91
92 double ChannelCircuit::electricField(double t) const
93 {
94 // Clamped to the step: CVODE can evaluate past t1 (its internal step
95 // overshoots the output time), and an extrapolated V_p there is a
96 // voltage the circuit never produced.
97 const double fraction =
98 (m_dt > 0.0) ? std::min(std::max((t - m_t0) / m_dt, 0.0), 1.0) : 0.0;
99 return (m_Vp0 + (m_Vp1 - m_Vp0) * fraction) / m_gap;
100 }
101
102 CircuitSample ChannelCircuit::sample(const std::vector<double> &x) const
103 {
104 CircuitSample out;
105 out.Rp = m_Rp;
106 out.Rp_state = resistanceFrom(conductance(x));
107 out.Vp = m_Vp1;
108 out.current = (m_Rp > 0.0 && std::isfinite(m_Rp)) ? m_Vp1 / m_Rp : 0.0;
109 out.electric_field = m_Vp1 / m_gap;
110 return out;
111 }
112
113 // ------------------------------------------------------------------------------ //
114 // Private methods //
115 // ------------------------------------------------------------------------------ //
116
117 double ChannelCircuit::conductance(const std::vector<double> &x) const
118 {
119 double total = 0.0;
120 for (std::size_t j = 0; j < m_n_points; j++)
121 {
122 total += m_channel->nodeElectricalConductivity(
123 x[m_channel_offset + m_n_vars * j + 0],
124 x[m_channel_offset + m_n_vars * j + 1],
125 &x[m_channel_offset + m_n_vars * j + 2], j) *
126 m_cell_area[j];
127 }
128 return total;
129 }
130
131 double ChannelCircuit::resistanceFrom(double conductance_value) const
132 {
133 return (conductance_value > 0.0)
134 ? m_gap / conductance_value
135 : std::numeric_limits<double>::infinity();
136 }
137
138} // namespace rizer
Closes an external electrical circuit on the channel's own resistance.
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].
ChannelCircuit(const PlasmaChannel1D &channel, double gap, const std::function< double(double, double)> &plasma_voltage, std::size_t channel_offset, std::size_t n_vars)
Build the coupling, or an inactive one when no callback is given.
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 nodeElectricalConductivity(double Tg, double Te, const double *Y, std::size_t j) const
Electrical conductivity [S/m] at one node's state, from the same composition-resolved closure the sou...
double faceRadius(std::size_t j) const
Radius of face j, the single definition of where a cell boundary sits (NUMERICS.md section 2).
const double Pi
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 ...