Cantera  3.3.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
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}
89
91{
92 std::unique_lock<std::mutex> appLock(app_mutex);
93 if (Application::s_app == 0) {
95 }
96 return s_app;
97}
98
100{
101 std::unique_lock<std::mutex> appLock(app_mutex);
102 if (Application::s_app != 0) {
103 delete Application::s_app;
105 }
106}
107
108void Application::warn_deprecated(const string& method, const string& extra)
109{
110 if (m_fatal_deprecation_warnings) {
111 throw CanteraError(method, "Deprecated: " + extra);
112 } else if (m_suppress_deprecation_warnings || warnings.count(method)) {
113 return;
114 }
115 warnings.insert(method);
116 warnlog("Deprecation", fmt::format("{}: {}", method, extra));
117}
118
119void Application::warn(const string& warning, const string& method, const string& extra)
120{
121 if (m_fatal_warnings) {
122 throw CanteraError(method, extra);
123 } else if (m_suppress_warnings) {
124 return;
125 }
126 warnlog(warning, fmt::format("{}: {}", method, extra));
127}
128
130{
131 pMessenger.removeThreadMessages();
132}
133
134#ifdef _WIN32
135long int Application::readStringRegistryKey(const string& keyName, const string& valueName,
136 string& value, const string& defaultValue)
137{
138 HKEY key;
139 long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
140 if (open_error != ERROR_SUCCESS) {
141 return open_error;
142 }
143 value = defaultValue;
144 CHAR buffer[1024];
145 DWORD bufferSize = sizeof(buffer);
146 ULONG error;
147 error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
148 if (ERROR_SUCCESS == error) {
149 value = buffer;
150 }
151 RegCloseKey(key);
152 return error;
153}
154#endif
155
157{
158 if (!errorMessage.empty()) {
159 errorMessage.pop_back();
160 }
161}
162
164{
165 if (!errorMessage.empty()) {
166 return errorMessage.back();
167 } else {
168 return "<no Cantera error>";
169 }
170}
171
173{
174 for (size_t j = 0; j < errorMessage.size(); j++) {
175 f << errorMessage[j] << std::endl;
176 }
177 errorMessage.clear();
178}
179
181{
182 for (size_t j = 0; j < errorMessage.size(); j++) {
183 Cantera::writelog(errorMessage[j]);
185 }
186 errorMessage.clear();
187}
188
189// Application methods
190
191void Application::setLogger(unique_ptr<Logger> _logwriter)
192{
193 m_logwriter = std::move(_logwriter);
194}
195
196void Application::writelog(const string& msg)
197{
198 m_logwriter->write(msg);
199}
200
202{
203 m_logwriter->writeendl();
204}
205
206void Application::warnlog(const string& warning, const string& msg)
207{
208 m_logwriter->warn(warning, msg);
209}
210
212{
213 // always look in the local directory first
214 inputDirs.push_back(".");
215
216 // if environment variable CANTERA_DATA is defined, then add it to the
217 // search path. CANTERA_DATA may include multiple directory, separated by
218 // the OS-dependent path separator (in the same manner as the PATH
219 // environment variable).
220#ifdef _WIN32
221 string pathsep = ";";
222 string dirsep = "\\";
223#else
224 string pathsep = ":";
225 string dirsep = "/";
226#endif
227
228 if (getenv("CANTERA_DATA") != nullptr) {
229 string s(getenv("CANTERA_DATA"));
230 size_t start = 0;
231 size_t end = s.find(pathsep);
232 while (end != npos) {
233 inputDirs.push_back(s.substr(start, end-start));
234 start = end + 1;
235 end = s.find(pathsep, start);
236 }
237 inputDirs.push_back(s.substr(start,end));
238 }
239
240 // If a conda environment is active, add the location of the Cantera data directory
241 // that may exist in that environment.
242 if (getenv("CONDA_PREFIX") != nullptr) {
243 string s(getenv("CONDA_PREFIX"));
244 inputDirs.push_back(s + dirsep + "share" + dirsep + "cantera" + dirsep + "data");
245 }
246
247#ifdef _WIN32
248 // Under Windows, the Cantera setup utility records the installation
249 // directory in the registry. Data files are stored in the 'data'
250 // subdirectory of the main installation directory.
251 string installDir;
252 readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
253 "InstallDir", installDir, "");
254 if (installDir != "") {
255 inputDirs.push_back(installDir + "data");
256
257 // Scripts for converting mechanisms to YAML are installed in
258 // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
259 const char* old_pythonpath = getenv("PYTHONPATH");
260 string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
261 if (old_pythonpath) {
262 pythonpath += ";";
263 pythonpath.append(old_pythonpath);
264 }
265 _putenv(pythonpath.c_str());
266 }
267
268#endif
269
270 // CANTERA_DATA is defined in file config.h. This file is written during the
271 // build process (unix), and points to the directory specified by the
272 // 'prefix' option to 'configure', or else to /usr/local/cantera.
273#ifdef CANTERA_DATA
274 string datadir = stripnonprint(string(CANTERA_DATA));
275 inputDirs.push_back(datadir);
276#endif
277}
278
279void Application::addDataDirectory(const string& dir)
280{
281 std::unique_lock<std::mutex> dirLock(dir_mutex);
282 if (inputDirs.empty()) {
284 }
285 string d = stripnonprint(dir);
286
287 // Expand "~/" to user's home directory, if possible
288 if (d.find("~/") == 0 || d.find("~\\") == 0) {
289 char* home = getenv("HOME"); // POSIX systems
290 if (!home) {
291 home = getenv("USERPROFILE"); // Windows systems
292 }
293 if (home) {
294 d = home + d.substr(1, npos);
295 }
296 }
297
298 // Remove any existing entry for this directory
299 auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
300 if (iter != inputDirs.end()) {
301 inputDirs.erase(iter);
302 }
303
304 // Insert this directory at the beginning of the search path
305 inputDirs.insert(inputDirs.begin(), d);
306}
307
308string Application::findInputFile(const string& name)
309{
310 std::unique_lock<std::mutex> dirLock(dir_mutex);
311 string::size_type islash = name.find('/');
312 string::size_type ibslash = name.find('\\');
313 string::size_type icolon = name.find(':');
314 vector<string>& dirs = inputDirs;
315
316 // Expand "~/" to user's home directory, if possible
317 if (name.find("~/") == 0 || name.find("~\\") == 0) {
318 char* home = getenv("HOME"); // POSIX systems
319 if (!home) {
320 home = getenv("USERPROFILE"); // Windows systems
321 }
322 if (home) {
323 string full_name = home + name.substr(1, npos);
324 std::ifstream fin(full_name);
325 if (fin) {
326 return full_name;
327 } else {
328 throw CanteraError("Application::findInputFile",
329 "Input file '{}' not found", name);
330 }
331 }
332 }
333
334 // If this is an absolute path, just look for the file there
335 if (islash == 0 || ibslash == 0
336 || (icolon == 1 && (ibslash == 2 || islash == 2)))
337 {
338 std::ifstream fin(name);
339 if (fin) {
340 return name;
341 } else {
342 throw CanteraError("Application::findInputFile",
343 "Input file '{}' not found", name);
344 }
345 }
346
347 // Search the Cantera data directories for the input file, and return
348 // the full path if a match is found
349 size_t nd = dirs.size();
350 for (size_t i = 0; i < nd; i++) {
351 string full_name = dirs[i] + "/" + name;
352 std::ifstream fin(full_name);
353 if (fin) {
354 return full_name;
355 }
356 }
357 string msg = "\nInput file " + name + " not found in director";
358 msg += (nd == 1 ? "y " : "ies ");
359 for (size_t i = 0; i < nd; i++) {
360 msg += "\n'" + dirs[i] + "'";
361 if (i+1 < nd) {
362 msg += ", ";
363 }
364 }
365 msg += "\n\n";
366 msg += "To fix this problem, either:\n";
367 msg += " a) move the missing files into the local directory;\n";
368 msg += " b) define environment variable CANTERA_DATA to\n";
369 msg += " point to the directory containing the file.";
370 throw CanteraError("Application::findInputFile", msg);
371}
372
373void Application::loadExtension(const string& extType, const string& name)
374{
375 if (!usingSharedLibrary()) {
376 throw CanteraError("Application::loadExtension",
377 "Loading extensions requires linking to the Cantera shared library\n"
378 "rather than the static library");
379 }
380 if (m_loaded_extensions.count({extType, name})) {
381 return;
382 }
383
384 if (extType == "python" && !ExtensionManagerFactory::factory().exists("python")) {
385 string errors;
386
387 // type of imported symbol: void function with no arguments
388 typedef void (loader_t)();
389
390 // Only one Python module can be loaded at a time, and a handle needs to be held
391 // to prevent it from being unloaded.
392 static function<loader_t> loader;
393 bool loaded = false;
394
395 for (const auto& py_ver : m_pythonSearchVersions) {
396 string py_ver_underscore = ba::replace_all_copy(py_ver, ".", "_");
397 try {
398 loader = boost::dll::import_alias<loader_t>(
399 "cantera_python" + py_ver_underscore, // library name
400 "registerPythonExtensionManager", // symbol to import
401 // append extensions and prefixes, search normal library path, and
402 // expose all loaded symbols (specifically, those from libpython)
403 boost::dll::load_mode::search_system_folders
404 | boost::dll::load_mode::append_decorations
405 | boost::dll::load_mode::rtld_global
406 );
407 loader();
408 loaded = true;
409 break;
410 } catch (std::exception& err) {
411 errors += fmt::format("\nPython {}: {}\n", py_ver, err.what());
412 }
413 }
414 if (!loaded) {
415 throw CanteraError("Application::loadExtension",
416 "Error loading Python extension support. Tried the following:{}",
417 errors);
418 }
419 }
420 ExtensionManagerFactory::build(extType)->registerRateBuilders(name);
421 m_loaded_extensions.insert({extType, name});
422}
423
424void Application::searchPythonVersions(const string& versions) {
425 ba::split(m_pythonSearchVersions, versions, ba::is_any_of(","));
426}
427
429
430} // 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.
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: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.