Cantera 2.6.0
IonFlow.cpp
Go to the documentation of this file.
1//! @file IonFlow.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8#include "cantera/oneD/refine.h"
9#include "cantera/base/ctml.h"
14
15using namespace std;
16
17namespace Cantera
18{
19
20IonFlow::IonFlow(IdealGasPhase* ph, size_t nsp, size_t points) :
21 StFlow(ph, nsp, points),
22 m_import_electron_transport(false),
23 m_stage(1),
24 m_kElectron(npos)
25{
26 // make a local copy of species charge
27 for (size_t k = 0; k < m_nsp; k++) {
28 m_speciesCharge.push_back(m_thermo->charge(k));
29 }
30
31 // Find indices for charge of species
32 for (size_t k = 0; k < m_nsp; k++){
33 if (m_speciesCharge[k] != 0){
34 m_kCharge.push_back(k);
35 } else {
36 m_kNeutral.push_back(k);
37 }
38 }
39
40 // Find the index of electron
41 if (m_thermo->speciesIndex("E") != npos ) {
42 m_kElectron = m_thermo->speciesIndex("E");
43 }
44
45 // no bound for electric potential
46 setBounds(c_offset_E, -1.0e20, 1.0e20);
47
48 // Set tighter negative species limit on charged species to avoid
49 // instabilities. Tolerance on electrons is even tighter to account for the
50 // low "molecular" weight.
51 for (size_t k : m_kCharge) {
52 setBounds(c_offset_Y + k, -1e-14, 1.0);
53 }
54 setBounds(c_offset_Y + m_kElectron, -1e-18, 1.0);
55
56 m_refiner->setActive(c_offset_E, false);
57 m_mobility.resize(m_nsp*m_points);
58 m_do_electric_field.resize(m_points,false);
59}
60
61void IonFlow::resize(size_t components, size_t points){
62 StFlow::resize(components, points);
63 m_mobility.resize(m_nsp*m_points);
64 m_do_species.resize(m_nsp,true);
65 m_do_electric_field.resize(m_points,false);
66}
67
68bool IonFlow::componentActive(size_t n) const
69{
70 if (n == c_offset_E) {
71 return true;
72 } else {
73 return StFlow::componentActive(n);
74 }
75}
76
77void IonFlow::updateTransport(double* x, size_t j0, size_t j1)
78{
79 StFlow::updateTransport(x,j0,j1);
80 for (size_t j = j0; j < j1; j++) {
81 setGasAtMidpoint(x,j);
82 m_trans->getMobilities(&m_mobility[j*m_nsp]);
83 if (m_import_electron_transport) {
84 size_t k = m_kElectron;
85 double tlog = log(m_thermo->temperature());
86 m_mobility[k+m_nsp*j] = poly5(tlog, m_mobi_e_fix.data());
87 m_diff[k+m_nsp*j] = poly5(tlog, m_diff_e_fix.data());
88 }
89 }
90}
91
92void IonFlow::updateDiffFluxes(const double* x, size_t j0, size_t j1)
93{
94 if (m_stage == 1) {
95 frozenIonMethod(x,j0,j1);
96 }
97 if (m_stage == 2) {
98 electricFieldMethod(x,j0,j1);
99 }
100}
101
102void IonFlow::frozenIonMethod(const double* x, size_t j0, size_t j1)
103{
104 for (size_t j = j0; j < j1; j++) {
105 double wtm = m_wtm[j];
106 double rho = density(j);
107 double dz = z(j+1) - z(j);
108 double sum = 0.0;
109 for (size_t k : m_kNeutral) {
110 m_flux(k,j) = m_wt[k]*(rho*m_diff[k+m_nsp*j]/wtm);
111 m_flux(k,j) *= (X(x,k,j) - X(x,k,j+1))/dz;
112 sum -= m_flux(k,j);
113 }
114
115 // correction flux to insure that \sum_k Y_k V_k = 0.
116 for (size_t k : m_kNeutral) {
117 m_flux(k,j) += sum*Y(x,k,j);
118 }
119
120 // flux for ions
121 // Set flux to zero to prevent some fast charged species (such electrons)
122 // to run away
123 for (size_t k : m_kCharge) {
124 m_flux(k,j) = 0;
125 }
126 }
127}
128
129void IonFlow::electricFieldMethod(const double* x, size_t j0, size_t j1)
130{
131 for (size_t j = j0; j < j1; j++) {
132 double wtm = m_wtm[j];
133 double rho = density(j);
134 double dz = z(j+1) - z(j);
135
136 // mixture-average diffusion
137 double sum = 0.0;
138 for (size_t k = 0; k < m_nsp; k++) {
139 m_flux(k,j) = m_wt[k]*(rho*m_diff[k+m_nsp*j]/wtm);
140 m_flux(k,j) *= (X(x,k,j) - X(x,k,j+1))/dz;
141 sum -= m_flux(k,j);
142 }
143
144 // ambipolar diffusion
145 double E_ambi = E(x,j);
146 for (size_t k : m_kCharge) {
147 double Yav = 0.5 * (Y(x,k,j) + Y(x,k,j+1));
148 double drift = rho * Yav * E_ambi
149 * m_speciesCharge[k] * m_mobility[k+m_nsp*j];
150 m_flux(k,j) += drift;
151 }
152
153 // correction flux
154 double sum_flux = 0.0;
155 for (size_t k = 0; k < m_nsp; k++) {
156 sum_flux -= m_flux(k,j); // total net flux
157 }
158 double sum_ion = 0.0;
159 for (size_t k : m_kCharge) {
160 sum_ion += Y(x,k,j);
161 }
162 // The portion of correction for ions is taken off
163 for (size_t k : m_kNeutral) {
164 m_flux(k,j) += Y(x,k,j) / (1-sum_ion) * sum_flux;
165 }
166 }
167}
168
169void IonFlow::setSolvingStage(const size_t stage)
170{
171 if (stage == 1 || stage == 2) {
172 m_stage = stage;
173 } else {
174 throw CanteraError("IonFlow::setSolvingStage",
175 "solution stage must be set to: "
176 "1) frozenIonMethod, "
177 "2) electricFieldEqnMethod");
178 }
179}
180
181void IonFlow::evalResidual(double* x, double* rsd, int* diag,
182 double rdt, size_t jmin, size_t jmax)
183{
184 StFlow::evalResidual(x, rsd, diag, rdt, jmin, jmax);
185 if (m_stage != 2) {
186 return;
187 }
188
189 for (size_t j = jmin; j <= jmax; j++) {
190 if (j == 0) {
191 // enforcing the flux for charged species is difficult
192 // since charged species are also affected by electric
193 // force, so Neumann boundary condition is used.
194 for (size_t k : m_kCharge) {
195 rsd[index(c_offset_Y + k, 0)] = Y(x,k,0) - Y(x,k,1);
196 }
197 rsd[index(c_offset_E, j)] = E(x,0);
198 diag[index(c_offset_E, j)] = 0;
199 } else if (j == m_points - 1) {
200 rsd[index(c_offset_E, j)] = dEdz(x,j) - rho_e(x,j) / epsilon_0;
201 diag[index(c_offset_E, j)] = 0;
202 } else {
203 //-----------------------------------------------
204 // Electric field by Gauss's law
205 //
206 // dE/dz = e/eps_0 * sum(q_k*n_k)
207 //
208 // E = -dV/dz
209 //-----------------------------------------------
210 rsd[index(c_offset_E, j)] = dEdz(x,j) - rho_e(x,j) / epsilon_0;
211 diag[index(c_offset_E, j)] = 0;
212 }
213 }
214}
215
216void IonFlow::solveElectricField(size_t j)
217{
218 bool changed = false;
219 if (j == npos) {
220 for (size_t i = 0; i < m_points; i++) {
221 if (!m_do_electric_field[i]) {
222 changed = true;
223 }
224 m_do_electric_field[i] = true;
225 }
226 } else {
227 if (!m_do_electric_field[j]) {
228 changed = true;
229 }
230 m_do_electric_field[j] = true;
231 }
232 m_refiner->setActive(c_offset_U, true);
233 m_refiner->setActive(c_offset_V, true);
234 m_refiner->setActive(c_offset_T, true);
235 m_refiner->setActive(c_offset_E, true);
236 if (changed) {
237 needJacUpdate();
238 }
239}
240
241void IonFlow::fixElectricField(size_t j)
242{
243 bool changed = false;
244 if (j == npos) {
245 for (size_t i = 0; i < m_points; i++) {
246 if (m_do_electric_field[i]) {
247 changed = true;
248 }
249 m_do_electric_field[i] = false;
250 }
251 } else {
252 if (m_do_electric_field[j]) {
253 changed = true;
254 }
255 m_do_electric_field[j] = false;
256 }
257 m_refiner->setActive(c_offset_U, false);
258 m_refiner->setActive(c_offset_V, false);
259 m_refiner->setActive(c_offset_T, false);
260 m_refiner->setActive(c_offset_E, false);
261 if (changed) {
262 needJacUpdate();
263 }
264}
265
266void IonFlow::setElectronTransport(vector_fp& tfix, vector_fp& diff_e,
267 vector_fp& mobi_e)
268{
269 m_import_electron_transport = true;
270 size_t degree = 5;
271 size_t n = tfix.size();
272 vector_fp tlog;
273 for (size_t i = 0; i < n; i++) {
274 tlog.push_back(log(tfix[i]));
275 }
276 vector_fp w(n, -1.0);
277 m_diff_e_fix.resize(degree + 1);
278 m_mobi_e_fix.resize(degree + 1);
279 polyfit(n, degree, tlog.data(), diff_e.data(), w.data(), m_diff_e_fix.data());
280 polyfit(n, degree, tlog.data(), mobi_e.data(), w.data(), m_mobi_e_fix.data());
281}
282
283void IonFlow::_finalize(const double* x)
284{
285 StFlow::_finalize(x);
286
287 bool p = m_do_electric_field[0];
288 if (p) {
289 solveElectricField();
290 }
291}
292
293}
Headers for the Transport object, which is the virtual base class for all transport property evaluato...
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
Header for a file containing miscellaneous numerical functions.
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
R poly5(D x, R *c)
Templated evaluation of a polynomial of order 5.
Definition: utilities.h:137
std::vector< double > vector_fp
Turn on the use of stl vectors for the basic array type within cantera Vector of doubles.
Definition: ct_defs.h:184
const double epsilon_0
Permittivity of free space [F/m].
Definition: ct_defs.h:134
double polyfit(size_t n, size_t deg, const double *x, const double *y, const double *w, double *p)
Fits a polynomial function to a set of data points.
Definition: polyfit.cpp:14
Various templated functions that carry out common vector operations (see Templated Utility Functions)...