Do implicit CTI to XML conversions without generating .xml files
When instantiating a phase from a .cti file, do the conversion in memory, without writing the XML representation to disk. This eliminates the unrequrested XML files that Cantera normally generates, and also avoids errors when running Cantera from a directory where the user does not have write permissons.
This commit is contained in:
parent
aae8982924
commit
ff9584105b
8 changed files with 156 additions and 75 deletions
|
|
@ -818,6 +818,15 @@ Cantera::XML_Node getCtmlTree(const std::string& file);
|
|||
*/
|
||||
void ct2ctml(const char* file, const int debug = 0);
|
||||
|
||||
//! Get a string with the ctml representation of a cti file.
|
||||
/*!
|
||||
* @param file Path to the input file in CTI format
|
||||
* @return String containing the xml representation of the input file
|
||||
*
|
||||
* @ingroup inputfiles
|
||||
*/
|
||||
std::string ct2ctml_string(const std::string& file);
|
||||
|
||||
//! Convert a Chemkin-format mechanism into a CTI file.
|
||||
/*!
|
||||
* @param in_file input file containing species and reactions
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
|
||||
class CTI_Error(Exception):
|
||||
"""Exception raised if an error is encountered while
|
||||
parsing the input file.
|
||||
|
|
@ -130,8 +132,11 @@ class XMLnode(object):
|
|||
s = ['<?xml version="1.0"?>\n']
|
||||
self._write(s, 0)
|
||||
s.append('\n')
|
||||
with open(filename, 'w') as f:
|
||||
f.write(''.join(s))
|
||||
if isinstance(filename, str):
|
||||
with open(filename, 'w') as f:
|
||||
f.write(''.join(s))
|
||||
else:
|
||||
filename.write(''.join(s))
|
||||
|
||||
def write_comment(self, s, level):
|
||||
s.append('\n'+indent[level]+'<!--')
|
||||
|
|
@ -332,7 +337,9 @@ def write(outName=None):
|
|||
for rx in _reactions:
|
||||
rx.build(r)
|
||||
|
||||
if outName is not None:
|
||||
if outName == 'STDOUT':
|
||||
x.write(sys.stdout)
|
||||
elif outName is not None:
|
||||
x.write(outName)
|
||||
elif _name != 'noname':
|
||||
x.write(_name+'.xml')
|
||||
|
|
@ -2583,7 +2590,7 @@ class Lindemann(object):
|
|||
validate()
|
||||
|
||||
def convert(filename, outName=None):
|
||||
import os, sys
|
||||
import os
|
||||
base = os.path.basename(filename)
|
||||
root, _ = os.path.splitext(base)
|
||||
dataset(root)
|
||||
|
|
|
|||
|
|
@ -281,10 +281,15 @@ XML_Node* Application::get_XML_File(const std::string& file, int debug)
|
|||
return xmlfiles[ff];
|
||||
}
|
||||
/*
|
||||
* Ok, we didn't find the processed XML tree. Do the conversion
|
||||
* to xml, possibly overwriting the file, ff, in the process.
|
||||
* Ok, we didn't find the processed XML tree. Do the conversion from cti
|
||||
*/
|
||||
ctml::ct2ctml(path.c_str(),debug);
|
||||
string phase_xml = ctml::ct2ctml_string(path);
|
||||
XML_Node* x = new XML_Node("doc");
|
||||
std::stringstream s(phase_xml);
|
||||
x->build(s);
|
||||
x->lock();
|
||||
xmlfiles[ff] = x;
|
||||
return x;
|
||||
} else {
|
||||
ff = path;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,53 +52,84 @@ static string pypath()
|
|||
}
|
||||
|
||||
void ct2ctml(const char* file, const int debug)
|
||||
{
|
||||
string xml = ct2ctml_string(file);
|
||||
string out_name = file;
|
||||
#ifdef _WIN32
|
||||
// For Windows, make the path POSIX compliant so code looking for directory
|
||||
// separators is simpler. Just look for '/' not both '/' and '\\'
|
||||
std::replace_if(out_name.begin(), out_name.end(),
|
||||
std::bind2nd(std::equal_to<char>(), '\\'), '/') ;
|
||||
#endif
|
||||
size_t idir = out_name.rfind('/');
|
||||
if (idir != npos) {
|
||||
out_name = out_name.substr(idir+1, out_name.size());
|
||||
}
|
||||
size_t idot = out_name.rfind('.');
|
||||
if (idot != npos) {
|
||||
out_name = out_name.substr(0, idot) + ".xml";
|
||||
} else {
|
||||
out_name += ".xml";
|
||||
}
|
||||
std::ofstream out(out_name.c_str());
|
||||
out << xml;
|
||||
}
|
||||
|
||||
std::string ct2ctml_string(const std::string& file)
|
||||
{
|
||||
#ifdef HAS_NO_PYTHON
|
||||
/*
|
||||
* Section to bomb out if python is not
|
||||
* present in the computation environment.
|
||||
*/
|
||||
string ppath = file;
|
||||
throw CanteraError("ct2ctml",
|
||||
"python cti to ctml conversion requested for file, " + ppath +
|
||||
"python cti to ctml conversion requested for file, " + file +
|
||||
", but not available in this computational environment");
|
||||
#endif
|
||||
|
||||
string python_output;
|
||||
string python_output, error_output;
|
||||
int python_exit_code;
|
||||
try {
|
||||
exec_stream_t python;
|
||||
python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
|
||||
python.start(pypath(), "-i");
|
||||
stringstream output_stream;
|
||||
python.in() <<
|
||||
"if True:\n" << // Use this so that the rest is a single block
|
||||
" import sys\n" <<
|
||||
" sys.stderr = sys.stdout\n" <<
|
||||
" try:\n" <<
|
||||
" from cantera import ctml_writer\n" <<
|
||||
" except ImportError:\n" <<
|
||||
" print('sys.path: ' + repr(sys.path) + '\\n')\n" <<
|
||||
" raise\n" <<
|
||||
" ctml_writer.convert(r'" << file << "')\n" <<
|
||||
" sys.exit(0)\n\n" <<
|
||||
"sys.exit(7)\n";
|
||||
python.close_in();
|
||||
stringstream output_stream, error_stream;
|
||||
std::vector<string> args;
|
||||
args.push_back("-c");
|
||||
args.push_back(
|
||||
"from __future__ import print_function\n"
|
||||
"import sys\n"
|
||||
"try:\n"
|
||||
" from cantera import ctml_writer\n"
|
||||
"except ImportError:\n"
|
||||
" print('sys.path: ' + repr(sys.path) + '\\n', file=sys.stderr)\n"
|
||||
" raise\n"
|
||||
"ctml_writer.convert(r'" + file + "', 'STDOUT')\n"
|
||||
"sys.exit(0)\n");
|
||||
|
||||
python.start(pypath(), args.begin(), args.end());
|
||||
std::string line;
|
||||
while (python.out().good()) {
|
||||
std::getline(python.out(), line);
|
||||
output_stream << line << std::endl;;
|
||||
while (true) {
|
||||
if (python.out().good()) {
|
||||
std::getline(python.out(), line);
|
||||
output_stream << line << std::endl;
|
||||
} else if (python.err().good()) {
|
||||
std::getline(python.err(), line);
|
||||
error_stream << line << std::endl;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
python.close();
|
||||
python_exit_code = python.exit_code();
|
||||
python_output = stripws(output_stream.str());
|
||||
error_output = stripws(error_stream.str());
|
||||
python_output = output_stream.str();
|
||||
} catch (std::exception& err) {
|
||||
// Report failure to execute Python
|
||||
stringstream message;
|
||||
message << "Error executing python while converting input file:\n";
|
||||
message << "Python command was: '" << pypath() << "'\n";
|
||||
message << err.what() << std::endl;
|
||||
throw CanteraError("ct2ctml", message.str());
|
||||
throw CanteraError("ct2ctml_string", message.str());
|
||||
}
|
||||
|
||||
if (python_exit_code != 0) {
|
||||
|
|
@ -107,27 +138,29 @@ void ct2ctml(const char* file, const int debug)
|
|||
message << "Error converting input file \"" << file << "\" to CTML.\n";
|
||||
message << "Python command was: '" << pypath() << "'\n";
|
||||
message << "The exit code was: " << python_exit_code << "\n";
|
||||
if (python_output.size() > 0) {
|
||||
if (error_output.size() > 0) {
|
||||
message << "-------------- start of converter log --------------\n";
|
||||
message << python_output << std::endl;
|
||||
message << error_output << std::endl;
|
||||
message << "--------------- end of converter log ---------------";
|
||||
} else {
|
||||
message << "The command did not produce any output." << endl;
|
||||
}
|
||||
throw CanteraError("ct2ctml", message.str());
|
||||
throw CanteraError("ct2ctml_string", message.str());
|
||||
}
|
||||
|
||||
if (python_output.size() > 0) {
|
||||
if (error_output.size() > 0) {
|
||||
// Warn if there was any output from the conversion process
|
||||
stringstream message;
|
||||
message << "Warning: Unexpected output from CTI converter\n";
|
||||
message << "-------------- start of converter log --------------\n";
|
||||
message << python_output << std::endl;
|
||||
message << error_output << std::endl;
|
||||
message << "--------------- end of converter log ---------------\n";
|
||||
writelog(message.str());
|
||||
}
|
||||
return python_output;
|
||||
}
|
||||
|
||||
|
||||
void ck2cti(const std::string& in_file, const std::string& thermo_file,
|
||||
const std::string& transport_file, const std::string& id_tag)
|
||||
{
|
||||
|
|
@ -219,7 +252,7 @@ void ck2cti(const std::string& in_file, const std::string& thermo_file,
|
|||
|
||||
void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string& file, const int debug)
|
||||
{
|
||||
std::string ff, ext = "";
|
||||
std::string ext = "";
|
||||
|
||||
// find the input file on the Cantera search path
|
||||
std::string inname = findInputFile(file);
|
||||
|
|
@ -238,27 +271,18 @@ void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string& file, const in
|
|||
ext = inname.substr(idot, inname.size());
|
||||
}
|
||||
if (ext != ".xml" && ext != ".ctml") {
|
||||
try {
|
||||
ctml::ct2ctml(inname.c_str(), debug);
|
||||
} catch (std::exception& err) {
|
||||
writelog("get_CTML_Tree: caught an exception:\n");
|
||||
writelog(err.what());
|
||||
}
|
||||
string ffull = inname.substr(0,idot) + ".xml";
|
||||
ff = "./" + getBaseName(ffull) + ".xml";
|
||||
if (debug > 0) {
|
||||
writelogf("ffull name = %s\n", ffull.c_str());
|
||||
writelogf("ff name = %s\n", ff.c_str());
|
||||
}
|
||||
} else {
|
||||
ff = inname;
|
||||
string phase_xml = ctml::ct2ctml_string(inname);
|
||||
stringstream s(phase_xml);
|
||||
rootPtr->build(s);
|
||||
return;
|
||||
}
|
||||
writelog("Attempting to parse xml file " + ff + "\n", debug);
|
||||
ifstream fin(ff.c_str());
|
||||
|
||||
writelog("Attempting to parse xml file " + inname + "\n", debug);
|
||||
ifstream fin(inname.c_str());
|
||||
if (!fin) {
|
||||
throw
|
||||
CanteraError("get_CTML_Tree",
|
||||
"XML file " + ff + " not found");
|
||||
"XML file " + inname + " not found");
|
||||
}
|
||||
rootPtr->build(fin);
|
||||
fin.close();
|
||||
|
|
|
|||
|
|
@ -35,23 +35,25 @@ def testRunner(target, source, env):
|
|||
testResults.failed[passedFile.name] = program
|
||||
|
||||
|
||||
def addTestProgram(subdir, progName):
|
||||
def addTestProgram(subdir, progName, env_vars={}):
|
||||
"""
|
||||
Compile a test program and create a targets for running
|
||||
and resetting the test.
|
||||
"""
|
||||
program = localenv.Program(pjoin(subdir, progName),
|
||||
mglob(localenv, subdir, 'cpp'))
|
||||
testenv = localenv.Clone()
|
||||
testenv['ENV'].update(env_vars)
|
||||
program = testenv.Program(pjoin(subdir, progName),
|
||||
mglob(testenv, subdir, 'cpp'))
|
||||
passedFile = File(pjoin(str(program[0].dir), '%s.passed' % program[0].name))
|
||||
PASSED_FILES[progName] = str(passedFile)
|
||||
testResults.tests[passedFile.name] = program
|
||||
run_program = localenv.Command(passedFile, program, testRunner)
|
||||
run_program = testenv.Command(passedFile, program, testRunner)
|
||||
Alias('test', run_program)
|
||||
Alias('test-%s' % progName, run_program)
|
||||
env['testNames'].append(progName)
|
||||
if os.path.exists(passedFile.abspath):
|
||||
Alias('test-reset', localenv.Command('reset-%s%s' % (subdir, progName),
|
||||
[], [Delete(passedFile.abspath)]))
|
||||
Alias('test-reset', testenv.Command('reset-%s%s' % (subdir, progName),
|
||||
[], [Delete(passedFile.abspath)]))
|
||||
|
||||
|
||||
def addPythonTest(testname, subdir, script, interpreter, outfile,
|
||||
|
|
@ -156,8 +158,18 @@ def addMatlabTest(script, testName, dependencies=None, env_vars=()):
|
|||
|
||||
return run_program
|
||||
|
||||
if localenv['python_package'] in ('full', 'minimal'):
|
||||
python_env_vars = {'PYTHONPATH': Dir('#build/python2').abspath,
|
||||
'PYTHON_CMD': localenv.subst('$python_cmd')}
|
||||
elif localenv['python3_package'] == 'y':
|
||||
python_env_vars = {'PYTHONPATH': Dir('#build/python3').abspath,
|
||||
'PYTHON_CMD': localenv.subst('$python3_cmd')}
|
||||
else:
|
||||
python_env_vars = {} # Tests calling ck2cti or ctml_writer will fail
|
||||
|
||||
|
||||
# Instantiate tests
|
||||
addTestProgram('thermo', 'thermo')
|
||||
addTestProgram('thermo', 'thermo', env_vars=python_env_vars)
|
||||
addTestProgram('kinetics', 'kinetics')
|
||||
|
||||
python_subtests = ['']
|
||||
|
|
@ -191,18 +203,9 @@ if localenv['python3_package'] == 'y':
|
|||
make_python_tests(3)
|
||||
|
||||
if localenv['matlab_toolbox'] == 'y':
|
||||
if localenv['python_package'] in ('full', 'minimal'):
|
||||
env_vars = {'PYTHONPATH': Dir('#build/python2').abspath,
|
||||
'PYTHON_CMD': localenv.subst('$python_cmd')}
|
||||
elif localenv['python3_package'] == 'y':
|
||||
env_vars = {'PYTHONPATH': Dir('#build/python3').abspath,
|
||||
'PYTHON_CMD': localenv.subst('$python3_cmd')}
|
||||
else:
|
||||
env_vars = {} # ck2cti test is likely to fail
|
||||
|
||||
matlabTest = addMatlabTest('runCanteraTests.m', 'matlab',
|
||||
dependencies=mglob(localenv, 'matlab', 'm'),
|
||||
env_vars=env_vars)
|
||||
env_vars=python_env_vars)
|
||||
localenv.Alias('test-matlab', matlabTest)
|
||||
env['testNames'].append('matlab')
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,3 @@ assertEqual(length(dkm), nSpecies(gas))
|
|||
function testImportCTI
|
||||
gas = importPhase('h2o2.cti');
|
||||
assertEqual(temperature(gas), 300)
|
||||
delete('h2o2.xml');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "gtest/gtest.h"
|
||||
#include "cantera/thermo/FixedChemPotSSTP.h"
|
||||
#include "cantera/thermo/ThermoFactory.h"
|
||||
#include "cantera/base/ctml.h"
|
||||
|
||||
namespace Cantera
|
||||
{
|
||||
|
|
@ -28,4 +29,38 @@ TEST_F(FixedChemPotSstpConstructorTest, SimpleConstructor)
|
|||
ASSERT_FLOAT_EQ(-2.3e7, mu);
|
||||
}
|
||||
|
||||
#ifndef HAS_NO_PYTHON // skip these tests if the Python converter is unavailable
|
||||
class InputFileConversionTest : public testing::Test
|
||||
{
|
||||
public:
|
||||
InputFileConversionTest() {
|
||||
appdelete();
|
||||
}
|
||||
|
||||
ThermoPhase* p1;
|
||||
ThermoPhase* p2;
|
||||
void compare()
|
||||
{
|
||||
ASSERT_EQ(p1->nSpecies(), p2->nSpecies());
|
||||
for (size_t i = 0; i < p1->nSpecies(); i++) {
|
||||
ASSERT_EQ(p1->speciesName(i), p2->speciesName(i));
|
||||
ASSERT_EQ(p1->molecularWeight(i), p2->molecularWeight(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(InputFileConversionTest, ExplicitConversion) {
|
||||
p1 = newPhase("../data/air-no-reactions.xml", "");
|
||||
ctml::ct2ctml("../data/air-no-reactions.cti");
|
||||
p2 = newPhase("air-no-reactions.xml", "");
|
||||
compare();
|
||||
}
|
||||
|
||||
TEST_F(InputFileConversionTest, ImplicitConversion) {
|
||||
p1 = newPhase("../data/air-no-reactions.xml", "");
|
||||
p2 = newPhase("../data/air-no-reactions.cti", "");
|
||||
compare();
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Cantera
|
||||
|
|
|
|||
|
|
@ -279,8 +279,7 @@ CompileAndTest('pecosTransport', 'PecosTransport', 'pecosTransport', 'output_ble
|
|||
CompileAndTest('printUtil', 'printUtilUnitTest', 'pUtest', 'output_blessed.txt')
|
||||
CompileAndTest('pureFluid', 'pureFluidTest', 'testPureWater', 'output_blessed.txt')
|
||||
if haveConverters:
|
||||
CompileAndTest('rankine_democxx', 'rankine_democxx', 'rankine', 'output_blessed.txt',
|
||||
artifacts=['liquidvapor.xml'])
|
||||
CompileAndTest('rankine_democxx', 'rankine_democxx', 'rankine', 'output_blessed.txt')
|
||||
CompileAndTest('silane_equil', 'silane_equil', 'silane_equi', 'output_blessed.txt')
|
||||
# spectroscopy is incomplete
|
||||
CompileAndTest('simpleTransport', 'simpleTransport', 'simpleTransport',
|
||||
|
|
@ -291,12 +290,12 @@ CompileAndTest('surfkin', 'surfkin', 'surfdemo', 'output_blessed.txt')
|
|||
CompileAndTest('surfSolver', 'surfSolverTest', 'surfaceSolver', None,
|
||||
arguments='haca2.xml',
|
||||
comparisons=[('results_blessed.txt', 'results.txt')],
|
||||
artifacts=['results.txt', 'diamond.xml'],
|
||||
artifacts=['results.txt'],
|
||||
extensions=['^surfaceSolver.cpp'])
|
||||
CompileAndTest('surfSolver2', 'surfSolverTest', 'surfaceSolver2', None,
|
||||
arguments='haca2.xml',
|
||||
comparisons=[('results2_blessed.txt', 'results2.txt')],
|
||||
artifacts=['results2.txt', 'diamond.xml'],
|
||||
artifacts=['results2.txt'],
|
||||
extensions=['^surfaceSolver2.cpp'])
|
||||
CompileAndTest('VCS-NaCl', pjoin('VCSnonideal', 'NaCl_equil'),
|
||||
'nacl_equil', 'good_out.txt',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue