Cantera  3.1.0a1
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"
10 
11 #define BOOST_DLL_USE_STD_FS
12 #include <boost/dll/import.hpp>
13 #include <boost/algorithm/string.hpp>
14 
15 #include <fstream>
16 #include <sstream>
17 #include <mutex>
18 
19 namespace ba = boost::algorithm;
20 
21 #ifdef _WIN32
22 #include <windows.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 Application::Messages::Messages()
39 {
40  // install a default logwriter that writes to standard
41  // output / standard error
42  logwriter = make_unique<Logger>();
43 }
44 
45 void Application::Messages::addError(const string& r, const string& msg)
46 {
47  if (msg.size() != 0) {
48  errorMessage.push_back(
49  "\n\n************************************************\n"
50  " Cantera Error! \n"
51  "************************************************\n\n"
52  "Procedure: " + r +
53  "\nError: " + msg + "\n");
54  } else {
55  errorMessage.push_back(r);
56  }
57 }
58 
60 {
61  return static_cast<int>(errorMessage.size());
62 }
63 
65 {
66  logwriter.reset(_logwriter);
67 }
68 
69 void Application::Messages::writelog(const string& msg)
70 {
71  logwriter->write(msg);
72 }
73 
75 {
76  logwriter->writeendl();
77 }
78 
79 void Application::Messages::warnlog(const string& warning, const string& msg)
80 {
81  logwriter->warn(warning, msg);
82 }
83 
84 //! Mutex for access to string messages
85 static std::mutex msg_mutex;
86 
88 {
89  std::unique_lock<std::mutex> msgLock(msg_mutex);
90  std::thread::id curId = std::this_thread::get_id();
91  auto iter = m_threadMsgMap.find(curId);
92  if (iter != m_threadMsgMap.end()) {
93  return iter->second.get();
94  }
95  pMessages_t pMsgs(new Messages());
96  m_threadMsgMap.insert({curId, pMsgs});
97  return pMsgs.get();
98 }
99 
101 {
102  std::unique_lock<std::mutex> msgLock(msg_mutex);
103  std::thread::id curId = std::this_thread::get_id();
104  auto iter = m_threadMsgMap.find(curId);
105  if (iter != m_threadMsgMap.end()) {
106  m_threadMsgMap.erase(iter);
107  }
108 }
109 
111 {
112  // install a default logwriter that writes to standard
113  // output / standard error
115 }
116 
118 {
119  std::unique_lock<std::mutex> appLock(app_mutex);
120  if (Application::s_app == 0) {
122  }
123  return s_app;
124 }
125 
127 {
128  std::unique_lock<std::mutex> appLock(app_mutex);
129  if (Application::s_app != 0) {
130  delete Application::s_app;
131  Application::s_app = 0;
132  }
133 }
134 
135 void Application::warn_deprecated(const string& method, const string& extra)
136 {
137  if (m_fatal_deprecation_warnings) {
138  throw CanteraError(method, "Deprecated: " + extra);
139  } else if (m_suppress_deprecation_warnings || warnings.count(method)) {
140  return;
141  }
142  warnings.insert(method);
143  warnlog("Deprecation", fmt::format("{}: {}", method, extra));
144 }
145 
146 void Application::warn(const string& warning, const string& method, const string& extra)
147 {
148  if (m_fatal_warnings) {
149  throw CanteraError(method, extra);
150  } else if (m_suppress_warnings) {
151  return;
152  }
153  warnlog(warning, fmt::format("{}: {}", method, extra));
154 }
155 
157 {
158  pMessenger.removeThreadMessages();
159 }
160 
161 #ifdef _WIN32
162 long int Application::readStringRegistryKey(const string& keyName, const string& valueName,
163  string& value, const string& defaultValue)
164 {
165  HKEY key;
166  long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
167  if (open_error != ERROR_SUCCESS) {
168  return open_error;
169  }
170  value = defaultValue;
171  CHAR buffer[1024];
172  DWORD bufferSize = sizeof(buffer);
173  ULONG error;
174  error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
175  if (ERROR_SUCCESS == error) {
176  value = buffer;
177  }
178  RegCloseKey(key);
179  return error;
180 }
181 #endif
182 
184 {
185  if (!errorMessage.empty()) {
186  errorMessage.pop_back();
187  }
188 }
189 
191 {
192  if (!errorMessage.empty()) {
193  return errorMessage.back();
194  } else {
195  return "<no Cantera error>";
196  }
197 }
198 
199 void Application::Messages::getErrors(std::ostream& f)
200 {
201  for (size_t j = 0; j < errorMessage.size(); j++) {
202  f << errorMessage[j] << std::endl;
203  }
204  errorMessage.clear();
205 }
206 
208 {
209  for (size_t j = 0; j < errorMessage.size(); j++) {
210  writelog(errorMessage[j]);
211  writelogendl();
212  }
213  errorMessage.clear();
214 }
215 
217 {
218  // always look in the local directory first
219  inputDirs.push_back(".");
220 
221  // if environment variable CANTERA_DATA is defined, then add it to the
222  // search path. CANTERA_DATA may include multiple directory, separated by
223  // the OS-dependent path separator (in the same manner as the PATH
224  // environment variable).
225 #ifdef _WIN32
226  string pathsep = ";";
227  string dirsep = "\\";
228 #else
229  string pathsep = ":";
230  string dirsep = "/";
231 #endif
232 
233  if (getenv("CANTERA_DATA") != nullptr) {
234  string s(getenv("CANTERA_DATA"));
235  size_t start = 0;
236  size_t end = s.find(pathsep);
237  while (end != npos) {
238  inputDirs.push_back(s.substr(start, end-start));
239  start = end + 1;
240  end = s.find(pathsep, start);
241  }
242  inputDirs.push_back(s.substr(start,end));
243  }
244 
245  // If a conda environment is active, add the location of the Cantera data directory
246  // that may exist in that environment.
247  if (getenv("CONDA_PREFIX") != nullptr) {
248  string s(getenv("CONDA_PREFIX"));
249  inputDirs.push_back(s + dirsep + "share" + dirsep + "cantera" + dirsep + "data");
250  }
251 
252 #ifdef _WIN32
253  // Under Windows, the Cantera setup utility records the installation
254  // directory in the registry. Data files are stored in the 'data'
255  // subdirectory of the main installation directory.
256  string installDir;
257  readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
258  "InstallDir", installDir, "");
259  if (installDir != "") {
260  inputDirs.push_back(installDir + "data");
261 
262  // Scripts for converting mechanisms to YAML are installed in
263  // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
264  const char* old_pythonpath = getenv("PYTHONPATH");
265  string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
266  if (old_pythonpath) {
267  pythonpath += ";";
268  pythonpath.append(old_pythonpath);
269  }
270  _putenv(pythonpath.c_str());
271  }
272 
273 #endif
274 
275  // CANTERA_DATA is defined in file config.h. This file is written during the
276  // build process (unix), and points to the directory specified by the
277  // 'prefix' option to 'configure', or else to /usr/local/cantera.
278 #ifdef CANTERA_DATA
279  string datadir = stripnonprint(string(CANTERA_DATA));
280  inputDirs.push_back(datadir);
281 #endif
282 }
283 
284 void Application::addDataDirectory(const string& dir)
285 {
286  std::unique_lock<std::mutex> dirLock(dir_mutex);
287  if (inputDirs.empty()) {
289  }
290  string d = stripnonprint(dir);
291 
292  // Expand "~/" to user's home directory, if possible
293  if (d.find("~/") == 0 || d.find("~\\") == 0) {
294  char* home = getenv("HOME"); // POSIX systems
295  if (!home) {
296  home = getenv("USERPROFILE"); // Windows systems
297  }
298  if (home) {
299  d = home + d.substr(1, npos);
300  }
301  }
302 
303  // Remove any existing entry for this directory
304  auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
305  if (iter != inputDirs.end()) {
306  inputDirs.erase(iter);
307  }
308 
309  // Insert this directory at the beginning of the search path
310  inputDirs.insert(inputDirs.begin(), d);
311 }
312 
313 string Application::findInputFile(const string& name)
314 {
315  std::unique_lock<std::mutex> dirLock(dir_mutex);
316  string::size_type islash = name.find('/');
317  string::size_type ibslash = name.find('\\');
318  string::size_type icolon = name.find(':');
319  vector<string>& dirs = inputDirs;
320 
321  // Expand "~/" to user's home directory, if possible
322  if (name.find("~/") == 0 || name.find("~\\") == 0) {
323  char* home = getenv("HOME"); // POSIX systems
324  if (!home) {
325  home = getenv("USERPROFILE"); // Windows systems
326  }
327  if (home) {
328  string full_name = home + name.substr(1, npos);
329  std::ifstream fin(full_name);
330  if (fin) {
331  return full_name;
332  } else {
333  throw CanteraError("Application::findInputFile",
334  "Input file '{}' not found", name);
335  }
336  }
337  }
338 
339  // If this is an absolute path, just look for the file there
340  if (islash == 0 || ibslash == 0
341  || (icolon == 1 && (ibslash == 2 || islash == 2)))
342  {
343  std::ifstream fin(name);
344  if (fin) {
345  return name;
346  } else {
347  throw CanteraError("Application::findInputFile",
348  "Input file '{}' not found", name);
349  }
350  }
351 
352  // Search the Cantera data directories for the input file, and return
353  // the full path if a match is found
354  size_t nd = dirs.size();
355  for (size_t i = 0; i < nd; i++) {
356  string full_name = dirs[i] + "/" + name;
357  std::ifstream fin(full_name);
358  if (fin) {
359  return full_name;
360  }
361  }
362  string msg = "\nInput file " + name + " not found in director";
363  msg += (nd == 1 ? "y " : "ies ");
364  for (size_t i = 0; i < nd; i++) {
365  msg += "\n'" + dirs[i] + "'";
366  if (i+1 < nd) {
367  msg += ", ";
368  }
369  }
370  msg += "\n\n";
371  msg += "To fix this problem, either:\n";
372  msg += " a) move the missing files into the local directory;\n";
373  msg += " b) define environment variable CANTERA_DATA to\n";
374  msg += " point to the directory containing the file.";
375  throw CanteraError("Application::findInputFile", msg);
376 }
377 
378 void Application::loadExtension(const string& extType, const string& name)
379 {
380  if (!usingSharedLibrary()) {
381  throw CanteraError("Application::loadExtension",
382  "Loading extensions requires linking to the Cantera shared library\n"
383  "rather than the static library");
384  }
385  if (m_loaded_extensions.count({extType, name})) {
386  return;
387  }
388 
389  if (extType == "python" && !ExtensionManagerFactory::factory().exists("python")) {
390  string errors;
391 
392  // type of imported symbol: void function with no arguments
393  typedef void (loader_t)();
394 
395  // Only one Python module can be loaded at a time, and a handle needs to be held
396  // to prevent it from being unloaded.
397  static function<loader_t> loader;
398  bool loaded = false;
399 
400  for (const auto& py_ver : m_pythonSearchVersions) {
401  string py_ver_underscore = ba::replace_all_copy(py_ver, ".", "_");
402  try {
403  loader = boost::dll::import_alias<loader_t>(
404  "cantera_python" + py_ver_underscore, // library name
405  "registerPythonExtensionManager", // symbol to import
406  // append extensions and prefixes, search normal library path, and
407  // expose all loaded symbols (specifically, those from libpython)
408  boost::dll::load_mode::search_system_folders
409  | boost::dll::load_mode::append_decorations
410  | boost::dll::load_mode::rtld_global
411  );
412  loader();
413  loaded = true;
414  break;
415  } catch (std::exception& err) {
416  errors += fmt::format("\nPython {}: {}\n", py_ver, err.what());
417  }
418  }
419  if (!loaded) {
420  throw CanteraError("Application::loadExtension",
421  "Error loading Python extension support. Tried the following:{}",
422  errors);
423  }
424  }
425  ExtensionManagerFactory::build(extType)->registerRateBuilders(name);
426  m_loaded_extensions.insert({extType, name});
427 }
428 
429 void Application::searchPythonVersions(const string& versions) {
430  ba::split(m_pythonSearchVersions, versions, ba::is_any_of(","));
431 }
432 
434 
435 } // namespace Cantera
Class to carry out messages.
Definition: application.h:46
unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:159
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:74
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
Definition: application.cpp:87
void removeThreadMessages()
Remove a local thread message.
Class to hold global data.
Definition: application.h:42
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:455
vector< string > m_pythonSearchVersions
Versions of Python to consider when attempting to load user extensions.
Definition: application.h:437
void warnlog(const string &warning, const string &msg)
Write a warning message to the screen.
Definition: application.h:315
static Application * Instance()
Return a pointer to the one and only instance of class Application.
void loadExtension(const string &extType, const string &name)
Load an extension implementing user-defined models.
void warn_deprecated(const string &method, const string &extra="")
Print a warning indicating that method is deprecated.
void searchPythonVersions(const string &versions)
Set the versions of Python to try when loading user-defined extensions, in order of preference.
vector< string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:434
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
void writelog(const string &msg)
Write a message to the screen.
Definition: application.h:305
set< string > warnings
Set of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:440
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:163
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:310
static void ApplicationDestroy()
Static function that destroys the application class's data.
void warn(const string &warning, const string &method, const string &extra="")
Generate a general purpose warning; repeated warnings are not suppressed.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:66
static ExtensionManagerFactory & factory()
Static function that returns the static instance of the factory, creating it if necessary.
static shared_ptr< ExtensionManager > build(const string &extensionType)
Create a new ExtensionManager.
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:41
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
string stripnonprint(const string &s)
Strip non-printing characters wherever they are.
Definition: stringUtils.cpp:47
void addError(const string &r, const string &msg="")
Set an error condition in the application class without throwing an exception.
Definition: application.cpp:45
void logErrors()
Prints all of the error messages using writelog.
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
string lastErrorMessage()
Retrieve the last error message in a string.
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:59
void popError()
Discard the last error message.
bool usingSharedLibrary()
Returns true if Cantera was loaded as a shared library in the current application.
string findInputFile(const string &name)
Find an input file.
void addDataDirectory(const string &dir)
Add a directory to the data file search path.
void setDefaultDirectories()
Set the default directories for input files.
void warnlog(const string &warning, const string &msg)
Write a warning message to the screen.
Definition: application.cpp:79
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:64
void writelog(const string &msg)
Write a message to the screen.
Definition: application.cpp:69
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:564
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:180
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
static std::mutex msg_mutex
Mutex for access to string messages.
Definition: application.cpp:85
Contains declarations for string manipulation functions within Cantera.