Cantera 2.6.0
ct2ctml.cpp
Go to the documentation of this file.
1/**
2 * @file ct2ctml.cpp
3 * Driver for the system call to the python executable that converts
4 * cti files to ctml files (see \ref inputfiles).
5 */
6
7// This file is part of Cantera. See License.txt in the top-level directory or
8// at https://cantera.org/license.txt for license and copyright information.
9
10#include "cantera/base/ctml.h"
13#include "cantera/base/global.h"
14#include "../../ext/libexecstream/exec-stream.h"
15
16#include <cstdio>
17#include <fstream>
18#include <sstream>
19#include <functional>
20
21#ifdef _WIN32
22#include <windows.h>
23#endif
24
25using namespace std;
26
27namespace Cantera
28{
29
30//! return the full path to the Python interpreter.
31/*!
32 * Use the environment variable PYTHON_CMD if it is set. If not, return
33 * the string 'python'.
34 *
35 * Note, there are hidden problems here that really direct us to use a full
36 * pathname for the location of python. Basically the system call will use the
37 * shell /bin/sh, in order to launch python. This default shell may not be the
38 * shell that the user is employing. Therefore, the default path to python may
39 * be different during a system call than during the default user shell
40 * environment. This is quite a headache. The answer is to always set the
41 * PYTHON_CMD environmental variable in the user environment to an absolute path
42 * to locate the python executable. Then this issue goes away.
43 */
44static string pypath()
45{
46 string s = "python";
47 const char* py = getenv("PYTHON_CMD");
48
49 if (py) {
50 string sp = trimCopy(string(py));
51 if (sp.size() > 0) {
52 s = sp;
53 }
54 }
55 return s;
56}
57
58void ct2ctml(const char* file, const int debug)
59{
60 string xml = ct2ctml_string(file);
61 string out_name = file;
62#ifdef _WIN32
63 // For Windows, make the path POSIX compliant so code looking for directory
64 // separators is simpler. Just look for '/' not both '/' and '\\'
65 std::replace_if(out_name.begin(), out_name.end(),
66 std::bind2nd(std::equal_to<char>(), '\\'), '/');
67#endif
68 size_t idir = out_name.rfind('/');
69 if (idir != npos) {
70 out_name = out_name.substr(idir+1, out_name.size());
71 }
72 size_t idot = out_name.rfind('.');
73 if (idot != npos) {
74 out_name = out_name.substr(0, idot) + ".xml";
75 } else {
76 out_name += ".xml";
77 }
78 std::ofstream out(out_name);
79 out << xml;
80}
81
82static std::string call_ctml_writer(const std::string& text, bool isfile)
83{
84 std::string file, arg;
85
86 if (isfile) {
87 file = text;
88 arg = "r'" + text + "'";
89 } else {
90 file = "<string>";
91 arg = "text=r'''" + text + "'''";
92 }
93
94 string python_output, error_output;
95 int python_exit_code;
96 try {
97 exec_stream_t python;
98 python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
99 stringstream output_stream, error_stream;
100 python.start(pypath(), "");
101 ostream& pyin = python.in();
102
103 pyin << "from __future__ import print_function\n"
104 "if True:\n"
105 " import sys\n"
106 " try:\n"
107 " from cantera import ctml_writer\n"
108 " except ImportError:\n"
109 " print('sys.path: ' + repr(sys.path) + '\\n', file=sys.stderr)\n"
110 " raise\n"
111 " ctml_writer.convert(";
112 pyin << arg << ", outName='STDOUT')\n";
113 pyin << " sys.exit(0)\n\n";
114 pyin << "sys.exit(7)\n";
115
116 python.close_in();
117 std::string line;
118
119 while (python.out().good()) {
120 std::getline(python.out(), line);
121 output_stream << line << std::endl;
122 }
123
124#ifdef _WIN32
125 // Sleeping for 1 ms prevents a (somewhat inexplicable) deadlock while
126 // reading from the stream.
127 Sleep(1);
128#endif
129 while (python.err().good()) {
130 std::getline(python.err(), line);
131 error_stream << line << std::endl;
132 }
133 python.close();
134 python_exit_code = python.exit_code();
135 error_output = trimCopy(error_stream.str());
136 python_output = output_stream.str();
137 } catch (std::exception& err) {
138 // Report failure to execute Python
139 stringstream message;
140 message << "Error executing python while converting input file:\n";
141 message << "Python command was: '" << pypath() << "'\n";
142 message << err.what() << std::endl;
143 throw CanteraError("call_ctml_writer", message.str());
144 }
145
146 if (python_exit_code != 0) {
147 // Report a failure in the conversion process
148 stringstream message;
149 message << "Error converting input file \"" << file << "\" to CTML.\n";
150 message << "Python command was: '" << pypath() << "'\n";
151 message << "The exit code was: " << python_exit_code << "\n";
152 if (error_output.size() > 0) {
153 message << "-------------- start of converter log --------------\n";
154 message << error_output << std::endl;
155 message << "--------------- end of converter log ---------------";
156 } else {
157 message << "The command did not produce any output." << endl;
158 }
159 throw CanteraError("call_ctml_writer", message.str());
160 }
161
162 if (error_output.size() > 0) {
163 // Warn if there was any output from the conversion process
164 stringstream message;
165 message << "Warning: Unexpected output from CTI converter\n";
166 message << "-------------- start of converter log --------------\n";
167 message << error_output << std::endl;
168 message << "--------------- end of converter log ---------------\n";
169 writelog(message.str());
170 }
171
172 return python_output;
173}
174
175std::string ct2ctml_string(const std::string& file)
176{
177 return call_ctml_writer(file, true);
178}
179
180std::string ct_string2ctml_string(const std::string& cti)
181{
182 return call_ctml_writer(cti, false);
183}
184
185void ck2cti(const std::string& in_file, const std::string& thermo_file,
186 const std::string& transport_file, const std::string& id_tag)
187{
188 string python_output;
189 int python_exit_code;
190 try {
191 exec_stream_t python;
192 python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
193 python.start(pypath(), "-i");
194 stringstream output_stream;
195
196 ostream& pyin = python.in();
197 pyin << "if True:\n" << // Use this so that the rest is a single block
198 " import sys\n" <<
199 " sys.stderr = sys.stdout\n" <<
200 " try:\n" <<
201 " from cantera import ck2cti\n" <<
202 " except ImportError:\n" <<
203 " print('sys.path: ' + repr(sys.path))\n" <<
204 " raise\n"
205 " _ = ck2cti.convertMech(r'" << in_file << "',";
206 if (thermo_file != "" && thermo_file != "-") {
207 pyin << " thermoFile=r'" << thermo_file << "',";
208 }
209 if (transport_file != "" && transport_file != "-") {
210 pyin << " transportFile=r'" << transport_file << "',";
211 }
212 pyin << " phaseName='" << id_tag << "',";
213 pyin << " permissive=True,";
214 pyin << " quiet=True)\n";
215 pyin << " sys.exit(0)\n\n";
216 pyin << "sys.exit(7)\n";
217 python.close_in();
218
219 std::string line;
220 while (python.out().good()) {
221 std::getline(python.out(), line);
222 output_stream << line << std::endl;;
223 }
224 python.close();
225 python_exit_code = python.exit_code();
226 python_output = trimCopy(output_stream.str());
227 } catch (std::exception& err) {
228 // Report failure to execute Python
229 stringstream message;
230 message << "Error executing python while converting input file:\n";
231 message << "Python command was: '" << pypath() << "'\n";
232 message << err.what() << std::endl;
233 throw CanteraError("ck2cti", message.str());
234 }
235
236 if (python_exit_code != 0) {
237 // Report a failure in the conversion process
238 stringstream message;
239 message << "Error converting input file \"" << in_file << "\" to CTI.\n";
240 message << "Python command was: '" << pypath() << "'\n";
241 message << "The exit code was: " << python_exit_code << "\n";
242 if (python_output.size() > 0) {
243 message << "-------------- start of converter log --------------\n";
244 message << python_output << std::endl;
245 message << "--------------- end of converter log ---------------";
246 } else {
247 message << "The command did not produce any output." << endl;
248 }
249 throw CanteraError("ck2cti", message.str());
250 }
251
252 if (python_output.size() > 0) {
253 // Warn if there was any output from the conversion process
254 stringstream message;
255 message << "Warning: Unexpected output from ck2cti\n";
256 message << "-------------- start of converter log --------------\n";
257 message << python_output << std::endl;
258 message << "--------------- end of converter log ---------------\n";
259 writelog(message.str());
260 }
261}
262
263}
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
This file contains definitions for utility functions and text for modules, inputfiles,...
void ct2ctml(const char *file, const int debug=0)
Convert a cti file into a ctml file.
Definition: ct2ctml.cpp:58
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:175
std::string ct_string2ctml_string(const std::string &cti)
Get a string with the ctml representation of a cti input string.
Definition: ct2ctml.cpp:180
void writelog(const std::string &fmt, const Args &... args)
Write a formatted message to the screen.
Definition: global.h:164
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
void ck2cti(const std::string &in_file, const std::string &thermo_file="", const std::string &transport_file="", const std::string &id_tag="gas")
Convert a Chemkin-format mechanism into a CTI file.
Definition: ct2ctml.cpp:185
std::string trimCopy(const std::string &input)
Trim.
static string pypath()
return the full path to the Python interpreter.
Definition: ct2ctml.cpp:44
Contains declarations for string manipulation functions within Cantera.