/** * @file ct2ctml.cpp * Driver for the system call to the python executable that converts * cti files to ctml files (see \ref inputfiles). */ // Copyright 2001-2005 California Institute of Technology #include "cantera/base/ctml.h" #include "cantera/base/stringUtils.h" #include "../../ext/libexecstream/exec-stream.h" #include #include #ifdef _WIN32 #include #endif using namespace Cantera; using namespace std; namespace ctml { //! return the full path to the Python interpreter. /*! * Use the environment variable PYTHON_CMD if it is set. If not, return * the string 'python'. * * Note, there are hidden problems here that really direct us to use * a full pathname for the location of python. Basically the system * call will use the shell /bin/sh, in order to launch python. * This default shell may not be the shell that the user is employing. * Therefore, the default path to python may be different during * a system call than during the default user shell environment. * This is quite a headache. The answer is to always set the * PYTHON_CMD environmental variable in the user environment to * an absolute path to locate the python executable. Then this * issue goes away. */ static string pypath() { string s = "python"; const char* py = getenv("PYTHON_CMD"); if (py) { string sp = stripws(string(py)); if (sp.size() > 0) { s = sp; } } return s; } 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(), '\\'), '/') ; #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; } static std::string call_ctml_writer(const std::string& text, bool isfile) { std::string file, arg; if (isfile) { file = text; arg = "r'" + text + "'"; } else { file = ""; arg = "text=r'''" + text + "'''"; } #ifdef HAS_NO_PYTHON /* * Section to bomb out if python is not * present in the computation environment. */ throw CanteraError("ct2ctml", "python cti to ctml conversion requested for file, " + file + ", but not available in this computational environment"); #endif 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 stringstream output_stream, error_stream; std::vector 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(" + arg + ", outName='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; } #ifdef _WIN32 // Sleeping for 1 ms prevents a (somewhat inexplicable) deadlock while // reading from the stream. Sleep(1); #endif while (python.err().good()) { std::getline(python.err(), line); error_stream << line << std::endl; } python.close(); python_exit_code = python.exit_code(); 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_string", message.str()); } if (python_exit_code != 0) { // Report a failure in the conversion process stringstream message; 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 (error_output.size() > 0) { message << "-------------- start of converter log --------------\n"; message << error_output << std::endl; message << "--------------- end of converter log ---------------"; } else { message << "The command did not produce any output." << endl; } throw CanteraError("ct2ctml_string", message.str()); } 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 << error_output << std::endl; message << "--------------- end of converter log ---------------\n"; writelog(message.str()); } return python_output; } std::string ct2ctml_string(const std::string& file) { return call_ctml_writer(file, true); } std::string ct_string2ctml_string(const std::string& cti) { return call_ctml_writer(cti, false); } void ck2cti(const std::string& in_file, const std::string& thermo_file, const std::string& transport_file, const std::string& id_tag) { #ifdef HAS_NO_PYTHON /* * Section to bomb out if python is not * present in the computation environment. */ string ppath = in_file; throw CanteraError("ct2ctml", "python ck to cti conversion requested for file, " + ppath + ", but not available in this computational environment"); #endif string python_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; ostream& pyin = python.in(); pyin << "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 ck2cti\n" << " except ImportError:\n" << " print('sys.path: ' + repr(sys.path))\n" << " raise\n" " ck2cti.Parser().convertMech(r'" << in_file << "',"; if (thermo_file != "" && thermo_file != "-") { pyin << " thermoFile=r'" << thermo_file << "',"; } if (transport_file != "" && transport_file != "-") { pyin << " transportFile=r'" << transport_file << "',"; } pyin << " phaseName='" << id_tag << "',"; pyin << " permissive=True,"; pyin << " quiet=True)\n"; pyin << " sys.exit(0)\n\n"; pyin << "sys.exit(7)\n"; python.close_in(); std::string line; while (python.out().good()) { std::getline(python.out(), line); output_stream << line << std::endl;; } python.close(); python_exit_code = python.exit_code(); python_output = stripws(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()); } if (python_exit_code != 0) { // Report a failure in the conversion process stringstream message; message << "Error converting input file \"" << in_file << "\" to CTI.\n"; message << "Python command was: '" << pypath() << "'\n"; message << "The exit code was: " << python_exit_code << "\n"; if (python_output.size() > 0) { message << "-------------- start of converter log --------------\n"; message << python_output << std::endl; message << "--------------- end of converter log ---------------"; } else { message << "The command did not produce any output." << endl; } throw CanteraError("ck2cti", message.str()); } if (python_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 << "--------------- end of converter log ---------------\n"; writelog(message.str()); } } void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string& file, const int debug) { warn_deprecated("get_CTML_Tree", "To be removed after Cantera 2.2. " "Use get_XML_File instead."); XML_Node* src = get_XML_File(file); src->copy(rootPtr); } Cantera::XML_Node getCtmlTree(const std::string& file) { warn_deprecated("getCtmlTree", "To be removed after Cantera 2.2. " "Use get_XML_File instead."); XML_Node root; XML_Node* src = get_XML_File(file); src->copy(&root); return root; } }