Cantera  2.0
application.cpp
1 #include "application.h"
2 
4 #include "cantera/base/ctml.h"
6 #include "cantera/base/xml.h"
7 #include "units.h"
8 
9 #include <map>
10 #include <string>
11 #include <fstream>
12 
13 using std::string;
14 using std::endl;
15 
16 #ifdef _WIN32
17 #include <windows.h>
18 #endif
19 
20 #ifdef _MSC_VER
21 #pragma comment(lib, "advapi32")
22 #endif
23 
24 namespace Cantera {
25 
26 // If running multiple threads in a cpp application, the Application class
27 // is the only internal object that is single instance with static data.
28 
29 #ifdef THREAD_SAFE_CANTERA
30 cthreadId_t getThisThreadId()
31 {
32 #if defined(BOOST_HAS_WINTHREADS)
33  return ::GetCurrentThreadId();
34 #elif defined(BOOST_HAS_PTHREADS)
35  return pthread_self();
36 #endif
37 }
38 
39 #endif
40 
41 //! Mutex for input directory access
42 static mutex_t dir_mutex;
43 
44 //! Mutex for creating singletons within the application object
45 static mutex_t app_mutex;
46 
47 //! Mutex for controlling access to XML file storage
48 static mutex_t xml_mutex;
49 
50 
52  errorMessage(0),
53  errorRoutine(0),
54  logwriter(0)
55 #ifdef WITH_HTML_LOGS
56  ,xmllog(0),
57  current(0),
58  loglevel(0),
59  loglevels(0),
60  loggroups(0)
61 #endif
62 {
63  // install a default logwriter that writes to standard
64  // output / standard error
65  logwriter = new Logger();
66 }
67 
69  errorMessage(r.errorMessage),
70  errorRoutine(r.errorRoutine),
71  logwriter(0)
72 #ifdef WITH_HTML_LOGS
73  , xmllog(r.xmllog),
74  current(r.current),
75  loglevel(r.loglevel),
76  loglevels(r.loglevels),
77  loggroups(r.loggroups)
78 #endif
79 {
80  // install a default logwriter that writes to standard
81  // output / standard error
82  logwriter = new Logger(*(r.logwriter));
83 }
84 
86  if (this == &r) {
87  return *this;
88  }
89  errorMessage = r.errorMessage;
90  errorRoutine = r.errorRoutine;
91  logwriter = new Logger(*(r.logwriter));
92 #ifdef WITH_HTML_LOGS
93  xmllog = r.xmllog;
94  current = r.current;
95  loglevel = r.loglevel;
96  loglevels = r.loglevels;
97  loggroups = r.loggroups;
98 #endif
99  return *this;
100 }
101 
103  delete logwriter;
104 #ifdef WITH_HTML_LOGS
105  if (xmllog) {
106  write_logfile("orphan");
107  }
108 #endif
109 }
110 
111 // Set an error condition in the application class without throwing an exception
112 void Application::Messages::addError(std::string r, std::string msg)
113 {
114  errorMessage.push_back(msg);
115  errorRoutine.push_back(r);
116 }
117 
118 // Return the number of errors encountered so far
120 {
121  return static_cast<int>(errorMessage.size()) ;
122 }
123 
125 {
126  if (logwriter == _logwriter) {
127  return ;
128  }
129  if (logwriter != 0) {
130  delete logwriter;
131  logwriter = 0 ;
132  }
133  logwriter = _logwriter;
134 }
135 
136 // Return an integer specifying the application environment.
138 {
139  return logwriter->env() ;
140 }
141 
142 // Write an error message and terminate execution
143 void Application::Messages::logerror(const std::string& msg)
144 {
145  logwriter->error(msg) ;
146 }
147 
148 // Write a message to the screen
149 void Application::Messages::writelog(const char* pszmsg)
150 {
151  logwriter->write(pszmsg) ;
152 }
153 
154 // Write a message to the screen
155 void Application::Messages::writelog(const std::string& msg)
156 {
157  logwriter->write(msg);
158 }
159 
160 // Write an endl to the screen and flush output
162 {
163  logwriter->writeendl();
164 }
165 
166 #ifdef WITH_HTML_LOGS
167 
168 void Application::Messages::beginLogGroup(std::string title, int _loglevel /*=-99*/)
169 {
170  // loglevel is a member of the Messages class.
171  if (_loglevel != -99) {
172  loglevel = _loglevel;
173  } else {
174  loglevel--;
175  }
176  if (loglevel <= 0) {
177  return;
178  }
179  // Add the current loglevel to the vector of loglevels
180  loglevels.push_back(loglevel);
181  // Add the title of the current logLevel to the vector of titles
182  loggroups.push_back(title);
183  // If we haven't started an XML tree for the log file, do so here
184  if (xmllog == 0) {
185  // The top of this tree will have a zero pointer.
186  xmllog = new XML_Node("html");
187  current = &xmllog->addChild("ul");
188  }
189  // Add two children to the XML tree.
190  current = &current->addChild("li","<b>"+title+"</b>");
191  current = &current->addChild("ul");
192 }
193 
194 void Application::Messages::addLogEntry(std::string tag, std::string value)
195 {
196  if (loglevel > 0 && current) {
197  current->addChild("li",tag+": "+value);
198  }
199 }
200 
201 void Application::Messages::addLogEntry(std::string tag, doublereal value)
202 {
203  if (loglevel > 0 && current) {
204  current->addChild("li",tag+": "+fp2str(value));
205  }
206 }
207 
208 void Application::Messages::addLogEntry(std::string tag, int value)
209 {
210  if (loglevel > 0 && current) {
211  current->addChild("li",tag+": "+int2str(value));
212  }
213 }
214 
216 {
217  if (loglevel > 0 && current) {
218  current->addChild("li",msg);
219  }
220 }
221 
222 void Application::Messages::endLogGroup(std::string title)
223 {
224  if (loglevel <= 0) {
225  return;
226  }
227  AssertThrowMsg(current, "Application::Messages::endLogGroup",
228  "Error while ending a LogGroup. This is probably due to an unmatched"
229  " beginning and ending group");
230  current = current->parent();
231  AssertThrowMsg(current, "Application::Messages::endLogGroup",
232  "Error while ending a LogGroup. This is probably due to an unmatched"
233  " beginning and ending group");
234  current = current->parent();
235  // Get the loglevel of the previous level and get rid of
236  // vector entry in loglevels.
237  loglevel = loglevels.back();
238  loglevels.pop_back();
239  if (title != "" && title != loggroups.back()) {
240  writelog("Logfile error."
241  "\n beginLogGroup: "+ loggroups.back()+
242  "\n endLogGroup: "+title+"\n");
243  write_logfile("logerror");
244  } else if (loggroups.size() == 1) {
245  write_logfile(loggroups.back()+"_log");
246  loggroups.clear();
247  loglevels.clear();
248  } else {
249  loggroups.pop_back();
250  }
251 }
252 
254 {
255  if (!xmllog) {
256  return;
257  }
258  std::string::size_type idot = file.rfind('.');
259  std::string ext = "";
260  std::string nm = file;
261  if (idot != std::string::npos) {
262  ext = file.substr(idot, file.size());
263  nm = file.substr(0,idot);
264  } else {
265  ext = ".html";
266  nm = file;
267  }
268 
269  // see if file exists. If it does, find an integer that
270  // can be appended to the name to create the name of a file
271  // that does not exist.
272  std::string fname = nm + ext;
273  std::ifstream f(fname.c_str());
274  if (f) {
275  int n = 0;
276  while (1 > 0) {
277  n++;
278  fname = nm + int2str(n) + ext;
279  std::ifstream f(fname.c_str());
280  if (!f) {
281  break;
282  }
283  }
284  }
285 
286  // Now we have a file name that does not correspond to any
287  // existing file. Open it as an output stream, and dump the
288  // XML (HTML) tree to it.
289 
290  if (xmllog) {
291  std::ofstream f(fname.c_str());
292  // go to the top of the tree, and write it all.
293  xmllog->root().write(f);
294  f.close();
295  writelog("Log file " + fname + " written.\n");
296  delete xmllog;
297  xmllog = 0;
298  current = 0;
299  }
300 }
301 
302 #endif // WITH_HTML_LOGS
303 
304 #ifdef THREAD_SAFE_CANTERA
305 
306 //! Mutex for access to string messages
307 static mutex_t msg_mutex;
308 
310 {
311  ScopedLock msgLock(msg_mutex);
312  cthreadId_t curId = getThisThreadId() ;
313  threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ;
314  if (iter != m_threadMsgMap.end()) {
315  return (iter->second.get()) ;
316  }
317  pMessages_t pMsgs(new Messages()) ;
318  m_threadMsgMap.insert(std::pair< cthreadId_t, pMessages_t >(curId, pMsgs)) ;
319  return pMsgs.get() ;
320 }
321 
323 {
324  ScopedLock msgLock(msg_mutex);
325  cthreadId_t curId = getThisThreadId() ;
326  threadMsgMap_t::iterator iter = m_threadMsgMap.find(curId) ;
327  if (iter != m_threadMsgMap.end()) {
328  m_threadMsgMap.erase(iter) ;
329  }
330 }
331 #endif // THREAD_SAFE_CANTERA
332 
334  inputDirs(0),
335  stop_on_error(false),
336  options(),
337  xmlfiles(),
338  pMessenger() {
339 #if !defined( THREAD_SAFE_CANTERA )
340  pMessenger = std::auto_ptr<Messages>(new Messages());
341 #endif
342 
343  // install a default logwriter that writes to standard
344  // output / standard error
345  // logwriter = new Logger();
346  //#ifdef WITH_HTML_LOGS
347  // // HTML log files
348  // xmllog = 0;
349  // current = 0;
350  // loglevel = 0;
351  //#endif
353 #if defined(THREAD_SAFE_CANTERA)
354  Unit::units() ;
355 #endif
356 }
357 
359  ScopedLock appLock(app_mutex);
360  if (Application::s_app == 0) {
362  }
363  return s_app;
364 }
365 
367 {
368  std::map<std::string, XML_Node*>::iterator pos;
369  for (pos = xmlfiles.begin(); pos != xmlfiles.end(); ++pos) {
370  pos->second->unlock();
371  delete pos->second;
372  pos->second = 0;
373  }
374 }
375 
377  ScopedLock appLock(app_mutex);
378  if (Application::s_app != 0) {
379  delete Application::s_app;
380  Application::s_app = 0;
381  }
382 }
383 
385 {
386 #if defined(THREAD_SAFE_CANTERA)
388 #endif
389 }
390 
391 
392 XML_Node* Application::get_XML_File(std::string file, int debug)
393 {
394  ScopedLock xmlLock(xml_mutex);
395  std::string path = "";
396  path = findInputFile(file);
397 #ifdef _WIN32
398  // RFB: For Windows make the path POSIX compliant so code looking for directory
399  // separators is simpler. Just look for '/' not both '/' and '\\'
400  std::replace_if(path.begin(), path.end(),
401  std::bind2nd(std::equal_to<char>(), '\\'), '/') ;
402 #endif
403 
404  string ff = path;
405  if (xmlfiles.find(path)
406  == xmlfiles.end()) {
407  /*
408  * Check whether or not the file is XML. If not, it will
409  * be first processed with the preprocessor. We determine
410  * whether it is an XML file by looking at the file extension.
411  */
412  string::size_type idot = path.rfind('.');
413  string ext;
414  if (idot != string::npos) {
415  ext = path.substr(idot, path.size());
416  } else {
417  ext = "";
418  idot = path.size();
419  }
420  if (ext != ".xml" && ext != ".ctml") {
421  /*
422  * We will assume that we are trying to open a cti file.
423  * First, determine the name of the xml file, ff, derived from
424  * the cti file.
425  * In all cases, we will write the xml file to the current
426  * directory.
427  */
428  string::size_type islash = path.rfind('/');
429  if (islash != string::npos) {
430  ff = string("./")+path.substr(islash+1,idot-islash - 1) + ".xml";
431  } else {
432  ff = string("./")+path.substr(0,idot) + ".xml";
433  }
434  if (debug > 0) {
435  writelog("get_XML_File(): Expected location of xml file = " +
436  ff + "\n");
437  }
438  /*
439  * Do a search of the existing XML trees to determine if we have
440  * already processed this file. If we have, return a pointer to
441  * the processed xml tree.
442  */
443  if (xmlfiles.find(ff) != xmlfiles.end()) {
444  if (debug > 0) {
445  writelog("get_XML_File(): File, " + ff +
446  ", was previously read." +
447  " Retrieving the stored xml tree.\n");
448  }
449  return xmlfiles[ff];
450  }
451  /*
452  * Ok, we didn't find the processed XML tree. Do the conversion
453  * to xml, possibly overwriting the file, ff, in the process.
454  */
455  ctml::ct2ctml(path.c_str(),debug);
456  } else {
457  ff = path;
458  }
459  /*
460  * Take the XML file ff, open it, and process it, creating an
461  * XML tree, and then adding an entry in the map. We will store
462  * the absolute pathname as the key for this map.
463  */
464  std::ifstream s(ff.c_str());
465 
466  XML_Node* x = new XML_Node("doc");
467  if (s) {
468  x->build(s);
469  x->lock();
470  xmlfiles[ff] = x;
471  } else {
472  string estring = "cannot open "+ff+" for reading.";
473  estring += "Note, this error indicates a possible configuration problem.";
474  throw CanteraError("get_XML_File", estring);
475  }
476  }
477 
478  /*
479  * Return the XML node pointer. At this point, we are sure that the
480  * lookup operation in the return statement will return a valid
481  * pointer.
482  */
483  return xmlfiles[ff];
484 }
485 
486 
487 void Application::close_XML_File(std::string file)
488 {
489  ScopedLock xmlLock(xml_mutex);
490  if (file == "all") {
491  std::map<string, XML_Node*>::iterator
492  b = xmlfiles.begin(),
493  e = xmlfiles.end();
494  for (; b != e; ++b) {
495  b->second->unlock();
496  delete b->second;
497  xmlfiles.erase(b->first);
498  }
499  } else if (xmlfiles.find(file) != xmlfiles.end()) {
500  xmlfiles[file]->unlock();
501  delete xmlfiles[file];
502  xmlfiles.erase(file);
503  }
504 }
505 
506 #ifdef _WIN32
507 long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
508  std::string& value, const std::string& defaultValue)
509 {
510 
511  HKEY key;
512  long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
513  if (open_error != ERROR_SUCCESS) {
514  return open_error;
515  }
516  value = defaultValue;
517  CHAR buffer[1024];
518  DWORD bufferSize = sizeof(buffer);
519  ULONG error;
520  error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
521  if (ERROR_SUCCESS == error) {
522  value = buffer;
523  }
524  RegCloseKey(key);
525  return error;
526 }
527 #endif
528 
530 {
531  if (static_cast<int>(errorMessage.size()) > 0) {
532  errorRoutine.pop_back() ;
533  errorMessage.pop_back() ;
534  }
535 }
536 
538 {
539  if (static_cast<int>(errorMessage.size()) > 0) {
540  string head =
541  "\n\n************************************************\n"
542  " Cantera Error! \n"
543  "************************************************\n\n";
544  return head+string("\nProcedure: ")+errorRoutine.back()
545  +string("\nError: ")+errorMessage.back();
546  } else {
547  return "<no Cantera error>";
548  }
549 }
550 
551 void Application::Messages::getErrors(std::ostream& f)
552 {
553  int i = static_cast<int>(errorMessage.size());
554  if (i == 0) {
555  return;
556  }
557  f << endl << endl;
558  f << "************************************************" << endl;
559  f << " Cantera Error! " << endl;
560  f << "************************************************" << endl
561  << endl;
562  int j;
563  for (j = 0; j < i; j++) {
564  f << endl;
565  f << "Procedure: " << errorRoutine[j] << endl;
566  f << "Error: " << errorMessage[j] << endl;
567  }
568  f << endl << endl;
569  errorMessage.clear();
570  errorRoutine.clear();
571 }
572 
574 {
575  int i = static_cast<int>(errorMessage.size());
576  if (i == 0) {
577  return;
578  }
579  writelog("\n\n");
580  writelog("************************************************\n");
581  writelog(" Cantera Error! \n");
582  writelog("************************************************\n\n");
583  int j;
584  for (j = 0; j < i; j++) {
585  writelog("\n");
586  writelog(string("Procedure: ")+ errorRoutine[j]+" \n");
587  writelog(string("Error: ")+ errorMessage[j]+" \n");
588  }
589  writelog("\n\n");
590  errorMessage.clear();
591  errorRoutine.clear();
592 }
593 
594 
595 
597 {
598  std::vector<string>& dirs = inputDirs;
599 
600  // always look in the local directory first
601  dirs.push_back(".");
602 
603 #ifdef _WIN32
604  // Under Windows, the Cantera setup utility records the installation
605  // directory in the registry. Data files are stored in the 'data' and
606  // 'templates' subdirectories of the main installation directory.
607 
608  std::string installDir;
609  readStringRegistryKey("SOFTWARE\\Cantera\\Cantera 2.0",
610  "InstallDir", installDir, "");
611  if (installDir != "") {
612  dirs.push_back(installDir + "data");
613  dirs.push_back(installDir + "templates");
614 
615  // Scripts for converting mechanisms to CTI and CMTL are installed in
616  // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
617  const char* old_pythonpath = getenv("PYTHONPATH");
618  std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
619  if (old_pythonpath) {
620  pythonpath += ";";
621  pythonpath.append(old_pythonpath);
622  }
623  putenv(pythonpath.c_str());
624  }
625 
626 #endif
627 
628 #ifdef DARWIN
629  //
630  // add a default data location for Mac OS X
631  //
632  dirs.push_back("/Applications/Cantera/data");
633 #endif
634 
635  //
636  // if environment variable CANTERA_DATA is defined, then add
637  // it to the search path
638  //
639  if (getenv("CANTERA_DATA") != 0) {
640  string datadir = string(getenv("CANTERA_DATA"));
641  dirs.push_back(datadir);
642  }
643 
644  // CANTERA_DATA is defined in file config.h. This file is written
645  // during the build process (unix), and points to the directory
646  // specified by the 'prefix' option to 'configure', or else to
647  // /usr/local/cantera.
648 #ifdef CANTERA_DATA
649  string datadir = string(CANTERA_DATA);
650  dirs.push_back(datadir);
651 #endif
652 }
653 
654 void Application::addDataDirectory(std::string dir)
655 {
656  ScopedLock dirLock(dir_mutex);
657  if (inputDirs.size() == 0) {
659  }
660  string d = stripnonprint(dir);
661  size_t m, n = inputDirs.size();
662 
663  // don't add if already present
664  for (m = 0; m < n; m++) {
665  if (d == inputDirs[m]) {
666  return;
667  }
668  }
669 
670  inputDirs.push_back(d);
671 }
672 
673 std::string Application::findInputFile(std::string name)
674 {
675  ScopedLock dirLock(dir_mutex);
676  string::size_type islash = name.find('/');
677  string::size_type ibslash = name.find('\\');
678  string inname;
679  std::vector<string>& dirs = inputDirs;
680 
681  int nd;
682  if (islash == string::npos && ibslash == string::npos) {
683  nd = static_cast<int>(dirs.size());
684  int i;
685  inname = "";
686  for (i = 0; i < nd; i++) {
687  inname = dirs[i] + "/" + name;
688  std::ifstream fin(inname.c_str());
689  if (fin) {
690  fin.close();
691  return inname;
692  }
693  }
694  string msg;
695  msg = "\nInput file " + name
696  + " not found in director";
697  msg += (nd == 1 ? "y " : "ies ");
698  for (i = 0; i < nd; i++) {
699  msg += "\n'" + dirs[i] + "'";
700  if (i < nd-1) {
701  msg += ", ";
702  }
703  }
704  msg += "\n\n";
705  msg += "To fix this problem, either:\n";
706  msg += " a) move the missing files into the local directory;\n";
707  msg += " b) define environment variable CANTERA_DATA to\n";
708  msg += " point to the directory containing the file.";
709  throw CanteraError("findInputFile", msg);
710  }
711 
712  return name;
713 }
714 
716 
717 } // namespace Cantera