Cantera  2.5.1
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 https://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 int get_modified_time(const std::string& path) {
42 #ifdef _WIN32
43  HANDLE hFile = CreateFile(path.c_str(), 0, 0,
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(fmt::format("DeprecationWarning: {}: {}", method, extra));
174  writelogendl();
175 }
176 
177 void Application::warn_user(const std::string& method,
178  const std::string& extra)
179 {
180  writelog(fmt::format("CanteraWarning: {}: {}", method, extra));
181  writelogendl();
182 }
183 
185 {
186  pMessenger.removeThreadMessages();
187 }
188 
189 XML_Node* Application::get_XML_File(const std::string& file, int debug)
190 {
191  std::unique_lock<std::mutex> xmlLock(xml_mutex);
192  std::string path = findInputFile(file);
193  int mtime = get_modified_time(path);
194 
195  if (xmlfiles.find(path) != xmlfiles.end()) {
196  // Already have a parsed XML tree for this file cached. Check the
197  // last-modified time.
198  std::pair<XML_Node*, int> cache = xmlfiles[path];
199  if (cache.second == mtime) {
200  return cache.first;
201  }
202  }
203 
204  // Check whether or not the file is XML (based on the file extension). If
205  // not, it will be first processed with the preprocessor.
206  string::size_type idot = path.rfind('.');
207  string ext;
208  if (idot != string::npos) {
209  ext = path.substr(idot, path.size());
210  } else {
211  ext = "";
212  }
213  XML_Node* x = new XML_Node("doc");
214  if (ext != ".xml" && ext != ".ctml") {
215  // Assume that we are trying to open a cti file. Do the conversion to XML.
216  std::stringstream phase_xml(ct2ctml_string(path));
217  x->build(phase_xml, path);
218  } else {
219  x->build(path);
220  }
221  x->lock();
222  xmlfiles[path] = {x, mtime};
223  return x;
224 }
225 
227 {
228  std::unique_lock<std::mutex> xmlLock(xml_mutex);
229  std::pair<XML_Node*, int>& entry = xmlfiles[text];
230  if (entry.first) {
231  // Return existing cached XML tree
232  return entry.first;
233  }
234  std::stringstream s;
235  size_t start = text.find_first_not_of(" \t\r\n");
236  if (text.substr(start,1) == "<") {
237  s << text;
238  } else {
239  s << ct_string2ctml_string(text.substr(start));
240  }
241  entry.first = new XML_Node();
242  entry.first->build(s, "[string]");
243  return entry.first;
244 }
245 
246 void Application::close_XML_File(const std::string& file)
247 {
248  std::unique_lock<std::mutex> xmlLock(xml_mutex);
249  if (file == "all") {
250  for (const auto& f : xmlfiles) {
251  f.second.first->unlock();
252  delete f.second.first;
253  }
254  xmlfiles.clear();
255  } else if (xmlfiles.find(file) != xmlfiles.end()) {
256  xmlfiles[file].first->unlock();
257  delete xmlfiles[file].first;
258  xmlfiles.erase(file);
259  }
260 }
261 
262 #ifdef _WIN32
263 long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
264  std::string& value, const std::string& defaultValue)
265 {
266  HKEY key;
267  long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
268  if (open_error != ERROR_SUCCESS) {
269  return open_error;
270  }
271  value = defaultValue;
272  CHAR buffer[1024];
273  DWORD bufferSize = sizeof(buffer);
274  ULONG error;
275  error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
276  if (ERROR_SUCCESS == error) {
277  value = buffer;
278  }
279  RegCloseKey(key);
280  return error;
281 }
282 #endif
283 
285 {
286  if (!errorMessage.empty()) {
287  errorMessage.pop_back();
288  }
289 }
290 
292 {
293  if (!errorMessage.empty()) {
294  return errorMessage.back();
295  } else {
296  return "<no Cantera error>";
297  }
298 }
299 
300 void Application::Messages::getErrors(std::ostream& f)
301 {
302  for (size_t j = 0; j < errorMessage.size(); j++) {
303  f << errorMessage[j] << endl;
304  }
305  errorMessage.clear();
306 }
307 
309 {
310  for (size_t j = 0; j < errorMessage.size(); j++) {
311  writelog(errorMessage[j]);
312  writelogendl();
313  }
314  errorMessage.clear();
315 }
316 
318 {
319  // always look in the local directory first
320  inputDirs.push_back(".");
321 
322  // if environment variable CANTERA_DATA is defined, then add it to the
323  // search path. CANTERA_DATA may include multiple directory, separated by
324  // the OS-dependent path separator (in the same manner as the PATH
325  // environment variable).
326 #ifdef _WIN32
327  std::string pathsep = ";";
328 #else
329  std::string pathsep = ":";
330 #endif
331 
332  if (getenv("CANTERA_DATA") != 0) {
333  string s = string(getenv("CANTERA_DATA"));
334  size_t start = 0;
335  size_t end = s.find(pathsep);
336  while(end != npos) {
337  inputDirs.push_back(s.substr(start, end-start));
338  start = end + 1;
339  end = s.find(pathsep, start);
340  }
341  inputDirs.push_back(s.substr(start,end));
342  }
343 
344 #ifdef _WIN32
345  // Under Windows, the Cantera setup utility records the installation
346  // directory in the registry. Data files are stored in the 'data'
347  // subdirectory of the main installation directory.
348  std::string installDir;
349  readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
350  "InstallDir", installDir, "");
351  if (installDir != "") {
352  inputDirs.push_back(installDir + "data");
353 
354  // Scripts for converting mechanisms to CTI and CMTL are installed in
355  // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
356  const char* old_pythonpath = getenv("PYTHONPATH");
357  std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
358  if (old_pythonpath) {
359  pythonpath += ";";
360  pythonpath.append(old_pythonpath);
361  }
362  _putenv(pythonpath.c_str());
363  }
364 
365 #endif
366 
367 #ifdef DARWIN
368  // add a default data location for Mac OS X
369  inputDirs.push_back("/Applications/Cantera/data");
370 #endif
371 
372  // CANTERA_DATA is defined in file config.h. This file is written during the
373  // build process (unix), and points to the directory specified by the
374  // 'prefix' option to 'configure', or else to /usr/local/cantera.
375 #ifdef CANTERA_DATA
376  string datadir = string(CANTERA_DATA);
377  inputDirs.push_back(datadir);
378 #endif
379 }
380 
381 void Application::addDataDirectory(const std::string& dir)
382 {
383  std::unique_lock<std::mutex> dirLock(dir_mutex);
384  if (inputDirs.empty()) {
386  }
387  string d = stripnonprint(dir);
388 
389  // Expand "~/" to user's home directory, if possible
390  if (d.find("~/") == 0 || d.find("~\\") == 0) {
391  char* home = getenv("HOME"); // POSIX systems
392  if (!home) {
393  home = getenv("USERPROFILE"); // Windows systems
394  }
395  if (home) {
396  d = home + d.substr(1, npos);
397  }
398  }
399 
400  // Remove any existing entry for this directory
401  auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
402  if (iter != inputDirs.end()) {
403  inputDirs.erase(iter);
404  }
405 
406  // Insert this directory at the beginning of the search path
407  inputDirs.insert(inputDirs.begin(), d);
408 }
409 
410 std::string Application::findInputFile(const std::string& name)
411 {
412  std::unique_lock<std::mutex> dirLock(dir_mutex);
413  string::size_type islash = name.find('/');
414  string::size_type ibslash = name.find('\\');
415  string::size_type icolon = name.find(':');
416  std::vector<string>& dirs = inputDirs;
417 
418  // Expand "~/" to user's home directory, if possible
419  if (name.find("~/") == 0 || name.find("~\\") == 0) {
420  char* home = getenv("HOME"); // POSIX systems
421  if (!home) {
422  home = getenv("USERPROFILE"); // Windows systems
423  }
424  if (home) {
425  string full_name = home + name.substr(1, npos);
426  std::ifstream fin(full_name);
427  if (fin) {
428  return full_name;
429  } else {
430  throw CanteraError("Application::findInputFile",
431  "Input file '{}' not found", name);
432  }
433  }
434  }
435 
436  // If this is an absolute path, just look for the file there
437  if (islash == 0 || ibslash == 0
438  || (icolon == 1 && (ibslash == 2 || islash == 2)))
439  {
440  std::ifstream fin(name);
441  if (fin) {
442  return name;
443  } else {
444  throw CanteraError("Application::findInputFile",
445  "Input file '{}' not found", name);
446  }
447  }
448 
449  // Search the Cantera data directories for the input file, and return
450  // the full path if a match is found
451  size_t nd = dirs.size();
452  for (size_t i = 0; i < nd; i++) {
453  string full_name = dirs[i] + "/" + name;
454  std::ifstream fin(full_name);
455  if (fin) {
456  return full_name;
457  }
458  }
459  string msg = "\nInput file " + name + " not found in director";
460  msg += (nd == 1 ? "y " : "ies ");
461  for (size_t i = 0; i < nd; i++) {
462  msg += "\n'" + dirs[i] + "'";
463  if (i+1 < nd) {
464  msg += ", ";
465  }
466  }
467  msg += "\n\n";
468  msg += "To fix this problem, either:\n";
469  msg += " a) move the missing files into the local directory;\n";
470  msg += " b) define environment variable CANTERA_DATA to\n";
471  msg += " point to the directory containing the file.";
472  throw CanteraError("Application::findInputFile", msg);
473 }
474 
476 
477 } // namespace Cantera
Cantera::XML_Node::lock
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:894
Cantera::Unit::units
static Unit * units()
Initialize the static Unit class.
Definition: units.h:31
Cantera::Application::warn_deprecated
void warn_deprecated(const std::string &method, const std::string &extra="")
Print a warning indicating that method is deprecated.
Definition: application.cpp:164
Cantera::Application::warn_user
void warn_user(const std::string &method, const std::string &extra="")
Print a user warning arising during usage of method.
Definition: application.cpp:177
Cantera::Application::warnings
std::set< std::string > warnings
Vector of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:423
Cantera::Application::Application
Application()
Constructor for class sets up the initial conditions Protected ctor access thru static member functio...
Definition: application.cpp:126
Cantera::Logger
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:40
Cantera::Application::get_XML_File
XML_Node * get_XML_File(const std::string &file, int debug=0)
Return a pointer to the XML tree for a Cantera input file.
Definition: application.cpp:189
Cantera::Application::thread_complete
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
Definition: application.cpp:184
Cantera::Application::Instance
static Application * Instance()
Return a pointer to the one and only instance of class Application.
Definition: application.cpp:137
Cantera::Application::Messages::lastErrorMessage
std::string lastErrorMessage()
Retrieve the last error message in a string.
Definition: application.cpp:291
Cantera::XML_Node::build
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:762
Cantera::ct_string2ctml_string
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
Cantera::Application::ApplicationDestroy
static void ApplicationDestroy()
Static function that destroys the application class's data.
Definition: application.cpp:155
Cantera::Application::Messages::logErrors
void logErrors()
Prints all of the error messages using writelog.
Definition: application.cpp:308
Cantera::Application::close_XML_File
void close_XML_File(const std::string &file)
Close an XML File.
Definition: application.cpp:246
Cantera::msg_mutex
static std::mutex msg_mutex
Mutex for access to string messages.
Definition: application.cpp:101
Cantera::Application::findInputFile
std::string findInputFile(const std::string &name)
Find an input file.
Definition: application.cpp:410
Cantera::Application::setDefaultDirectories
void setDefaultDirectories()
Set the default directories for input files.
Definition: application.cpp:317
Cantera::Application::Messages::popError
void popError()
Discard the last error message.
Definition: application.cpp:284
Cantera::Application::ThreadMessages::removeThreadMessages
void removeThreadMessages()
Remove a local thread message.
Definition: application.cpp:116
Cantera::Application::Messages::writelogendl
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:95
Cantera::Application::Messages::setLogger
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:85
Cantera::Application::Messages::errorMessage
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:153
Cantera::Application
Class to hold global data.
Definition: application.h:46
Cantera::Application::~Application
virtual ~Application()
Destructor for class deletes global data.
Definition: application.cpp:146
Cantera::XML_Node
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
Cantera::Application::writelogendl
void writelogendl()
Write an endl to the screen and flush output.
Definition: application.h:330
ctml.h
stringUtils.h
Cantera::Application::s_app
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:433
Cantera::Application::Messages::addError
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
units.h
Cantera::Application::addDataDirectory
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
Definition: application.cpp:381
Cantera::dir_mutex
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
Cantera::ct2ctml_string
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:173
Cantera::Application::get_XML_from_string
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
Definition: application.cpp:226
Cantera::Application::inputDirs
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:412
Cantera::Application::Messages::getErrorCount
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:80
Cantera::Application::ThreadMessages::operator->
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
Definition: application.cpp:103
Cantera::Application::Messages
Class to carry out messages.
Definition: application.h:50
Cantera::stripnonprint
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Definition: stringUtils.cpp:49
Cantera::Application::writelog
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:325
Cantera::CanteraError
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:60
Cantera::Application::Messages::writelog
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.cpp:90
Cantera::Application::xmlfiles
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:420
Cantera::Application::Messages::getErrors
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
Definition: application.cpp:300
Cantera::xml_mutex
static std::mutex xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:39
Cantera::npos
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
Cantera::Application::pMessages_t
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:160
Cantera
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:263
Cantera::Application::Messages::logwriter
std::unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:156
Cantera::app_mutex
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
application.h