Skip to content

Commit 8a9b719

Browse files
committed
initial work on Radiation1D class
1 parent 29de0fb commit 8a9b719

File tree

6 files changed

+337
-184
lines changed

6 files changed

+337
-184
lines changed

include/cantera/oneD/Flow1D.h

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
#define CT_FLOW1D_H
88

99
#include "Domain1D.h"
10+
#include "Radiation1D.h"
1011
#include "cantera/base/Array.h"
1112
#include "cantera/base/Solution.h"
1213
#include "cantera/thermo/ThermoPhase.h"
1314
#include "cantera/kinetics/Kinetics.h"
1415

16+
1517
namespace Cantera
1618
{
1719

@@ -259,12 +261,12 @@ class Flow1D : public Domain1D
259261

260262
//! Return emissivity at left boundary
261263
double leftEmissivity() const {
262-
return m_epsilon_left;
264+
return m_radiation->leftEmissivity();
263265
}
264266

265267
//! Return emissivity at right boundary
266268
double rightEmissivity() const {
267-
return m_epsilon_right;
269+
return m_radiation->rightEmissivity();
268270
}
269271

270272
//! Specify that the the temperature should be held fixed at point `j`.
@@ -461,38 +463,8 @@ class Flow1D : public Domain1D
461463
//! to be updated are defined.
462464
virtual void updateProperties(size_t jg, double* x, size_t jmin, size_t jmax);
463465

464-
/**
465-
* Computes the radiative heat loss vector over points jmin to jmax and stores
466-
* the data in the qdotRadiation variable.
467-
*
468-
* The `fit-type` of `polynomial` is uses the model described below.
469-
*
470-
* The simple radiation model used was established by Liu and Rogg
471-
* @cite liu1991. This model considers the radiation of CO2 and H2O.
472-
*
473-
* This model uses the optically thin limit and the gray-gas approximation to
474-
* simply calculate a volume specified heat flux out of the Planck absorption
475-
* coefficients, the boundary emissivities and the temperature. Polynomial lines
476-
* calculate the species Planck coefficients for H2O and CO2. The data for the
477-
* lines are taken from the RADCAL program @cite RADCAL.
478-
* The coefficients for the polynomials are taken from
479-
* [TNF Workshop](https://tnfworkshop.org/radiation/) material.
480-
*
481-
*
482-
* The `fit-type` of `table` is uses the model described below.
483-
*
484-
* Spectra for molecules are downloaded with HAPI library from // https://hitran.org/hapi/
485-
* [R.V. Kochanov, I.E. Gordon, L.S. Rothman, P. Wcislo, C. Hill, J.S. Wilzewski,
486-
* HITRAN Application Programming Interface (HAPI): A comprehensive approach
487-
* to working with spectroscopic data, J. Quant. Spectrosc. Radiat. Transfer 177,
488-
* 15-30 (2016), https://doi.org/10.1016/j.jqsrt.2016.03.005].
489-
*
490-
* Planck mean optical path lengths are what are read in from a YAML input file.
491-
*
492-
*
493-
*
494-
*/
495-
void computeRadiation(double* x, size_t jmin, size_t jmax);
466+
//! Compute the radiative heat loss at each grid point
467+
void computeRadiation(double*, size_t, size_t);
496468

497469
//! @}
498470

@@ -918,6 +890,9 @@ class Flow1D : public Domain1D
918890
//! radiative heat loss.
919891
double m_epsilon_right = 0.0;
920892

893+
//! Radiation object used for calculating radiative heat loss
894+
std::unique_ptr<Radiation1D> m_radiation;
895+
921896
//! Indices within the ThermoPhase of the radiating species. First index is
922897
//! for CO2, second is for H2O.
923898
vector<size_t> m_kRadiating;
@@ -968,16 +943,6 @@ class Flow1D : public Domain1D
968943
//! radiative heat loss vector
969944
vector<double> m_qdotRadiation;
970945

971-
// boundary emissivities for the radiation calculations
972-
double m_epsilon_left = 0.0;
973-
double m_epsilon_right = 0.0;
974-
975-
std::map<std::string, int> m_absorptionSpecies; //!< Absorbing species
976-
AnyMap m_PMAC; //!< Absorption coefficient data for each species
977-
978-
// Old radiation variable that can not be deleted for some reason
979-
std::vector<size_t> m_kRadiating;
980-
981946
// fixed T and Y values
982947
//! Fixed values of the temperature at each grid point that are used when solving
983948
//! with the energy equation disabled.
@@ -1032,5 +997,4 @@ class Flow1D : public Domain1D
1032997
};
1033998

1034999
}
1035-
10361000
#endif

include/cantera/oneD/Radiation1D.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! @file Radiation1D.h
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+
6+
#ifndef RADIATION1D_H
7+
#define RADIATION1D_H
8+
9+
#include "Domain1D.h"
10+
#include "cantera/base/Array.h"
11+
#include "cantera/thermo/ThermoPhase.h"
12+
13+
#include <functional>
14+
15+
namespace Cantera
16+
{
17+
18+
/**
19+
* Computes the radiative heat loss vector over points jmin to jmax and stores
20+
* the data in the qdotRadiation variable.
21+
*
22+
* The `fit-type` of `polynomial` is uses the model described below.
23+
*
24+
* The simple radiation model used was established by Liu and Rogg
25+
* @cite liu1991. This model considers the radiation of CO2 and H2O.
26+
*
27+
* This model uses the optically thin limit and the gray-gas approximation to
28+
* simply calculate a volume specified heat flux out of the Planck absorption
29+
* coefficients, the boundary emissivities and the temperature. Polynomial lines
30+
* calculate the species Planck coefficients for H2O and CO2. The data for the
31+
* lines are taken from the RADCAL program @cite RADCAL.
32+
* The coefficients for the polynomials are taken from
33+
* [TNF Workshop](https://tnfworkshop.org/radiation/) material.
34+
*
35+
*
36+
* The `fit-type` of `table` is uses the model described below.
37+
*
38+
* Spectra for molecules are downloaded with HAPI library from // https://hitran.org/hapi/
39+
* [R.V. Kochanov, I.E. Gordon, L.S. Rothman, P. Wcislo, C. Hill, J.S. Wilzewski,
40+
* HITRAN Application Programming Interface (HAPI): A comprehensive approach
41+
* to working with spectroscopic data, J. Quant. Spectrosc. Radiat. Transfer 177,
42+
* 15-30 (2016), https://doi.org/10.1016/j.jqsrt.2016.03.005].
43+
*
44+
* Planck mean optical path lengths are what are read in from a YAML input file.
45+
*/
46+
class Radiation1D {
47+
public:
48+
Radiation1D(ThermoPhase* thermo, double pressure, size_t points,
49+
std::function<double(const double*, size_t)> temperatureFunction,
50+
std::function<double(const double*, size_t, size_t)> moleFractionFunction);
51+
52+
// Parse radiation data from YAML input
53+
void parseRadiationData();
54+
55+
// Compute radiative heat loss
56+
void computeRadiation(double* x, size_t jmin, size_t jmax, vector<double>& qdotRadiation);
57+
58+
//! Set the emissivities for the boundary values
59+
/*!
60+
* Reads the emissivities for the left and right boundary values in the
61+
* radiative term and writes them into the variables, which are used for the
62+
* calculation.
63+
*/
64+
void setBoundaryEmissivities(double e_left, double e_right);
65+
66+
//! Return emissivity at left boundary
67+
double leftEmissivity() const {
68+
return m_epsilon_left;
69+
}
70+
71+
//! Return emissivity at right boundary
72+
double rightEmissivity() const {
73+
return m_epsilon_right;
74+
}
75+
76+
private:
77+
ThermoPhase* m_thermo;
78+
double m_press;
79+
size_t m_points;
80+
81+
map<string, size_t> m_absorptionSpecies; //!< Absorbing species
82+
AnyMap m_PMAC; //!< Absorption coefficient data for each species
83+
84+
//! Emissivity of the surface to the left of the domain. Used for calculating
85+
//! radiative heat loss.
86+
double m_epsilon_left = 0.0;
87+
88+
//! Emissivity of the surface to the right of the domain. Used for calculating
89+
//! radiative heat loss.
90+
double m_epsilon_right = 0.0;
91+
92+
// Helper functions
93+
double calculatePolynomial(const vector<double>& coefficients, double temperature);
94+
double interpolateTable(const vector<double>& temperatures, const vector<double>& data, double temperature);
95+
96+
//! Lambda function to get temperature at a given point
97+
std::function<double(const double*, size_t)> m_T;
98+
99+
//! Lambda function to get mole fraction at a given point
100+
std::function<double(const double*, size_t, size_t)> m_X;
101+
};
102+
103+
} // namespace Cantera
104+
105+
#endif // RADIATION1D_H

src/oneD/Flow1D.cpp

Lines changed: 6 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ namespace Cantera
2020

2121
Flow1D::Flow1D(ThermoPhase* ph, size_t nsp, size_t points) :
2222
Domain1D(nsp+c_offset_Y, points),
23-
m_nsp(nsp)
23+
m_nsp(nsp),
24+
m_radiation(make_unique<Radiation1D>(ph, ph->pressure(), points,
25+
[this](const double* x, size_t j) {return this->T(x,j);},
26+
[this](const double* x, size_t k, size_t j) {return this->X(x,k,j);}))
2427
{
2528
m_points = points;
2629

@@ -82,70 +85,6 @@ Flow1D::Flow1D(ThermoPhase* ph, size_t nsp, size_t points) :
8285
gr.push_back(1.0*ng/m_points);
8386
}
8487
setupGrid(m_points, gr.data());
85-
86-
// Parse radiation data from the YAML file
87-
for (auto& name : m_thermo->speciesNames()) {
88-
auto& data = m_thermo->species(name)->input;
89-
if (data.hasKey("radiation")) {
90-
cout << "Radiation data found for species " << name << endl;
91-
m_absorptionSpecies.insert({name, m_thermo->speciesIndex(name)});
92-
if (data["radiation"].hasKey("fit-type")) {
93-
m_PMAC[name]["fit-type"] = data["radiation"]["fit-type"].asString();
94-
} else {
95-
throw InputFileError("Flow1D::Flow1D", data,
96-
"No 'fit-type' entry found for species '{}'", name);
97-
}
98-
99-
// This is the direct tabulation of the optical path length
100-
if (data["radiation"]["fit-type"] == "table") {
101-
if (data["radiation"].hasKey("temperatures")) {
102-
cout << "Storing temperatures for species " << name << endl;
103-
// Each species may have a specific set of temperatures that are used
104-
m_PMAC[name]["temperatures"] = data["radiation"]["temperatures"].asVector<double>();
105-
} else {
106-
throw InputFileError("Flow1D::Flow1D", data,
107-
"No 'temperatures' entry found for species '{}'", name);
108-
}
109-
if (data["radiation"].hasKey("data")) {
110-
cout << "Storing data for species " << name << endl;
111-
// This data is the Plank mean absorption coefficient
112-
m_PMAC[name]["coefficients"] = data["radiation"]["data"].asVector<double>();
113-
} else {
114-
throw InputFileError("Flow1D::Flow1D", data,
115-
"No 'data' entry found for species '{}'", name);
116-
}
117-
} else if (data["radiation"]["fit-type"] == "polynomial") {
118-
cout << "Polynomial fit found for species " << name << endl;
119-
if (data["radiation"].hasKey("data")) {
120-
cout << "Storing data for species " << name << endl;
121-
m_PMAC[name]["coefficients"] = data["radiation"]["data"].asVector<double>();
122-
} else {
123-
throw InputFileError("Flow1D::Flow1D", data,
124-
"No 'data' entry found for species '{}'", name);
125-
}
126-
} else {
127-
throw InputFileError("Flow1D::Flow1D", data,
128-
"Invalid 'fit-type' entry found for species '{}'", name);
129-
}
130-
}
131-
}
132-
133-
// Polynomial coefficients for CO2 and H2O (backwards compatibility)
134-
// Check if "CO2" is already in the map, if not, add the polynomial fit data
135-
if (!m_PMAC.hasKey("CO2")) {
136-
const std::vector<double> c_CO2 = {18.741, -121.310, 273.500, -194.050, 56.310,
137-
-5.8169};
138-
m_PMAC["CO2"]["fit-type"] = "polynomial";
139-
m_PMAC["CO2"]["coefficients"] = c_CO2;
140-
}
141-
142-
// Check if "H2O" is already in the map, if not, add the polynomial fit data
143-
if (!m_PMAC.hasKey("H2O")) {
144-
const std::vector<double> c_H2O = {-0.23093, -1.12390, 9.41530, -2.99880,
145-
0.51382, -1.86840e-5};
146-
m_PMAC["H2O"]["fit-type"] = "polynomial";
147-
m_PMAC["H2O"]["coefficients"] = c_H2O;
148-
}
14988
}
15089

15190
Flow1D::Flow1D(shared_ptr<ThermoPhase> th, size_t nsp, size_t points)
@@ -527,70 +466,7 @@ void Flow1D::updateDiffFluxes(const double* x, size_t j0, size_t j1)
527466

528467
void Flow1D::computeRadiation(double* x, size_t jmin, size_t jmax)
529468
{
530-
// Variable definitions for the Planck absorption coefficient and the
531-
// radiation calculation:
532-
double k_P_ref = 1.0*OneAtm;
533-
534-
// Calculation of the two boundary values
535-
double boundary_Rad_left = m_epsilon_left * StefanBoltz * pow(T(x, 0), 4);
536-
double boundary_Rad_right = m_epsilon_right * StefanBoltz * pow(T(x, m_points - 1), 4);
537-
538-
double coef = 0.0;
539-
for (size_t j = jmin; j < jmax; j++) {
540-
// calculation of the mean Planck absorption coefficient
541-
double k_P = 0;
542-
543-
for(const auto& [sp_name, sp_idx] : m_absorptionSpecies) {
544-
if (m_PMAC[sp_name]["fit-type"].asString() == "table") {
545-
// temperature table interval search
546-
int T_index = 0;
547-
const int OPL_table_size = m_PMAC[sp_name]["temperatures"].asVector<double>().size();
548-
for (int k = 0; k < OPL_table_size; k++) {
549-
if (T(x, j) < m_PMAC[sp_name]["temperatures"].asVector<double>()[k]) {
550-
if (T(x, j) < m_PMAC[sp_name]["temperatures"].asVector<double>()[0]) {
551-
T_index = 0; //lower table limit
552-
}
553-
else {
554-
T_index = k;
555-
}
556-
break;
557-
}
558-
else {
559-
T_index=OPL_table_size-1; //upper table limit
560-
}
561-
}
562-
563-
// absorption coefficient for specie
564-
double k_P_specie = 0.0;
565-
if ((T_index == 0) || (T_index == OPL_table_size-1)) {
566-
coef=log(1.0/m_PMAC[sp_name]["coefficients"].asVector<double>()[T_index]);
567-
}
568-
else {
569-
coef=log(1.0/m_PMAC[sp_name]["coefficients"].asVector<double>()[T_index-1])+
570-
(log(1.0/m_PMAC[sp_name]["coefficients"].asVector<double>()[T_index])-log(1.0/m_PMAC[sp_name]["coefficients"].asVector<double>()[T_index-1]))*
571-
(T(x, j)-m_PMAC[sp_name]["temperatures"].asVector<double>()[T_index-1])/(m_PMAC[sp_name]["temperatures"].asVector<double>()[T_index]-m_PMAC[sp_name]["temperatures"].asVector<double>()[T_index-1]);
572-
}
573-
k_P_specie = exp(coef);
574-
575-
k_P_specie /= k_P_ref;
576-
k_P += m_press * X(x, m_absorptionSpecies[sp_name], j) * k_P_specie;
577-
} else if (m_PMAC[sp_name]["fit-type"].asString() == "polynomial") {
578-
double k_P_specie = 0.0;
579-
for (size_t n = 0; n <= 5; n++) {
580-
k_P_specie += m_PMAC[sp_name]["coefficients"].asVector<double>()[n] * pow(1000 / T(x, j), (double) n);
581-
}
582-
k_P_specie /= k_P_ref;
583-
k_P += m_press * X(x, m_absorptionSpecies[sp_name], j) * k_P_specie;
584-
}
585-
}
586-
587-
// Calculation of the radiative heat loss term
588-
double radiative_heat_loss = 2 * k_P *(2 * StefanBoltz * pow(T(x, j), 4)
589-
- boundary_Rad_left - boundary_Rad_right);
590-
591-
// set the radiative heat loss vector
592-
m_qdotRadiation[j] = radiative_heat_loss;
593-
}
469+
m_radiation->computeRadiation(x, jmin, jmax, m_qdotRadiation);
594470
}
595471

596472
void Flow1D::evalContinuity(double* x, double* rsd, int* diag,
@@ -1182,16 +1058,7 @@ bool Flow1D::doElectricField(size_t j) const
11821058

11831059
void Flow1D::setBoundaryEmissivities(double e_left, double e_right)
11841060
{
1185-
if (e_left < 0 || e_left > 1) {
1186-
throw CanteraError("Flow1D::setBoundaryEmissivities",
1187-
"The left boundary emissivity must be between 0.0 and 1.0!");
1188-
} else if (e_right < 0 || e_right > 1) {
1189-
throw CanteraError("Flow1D::setBoundaryEmissivities",
1190-
"The right boundary emissivity must be between 0.0 and 1.0!");
1191-
} else {
1192-
m_epsilon_left = e_left;
1193-
m_epsilon_right = e_right;
1194-
}
1061+
m_radiation->setBoundaryEmissivities(e_left, e_right);
11951062
}
11961063

11971064
void Flow1D::fixTemperature(size_t j)

0 commit comments

Comments
 (0)