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