276 lines
8.2 KiB
C++
276 lines
8.2 KiB
C++
/*!
|
|
* @file flamespeed.cpp
|
|
* C++ demo program to compute flame speeds using GRI-Mech.
|
|
*
|
|
* @todo This demo compiles but does not run correctly.
|
|
*/
|
|
|
|
#include "cantera/oneD/Sim1D.h"
|
|
#include "cantera/oneD/Inlet1D.h"
|
|
#include "cantera/oneD/StFlow.h"
|
|
#include "cantera/IdealGasMix.h"
|
|
#include "cantera/equilibrium.h"
|
|
#include "cantera/transport.h"
|
|
|
|
using namespace Cantera;
|
|
using std::cout;
|
|
using std::endl;
|
|
|
|
int flamespeed(int np, void* p)
|
|
{
|
|
try {
|
|
int i;
|
|
IdealGasMix gas("gri30.cti","gri30_mix");
|
|
|
|
doublereal temp = 300.0; // K
|
|
doublereal pressure = 1.0*OneAtm; //atm
|
|
doublereal uin=0.3; //m/sec
|
|
|
|
gas.setState_TPX(temp, pressure, "CH4:1.0, O2:2.0, N2:7.52");
|
|
int nsp = gas.nSpecies();
|
|
|
|
vector_fp x;
|
|
x.resize(nsp);
|
|
|
|
double phi = 0.0;
|
|
if (np > 0) {
|
|
phi = *(double*)(p);
|
|
}
|
|
if (phi == 0.0) {
|
|
cout << "Enter phi: ";
|
|
std::cin >> phi;
|
|
}
|
|
|
|
doublereal C_atoms=1.0;
|
|
doublereal H_atoms=4.0;
|
|
doublereal ax=C_atoms+H_atoms/4.0;
|
|
doublereal fa_stoic=1.0/(4.76*ax);
|
|
for (int k=0; k<nsp; k++) {
|
|
if (k==gas.speciesIndex("CH4")) {
|
|
x[k]=1.0;
|
|
} else if (k==gas.speciesIndex("O2")) {
|
|
x[k]=0.21/phi/fa_stoic;
|
|
} else if (k==gas.speciesIndex("N2")) {
|
|
x[k]=0.79/phi/fa_stoic;
|
|
} else {
|
|
x[k]=0.0;
|
|
}
|
|
}
|
|
|
|
gas.setState_TPX(temp,pressure,DATA_PTR(x));
|
|
doublereal rho_in=gas.density();
|
|
|
|
double* yin=new double[nsp];
|
|
gas.getMassFractions(yin);
|
|
|
|
try {
|
|
equilibrate(gas,"HP");
|
|
} catch (CanteraError& err) {
|
|
std::cout << err.what() << std::endl;
|
|
}
|
|
double* yout=new double[nsp];
|
|
gas.getMassFractions(yout);
|
|
doublereal rho_out = gas.density();
|
|
doublereal Tad=gas.temperature();
|
|
cout << phi<<' '<<Tad<<endl;
|
|
|
|
//double Tin=temp;
|
|
//double Tout=Tad;
|
|
//double breakpt=0.2;
|
|
|
|
|
|
//============= build each domain ========================
|
|
|
|
|
|
//-------- step 1: create the flow -------------
|
|
|
|
//AxiStagnFlow flow(&gas);
|
|
FreeFlame flow(&gas);
|
|
|
|
// create an initial grid
|
|
int nz=5;
|
|
doublereal lz=0.02;
|
|
doublereal* z=new double[nz+1];
|
|
doublereal dz=lz/((doublereal)(nz-1));
|
|
for (int iz=0; iz<nz; iz++) {
|
|
z[iz]=((doublereal)iz)*dz;
|
|
}
|
|
//add one node onto end of domain to help with zero gradient at outlet
|
|
z[nz]=lz*1.05;
|
|
nz++;
|
|
|
|
flow.setupGrid(nz, z);
|
|
|
|
// specify the objects to use to compute kinetic rates and
|
|
// transport properties
|
|
|
|
Transport* trmix = newTransportMgr("Mix", &gas);
|
|
Transport* trmulti = newTransportMgr("Multi", &gas);
|
|
|
|
flow.setTransport(*trmix);
|
|
flow.setKinetics(gas);
|
|
flow.setPressure(pressure);
|
|
|
|
//------- step 2: create the inlet -----------------------
|
|
|
|
Inlet1D inlet;
|
|
|
|
inlet.setMoleFractions(DATA_PTR(x));
|
|
doublereal mdot=uin*rho_in;
|
|
inlet.setMdot(mdot);
|
|
inlet.setTemperature(temp);
|
|
|
|
|
|
//------- step 3: create the outlet ---------------------
|
|
|
|
Outlet1D outlet;
|
|
|
|
//=================== create the container and insert the domains =====
|
|
|
|
std::vector<Domain1D*> domains;
|
|
domains.push_back(&inlet);
|
|
domains.push_back(&flow);
|
|
domains.push_back(&outlet);
|
|
|
|
// OneDim flamesim(domains);
|
|
|
|
Sim1D flame(domains);
|
|
|
|
//----------- Supply initial guess----------------------
|
|
|
|
vector_fp locs;
|
|
vector_fp value;
|
|
|
|
locs.resize(3);
|
|
value.resize(3);
|
|
|
|
//ramp values from inlet to adiabatic flame conditions
|
|
// over 70% of domain and then level off at equilibrium
|
|
double z1=0.7;
|
|
|
|
double uout;
|
|
uout=inlet.mdot()/rho_out;
|
|
uin=inlet.mdot()/rho_in;
|
|
locs[0]=0.0;
|
|
locs[1]=z1;
|
|
locs[2]=1.0;
|
|
value[0]=uin;
|
|
value[1]=uout;
|
|
value[2]=uout;
|
|
flame.setInitialGuess("u",locs,value);
|
|
|
|
value[0]=temp;
|
|
value[1]=Tad;
|
|
value[2]=Tad;
|
|
flame.setInitialGuess("T",locs,value);
|
|
|
|
for (i=0; i<nsp; i++) {
|
|
value[0]=yin[i];
|
|
value[1]=yout[i];
|
|
value[2]=yout[i];
|
|
flame.setInitialGuess(gas.speciesName(i),locs,value);
|
|
}
|
|
|
|
inlet.setMoleFractions(DATA_PTR(x));
|
|
inlet.setMdot(mdot);
|
|
inlet.setTemperature(temp);
|
|
|
|
flame.showSolution();
|
|
|
|
int flowdomain=1;
|
|
double ratio=10.0;
|
|
double slope=0.2;
|
|
double curve=0.02;
|
|
double prune=-0.00005;
|
|
|
|
flame.setRefineCriteria(flowdomain,ratio,slope,curve,prune);
|
|
|
|
int loglevel=1;
|
|
bool refine_grid = true;
|
|
|
|
/* Solve species*/
|
|
//flow.fixTemperature();
|
|
//refine_grid=false;
|
|
//flame.solve(loglevel,refine_grid);
|
|
|
|
/* Solve freely propagating flame*/
|
|
|
|
/* Linearly interpolate to find location where this
|
|
temperature would exist. The temperature at this
|
|
location will then be fixed for remainder of
|
|
calculation.*/
|
|
|
|
flow.fixTemperature();
|
|
refine_grid=false;
|
|
flame.setFixedTemperature(900.0);
|
|
// flame.setAdiabaticFlame();
|
|
flame.solve(loglevel,refine_grid);
|
|
refine_grid = true;
|
|
flow.solveEnergyEqn();
|
|
flame.solve(loglevel,refine_grid);
|
|
double flameSpeed_mix = flame.value(flowdomain,flow.componentIndex("u"),0);
|
|
cout << "Flame speed with mixture-averaged transport: " <<
|
|
flame.value(flowdomain,flow.componentIndex("u"),0) << " m/s" << endl;
|
|
|
|
// now switch to multicomponent transport
|
|
flow.setTransport(*trmulti);
|
|
flame.solve(loglevel, refine_grid);
|
|
double flameSpeed_multi = flame.value(flowdomain,flow.componentIndex("u"),0);
|
|
cout << "Flame speed with multicomponent transport: " <<
|
|
flame.value(flowdomain,flow.componentIndex("u"),0) << " m/s" << endl;
|
|
|
|
// now enable Soret diffusion
|
|
flow.enableSoret(true);
|
|
flame.solve(loglevel, refine_grid);
|
|
double flameSpeed_full = flame.value(flowdomain,flow.componentIndex("u"),0);
|
|
cout << "Flame speed with multicomponent transport + Soret: " <<
|
|
flame.value(flowdomain,flow.componentIndex("u"),0) << " m/s" << endl;
|
|
|
|
int np=flow.nPoints();
|
|
std::vector<doublereal> zvec,Tvec,COvec,CO2vec,Uvec;
|
|
|
|
printf("\n%9s\t%8s\t%5s\t%7s\n","z (m)", "T (K)", "U (m/s)", "Y(CO)");
|
|
for (int n=0; n<np; n++) {
|
|
Tvec.push_back(flame.value(flowdomain,flow.componentIndex("T"),n));
|
|
COvec.push_back(flame.value(flowdomain,flow.componentIndex("CO"),n));
|
|
CO2vec.push_back(flame.value(flowdomain,flow.componentIndex("CO2"),n));
|
|
Uvec.push_back(flame.value(flowdomain,flow.componentIndex("u"),n));
|
|
zvec.push_back(flow.grid(n));
|
|
printf("%9.6f\t%8.3f\t%5.3f\t%7.5f\n",flow.grid(n),Tvec[n],Uvec[n],COvec[n]);
|
|
}
|
|
|
|
cout << endl<<"Adiabatic flame temperature from equilibrium is: "<<Tad<<endl;
|
|
cout << "Flame speed for phi="<<phi<<" is "<<Uvec[0]<<" m/s."<<endl;
|
|
|
|
std::string reportFile = "flamespeed.csv";
|
|
FILE* FP = fopen(reportFile.c_str(), "w");
|
|
if (!FP) {
|
|
printf("Failure to open file\n");
|
|
exit(-1);
|
|
}
|
|
|
|
fprintf(FP," Flame speed (mixture-averaged ) = %11.3e m/s\n", flameSpeed_mix);
|
|
fprintf(FP," Flame speed (multicomponent ) = %11.3e m/s\n", flameSpeed_multi);
|
|
fprintf(FP," Flame speed (multicomponent + Soret) = %11.3e m/s\n", flameSpeed_full);
|
|
fprintf(FP," Grid, Temperature, Uvec, CO, CO2\n");
|
|
for (int n = 0; n < np; n++) {
|
|
fprintf(FP," %11.3e, %11.3e, %11.3e, %11.3e, %11.3e\n",
|
|
flow.grid(n), Tvec[n], Uvec[n], COvec[n], CO2vec[n]);
|
|
}
|
|
fclose(FP);
|
|
|
|
return 0;
|
|
} catch (CanteraError& err) {
|
|
std::cerr << err.what() << std::endl;
|
|
std::cerr << "program terminating." << endl;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
#ifndef CXX_DEMO
|
|
int main()
|
|
{
|
|
return flamespeed(0, 0);
|
|
}
|
|
#endif
|
|
|