| Cantera 3.0.0

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"
7#include "cantera/base/ctexceptions.h"
8#include "cantera/base/stringUtils.h"
9#include "cantera/base/ExtensionManagerFactory.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
38Application::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
59 int Application::Messages::getErrorCount()
60{
61 return static_cast<int>(errorMessage.size());
62}
63
64 void Application::Messages::setLogger(Logger* _logwriter)
65{
66 logwriter.reset(_logwriter);
67}
68
69 void Application::Messages::writelog(const string& msg)
70{
71 logwriter->write(msg);
72}
73
74 void Application::Messages::writelogendl()
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
87 Application::Messages* Application::ThreadMessages::operator ->()
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
100 void Application::ThreadMessages::removeThreadMessages()
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
110 Application::Application()
111{
112 // install a default logwriter that writes to standard
113 // output / standard error
114 setDefaultDirectories();
115}
116
117 Application* Application::Instance()
118{
119 std::unique_lock<std::mutex> appLock(app_mutex);
120 if (Application::s_app == 0) {
121 Application::s_app = new Application();
122 }
123 return s_app;
124}
125
126 void Application::ApplicationDestroy()
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
156 void Application::thread_complete()
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
183 void Application::Messages::popError()
184{
185 if (!errorMessage.empty()) {
186 errorMessage.pop_back();
187 }
188}
189
190 string Application::Messages::lastErrorMessage()
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
207 void Application::Messages::logErrors()
208{
209 for (size_t j = 0; j < errorMessage.size(); j++) {
210 writelog(errorMessage[j]);
211 writelogendl();
212 }
213 errorMessage.clear();
214}
215
216 void Application::setDefaultDirectories()
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()) {
288 setDefaultDirectories();
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
433 Application* Application::s_app = 0;
434
435} // namespace Cantera
ExtensionManagerFactory.h
application.h
Cantera::Application::Messages
Class to carry out messages.
Definition application.h:46
Cantera::Application::Messages::logwriter
unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition application.h:159
Cantera::Application::Messages::writelogendl
void writelogendl()
Write an end of line character to the screen and flush output.
Definition application.cpp:74
Cantera::Application::ThreadMessages::operator->
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
Definition application.cpp:87
Cantera::Application::ThreadMessages::removeThreadMessages
void removeThreadMessages()
Remove a local thread message.
Definition application.cpp:100
Cantera::Application
Class to hold global data.
Definition application.h:42
Cantera::Application::s_app
static Application * s_app
Pointer to the single Application instance.
Definition application.h:455
Cantera::Application::m_pythonSearchVersions
vector< string > m_pythonSearchVersions
Versions of Python to consider when attempting to load user extensions.
Definition application.h:437
Cantera::Application::warnlog
void warnlog(const string &warning, const string &msg)
Write a warning message to the screen.
Definition application.h:315
Cantera::Application::Instance
static Application * Instance()
Return a pointer to the one and only instance of class Application.
Definition application.cpp:117
Cantera::Application::loadExtension
void loadExtension(const string &extType, const string &name)
Load an extension implementing user-defined models.
Definition application.cpp:378
Cantera::Application::warn_deprecated
void warn_deprecated(const string &method, const string &extra="")
Print a warning indicating that method is deprecated.
Definition application.cpp:135
Cantera::Application::searchPythonVersions
void searchPythonVersions(const string &versions)
Set the versions of Python to try when loading user-defined extensions, in order of preference.
Definition application.cpp:429
Cantera::Application::inputDirs
vector< string > inputDirs
Current vector of input directories to search for input files.
Definition application.h:434
Cantera::Application::thread_complete
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
Definition application.cpp:156
Cantera::Application::writelog
void writelog(const string &msg)
Write a message to the screen.
Definition application.h:305
Cantera::Application::warnings
set< string > warnings
Set of deprecation warnings that have been emitted (to suppress duplicates)
Definition application.h:440
Cantera::Application::pMessages_t
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition application.h:163
Cantera::Application::Application
Application()
Constructor for class sets up the initial conditions Protected ctor access thru static member functio...
Definition application.cpp:110
Cantera::Application::writelogendl
void writelogendl()
Write an endl to the screen and flush output.
Definition application.h:310
Cantera::Application::ApplicationDestroy
static void ApplicationDestroy()
Static function that destroys the application class's data.
Definition application.cpp:126
Cantera::Application::warn
void warn(const string &warning, const string &method, const string &extra="")
Generate a general purpose warning; repeated warnings are not suppressed.
Definition application.cpp:146
Cantera::CanteraError
Base class for exceptions thrown by Cantera classes.
Definition ctexceptions.h:66
Cantera::ExtensionManagerFactory::build
static shared_ptr< ExtensionManager > build(const string &extensionType)
Create a new ExtensionManager.
Definition ExtensionManagerFactory.h:22
Cantera::ExtensionManagerFactory::factory
static ExtensionManagerFactory & factory()
Static function that returns the static instance of the factory, creating it if necessary.
Definition ExtensionManagerFactory.cpp:16
Cantera::Logger
Base class for 'loggers' that write text messages to log files.
Definition logger.h:41
ctexceptions.h
Definitions for the classes that are thrown when Cantera experiences an error condition (also contain...
Cantera::stripnonprint
string stripnonprint(const string &s)
Strip non-printing characters wherever they are.
Definition stringUtils.cpp:47
Cantera::Application::Messages::addError
void addError(const string &r, const string &msg="")
Set an error condition in the application class without throwing an exception.
Definition application.cpp:45
Cantera::Application::Messages::logErrors
void logErrors()
Prints all of the error messages using writelog.
Definition application.cpp:207
Cantera::Application::Messages::getErrors
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
Definition application.cpp:199
Cantera::Application::Messages::lastErrorMessage
string lastErrorMessage()
Retrieve the last error message in a string.
Definition application.cpp:190
Cantera::Application::Messages::getErrorCount
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition application.cpp:59
Cantera::Application::Messages::popError
void popError()
Discard the last error message.
Definition application.cpp:183
Cantera::usingSharedLibrary
bool usingSharedLibrary()
Returns true if Cantera was loaded as a shared library in the current application.
Cantera::Application::findInputFile
string findInputFile(const string &name)
Find an input file.
Definition application.cpp:313
Cantera::Application::addDataDirectory
void addDataDirectory(const string &dir)
Add a directory to the data file search path.
Definition application.cpp:284
Cantera::Application::setDefaultDirectories
void setDefaultDirectories()
Set the default directories for input files.
Definition application.cpp:216
Cantera::Application::Messages::warnlog
void warnlog(const string &warning, const string &msg)
Write a warning message to the screen.
Definition application.cpp:79
Cantera::Application::Messages::setLogger
void setLogger(Logger *logwriter)
Install a logger.
Definition application.cpp:64
Cantera::Application::Messages::writelog
void writelog(const string &msg)
Write a message to the screen.
Definition application.cpp:69
Cantera
Namespace for the Cantera kernel.
Definition AnyMap.cpp:564
Cantera::npos
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:195
Cantera::app_mutex
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition application.cpp:36
Cantera::dir_mutex
static std::mutex dir_mutex
Mutex for input directory access.
Definition application.cpp:33
Cantera::msg_mutex
static std::mutex msg_mutex
Mutex for access to string messages.
Definition application.cpp:85
stringUtils.h
Contains declarations for string manipulation functions within Cantera.