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