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
Class to carry out messages.
Definition: application.h:51
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:153
std::unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:156
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:95
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
void removeThreadMessages()
Remove a local thread message.
Class to hold global data.
Definition: application.h:47
std::set< std::string > warnings
Vector of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:423
XML_Node * get_XML_File(const std::string &file, int debug=0)
Return a pointer to the XML tree for a Cantera input file.
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:433
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:325
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:412
static Application * Instance()
Return a pointer to the one and only instance of class Application.
virtual ~Application()
Destructor for class deletes global data.
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
void close_XML_File(const std::string &file)
Close an XML File.
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
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
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:160
void warn_deprecated(const std::string &method, const std::string &extra="")
Print a warning indicating that method is deprecated.
void warn_user(const std::string &method, const std::string &extra="")
Print a user warning arising during usage of method.
Application()
Constructor for class sets up the initial conditions Protected ctor access thru static member functio...
void writelogendl()
Write an endl to the screen and flush output.
Definition: application.h:330
static void ApplicationDestroy()
Static function that destroys the application class's data.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:41
static Unit * units()
Initialize the static Unit class.
Definition: units.h:31
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:104
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:762
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:894
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
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.
std::string lastErrorMessage()
Retrieve the last error message in a string.
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:80
void popError()
Discard the last error message.
std::string findInputFile(const std::string &name)
Find an input file.
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:173
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
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
void setDefaultDirectories()
Set the default directories for input files.
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:188
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.cpp:90
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:85
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:264
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
static std::mutex xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:39
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Definition: stringUtils.cpp:49
static std::mutex msg_mutex
Mutex for access to string messages.
Contains declarations for string manipulation functions within Cantera.
Header for units conversion utilities, which are used to translate user input from input files (See I...