mirror of
https://github.com/Cantera/cantera.git
synced 2025-02-25 18:55:29 -06:00
425 lines
12 KiB
C++
425 lines
12 KiB
C++
//! @file application.cpp
|
|
|
|
// This file is part of Cantera. See License.txt in the top-level directory or
|
|
// at https://cantera.org/license.txt for license and copyright information.
|
|
|
|
#include "application.h"
|
|
#include "cantera/base/ctexceptions.h"
|
|
#include "cantera/base/stringUtils.h"
|
|
#include "cantera/base/ExtensionManagerFactory.h"
|
|
|
|
#define BOOST_DLL_USE_STD_FS
|
|
#include <boost/dll/import.hpp>
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <mutex>
|
|
|
|
using std::string;
|
|
using std::endl;
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "advapi32")
|
|
#endif
|
|
|
|
namespace Cantera
|
|
{
|
|
|
|
//! Mutex for input directory access
|
|
static std::mutex dir_mutex;
|
|
|
|
//! Mutex for creating singletons within the application object
|
|
static std::mutex app_mutex;
|
|
|
|
int get_modified_time(const std::string& path) {
|
|
#ifdef _WIN32
|
|
HANDLE hFile = CreateFile(path.c_str(), 0, 0,
|
|
NULL, OPEN_EXISTING, 0, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
throw CanteraError("get_modified_time", "Couldn't open file:" + path);
|
|
}
|
|
FILETIME modified;
|
|
GetFileTime(hFile, NULL, NULL, &modified);
|
|
CloseHandle(hFile);
|
|
return static_cast<int>(modified.dwLowDateTime);
|
|
#else
|
|
struct stat attrib;
|
|
stat(path.c_str(), &attrib);
|
|
return static_cast<int>(attrib.st_mtime);
|
|
#endif
|
|
}
|
|
|
|
Application::Messages::Messages()
|
|
{
|
|
// install a default logwriter that writes to standard
|
|
// output / standard error
|
|
logwriter.reset(new Logger());
|
|
}
|
|
|
|
void Application::Messages::addError(const std::string& r, const std::string& msg)
|
|
{
|
|
if (msg.size() != 0) {
|
|
errorMessage.push_back(
|
|
"\n\n************************************************\n"
|
|
" Cantera Error! \n"
|
|
"************************************************\n\n"
|
|
"Procedure: " + r +
|
|
"\nError: " + msg + "\n");
|
|
} else {
|
|
errorMessage.push_back(r);
|
|
}
|
|
}
|
|
|
|
int Application::Messages::getErrorCount()
|
|
{
|
|
return static_cast<int>(errorMessage.size());
|
|
}
|
|
|
|
void Application::Messages::setLogger(Logger* _logwriter)
|
|
{
|
|
logwriter.reset(_logwriter);
|
|
}
|
|
|
|
void Application::Messages::writelog(const std::string& msg)
|
|
{
|
|
logwriter->write(msg);
|
|
}
|
|
|
|
void Application::Messages::writelogendl()
|
|
{
|
|
logwriter->writeendl();
|
|
}
|
|
|
|
void Application::Messages::warnlog(const std::string& warning, const std::string& msg)
|
|
{
|
|
logwriter->warn(warning, msg);
|
|
}
|
|
|
|
//! Mutex for access to string messages
|
|
static std::mutex msg_mutex;
|
|
|
|
Application::Messages* Application::ThreadMessages::operator ->()
|
|
{
|
|
std::unique_lock<std::mutex> msgLock(msg_mutex);
|
|
std::thread::id curId = std::this_thread::get_id();
|
|
auto iter = m_threadMsgMap.find(curId);
|
|
if (iter != m_threadMsgMap.end()) {
|
|
return iter->second.get();
|
|
}
|
|
pMessages_t pMsgs(new Messages());
|
|
m_threadMsgMap.insert({curId, pMsgs});
|
|
return pMsgs.get();
|
|
}
|
|
|
|
void Application::ThreadMessages::removeThreadMessages()
|
|
{
|
|
std::unique_lock<std::mutex> msgLock(msg_mutex);
|
|
std::thread::id curId = std::this_thread::get_id();
|
|
auto iter = m_threadMsgMap.find(curId);
|
|
if (iter != m_threadMsgMap.end()) {
|
|
m_threadMsgMap.erase(iter);
|
|
}
|
|
}
|
|
|
|
Application::Application() :
|
|
m_suppress_deprecation_warnings(false),
|
|
m_fatal_deprecation_warnings(false),
|
|
m_suppress_thermo_warnings(false),
|
|
m_suppress_warnings(false),
|
|
m_fatal_warnings(false),
|
|
m_use_legacy_rate_constants(false)
|
|
{
|
|
// install a default logwriter that writes to standard
|
|
// output / standard error
|
|
setDefaultDirectories();
|
|
}
|
|
|
|
Application* Application::Instance()
|
|
{
|
|
std::unique_lock<std::mutex> appLock(app_mutex);
|
|
if (Application::s_app == 0) {
|
|
Application::s_app = new Application();
|
|
}
|
|
return s_app;
|
|
}
|
|
|
|
void Application::ApplicationDestroy()
|
|
{
|
|
std::unique_lock<std::mutex> appLock(app_mutex);
|
|
if (Application::s_app != 0) {
|
|
delete Application::s_app;
|
|
Application::s_app = 0;
|
|
}
|
|
}
|
|
|
|
void Application::warn_deprecated(const std::string& method,
|
|
const std::string& extra)
|
|
{
|
|
if (m_fatal_deprecation_warnings) {
|
|
throw CanteraError(method, "Deprecated: " + extra);
|
|
} else if (m_suppress_deprecation_warnings || warnings.count(method)) {
|
|
return;
|
|
}
|
|
warnings.insert(method);
|
|
warnlog("Deprecation", fmt::format("{}: {}", method, extra));
|
|
}
|
|
|
|
void Application::warn(const std::string& warning,
|
|
const std::string& method,
|
|
const std::string& extra)
|
|
{
|
|
if (m_fatal_warnings) {
|
|
throw CanteraError(method, extra);
|
|
} else if (m_suppress_warnings) {
|
|
return;
|
|
}
|
|
warnlog(warning, fmt::format("{}: {}", method, extra));
|
|
}
|
|
|
|
void Application::thread_complete()
|
|
{
|
|
pMessenger.removeThreadMessages();
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
|
|
std::string& value, const std::string& defaultValue)
|
|
{
|
|
HKEY key;
|
|
long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
|
|
if (open_error != ERROR_SUCCESS) {
|
|
return open_error;
|
|
}
|
|
value = defaultValue;
|
|
CHAR buffer[1024];
|
|
DWORD bufferSize = sizeof(buffer);
|
|
ULONG error;
|
|
error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
|
|
if (ERROR_SUCCESS == error) {
|
|
value = buffer;
|
|
}
|
|
RegCloseKey(key);
|
|
return error;
|
|
}
|
|
#endif
|
|
|
|
void Application::Messages::popError()
|
|
{
|
|
if (!errorMessage.empty()) {
|
|
errorMessage.pop_back();
|
|
}
|
|
}
|
|
|
|
std::string Application::Messages::lastErrorMessage()
|
|
{
|
|
if (!errorMessage.empty()) {
|
|
return errorMessage.back();
|
|
} else {
|
|
return "<no Cantera error>";
|
|
}
|
|
}
|
|
|
|
void Application::Messages::getErrors(std::ostream& f)
|
|
{
|
|
for (size_t j = 0; j < errorMessage.size(); j++) {
|
|
f << errorMessage[j] << endl;
|
|
}
|
|
errorMessage.clear();
|
|
}
|
|
|
|
void Application::Messages::logErrors()
|
|
{
|
|
for (size_t j = 0; j < errorMessage.size(); j++) {
|
|
writelog(errorMessage[j]);
|
|
writelogendl();
|
|
}
|
|
errorMessage.clear();
|
|
}
|
|
|
|
void Application::setDefaultDirectories()
|
|
{
|
|
// always look in the local directory first
|
|
inputDirs.push_back(".");
|
|
|
|
// if environment variable CANTERA_DATA is defined, then add it to the
|
|
// search path. CANTERA_DATA may include multiple directory, separated by
|
|
// the OS-dependent path separator (in the same manner as the PATH
|
|
// environment variable).
|
|
#ifdef _WIN32
|
|
std::string pathsep = ";";
|
|
#else
|
|
std::string pathsep = ":";
|
|
#endif
|
|
|
|
if (getenv("CANTERA_DATA") != 0) {
|
|
string s = string(getenv("CANTERA_DATA"));
|
|
size_t start = 0;
|
|
size_t end = s.find(pathsep);
|
|
while (end != npos) {
|
|
inputDirs.push_back(s.substr(start, end-start));
|
|
start = end + 1;
|
|
end = s.find(pathsep, start);
|
|
}
|
|
inputDirs.push_back(s.substr(start,end));
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// Under Windows, the Cantera setup utility records the installation
|
|
// directory in the registry. Data files are stored in the 'data'
|
|
// subdirectory of the main installation directory.
|
|
std::string installDir;
|
|
readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
|
|
"InstallDir", installDir, "");
|
|
if (installDir != "") {
|
|
inputDirs.push_back(installDir + "data");
|
|
|
|
// Scripts for converting mechanisms to YAML are installed in
|
|
// the 'bin' subdirectory. Add that directory to the PYTHONPATH.
|
|
const char* old_pythonpath = getenv("PYTHONPATH");
|
|
std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
|
|
if (old_pythonpath) {
|
|
pythonpath += ";";
|
|
pythonpath.append(old_pythonpath);
|
|
}
|
|
_putenv(pythonpath.c_str());
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DARWIN
|
|
// add a default data location for Mac OS X
|
|
inputDirs.push_back("/Applications/Cantera/data");
|
|
#endif
|
|
|
|
// CANTERA_DATA is defined in file config.h. This file is written during the
|
|
// build process (unix), and points to the directory specified by the
|
|
// 'prefix' option to 'configure', or else to /usr/local/cantera.
|
|
#ifdef CANTERA_DATA
|
|
string datadir = stripnonprint(string(CANTERA_DATA));
|
|
inputDirs.push_back(datadir);
|
|
#endif
|
|
}
|
|
|
|
void Application::addDataDirectory(const std::string& dir)
|
|
{
|
|
std::unique_lock<std::mutex> dirLock(dir_mutex);
|
|
if (inputDirs.empty()) {
|
|
setDefaultDirectories();
|
|
}
|
|
string d = stripnonprint(dir);
|
|
|
|
// Expand "~/" to user's home directory, if possible
|
|
if (d.find("~/") == 0 || d.find("~\\") == 0) {
|
|
char* home = getenv("HOME"); // POSIX systems
|
|
if (!home) {
|
|
home = getenv("USERPROFILE"); // Windows systems
|
|
}
|
|
if (home) {
|
|
d = home + d.substr(1, npos);
|
|
}
|
|
}
|
|
|
|
// Remove any existing entry for this directory
|
|
auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
|
|
if (iter != inputDirs.end()) {
|
|
inputDirs.erase(iter);
|
|
}
|
|
|
|
// Insert this directory at the beginning of the search path
|
|
inputDirs.insert(inputDirs.begin(), d);
|
|
}
|
|
|
|
std::string Application::findInputFile(const std::string& name)
|
|
{
|
|
std::unique_lock<std::mutex> dirLock(dir_mutex);
|
|
string::size_type islash = name.find('/');
|
|
string::size_type ibslash = name.find('\\');
|
|
string::size_type icolon = name.find(':');
|
|
std::vector<string>& dirs = inputDirs;
|
|
|
|
// Expand "~/" to user's home directory, if possible
|
|
if (name.find("~/") == 0 || name.find("~\\") == 0) {
|
|
char* home = getenv("HOME"); // POSIX systems
|
|
if (!home) {
|
|
home = getenv("USERPROFILE"); // Windows systems
|
|
}
|
|
if (home) {
|
|
string full_name = home + name.substr(1, npos);
|
|
std::ifstream fin(full_name);
|
|
if (fin) {
|
|
return full_name;
|
|
} else {
|
|
throw CanteraError("Application::findInputFile",
|
|
"Input file '{}' not found", name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this is an absolute path, just look for the file there
|
|
if (islash == 0 || ibslash == 0
|
|
|| (icolon == 1 && (ibslash == 2 || islash == 2)))
|
|
{
|
|
std::ifstream fin(name);
|
|
if (fin) {
|
|
return name;
|
|
} else {
|
|
throw CanteraError("Application::findInputFile",
|
|
"Input file '{}' not found", name);
|
|
}
|
|
}
|
|
|
|
// Search the Cantera data directories for the input file, and return
|
|
// the full path if a match is found
|
|
size_t nd = dirs.size();
|
|
for (size_t i = 0; i < nd; i++) {
|
|
string full_name = dirs[i] + "/" + name;
|
|
std::ifstream fin(full_name);
|
|
if (fin) {
|
|
return full_name;
|
|
}
|
|
}
|
|
string msg = "\nInput file " + name + " not found in director";
|
|
msg += (nd == 1 ? "y " : "ies ");
|
|
for (size_t i = 0; i < nd; i++) {
|
|
msg += "\n'" + dirs[i] + "'";
|
|
if (i+1 < nd) {
|
|
msg += ", ";
|
|
}
|
|
}
|
|
msg += "\n\n";
|
|
msg += "To fix this problem, either:\n";
|
|
msg += " a) move the missing files into the local directory;\n";
|
|
msg += " b) define environment variable CANTERA_DATA to\n";
|
|
msg += " point to the directory containing the file.";
|
|
throw CanteraError("Application::findInputFile", msg);
|
|
}
|
|
|
|
void Application::loadExtension(const string& extType, const string& name)
|
|
{
|
|
if (m_loaded_extensions.count({extType, name})) {
|
|
return;
|
|
}
|
|
|
|
if (extType == "python" && !ExtensionManagerFactory::factory().exists("python")) {
|
|
typedef void (creator_t)();
|
|
static auto loader = boost::dll::import_alias<creator_t>( // type of imported symbol must be explicitly specified
|
|
"cantera_python", // path to library
|
|
"registerPythonExtensionManager", // symbol to import
|
|
boost::dll::load_mode::search_system_folders | boost::dll::load_mode::append_decorations | boost::dll::load_mode::rtld_global // append extensions and prefixes, and search normal library path
|
|
);
|
|
loader();
|
|
}
|
|
ExtensionManagerFactory::build(extType)->registerRateBuilders(name);
|
|
m_loaded_extensions.insert({extType, name});
|
|
}
|
|
|
|
Application* Application::s_app = 0;
|
|
|
|
} // namespace Cantera
|