Cantera  3.1.0a1
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
38Application::Messages::Messages()
39{
40 // install a default logwriter that writes to standard
41 // output / standard error
42 logwriter = make_unique<Logger>();
43}
44
45void 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
69void Application::Messages::writelog(const string& msg)
70{
71 logwriter->write(msg);
72}
73
75{
76 logwriter->writeendl();
77}
78
79void Application::Messages::warnlog(const string& warning, const string& msg)
80{
81 logwriter->warn(warning, msg);
82}
83
84//! Mutex for access to string messages
85static 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;
132 }
133}
134
135void 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
146void 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
162long 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
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
284void 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
313string 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
378void 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
429void 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.
void writelogendl()
Write an end of line character to the screen and flush output.
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.
void warnlog(const string &warning, const string &msg)
Write a warning message to the screen.
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.
void writelog(const string &msg)
Write a message to the screen.
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 endl to the screen 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.
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.
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 screen.
void setLogger(Logger *logwriter)
Install a logger.
void writelog(const string &msg)
Write a message to the screen.
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.
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.