version 3.10-dev
electrochemistry.hh
Go to the documentation of this file.
1// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2// vi: set et ts=4 sw=4 sts=4:
3//
4// SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_ELECTROCHEMISTRY_HH
13#define DUMUX_ELECTROCHEMISTRY_HH
14
15#include <cmath>
16
21
23
24namespace Dumux {
25
31
39template <class Scalar, class Indices, class FluidSystem, class GridGeometry, ElectroChemistryModel electroChemistryModel>
41{
42
44
45 enum
46 {
47 //indices of the primary variables
48 pressureIdx = Indices::pressureIdx, // gas-phase pressure
49 switchIdx = Indices::switchIdx, // liquid saturation or mole fraction
50 };
51 enum
52 {
53 //equation indices
54 contiH2OEqIdx = Indices::conti0EqIdx + FluidSystem::H2OIdx,
55 contiO2EqIdx = Indices::conti0EqIdx + FluidSystem::O2Idx,
56 };
57
58 enum
59 {
60 // phase indices
61 liquidPhaseIdx = FluidSystem::liquidPhaseIdx,
62 gasPhaseIdx = FluidSystem::gasPhaseIdx
63 };
64
65 using GridView = typename GridGeometry::GridView;
66 static constexpr bool isBox = GridGeometry::discMethod == DiscretizationMethods::box;
67 using GlobalPosition = typename Dune::FieldVector<typename GridView::ctype, GridView::dimensionworld>;
68
69public:
79 template<class SourceValues>
80 static void reactionSource(SourceValues &values, Scalar currentDensity,
81 const std::string& paramGroup = "")
82 {
83 // correction to account for actually relevant reaction area
84 // current density has to be divided by the half length of the box
85 // \todo Do we have multiply with the electrochemically active surface area (ECSA) here instead?
86 static Scalar gridYMax = getParamFromGroup<GlobalPosition>(paramGroup, "Grid.UpperRight")[1];
87 static Scalar nCellsY = getParamFromGroup<GlobalPosition>(paramGroup, "Grid.Cells")[1];
88
89 // Warning: This assumes the reaction layer is always just one cell (cell-centered) or half a box (box) thick
90 const auto lengthBox = gridYMax/nCellsY;
91 if (isBox)
92 currentDensity *= 2.0/lengthBox;
93 else
94 currentDensity *= 1.0/lengthBox;
95
96 static Scalar transportNumberH2O = getParam<Scalar>("ElectroChemistry.TransportNumberH20");
97
98 //calculation of flux terms with Faraday equation
99 values[contiH2OEqIdx] = currentDensity/(2*Constant::F); //reaction term in reaction layer
100 values[contiH2OEqIdx] += currentDensity/Constant::F*transportNumberH2O; //osmotic term in membrane
101 values[contiO2EqIdx] = -currentDensity/(4*Constant::F); //O2-equation
102 }
103
109 template<class VolumeVariables>
110 static Scalar calculateCurrentDensity(const VolumeVariables &volVars)
111 {
112
113 static int maxIter = getParam<int>("ElectroChemistry.MaxIterations");
114 static Scalar specificResistance = getParam<Scalar>("ElectroChemistry.SpecificResistance");
115 static Scalar reversibleVoltage = getParam<Scalar>("ElectroChemistry.ReversibleVoltage");
116 static Scalar cellVoltage = getParam<Scalar>("ElectroChemistry.CellVoltage");
117
118 //initial guess for the current density and initial newton solver parameters
119 Scalar currentDensity = reversibleVoltage - cellVoltage - 0.5;
120 Scalar increment = 1e-4;
121 Scalar deltaCurrentDensity = currentDensity*increment;
122 Scalar deltaVoltage = 1.0;
123 int iterations = 0;
124
125 //Newton Solver for current Density
126 using std::abs;
127 while (abs(deltaVoltage) > 1e-6)
128 {
129
130 Scalar activationLosses = calculateActivationLosses_(volVars, currentDensity);
131 Scalar activationLossesNext = calculateActivationLosses_(volVars, currentDensity+deltaCurrentDensity);
132 Scalar concentrationLosses = calculateConcentrationLosses_(volVars);
133 Scalar activationLossesDiff = activationLossesNext - activationLosses;
134 Scalar sw = volVars.saturation(liquidPhaseIdx);
135
136 if(electroChemistryModel == ElectroChemistryModel::Acosta)
137 {
138 // Acosta calculation
139 deltaVoltage = currentDensity*specificResistance - reversibleVoltage + cellVoltage + activationLosses + concentrationLosses;
140
141 currentDensity = currentDensity - (deltaVoltage*deltaCurrentDensity)/(deltaCurrentDensity*specificResistance + activationLossesDiff);
142
143 activationLosses = calculateActivationLosses_(volVars, currentDensity);
144
145 deltaVoltage = currentDensity*specificResistance - reversibleVoltage + cellVoltage + activationLosses + concentrationLosses;
146
147 iterations++;
148
149 }
150 else
151 {
152 // Ochs & other calculation
153 deltaVoltage = currentDensity*specificResistance/(1-sw) - reversibleVoltage + cellVoltage + activationLosses + concentrationLosses;
154
155 currentDensity = currentDensity - (deltaVoltage*deltaCurrentDensity)/(deltaCurrentDensity*specificResistance/(1-sw) + activationLossesDiff);
156
157 activationLosses = calculateActivationLosses_(volVars, currentDensity);
158
159 deltaVoltage = currentDensity*specificResistance/(1-sw) - reversibleVoltage + cellVoltage + activationLosses + concentrationLosses;
160
161 iterations++;
162 }
163
164 if(iterations >= maxIter)
165 {
166 DUNE_THROW(NumericalProblem, "Newton solver for electrochemistry didn't converge");
167 }
168 }
169
170 //conversion from [A/cm^2] to [A/m^2]
171 return currentDensity*10000;
172 }
173
174private:
175
181 template<class VolumeVariables>
182 static Scalar calculateActivationLosses_(const VolumeVariables &volVars, const Scalar currentDensity)
183 {
184 static Scalar refO2PartialPressure = getParam<Scalar>("ElectroChemistry.RefO2PartialPressure");
185 static Scalar numElectrons = getParam<Scalar>("ElectroChemistry.NumElectrons");
186 static Scalar transferCoefficient = getParam<Scalar>("ElectroChemistry.TransferCoefficient");
187
188 //Saturation sw for Acosta calculation
189 Scalar sw = volVars.saturation(liquidPhaseIdx);
190 //Calculate prefactor
191 Scalar preFactor = Constant::R*volVars.fluidState().temperature()/transferCoefficient/Constant::F/numElectrons;
192 //Get partial pressure of O2 in the gas phase
193 Scalar pO2 = volVars.pressure(gasPhaseIdx) * volVars.fluidState().moleFraction(gasPhaseIdx, FluidSystem::O2Idx);
194
195 Scalar losses = 0.0;
196 //Calculate activation losses
197 using std::log;
198 using std::abs;
199 if(electroChemistryModel == ElectroChemistryModel::Acosta)
200 {
201 losses = preFactor
202 *( log(abs(currentDensity)/abs(exchangeCurrentDensity_(volVars)))
203 - log(pO2/refO2PartialPressure)
204 - log(1 - sw)
205 );
206 }
207 else
208 {
209 losses = preFactor
210 *( log(abs(currentDensity)/abs(exchangeCurrentDensity_(volVars)))
211 - log(pO2/refO2PartialPressure)
212 );
213 }
214 return losses;
215 }
216
217
222 template<class VolumeVariables>
223 static Scalar calculateConcentrationLosses_(const VolumeVariables &volVars)
224 {
225 static Scalar pO2Inlet = getParam<Scalar>("ElectroChemistry.pO2Inlet");
226 static Scalar numElectrons = getParam<Scalar>("ElectroChemistry.NumElectrons");
227 static Scalar transferCoefficient = getParam<Scalar>("ElectroChemistry.TransferCoefficient");
228
229 //Calculate preFactor
230 Scalar preFactor = Constant::R*volVars.temperature()/transferCoefficient/Constant::F/numElectrons;
231 //Get partial pressure of O2 in the gas phase
232 Scalar pO2 = volVars.pressure(gasPhaseIdx) * volVars.fluidState().moleFraction(gasPhaseIdx, FluidSystem::O2Idx);
233
234 Scalar losses = 0.0;
235 //Calculate concentration losses
236 using std::log;
237 if(electroChemistryModel == ElectroChemistryModel::Acosta)
238 {
239 losses = -1.0*preFactor*(transferCoefficient/2)*log(pO2/pO2Inlet);
240 }else
241 {
242 // +1 is the Nernst part of the equation
243 losses = -1.0*preFactor*(transferCoefficient/2+1)*log(pO2/pO2Inlet);
244 }
245
246 return losses;
247 }
248
249
254 template<class VolumeVariables>
255 static Scalar exchangeCurrentDensity_(const VolumeVariables &volVars)
256 {
257 using std::exp;
258 static Scalar activationBarrier = getParam<Scalar>("ElectroChemistry.ActivationBarrier");
259 static Scalar surfaceIncreasingFactor = getParam<Scalar>("ElectroChemistry.SurfaceIncreasingFactor");
260 static Scalar refTemperature = getParam<Scalar>("ElectroChemistry.RefTemperature");
261 static Scalar refCurrentDensity = getParam<Scalar>("ElectroChemistry.RefCurrentDensity");
262
263 Scalar T = volVars.fluidState().temperature();
264 Scalar refExchangeCurrentDensity = -1.0
265 * refCurrentDensity
266 * surfaceIncreasingFactor
267 * exp(-1.0 * activationBarrier / Constant::R * (1/T-1/refTemperature));
268
269 return refExchangeCurrentDensity;
270 }
271};
272
273} // end namespace Dumux
274
275#endif
A central place for various physical constants occurring in some equations.
Definition: constants.hh:27
static constexpr Scalar F
Faraday constant .
Definition: constants.hh:54
static constexpr Scalar R
The ideal gas constant .
Definition: constants.hh:32
This class calculates source terms and current densities for fuel cells with the electrochemical mode...
Definition: electrochemistry.hh:41
static void reactionSource(SourceValues &values, Scalar currentDensity, const std::string &paramGroup="")
Calculates reaction sources with an electrochemical model approach.
Definition: electrochemistry.hh:80
static Scalar calculateCurrentDensity(const VolumeVariables &volVars)
Newton solver for calculation of the current density.
Definition: electrochemistry.hh:110
Exception thrown if a fixable numerical problem occurs.
Definition: exceptions.hh:27
A central place for various physical constants occurring in some equations.
Some exceptions thrown in DuMux
ElectroChemistryModel
The type of electrochemistry models implemented here (Ochs (2008) or Acosta et al....
Definition: electrochemistry.hh:30
@ Acosta
Definition: electrochemistry.hh:30
@ Ochs
Definition: electrochemistry.hh:30
The available discretization methods in Dumux.
constexpr Box box
Definition: method.hh:147
Definition: adapt.hh:17
The infrastructure to retrieve run-time parameters from Dune::ParameterTrees.