version 3.9.0
Loading...
Searching...
No Matches
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) ochs2008 or Acosta et al....
Definition electrochemistry.hh:30
@ Acosta
Definition electrochemistry.hh:30
@ Ochs
Definition electrochemistry.hh:30
T getParamFromGroup(Args &&... args)
A free function to get a parameter from the parameter tree singleton with a model group.
Definition parameters.hh:149
T getParam(Args &&... args)
A free function to get a parameter from the parameter tree singleton.
Definition parameters.hh:139
A two-phase (water and air) fluid system with water, nitrogen and oxygen as components.
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.