[unittest] Implement gtest for 'test-clib'

Unit tests cover 'clib' from within C++ googletest code
- port test-clib to google tests
- rename pre-existing version to test-clib-demo
- add tests indirectly probing SharedCabinet
- add tests for Solution access routines
- add tests for ctonedim
This commit is contained in:
Ingmar Schoegl 2023-02-28 21:33:20 -06:00
parent d84af4315c
commit 1f16bf247e
6 changed files with 553 additions and 2 deletions

View File

@ -281,6 +281,7 @@ def addMatlabTest(script, testName, dependencies=None, env_vars=()):
return run_program
# Instantiate tests
addTestProgram('clib', 'clib')
addTestProgram('general', 'general')
addTestProgram('thermo', 'thermo')
addTestProgram('equil', 'equil')

227
test/clib/test_clib.cpp Normal file
View File

@ -0,0 +1,227 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <fstream>
#include "cantera/core.h"
#include "cantera/clib/ct.h"
using namespace Cantera;
using ::testing::HasSubstr;
string reportError()
{
int buflen = 0;
char* output_buf = 0;
buflen = ct_getCanteraError(buflen, output_buf) + 1;
output_buf = new char[buflen];
ct_getCanteraError(buflen, output_buf);
string err = output_buf;
delete[] output_buf;
return err;
}
TEST(ct, cabinet_exceptions)
{
soln_newSolution("h2o2.yaml", "ohmech", "");
soln_name(999, 0, 0);
string err = reportError();
EXPECT_THAT(err, HasSubstr("Index 999 out of range."));
int ret = soln_del(999);
ASSERT_EQ(ret, -1);
err = reportError();
EXPECT_THAT(err, HasSubstr("delete a non-existing object."));
ct_resetStorage();
ret = soln_del(0);
ASSERT_EQ(ret, -1);
err = reportError();
EXPECT_THAT(err, HasSubstr("delete a non-existing object."));
}
TEST(ct, new_solution)
{
string name = "ohmech";
int ref = soln_newSolution("h2o2.yaml", name.c_str(), "");
int buflen = soln_name(ref, 0, 0) + 1; // include \0
ASSERT_EQ(buflen, int(name.size() + 1));
char* buf = new char[buflen];
soln_name(ref, buflen, buf);
string solName = buf;
ASSERT_EQ(solName, name);
delete[] buf;
}
TEST(ct, soln_objects)
{
ct_resetStorage();
thermo_newFromFile("gri30.yaml", "gri30");
int ref = soln_newSolution("gri30.yaml", "gri30", "none");
ASSERT_EQ(ref, 0);
int ref2 = soln_newSolution("h2o2.yaml", "ohmech", "Mix");
ASSERT_EQ(ref2, 1);
int thermo = soln_thermo(ref2);
ASSERT_EQ(thermo, 2);
ASSERT_EQ(thermo_nSpecies(thermo), 10);
int kin = soln_kinetics(ref2);
ASSERT_EQ(kin, 1);
ASSERT_EQ(kin_nReactions(kin), 29);
int trans = soln_kinetics(ref2);
ASSERT_EQ(trans, 1);
soln_del(ref2);
size_t nsp = thermo_nSpecies(thermo);
ASSERT_EQ(nsp, npos);
string err = reportError();
EXPECT_THAT(err, HasSubstr("has been deleted."));
size_t nr = thermo_nSpecies(thermo);
ASSERT_EQ(nr, npos);
err = reportError();
EXPECT_THAT(err, HasSubstr("has been deleted."));
}
TEST(ct, new_interface)
{
ct_resetStorage();
int sol = soln_newSolution("ptcombust.yaml", "gas", "");
ASSERT_EQ(sol, 0);
std::vector<int> adj{sol};
int surf = soln_newInterface("ptcombust.yaml", "Pt_surf", 1, adj.data());
ASSERT_EQ(surf, 1);
int ph_surf = soln_thermo(surf);
int buflen = soln_name(ph_surf, 0, 0) + 1; // include \0
char* buf = new char[buflen];
soln_name(ph_surf, buflen, buf);
string solName = buf;
ASSERT_EQ(solName, "Pt_surf");
delete[] buf;
int kin_surf = soln_kinetics(surf);
buflen = kin_getType(kin_surf, 0, 0) + 1; // include \0
buf = new char[buflen];
kin_getType(ph_surf, buflen, buf);
string kinType = buf;
ASSERT_EQ(kinType, "Surf");
delete[] buf;
}
TEST(ct, new_interface_auto)
{
ct_resetStorage();
std::vector<int> adj;
int surf = soln_newInterface("ptcombust.yaml", "Pt_surf", 0, adj.data());
ASSERT_EQ(surf, 1);
ASSERT_EQ(soln_nAdjacent(surf), 1);
int gas = soln_adjacent(surf, 0);
ASSERT_EQ(gas, 0);
int buflen = soln_name(gas, 0, 0) + 1; // include \0
char* buf = new char[buflen];
soln_name(gas, buflen, buf);
string solName = buf;
ASSERT_EQ(solName, "gas");
delete[] buf;
}
TEST(ct, thermo)
{
int ret;
int thermo = thermo_newFromFile("gri30.yaml", "gri30");
ASSERT_GE(thermo, 0);
size_t nsp = thermo_nSpecies(thermo);
ASSERT_EQ(nsp, 53);
ret = thermo_setTemperature(thermo, 500);
ASSERT_EQ(ret, 0);
ret = thermo_setPressure(thermo, 5 * 101325);
ASSERT_EQ(ret, 0);
ret = thermo_setMoleFractionsByName(thermo, "CH4:1.0, O2:2.0, N2:7.52");
ASSERT_EQ(ret, 0);
ret = thermo_equilibrate(thermo, "HP", 0, 1e-9, 50000, 1000, 0);
ASSERT_EQ(ret, 0);
double T = thermo_temperature(thermo);
ASSERT_GT(T, 2200);
ASSERT_LT(T, 2300);
}
TEST(ct, kinetics)
{
int thermo = thermo_newFromFile("gri30.yaml", "gri30");
int kin = kin_newFromFile("gri30.yaml", "gri30", thermo, -1, -1, -1, -1);
ASSERT_GE(kin, 0);
size_t nr = kin_nReactions(kin);
ASSERT_EQ(nr, 325);
thermo_equilibrate(thermo, "HP", 0, 1e-9, 50000, 1000, 0);
double T = thermo_temperature(thermo);
thermo_setTemperature(thermo, T - 200);
auto sol = newSolution("gri30.yaml", "gri30", "none");
auto phase = sol->thermo();
auto kinetics = sol->kinetics();
phase->equilibrate("HP");
ASSERT_NEAR(T, phase->temperature(), 1e-2);
phase->setTemperature(T - 200);
vector<double> c_ropf(nr);
kin_getFwdRatesOfProgress(kin, 325, c_ropf.data());
vector<double> cpp_ropf(nr);
kinetics->getFwdRatesOfProgress(cpp_ropf.data());
for (size_t n = 0; n < nr; n++) {
ASSERT_NEAR(cpp_ropf[n], c_ropf[n], 1e-6);
}
}
TEST(ct, transport)
{
int thermo = thermo_newFromFile("gri30.yaml", "gri30");
int tran = trans_newDefault(thermo, 0);
ASSERT_GE(tran, 0);
size_t nsp = thermo_nSpecies(thermo);
vector<double> c_dkm(nsp);
int ret = trans_getMixDiffCoeffs(tran, 53, c_dkm.data());
ASSERT_EQ(ret, 0);
vector<double> cpp_dkm(nsp);
auto sol = newSolution("gri30.yaml", "gri30");
auto transport = sol->transport();
transport->getMixDiffCoeffs(cpp_dkm.data());
for (size_t n = 0; n < nsp; n++) {
ASSERT_NEAR(cpp_dkm[n], c_dkm[n], 1e-10);
}
}
int main(int argc, char** argv)
{
printf("Running main() from test_clib.cpp\n");
testing::InitGoogleTest(&argc, argv);
make_deprecation_warnings_fatal();
string fileName = "gtest-freeflame.h5";
if (std::ifstream(fileName).good()) {
std::remove(fileName.c_str());
}
int result = RUN_ALL_TESTS();
appdelete();
return result;
}

206
test/clib/test_ctonedim.cpp Normal file
View File

@ -0,0 +1,206 @@
#include <gtest/gtest.h>
#include "cantera/core.h"
#include "cantera/thermo/ThermoFactory.h"
#include "cantera/clib/ct.h"
#include "cantera/clib/ctonedim.h"
using namespace Cantera;
TEST(ctonedim, freeflow)
{
ct_resetStorage();
int sol = soln_newSolution("h2o2.yaml", "ohmech", "Mix");
int ph = soln_thermo(sol);
ASSERT_GE(ph, 0);
int kin = soln_kinetics(sol);
ASSERT_GE(kin, 0);
int tr = soln_transport(sol);
ASSERT_GE(tr, 0);
double T = 1050;
double P = 5 * 101325;
string X = "CH4:1.0, O2:2.0, N2:7.52";
thermo_setMoleFractionsByName(ph, X.c_str());
thermo_setTemperature(ph, T);
thermo_setPressure(ph, P);
int itype = 2; // free flow
int flow = stflow_new(ph, kin, tr, itype);
ASSERT_GE(flow, 0);
ASSERT_NEAR(stflow_pressure(flow), P, 1e-5);
}
TEST(ctonedim, inlet)
{
int index = inlet_new();
ASSERT_GE(index, 0);
}
TEST(ctonedim, outlet)
{
int index = outlet_new();
ASSERT_GE(index, 0);
}
TEST(ctonedim, reacting_surface)
{
int index = reactingsurf_new();
ASSERT_GE(index, 0);
int gas = thermo_newFromFile("ptcombust.yaml", "gas");
int surf = thermo_newFromFile("ptcombust.yaml", "Pt_surf");
int kin = kin_newFromFile("ptcombust.yaml", "Pt_surf", surf, gas, -1, -1, -1);
ASSERT_GE(kin, 0);
int ret = reactingsurf_setkineticsmgr(index, kin);
ASSERT_EQ(ret, 0);
}
TEST(ctonedim, catcomb_stack)
{
int sol = soln_newSolution("ptcombust.yaml", "gas", "");
int gas = soln_thermo(sol);
int gas_kin = soln_kinetics(sol);
int trans = soln_transport(sol);
int surf = thermo_newFromFile("ptcombust.yaml", "Pt_surf");
int surf_kin = kin_newFromFile("ptcombust.yaml", "Pt_surf", surf, gas, -1, -1, -1);
// inlet
int inlet = inlet_new();
// flow
int itype = 1; // free flow
int flow = stflow_new(gas, gas_kin, trans, itype);
domain_setID(flow, "flow");
// reacting surface
int reac_surf = reactingsurf_new();
int ret = reactingsurf_setkineticsmgr(reac_surf, surf_kin);
ASSERT_EQ(ret, 0);
// set up stack
std::vector<int> doms{inlet, flow, reac_surf};
int flame = sim1D_new(3, doms.data());
ASSERT_GE(flame, 0);
int dom = sim1D_domainIndex(flame, "flow");
ASSERT_GE(dom, 0);
}
TEST(ctonedim, freeflame_from_parts)
{
ct_resetStorage();
auto gas = newThermo("h2o2.yaml", "ohmech");
int sol = soln_newSolution("h2o2.yaml", "ohmech", "Mix");
int ph = soln_thermo(sol);
int kin = soln_kinetics(sol);
int tr = soln_transport(sol);
size_t nsp = thermo_nSpecies(ph);
// reactants
double uin = .3;
double T = 300;
double P = 101325;
string X = "H2:0.65, O2:0.5, AR:2";
thermo_setMoleFractionsByName(ph, X.c_str());
thermo_setTemperature(ph, T);
thermo_setPressure(ph, P);
double rho_in = thermo_density(ph);
vector<double> yin(nsp);
thermo_getMassFractions(ph, nsp, yin.data());
// product estimate
int ret = thermo_equilibrate(ph, "HP", 0, 1e-9, 50000, 1000, 0);
ASSERT_GE(ret, 0);
double rho_out = thermo_density(ph);
double Tad = thermo_temperature(ph);
vector<double> yout(nsp);
thermo_getMassFractions(ph, nsp, yout.data());
// flow
int itype = 2; // free flow
int flow = stflow_new(ph, kin, tr, itype);
domain_setID(flow, "flow");
// grid
int nz = 21;
double lz = 0.02;
vector_fp z(nz);
double dz = lz;
dz /= (double)(nz - 1);
for (int iz = 0; iz < nz; iz++) {
z[iz] = iz * dz;
}
domain_setupGrid(flow, nz, z.data());
// inlet
int reac = inlet_new();
domain_setID(reac, "inlet");
bdry_setMoleFractions(reac, X.c_str());
bdry_setMdot(reac, uin * rho_in);
bdry_setTemperature(reac, T);
// outlet
int prod = outlet_new();
domain_setID(prod, "outlet");
double uout = bdry_mdot(reac) / rho_out;
// set up stack
std::vector<int> doms{reac, flow, prod};
int flame = sim1D_new(3, doms.data());
int dom = sim1D_domainIndex(flame, "flow");
// set up initial guess
vector<double> locs{0.0, 0.3, 0.7, 1.0};
vector<double> value{uin, uin, uout, uout};
int comp = domain_componentIndex(dom, "velocity");
sim1D_setProfile(flame, dom, comp, 4, locs.data(), 4, value.data());
value = {T, T, Tad, Tad};
comp = domain_componentIndex(dom, "T");
sim1D_setProfile(flame, dom, comp, 4, locs.data(), 4, value.data());
for (size_t i = 0; i < nsp; i++) {
value = {yin[i], yin[i], yout[i], yout[i]};
int buflen = thermo_getSpeciesName(ph, i, 0, 0) + 1; // include \0
char* buf = new char[buflen];
thermo_getSpeciesName(ph, i, buflen, buf);
string name = buf;
ASSERT_EQ(name, gas->speciesName(i));
comp = domain_componentIndex(dom, buf);
sim1D_setProfile(flame, dom, comp, 4, locs.data(), 4, value.data());
}
// simulation settings
double ratio = 15.0;
double slope = 0.3;
double curve = 0.5;
sim1D_setRefineCriteria(flame, dom, ratio, slope, curve, 0.);
sim1D_setFixedTemperature(flame, 0.85 * T + .15 * Tad);
// solve and save
stflow_solveEnergyEqn(flow, 1);
bool refine_grid = false;
int loglevel = 0;
sim1D_solve(flame, loglevel, refine_grid);
ret = sim1D_save(flame, "gtest-freeflame.yaml", "clib",
"Solution from CLib interface");
ASSERT_GE(ret, 0);
if (usesHDF5()) {
ret = sim1D_save(flame, "gtest-freeflame.h5", "clib",
"Solution from CLib interface");
ASSERT_GE(ret, 0);
}
ASSERT_EQ(domain_nPoints(flow),nz + 1);
comp = domain_componentIndex(dom, "T");
double Tprev = sim1D_value(flame, dom, comp, 0);
for (size_t n = 0; n < domain_nPoints(flow); n++) {
T = sim1D_value(flame, dom, comp, n);
ASSERT_GE(T, Tprev);
Tprev = T;
}
}

View File

@ -0,0 +1,90 @@
#include <gtest/gtest.h>
#include "cantera/core.h"
#include "cantera/zerodim.h"
#include "cantera/clib/ct.h"
#include "cantera/clib/ctreactor.h"
using namespace Cantera;
TEST(ctreactor, reactor_objects)
{
int thermo = thermo_newFromFile("gri30.yaml", "gri30");
int kin = kin_newFromFile("gri30.yaml", "gri30", thermo, -1, -1, -1, -1);
int reactor = reactor_new("IdealGasReactor");
ASSERT_GT(reactor, 0);
int ret = reactor_setThermoMgr(reactor, thermo);
ASSERT_EQ(ret, 0);
ret = reactor_setKineticsMgr(reactor, kin);
ASSERT_EQ(ret, 0);
}
vector<double> T_ctreactor = {
1050.000, 1050.064, 1050.197, 1050.369, 1050.593, 1050.881, 1051.253, 1051.736,
1052.370, 1053.216, 1054.372, 1056.007, 1058.448, 1062.431, 1070.141, 1094.331,
2894.921, 2894.921, 2894.921, 2894.921, 2894.921};
TEST(ctreactor, reactor_insert)
{
double T = 1050;
double P = 5 * 101325;
string X = "CH4:1.0, O2:2.0, N2:7.52";
int sol = soln_newSolution("gri30.yaml", "gri30", "none");
int thermo = soln_thermo(sol);
thermo_setMoleFractionsByName(thermo, X.c_str());
thermo_setTemperature(thermo, T);
thermo_setPressure(thermo, P);
int reactor = reactor_new("IdealGasReactor");
int ret = reactor_insert(reactor, sol);
ASSERT_EQ(ret, 0);
int net = reactornet_new();
ret = reactornet_addreactor(net, reactor);
ASSERT_EQ(ret, 0);
double t = 0.0;
int count = 0;
while (t < 0.1) {
double Tref = T_ctreactor[count];
ASSERT_NEAR(reactor_temperature(reactor), Tref, 1e-2);
t = reactornet_time(net) + 5e-3;
ret = reactornet_advance(net, t);
ASSERT_EQ(ret, 0);
count++;
}
}
TEST(ctreactor, reactor_from_parts)
{
double T = 1050;
double P = 5 * 101325;
string X = "CH4:1.0, O2:2.0, N2:7.52";
int thermo = thermo_newFromFile("gri30.yaml", "gri30");
int kin = kin_newFromFile("gri30.yaml", "gri30", thermo, -1, -1, -1, -1);
thermo_setMoleFractionsByName(thermo, X.c_str());
thermo_setTemperature(thermo, T);
thermo_setPressure(thermo, P);
int reactor = reactor_new("IdealGasReactor");
reactor_setThermoMgr(reactor, thermo);
reactor_setKineticsMgr(reactor, kin);
int net = reactornet_new();
int ret = reactornet_addreactor(net, reactor);
ASSERT_EQ(ret, 0);
double t = 0.0;
int count = 0;
while (t < 0.1) {
T = reactor_temperature(reactor);
double Tref = T_ctreactor[count];
ASSERT_NEAR(reactor_temperature(reactor), Tref, 1e-2);
t = reactornet_time(net) + 5e-3;
ret = reactornet_advance(net, t);
ASSERT_EQ(ret, 0);
count++;
}
}

View File

@ -9,10 +9,37 @@
using namespace Cantera;
// This test is an exact equivalent of a clib test
// (clib::test_ctreactor.cpp::ctreactor::reactor_insert)
TEST(zerodim, simple)
{
double T = 1050;
double P = 5 * 101325;
string X = "CH4:1.0, O2:2.0, N2:7.52";
auto sol = newSolution("gri30.yaml", "gri30", "none");
sol->thermo()->setState_TPX(T, P, X);
IdealGasReactor cppReactor;
cppReactor.insert(sol);
cppReactor.initialize();
ReactorNet network;
network.addReactor(cppReactor);
network.initialize();
double t = 0.0;
int count = 0;
while (t < 0.1) {
ASSERT_GE(cppReactor.temperature(), T);
t = network.time() + 5e-3;
network.advance(t);
count++;
}
}
// This test ensures that prior reactor initialization of a reactor does
// not affect later integration within a network. This example was
// adapted from test_reactor.py::test_equilibrium_HP.
TEST(ZeroDim, test_individual_reactor_initialization)
TEST(zerodim, test_individual_reactor_initialization)
{
// initial conditions
double T0 = 1100.0;

View File

@ -208,7 +208,7 @@ Test('VCS-LiSi-verbose', 'VCSnonideal/LatticeSolid_LiSi', vcs_LiSi.program,
'verbose_blessed.txt', options='8',
artifacts=['vcs_equilibrate_res.csv'])
CompileAndTest('clib', 'clib_test', 'clib_test',
CompileAndTest('clib-demo', 'clib_test', 'clib_test',
extensions=['^clib_test.c'], libs=localenv['cantera_shared_libs'])
# C++ Samples