Cantera  2.2.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
application.cpp
Go to the documentation of this file.
1 //! @file application.cpp
2 #include "application.h"
3 
4 #include "cantera/base/ctml.h"
6 #include "units.h"
7 
8 #include <fstream>
9 #include <sstream>
10 
11 using std::string;
12 using std::endl;
13 
14 #ifdef _WIN32
15 #include <windows.h>
16 #else
17 #include <sys/stat.h>
18 #endif
19 
20 #ifdef _MSC_VER
21 #pragma comment(lib, "advapi32")
22 #endif
23 
24 namespace Cantera
25 {
26 
27 // If running multiple threads in a cpp application, the Application class
28 // is the only internal object that is single instance with static data.
29 
30 #ifdef THREAD_SAFE_CANTERA
31 cthreadId_t getThisThreadId()
32 {
33 #if defined(BOOST_HAS_WINTHREADS)
34  return ::GetCurrentThreadId();
35 #elif defined(BOOST_HAS_PTHREADS)
36  return pthread_self();
37 #endif
38 }
39 
40 #endif
41 
42 //! Mutex for input directory access
43 static mutex_t dir_mutex;
44 
45 //! Mutex for creating singletons within the application object
46 static mutex_t app_mutex;
47 
48 //! Mutex for controlling access to XML file storage
49 static mutex_t xml_mutex;
50 
51 static int get_modified_time(const std::string& path) {
52 #ifdef _WIN32
53  HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_WRITE,
54  NULL, OPEN_EXISTING, 0, NULL);
55  if (hFile == INVALID_HANDLE_VALUE) {
56  throw CanteraError("get_modified_time", "Couldn't open file:" + path);
57  }
58  FILETIME modified;
59  GetFileTime(hFile, NULL, NULL, &modified);
60  CloseHandle(hFile);
61  return static_cast<int>(modified.dwLowDateTime);
62 #else
63  struct stat attrib;
64  stat(path.c_str(), &attrib);
65  return static_cast<int>(attrib.st_mtime);
66 #endif
67 }
68 
70  logwriter(0)
71 {
72  // install a default logwriter that writes to standard
73  // output / standard error
74  logwriter = new Logger();
75 }
76 
78  errorMessage(r.errorMessage),
79  errorRoutine(r.errorRoutine),
80  logwriter(0)
81 {
82  // install a default logwriter that writes to standard
83  // output / standard error
84  logwriter = new Logger(*(r.logwriter));
85 }
86 
87 Application::Messages& Application::Messages::operator=(const Messages& r)
88 {
89  if (this == &r) {
90  return *this;
91  }
92  errorMessage = r.errorMessage;
93  errorRoutine = r.errorRoutine;
94  logwriter = new Logger(*(r.logwriter));
95  return *this;
96 }
97 
98 Application::Messages::~Messages()
99 {
100  delete logwriter;
101 }
102 
103 void Application::Messages::addError(const std::string& r, const std::string& msg)
104 {
105  errorMessage.push_back(msg);
106  errorRoutine.push_back(r);
107 }
108 
110 {
111  return static_cast<int>(errorMessage.size()) ;
112 }
113 
115 {
116  if (logwriter == _logwriter) {
117  return ;
118  }
119  if (logwriter != 0) {
120  delete logwriter;
121  logwriter = 0 ;
122  }
123  logwriter = _logwriter;
124 }
125 
126 void Application::Messages::logerror(const std::string& msg)
127 {
128  Cantera::warn_deprecated("Application::Messages::logerror");
129  logwriter->error(msg) ;
130 }
131 
132 void Application::Messages::writelog(const std::string& msg)
133 {
134  logwriter->write(msg);
135 }
136 
138 {
139  logwriter->writeendl();
140 }
141 
142 #ifdef THREAD_SAFE_CANTERA
143 
144 //! Mutex for access to string messages
145 static mutex_t msg_mutex;
146 
148 {
149  ScopedLock msgLock(msg_mutex);
150  cthreadId_t curId = getThisThreadId() ;
151  threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ;
152  if (iter != m_threadMsgMap.end()) {
153  return iter->second.get();
154  }
155  pMessages_t pMsgs(new Messages()) ;
156  m_threadMsgMap.insert(std::pair< cthreadId_t, pMessages_t >(curId, pMsgs)) ;
157  return pMsgs.get() ;
158 }
159 
161 {
162  ScopedLock msgLock(msg_mutex);
163  cthreadId_t curId = getThisThreadId() ;
164  threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ;
165  if (iter != m_threadMsgMap.end()) {
166  m_threadMsgMap.erase(iter) ;
167  }
168 }
169 #endif // THREAD_SAFE_CANTERA
170 
172  stop_on_error(false),
173  m_suppress_deprecation_warnings(false)
174 {
175 #if !defined( THREAD_SAFE_CANTERA )
176  pMessenger = std::auto_ptr<Messages>(new Messages());
177 #endif
178 
179  // install a default logwriter that writes to standard
180  // output / standard error
182 #if defined(THREAD_SAFE_CANTERA)
183  Unit::units() ;
184 #endif
185 }
186 
188 {
189  ScopedLock appLock(app_mutex);
190  if (Application::s_app == 0) {
192  }
193  return s_app;
194 }
195 
197 {
198  std::map<std::string, std::pair<XML_Node*, int> >::iterator pos;
199  for (pos = xmlfiles.begin(); pos != xmlfiles.end(); ++pos) {
200  pos->second.first->unlock();
201  delete pos->second.first;
202  pos->second.first = 0;
203  }
204 }
205 
207 {
208  ScopedLock appLock(app_mutex);
209  if (Application::s_app != 0) {
210  delete Application::s_app;
211  Application::s_app = 0;
212  }
213 }
214 
215 void Application::warn_deprecated(const std::string& method,
216  const std::string& extra)
217 {
218  if (m_suppress_deprecation_warnings || warnings.count(method)) {
219  return;
220  }
221  warnings.insert(method);
222  writelog("WARNING: '" + method + "' is deprecated. " + extra);
223  writelogendl();
224 }
225 
227 {
228 #if defined(THREAD_SAFE_CANTERA)
230 #endif
231 }
232 
233 XML_Node* Application::get_XML_File(const std::string& file, int debug)
234 {
235  ScopedLock xmlLock(xml_mutex);
236  std::string path = "";
237  path = findInputFile(file);
238  int mtime = get_modified_time(path);
239 
240  if (xmlfiles.find(path) != xmlfiles.end()) {
241  // Already have a parsed XML tree for this file cached. Check the
242  // last-modified time.
243  std::pair<XML_Node*, int> cache = xmlfiles[path];
244  if (cache.second == mtime) {
245  return cache.first;
246  }
247  }
248  /*
249  * Check whether or not the file is XML (based on the file extension). If
250  * not, it will be first processed with the preprocessor.
251  */
252  string::size_type idot = path.rfind('.');
253  string ext;
254  if (idot != string::npos) {
255  ext = path.substr(idot, path.size());
256  } else {
257  ext = "";
258  }
259  XML_Node* x = new XML_Node("doc");
260  if (ext != ".xml" && ext != ".ctml") {
261  // Assume that we are trying to open a cti file. Do the conversion to XML.
262  std::stringstream phase_xml(ct2ctml_string(path));
263  x->build(phase_xml);
264  } else {
265  std::ifstream s(path.c_str());
266  if (s) {
267  x->build(s);
268  } else {
269  throw CanteraError("get_XML_File",
270  "cannot open "+file+" for reading.\n"
271  "Note, this error indicates a possible configuration problem.");
272  }
273  }
274  x->lock();
275  xmlfiles[path] = std::make_pair(x, mtime);
276  return x;
277 }
278 
280 {
281  ScopedLock xmlLock(xml_mutex);
282  std::pair<XML_Node*, int>& entry = xmlfiles[text];
283  if (entry.first) {
284  // Return existing cached XML tree
285  return entry.first;
286  }
287  std::stringstream s;
288  size_t start = text.find_first_not_of(" \t\r\n");
289  if (text.substr(start,1) == "<") {
290  s << text;
291  } else {
292  s << ct_string2ctml_string(text.substr(start));
293  }
294  entry.first = new XML_Node();
295  entry.first->build(s);
296  return entry.first;
297 }
298 
299 void Application::close_XML_File(const std::string& file)
300 {
301  ScopedLock xmlLock(xml_mutex);
302  if (file == "all") {
303  std::map<string, std::pair<XML_Node*, int> >::iterator
304  b = xmlfiles.begin(),
305  e = xmlfiles.end();
306  for (; b != e; ++b) {
307  b->second.first->unlock();
308  delete b->second.first;
309  }
310  xmlfiles.clear();
311  } else if (xmlfiles.find(file) != xmlfiles.end()) {
312  xmlfiles[file].first->unlock();
313  delete xmlfiles[file].first;
314  xmlfiles.erase(file);
315  }
316 }
317 
318 #ifdef _WIN32
319 long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
320  std::string& value, const std::string& defaultValue)
321 {
322 
323  HKEY key;
324  long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
325  if (open_error != ERROR_SUCCESS) {
326  return open_error;
327  }
328  value = defaultValue;
329  CHAR buffer[1024];
330  DWORD bufferSize = sizeof(buffer);
331  ULONG error;
332  error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
333  if (ERROR_SUCCESS == error) {
334  value = buffer;
335  }
336  RegCloseKey(key);
337  return error;
338 }
339 #endif
340 
342 {
343  if (!errorMessage.empty()) {
344  errorRoutine.pop_back() ;
345  errorMessage.pop_back() ;
346  }
347 }
348 
350 {
351  if (!errorMessage.empty()) {
352  string head =
353  "\n\n************************************************\n"
354  " Cantera Error! \n"
355  "************************************************\n\n";
356  return head+string("\nProcedure: ")+errorRoutine.back()
357  +string("\nError: ")+errorMessage.back();
358  } else {
359  return "<no Cantera error>";
360  }
361 }
362 
363 void Application::Messages::getErrors(std::ostream& f)
364 {
365  size_t i = errorMessage.size();
366  if (i == 0) {
367  return;
368  }
369  f << endl << endl;
370  f << "************************************************" << endl;
371  f << " Cantera Error! " << endl;
372  f << "************************************************" << endl
373  << endl;
374  for (size_t j = 0; j < i; j++) {
375  f << endl;
376  f << "Procedure: " << errorRoutine[j] << endl;
377  f << "Error: " << errorMessage[j] << endl;
378  }
379  f << endl << endl;
380  errorMessage.clear();
381  errorRoutine.clear();
382 }
383 
385 {
386  size_t i = errorMessage.size();
387  if (i == 0) {
388  return;
389  }
390  writelog("\n\n");
391  writelog("************************************************\n");
392  writelog(" Cantera Error! \n");
393  writelog("************************************************\n\n");
394  for (size_t j = 0; j < i; j++) {
395  writelog("\n");
396  writelog(string("Procedure: ")+ errorRoutine[j]+" \n");
397  writelog(string("Error: ")+ errorMessage[j]+" \n");
398  }
399  writelog("\n\n");
400  errorMessage.clear();
401  errorRoutine.clear();
402 }
403 
405 {
406  std::vector<string>& dirs = inputDirs;
407 
408  // always look in the local directory first
409  dirs.push_back(".");
410 
411 #ifdef _WIN32
412  // Under Windows, the Cantera setup utility records the installation
413  // directory in the registry. Data files are stored in the 'data'
414  // subdirectory of the main installation directory.
415 
416  std::string installDir;
417  readStringRegistryKey("SOFTWARE\\Cantera\\Cantera 2.2",
418  "InstallDir", installDir, "");
419  if (installDir != "") {
420  dirs.push_back(installDir + "data");
421 
422  // Scripts for converting mechanisms to CTI and CMTL are installed in
423  // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
424  const char* old_pythonpath = getenv("PYTHONPATH");
425  std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
426  if (old_pythonpath) {
427  pythonpath += ";";
428  pythonpath.append(old_pythonpath);
429  }
430  putenv(pythonpath.c_str());
431  }
432 
433 #endif
434 
435 #ifdef DARWIN
436  //
437  // add a default data location for Mac OS X
438  //
439  dirs.push_back("/Applications/Cantera/data");
440 #endif
441 
442  // if environment variable CANTERA_DATA is defined, then add it to the
443  // search path. CANTERA_DATA may include multiple directory, separated by
444  // the OS-dependent path separator (in the same manner as the PATH
445  // environment variable).
446 #ifdef _WIN32
447  std::string pathsep = ";";
448 #else
449  std::string pathsep = ":";
450 #endif
451 
452  if (getenv("CANTERA_DATA") != 0) {
453  string s = string(getenv("CANTERA_DATA"));
454  size_t start = 0;
455  size_t end = s.find(pathsep);
456  while(end != npos) {
457  dirs.push_back(s.substr(start, end-start));
458  start = end + 1;
459  end = s.find(pathsep, start);
460  }
461  dirs.push_back(s.substr(start,end));
462  }
463 
464  // CANTERA_DATA is defined in file config.h. This file is written
465  // during the build process (unix), and points to the directory
466  // specified by the 'prefix' option to 'configure', or else to
467  // /usr/local/cantera.
468 #ifdef CANTERA_DATA
469  string datadir = string(CANTERA_DATA);
470  dirs.push_back(datadir);
471 #endif
472 }
473 
474 void Application::addDataDirectory(const std::string& dir)
475 {
476  ScopedLock dirLock(dir_mutex);
477  if (inputDirs.empty()) {
479  }
480  string d = stripnonprint(dir);
481 
482  // Remove any existing entry for this directory
483  std::vector<string>::iterator iter = std::find(inputDirs.begin(),
484  inputDirs.end(), d);
485  if (iter != inputDirs.end()) {
486  inputDirs.erase(iter);
487  }
488 
489  // Insert this directory at the beginning of the search path
490  inputDirs.insert(inputDirs.begin(), d);
491 }
492 
493 std::string Application::findInputFile(const std::string& name)
494 {
495  ScopedLock dirLock(dir_mutex);
496  string::size_type islash = name.find('/');
497  string::size_type ibslash = name.find('\\');
498  string inname;
499  std::vector<string>& dirs = inputDirs;
500 
501  // Expand "~/" to user's home directory, if possible
502  if (name.find("~/") == 0) {
503  char* home = getenv("HOME"); // POSIX systems
504  if (!home) {
505  home = getenv("USERPROFILE"); // Windows systems
506  }
507  if (home) {
508  return home + name.substr(1, npos);
509  }
510  }
511 
512  if (islash == string::npos && ibslash == string::npos) {
513  size_t nd = dirs.size();
514  inname = "";
515  for (size_t i = 0; i < nd; i++) {
516  inname = dirs[i] + "/" + name;
517  std::ifstream fin(inname.c_str());
518  if (fin) {
519  fin.close();
520  return inname;
521  }
522  }
523  string msg;
524  msg = "\nInput file " + name
525  + " not found in director";
526  msg += (nd == 1 ? "y " : "ies ");
527  for (size_t i = 0; i < nd; i++) {
528  msg += "\n'" + dirs[i] + "'";
529  if (i+1 < nd) {
530  msg += ", ";
531  }
532  }
533  msg += "\n\n";
534  msg += "To fix this problem, either:\n";
535  msg += " a) move the missing files into the local directory;\n";
536  msg += " b) define environment variable CANTERA_DATA to\n";
537  msg += " point to the directory containing the file.";
538  throw CanteraError("findInputFile", msg);
539  }
540 
541  return name;
542 }
543 
545 
546 } // namespace Cantera
void popError()
Discard the last error message.
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:178
void writelogendl()
Write an end of line character to the screen and flush output.
void writelogendl()
Write an endl to the screen and flush output.
Definition: application.h:333
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:39
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data...
Class to hold global data.
Definition: application.h:40
int getErrorCount()
Return the number of errors that have been encountered so far.
ThreadMessages pMessenger
Current pointer to the logwriter.
Definition: application.h:428
virtual ~Application()
Destructor for class deletes global data.
Messages()
Constructor for the Messages class.
Definition: application.cpp:69
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:435
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:100
void logerror(const std::string &msg)
Write an error message and quit.
void warn_deprecated(const std::string &method, const std::string &extra)
Print a warning indicating that method is deprecated.
Definition: global.cpp:78
void addError(const std::string &r, const std::string &msg)
Set an error condition in the application class without throwing an exception.
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:328
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
boost::shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:175
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:398
std::string lastErrorMessage()
Retrieve the last error message in a string.
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:418
static mutex_t msg_mutex
Mutex for access to string messages.
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:899
static Unit * units()
Initialize the static Unit class.
Definition: units.h:29
void error(const std::string &msg)
Write an error message and quit.
Definition: global.cpp:72
void setDefaultDirectories()
Set the default directories for input files.
static mutex_t app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:46
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:99
void setLogger(Logger *logwriter)
Install a logger.
std::string ct_string2ctml_string(const std::string &cti)
Get a string with the ctml representation of a cti input string.
Definition: ct2ctml.cpp:183
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:420
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.
Class to carry out messages.
Definition: application.h:44
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:164
static mutex_t xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:49
Contains declarations for string manipulation functions within Cantera.
std::vector< std::string > errorRoutine
Current error Routine.
Definition: application.h:167
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
static mutex_t dir_mutex
Mutex for input directory access.
Definition: application.cpp:43
static void ApplicationDestroy()
Static function that destroys the application class's data.
Logger * logwriter
Current pointer to the logwriter.
Definition: application.h:170
void writelog(const std::string &msg)
Write a message to the screen.
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
void build(std::istream &f)
Main routine to create an tree-like representation of an XML file.
Definition: xml.cpp:764
void logErrors()
Prints all of the error messages using writelog.
bool stop_on_error
Current list of error messages.
Definition: application.h:410
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.