forked from Cantera/cantera
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PSRv2.cpp
186 lines (161 loc) · 7.53 KB
/
PSRv2.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/// @file PSRv2.cpp
/// Perfectly Stirred Reactor Solver (v0.2)
#include "BoundaryValueProblem.h" // PSR uses the simplified BoundaryValueProblem interface to Cantera's solver (for now)
const char *MECHANISM = "gri30.yaml";
const double PRESSURE = Cantera::OneAtm;
class PSR : public BVP::BoundaryValueProblem
{
public:
// constructor
PSR(std::shared_ptr<Cantera::Solution> inletSol, std::shared_ptr<Cantera::Solution> reactorSol, double mdot, double volume, bool doEnergy = false, double fixedTemp = 0)
// ------------------------ INITIALIZE GLOBAL VARS ------------------------
: BVP::BoundaryValueProblem(inletSol->thermo()->nSpecies() + doEnergy, 1, 0.0, 1), // initialize BVP interface. 1st arg is number of solution components, 2nd arg is number of grid points (1 point for 0D...), 3rd and 4th are arbitrary (not used in this problem)
inletThermo(inletSol->thermo()),
reactorThermo(reactorSol->thermo()),
reactorKinetics(reactorSol->kinetics()),
nSpecies(inletSol->thermo()->nSpecies()),
index_T(inletSol->thermo()->nSpecies()), // location of the "Temperature" component in the solution vector
Y_in(inletSol->thermo()->massFractions()), // mass fractions at the inlet
molecularWeight(inletSol->thermo()->molecularWeights()), // molecular weight of each species (these never change)
volume(volume),
mdot(mdot),
fixedTemp(fixedTemp * !doEnergy), // bool: true if fixed reactor temperature provided, *always* false if doEnergy = true
doEnergy(doEnergy)
{
// ------------------ INITIALIZE SOLUTION COMPONENTS ------------------
// solution vector contains nSpecies reactor mass fractions, ordered by species index.
// if energy is enabled, reactor temp is added as the final solution component.
// i.e.:
// sol = | Y_0 | Y_1 | Y_2 | ... | Y_(nSpecies-1) | Temp |
BVP::Component initializer;
initializer.lower = -1.0e-3;
initializer.upper = 1.01;
initializer.rtol = 1.0e-4;
initializer.atol = 1.0e-9;
initializer.refine = false;
for (int k = 0; k < nSpecies; k++)
{
initializer.name = inletThermo->speciesName(k);
setComponent(k, initializer);
}
if (doEnergy)
{
initializer.lower = 200;
initializer.upper = 6000;
initializer.rtol = 1;
initializer.atol = 1;
initializer.name = "Temperature";
setComponent(index_T, initializer);
}
}
// destructor
virtual ~PSR() {}
// Specify guesses for the initial values.
virtual doublereal initialValue(size_t i, size_t j)
{
if (i < nSpecies)
return reactorThermo->massFraction(i);
return reactorThermo->temperature();
}
// Specify the residual function. The solver will attempt to find a solution
// sol so that rsd is zero.
void eval(size_t jg, double *sol, double *rsd, int *diag, double rdt)
{
size_t j = 0; // current grid point (only one point in a 0D problem...)
// ----------------------- UPDATE REACTOR STATE -----------------------
reactorThermo->setMassFractions_NoNorm(sol);
// reactorThermo->setMassFractions(sol); -> alternative method, will compare performance between the two
if (doEnergy)
reactorThermo->setState_TP(sol[index_T], PRESSURE); // use the reactor temperature based on the energy equation
else if (fixedTemp)
reactorThermo->setState_TP(fixedTemp, PRESSURE); // use a fixed reactor temperature (if provided)
else
reactorThermo->setState_HP(inletThermo->enthalpy_mass(), PRESSURE); // keep total enthalpy constant, allow Cantera to control reactor temperature
// ----------------------- GET REQ'D PROPERTIES -----------------------
double reactorDensity = reactorThermo->density();
double resTime = volume * reactorDensity / mdot;
doublereal wdot[nSpecies];
reactorKinetics->getNetProductionRates(wdot);
// ----------------------- SPECIES CONSERVATION -----------------------
for (int k = 0; k < nSpecies; k++)
{
//rsd[index(i, j)] = wdot[k] * molecularWeight[k] * volume + mdot * (Y_in[k] - sol[k])
rsd[k] = wdot[k] * (molecularWeight[k] / reactorDensity) - (sol[k] - Y_in[k]) / resTime;
// -------------------- TIME INTEGRATION --------------------------
if (rdt != 0) // rdt is the reciprocal of the time step "dt".
{
rsd[k] -= rdt * (sol[k] - prevSoln(k, j)); // backward euler method (will be extracted from the residual)
diag[k] = 1; // enable time stepping for this solution component (automatically resets each iteration)
}
}
// -------------------------- ENERGY EQUATION -------------------------
if (doEnergy)
{
// ------------------ GET REQ'D PROPERTIES ------------------------
doublereal reactorCp = reactorThermo->cp_mass();
// calculate the specific enthalpy of each species:
doublereal h_in[nSpecies]; // inlet specific enthalpies
inletThermo->getEnthalpy_RT(h_in);
doublereal h[nSpecies]; // reactor specific enthalpies
reactorThermo->getEnthalpy_RT(h);
for (int k = 0; k < nSpecies; k++)
{
h_in[k] *= inletThermo->RT() * inletThermo->moleFraction(k) / inletThermo->meanMolecularWeight();
h[k] *= reactorThermo->RT() * reactorThermo->moleFraction(k) / reactorThermo->meanMolecularWeight();
}
// ----------------------- EVAL ENERGY ----------------------------
double sum1 = 0;
double sum2 = 0;
for (int k = 0; k < nSpecies; k++)
{
sum1 += Y_in[k] * (h_in[k] - h[k]);
sum2 += h[k] * molecularWeight[k] * wdot[k];
}
rsd[index_T] = sum1 / (reactorCp * resTime) - sum2 / (reactorDensity * reactorCp);
// -------------------- TIME INTEGRATION --------------------------
if (rdt != 0)
{
rsd[index_T] -= rdt * (sol[index_T] - prevSoln(index_T, j));
diag[index_T] = 1;
}
}
}
private:
std::shared_ptr<Cantera::ThermoPhase> inletThermo;
std::shared_ptr<Cantera::ThermoPhase> reactorThermo;
std::shared_ptr<Cantera::Kinetics> reactorKinetics;
const int nSpecies;
const int index_T;
const double *Y_in;
Cantera::vector_fp molecularWeight;
bool doEnergy;
double fixedTemp;
double volume;
double mdot;
};
int main()
{
try
{
std::shared_ptr<Cantera::Solution> mixInlet(Cantera::newSolution(MECHANISM));
std::shared_ptr<Cantera::Solution> mixOutlet(Cantera::newSolution(MECHANISM));
mixInlet->thermo()->setState_TPX(300, PRESSURE, "O2:1 CH4:0.5 N2:3.76");
mixOutlet->thermo()->setState_TPX(300, PRESSURE, "O2:1 CH4:0.5 N2:3.76");
mixOutlet->thermo()->equilibrate("HP");
double mdot;
double volume;
std::cout << "Enter mdot: ";
std::cin >> mdot;
std::cout << "Enter volume: ";
std::cin >> volume;
PSR reactor(mixInlet, mixOutlet, mdot, volume);
reactor.solve();
std::cout << mixOutlet->thermo()->report() << "\n";
return 0;
}
catch (Cantera::CanteraError &err)
{
std::cerr << err.what() << std::endl;
return -1;
}
}