Cantera  2.3.0
application.cpp
Go to the documentation of this file.
1 //! @file application.cpp
2 
3 // This file is part of Cantera. See License.txt in the top-level directory or
4 // at http://www.cantera.org/license.txt for license and copyright information.
5 
6 #include "application.h"
7 
8 #include "cantera/base/ctml.h"
10 #include "units.h"
11 
12 #include <fstream>
13 #include <sstream>
14 #include <mutex>
15 
16 using std::string;
17 using std::endl;
18 
19 #ifdef _WIN32
20 #include <windows.h>
21 #else
22 #include <sys/stat.h>
23 #endif
24 
25 #ifdef _MSC_VER
26 #pragma comment(lib, "advapi32")
27 #endif
28 
29 namespace Cantera
30 {
31 
32 //! Mutex for input directory access
33 static std::mutex dir_mutex;
34 
35 //! Mutex for creating singletons within the application object
36 static std::mutex app_mutex;
37 
38 //! Mutex for controlling access to XML file storage
39 static std::mutex xml_mutex;
40 
41 static int get_modified_time(const std::string& path) {
42 #ifdef _WIN32
43  HANDLE hFile = CreateFile(path.c_str(), NULL, NULL,
44  NULL, OPEN_EXISTING, 0, NULL);
45  if (hFile == INVALID_HANDLE_VALUE) {
46  throw CanteraError("get_modified_time", "Couldn't open file:" + path);
47  }
48  FILETIME modified;
49  GetFileTime(hFile, NULL, NULL, &modified);
50  CloseHandle(hFile);
51  return static_cast<int>(modified.dwLowDateTime);
52 #else
53  struct stat attrib;
54  stat(path.c_str(), &attrib);
55  return static_cast<int>(attrib.st_mtime);
56 #endif
57 }
58 
59 Application::Messages::Messages()
60 {
61  // install a default logwriter that writes to standard
62  // output / standard error
63  logwriter.reset(new Logger());
64 }
65 
66 void Application::Messages::addError(const std::string& r, const std::string& msg)
67 {
68  if (msg.size() != 0) {
69  errorMessage.push_back(
70  "\n\n************************************************\n"
71  " Cantera Error! \n"
72  "************************************************\n\n"
73  "Procedure: " + r +
74  "\nError: " + msg + "\n");
75  } else {
76  errorMessage.push_back(r);
77  }
78 }
79 
81 {
82  return static_cast<int>(errorMessage.size());
83 }
84 
86 {
87  logwriter.reset(_logwriter);
88 }
89 
90 void Application::Messages::writelog(const std::string& msg)
91 {
92  logwriter->write(msg);
93 }
94 
96 {
97  logwriter->writeendl();
98 }
99 
100 //! Mutex for access to string messages
101 static std::mutex msg_mutex;
102 
104 {
105  std::unique_lock<std::mutex> msgLock(msg_mutex);
106  std::thread::id curId = std::this_thread::get_id();
107  auto iter = m_threadMsgMap.find(curId);
108  if (iter != m_threadMsgMap.end()) {
109  return iter->second.get();
110  }
111  pMessages_t pMsgs(new Messages());
112  m_threadMsgMap.insert({curId, pMsgs});
113  return pMsgs.get();
114 }
115 
117 {
118  std::unique_lock<std::mutex> msgLock(msg_mutex);
119  std::thread::id curId = std::this_thread::get_id();
120  auto iter = m_threadMsgMap.find(curId);
121  if (iter != m_threadMsgMap.end()) {
122  m_threadMsgMap.erase(iter);
123  }
124 }
125 
127  m_suppress_deprecation_warnings(false),
128  m_fatal_deprecation_warnings(false),
129  m_suppress_thermo_warnings(false)
130 {
131  // install a default logwriter that writes to standard
132  // output / standard error
134  Unit::units();
135 }
136 
138 {
139  std::unique_lock<std::mutex> appLock(app_mutex);
140  if (Application::s_app == 0) {
142  }
143  return s_app;
144 }
145 
147 {
148  for (auto& f : xmlfiles) {
149  f.second.first->unlock();
150  delete f.second.first;
151  f.second.first = 0;
152  }
153 }
154 
156 {
157  std::unique_lock<std::mutex> appLock(app_mutex);
158  if (Application::s_app != 0) {
159  delete Application::s_app;
160  Application::s_app = 0;
161  }
162 }
163 
164 void Application::warn_deprecated(const std::string& method,
165  const std::string& extra)
166 {
167  if (m_fatal_deprecation_warnings) {
168  throw CanteraError(method, "Deprecated: " + extra);
169  } else if (m_suppress_deprecation_warnings || warnings.count(method)) {
170  return;
171  }
172  warnings.insert(method);
173  writelog("WARNING: '" + method + "' is deprecated. " + extra);
174  writelogendl();
175 }
176 
178 {
179  pMessenger.removeThreadMessages();
180 }
181 
182 XML_Node* Application::get_XML_File(const std::string& file, int debug)
183 {
184  std::unique_lock<std::mutex> xmlLock(xml_mutex);
185  std::string path = findInputFile(file);
186  int mtime = get_modified_time(path);
187 
188  if (xmlfiles.find(path) != xmlfiles.end()) {
189  // Already have a parsed XML tree for this file cached. Check the
190  // last-modified time.
191  std::pair<XML_Node*, int> cache = xmlfiles[path];
192  if (cache.second == mtime) {
193  return cache.first;
194  }
195  }
196 
197  // Check whether or not the file is XML (based on the file extension). If
198  // not, it will be first processed with the preprocessor.
199  string::size_type idot = path.rfind('.');
200  string ext;
201  if (idot != string::npos) {
202  ext = path.substr(idot, path.size());
203  } else {
204  ext = "";
205  }
206  XML_Node* x = new XML_Node("doc");
207  if (ext != ".xml" && ext != ".ctml") {
208  // Assume that we are trying to open a cti file. Do the conversion to XML.
209  std::stringstream phase_xml(ct2ctml_string(path));
210  x->build(phase_xml, path);
211  } else {
212  x->build(path);
213  }
214  x->lock();
215  xmlfiles[path] = {x, mtime};
216  return x;
217 }
218 
220 {
221  std::unique_lock<std::mutex> xmlLock(xml_mutex);
222  std::pair<XML_Node*, int>& entry = xmlfiles[text];
223  if (entry.first) {
224  // Return existing cached XML tree
225  return entry.first;
226  }
227  std::stringstream s;
228  size_t start = text.find_first_not_of(" \t\r\n");
229  if (text.substr(start,1) == "<") {
230  s << text;
231  } else {
232  s << ct_string2ctml_string(text.substr(start));
233  }
234  entry.first = new XML_Node();
235  entry.first->build(s, "[string]");
236  return entry.first;
237 }
238 
239 void Application::close_XML_File(const std::string& file)
240 {
241  std::unique_lock<std::mutex> xmlLock(xml_mutex);
242  if (file == "all") {
243  for (const auto& f : xmlfiles) {
244  f.second.first->unlock();
245  delete f.second.first;
246  }
247  xmlfiles.clear();
248  } else if (xmlfiles.find(file) != xmlfiles.end()) {
249  xmlfiles[file].first->unlock();
250  delete xmlfiles[file].first;
251  xmlfiles.erase(file);
252  }
253 }
254 
255 #ifdef _WIN32
256 long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
257  std::string& value, const std::string& defaultValue)
258 {
259  HKEY key;
260  long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
261  if (open_error != ERROR_SUCCESS) {
262  return open_error;
263  }
264  value = defaultValue;
265  CHAR buffer[1024];
266  DWORD bufferSize = sizeof(buffer);
267  ULONG error;
268  error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
269  if (ERROR_SUCCESS == error) {
270  value = buffer;
271  }
272  RegCloseKey(key);
273  return error;
274 }
275 #endif
276 
278 {
279  if (!errorMessage.empty()) {
280  errorMessage.pop_back();
281  }
282 }
283 
285 {
286  if (!errorMessage.empty()) {
287  return errorMessage.back();
288  } else {
289  return "<no Cantera error>";
290  }
291 }
292 
293 void Application::Messages::getErrors(std::ostream& f)
294 {
295  for (size_t j = 0; j < errorMessage.size(); j++) {
296  f << errorMessage[j] << endl;
297  }
298  errorMessage.clear();
299 }
300 
302 {
303  for (size_t j = 0; j < errorMessage.size(); j++) {
304  writelog(errorMessage[j]);
305  writelogendl();
306  }
307  errorMessage.clear();
308 }
309 
311 {
312  // always look in the local directory first
313  inputDirs.push_back(".");
314 
315  // if environment variable CANTERA_DATA is defined, then add it to the
316  // search path. CANTERA_DATA may include multiple directory, separated by
317  // the OS-dependent path separator (in the same manner as the PATH
318  // environment variable).
319 #ifdef _WIN32
320  std::string pathsep = ";";
321 #else
322  std::string pathsep = ":";
323 #endif
324 
325  if (getenv("CANTERA_DATA") != 0) {
326  string s = string(getenv("CANTERA_DATA"));
327  size_t start = 0;
328  size_t end = s.find(pathsep);
329  while(end != npos) {
330  inputDirs.push_back(s.substr(start, end-start));
331  start = end + 1;
332  end = s.find(pathsep, start);
333  }
334  inputDirs.push_back(s.substr(start,end));
335  }
336 
337 #ifdef _WIN32
338  // Under Windows, the Cantera setup utility records the installation
339  // directory in the registry. Data files are stored in the 'data'
340  // subdirectory of the main installation directory.
341  std::string installDir;
342  readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
343  "InstallDir", installDir, "");
344  if (installDir != "") {
345  inputDirs.push_back(installDir + "data");
346 
347  // Scripts for converting mechanisms to CTI and CMTL are installed in
348  // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
349  const char* old_pythonpath = getenv("PYTHONPATH");
350  std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
351  if (old_pythonpath) {
352  pythonpath += ";";
353  pythonpath.append(old_pythonpath);
354  }
355  _putenv(pythonpath.c_str());
356  }
357 
358 #endif
359 
360 #ifdef DARWIN
361  // add a default data location for Mac OS X
362  inputDirs.push_back("/Applications/Cantera/data");
363 #endif
364 
365  // CANTERA_DATA is defined in file config.h. This file is written during the
366  // build process (unix), and points to the directory specified by the
367  // 'prefix' option to 'configure', or else to /usr/local/cantera.
368 #ifdef CANTERA_DATA
369  string datadir = string(CANTERA_DATA);
370  inputDirs.push_back(datadir);
371 #endif
372 }
373 
374 void Application::addDataDirectory(const std::string& dir)
375 {
376  std::unique_lock<std::mutex> dirLock(dir_mutex);
377  if (inputDirs.empty()) {
379  }
380  string d = stripnonprint(dir);
381 
382  // Remove any existing entry for this directory
383  auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
384  if (iter != inputDirs.end()) {
385  inputDirs.erase(iter);
386  }
387 
388  // Insert this directory at the beginning of the search path
389  inputDirs.insert(inputDirs.begin(), d);
390 }
391 
392 std::string Application::findInputFile(const std::string& name)
393 {
394  std::unique_lock<std::mutex> dirLock(dir_mutex);
395  string::size_type islash = name.find('/');
396  string::size_type ibslash = name.find('\\');
397  std::vector<string>& dirs = inputDirs;
398 
399  // Expand "~/" to user's home directory, if possible
400  if (name.find("~/") == 0) {
401  char* home = getenv("HOME"); // POSIX systems
402  if (!home) {
403  home = getenv("USERPROFILE"); // Windows systems
404  }
405  if (home) {
406  return home + name.substr(1, npos);
407  }
408  }
409 
410  if (islash == string::npos && ibslash == string::npos) {
411  size_t nd = dirs.size();
412  for (size_t i = 0; i < nd; i++) {
413  string inname = dirs[i] + "/" + name;
414  std::ifstream fin(inname);
415  if (fin) {
416  return inname;
417  }
418  }
419  string msg = "\nInput file " + name + " not found in director";
420  msg += (nd == 1 ? "y " : "ies ");
421  for (size_t i = 0; i < nd; i++) {
422  msg += "\n'" + dirs[i] + "'";
423  if (i+1 < nd) {
424  msg += ", ";
425  }
426  }
427  msg += "\n\n";
428  msg += "To fix this problem, either:\n";
429  msg += " a) move the missing files into the local directory;\n";
430  msg += " b) define environment variable CANTERA_DATA to\n";
431  msg += " point to the directory containing the file.";
432  throw CanteraError("findInputFile", msg);
433  }
434 
435  return name;
436 }
437 
439 
440 } // namespace Cantera
void popError()
Discard the last error message.
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:235
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:95
void writelogendl()
Write an endl to the screen and flush output.
Definition: application.h:325
Base class for &#39;loggers&#39; that write text messages to log files.
Definition: logger.h:40
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
Class to hold global data.
Definition: application.h:44
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:80
virtual ~Application()
Destructor for class deletes global data.
std::string findInputFile(const std::string &name)
Find an input file.
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:165
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:420
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:97
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:320
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:402
std::string lastErrorMessage()
Retrieve the last error message in a string.
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:158
std::map< std::string, std::pair< XML_Node *, int > > xmlfiles
Current vector of XML file trees that have been previously parsed The second element of the value is ...
Definition: application.h:407
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:848
static Unit * units()
Initialize the static Unit class.
Definition: units.h:31
static std::mutex msg_mutex
Mutex for access to string messages.
void setDefaultDirectories()
Set the default directories for input files.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:65
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:85
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:716
std::string ct_string2ctml_string(const std::string &cti)
Get a string with the ctml representation of a cti input string.
Definition: ct2ctml.cpp:240
std::unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:154
void warn_deprecated(const std::string &method, const std::string &extra="")
Print a warning indicating that method is deprecated.
Header for units conversion utilities, which are used to translate user input from input files (See I...
std::set< std::string > warnings
Vector of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:410
XML_Node * get_XML_File(const std::string &file, int debug=0)
Return a pointer to the XML tree for a Cantera input file.
Application()
Constructor for class sets up the initial conditions Protected ctor access thru static member functio...
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
static Application * Instance()
Return a pointer to the one and only instance of class Application.
void removeThreadMessages()
Remove a local thread message.
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
Class to carry out messages.
Definition: application.h:48
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:151
Contains declarations for string manipulation functions within Cantera.
static std::mutex xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:39
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
static void ApplicationDestroy()
Static function that destroys the application class&#39;s data.
Namespace for the Cantera kernel.
Definition: application.cpp:29
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.cpp:90
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
void addError(const std::string &r, const std::string &msg="")
Set an error condition in the application class without throwing an exception.
Definition: application.cpp:66
void logErrors()
Prints all of the error messages using writelog.
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
void close_XML_File(const std::string &file)
Close an XML File.