Cantera 2.6.0

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
8#include "cantera/base/ctml.h"
9#include "cantera/base/stringUtils.h"
10#include "units.h"
11
12#include <fstream>
13#include <sstream>
14#include <mutex>
15
16 using std::string;
17 using std::endl;
18
19#ifdef _WIN32
20#include <windows.h>
21#else
22#include <sys/stat.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
38//! Mutex for controlling access to XML file storage
39 static std::mutex xml_mutex;
40
41 int get_modified_time(const std::string& path) {
42#ifdef _WIN32
43 HANDLE hFile = CreateFile(path.c_str(), 0, 0,
44 NULL, OPEN_EXISTING, 0, NULL);
45 if (hFile == INVALID_HANDLE_VALUE) {
46 throw CanteraError("get_modified_time", "Couldn't open file:" + path);
47 }
48 FILETIME modified;
49 GetFileTime(hFile, NULL, NULL, &modified);
50 CloseHandle(hFile);
51 return static_cast<int>(modified.dwLowDateTime);
52#else
53 struct stat attrib;
54 stat(path.c_str(), &attrib);
55 return static_cast<int>(attrib.st_mtime);
56#endif
57}
58
59Application::Messages::Messages()
60{
61 // install a default logwriter that writes to standard
62 // output / standard error
63 logwriter.reset(new Logger());
64}
65
66 void Application::Messages::addError(const std::string& r, const std::string& msg)
67{
68 if (msg.size() != 0) {
69 errorMessage.push_back(
70 "\n\n************************************************\n"
71 " Cantera Error! \n"
72 "************************************************\n\n"
73 "Procedure: " + r +
74 "\nError: " + msg + "\n");
75 } else {
76 errorMessage.push_back(r);
77 }
78}
79
80 int Application::Messages::getErrorCount()
81{
82 return static_cast<int>(errorMessage.size());
83}
84
85 void Application::Messages::setLogger(Logger* _logwriter)
86{
87 logwriter.reset(_logwriter);
88}
89
90 void Application::Messages::writelog(const std::string& msg)
91{
92 logwriter->write(msg);
93}
94
95 void Application::Messages::writelogendl()
96{
97 logwriter->writeendl();
98}
99
100 void Application::Messages::warnlog(const std::string& warning, const std::string& msg)
101{
102 logwriter->warn(warning, msg);
103}
104
105//! Mutex for access to string messages
106 static std::mutex msg_mutex;
107
108 Application::Messages* Application::ThreadMessages::operator ->()
109{
110 std::unique_lock<std::mutex> msgLock(msg_mutex);
111 std::thread::id curId = std::this_thread::get_id();
112 auto iter = m_threadMsgMap.find(curId);
113 if (iter != m_threadMsgMap.end()) {
114 return iter->second.get();
115 }
116 pMessages_t pMsgs(new Messages());
117 m_threadMsgMap.insert({curId, pMsgs});
118 return pMsgs.get();
119}
120
121 void Application::ThreadMessages::removeThreadMessages()
122{
123 std::unique_lock<std::mutex> msgLock(msg_mutex);
124 std::thread::id curId = std::this_thread::get_id();
125 auto iter = m_threadMsgMap.find(curId);
126 if (iter != m_threadMsgMap.end()) {
127 m_threadMsgMap.erase(iter);
128 }
129}
130
131 Application::Application() :
132 m_suppress_deprecation_warnings(false),
133 m_fatal_deprecation_warnings(false),
134 m_suppress_thermo_warnings(false),
135 m_suppress_warnings(false),
136 m_fatal_warnings(false),
137#if CT_LEGACY_RATE_CONSTANTS
138 m_use_legacy_rate_constants(true)
139#else
140 m_use_legacy_rate_constants(false)
141#endif
142{
143 // install a default logwriter that writes to standard
144 // output / standard error
145 setDefaultDirectories();
146 Unit::units();
147}
148
149 Application* Application::Instance()
150{
151 std::unique_lock<std::mutex> appLock(app_mutex);
152 if (Application::s_app == 0) {
153 Application::s_app = new Application();
154 }
155 return s_app;
156}
157
158 Application::~Application()
159{
160 for (auto& f : xmlfiles) {
161 f.second.first->unlock();
162 delete f.second.first;
163 f.second.first = 0;
164 }
165}
166
167 void Application::ApplicationDestroy()
168{
169 std::unique_lock<std::mutex> appLock(app_mutex);
170 if (Application::s_app != 0) {
171 delete Application::s_app;
172 Application::s_app = 0;
173 }
174}
175
176 void Application::warn_deprecated(const std::string& method,
177 const std::string& extra)
178{
179 if (m_fatal_deprecation_warnings) {
180 throw CanteraError(method, "Deprecated: " + extra);
181 } else if (m_suppress_deprecation_warnings || warnings.count(method)) {
182 return;
183 }
184 warnings.insert(method);
185 warnlog("Deprecation", fmt::format("{}: {}", method, extra));
186}
187
188 void Application::warn(const std::string& warning,
189 const std::string& method,
190 const std::string& extra)
191{
192 if (m_fatal_warnings) {
193 throw CanteraError(method, extra);
194 } else if (m_suppress_warnings) {
195 return;
196 }
197 warnlog(warning, fmt::format("{}: {}", method, extra));
198}
199
200 void Application::thread_complete()
201{
202 pMessenger.removeThreadMessages();
203}
204
205 XML_Node* Application::get_XML_File(const std::string& file, int debug)
206{
207 std::unique_lock<std::mutex> xmlLock(xml_mutex);
208 std::string path = findInputFile(file);
209 int mtime = get_modified_time(path);
210
211 if (xmlfiles.find(path) != xmlfiles.end()) {
212 // Already have a parsed XML tree for this file cached. Check the
213 // last-modified time.
214 std::pair<XML_Node*, int> cache = xmlfiles[path];
215 if (cache.second == mtime) {
216 return cache.first;
217 }
218 }
219
220 // Check whether or not the file is XML (based on the file extension). If
221 // not, it will be first processed with the preprocessor.
222 string::size_type idot = path.rfind('.');
223 string ext;
224 if (idot != string::npos) {
225 ext = path.substr(idot, path.size());
226 } else {
227 ext = "";
228 }
229 XML_Node* x = new XML_Node("doc");
230 if (ext != ".xml" && ext != ".ctml") {
231 // Assume that we are trying to open a cti file. Do the conversion to XML.
232 std::stringstream phase_xml(ct2ctml_string(path));
233 x->build(phase_xml, path);
234 } else {
235 x->build(path);
236 }
237 x->lock();
238 xmlfiles[path] = {x, mtime};
239 return x;
240}
241
242 XML_Node* Application::get_XML_from_string(const std::string& text)
243{
244 std::unique_lock<std::mutex> xmlLock(xml_mutex);
245 std::pair<XML_Node*, int>& entry = xmlfiles[text];
246 if (entry.first) {
247 // Return existing cached XML tree
248 return entry.first;
249 }
250 std::stringstream s;
251 size_t start = text.find_first_not_of(" \t\r\n");
252 if (text.substr(start,1) == "<") {
253 s << text;
254 } else {
255 s << ct_string2ctml_string(text.substr(start));
256 }
257 entry.first = new XML_Node();
258 entry.first->build(s, "[string]");
259 return entry.first;
260}
261
262 void Application::close_XML_File(const std::string& file)
263{
264 std::unique_lock<std::mutex> xmlLock(xml_mutex);
265 if (file == "all") {
266 for (const auto& f : xmlfiles) {
267 f.second.first->unlock();
268 delete f.second.first;
269 }
270 xmlfiles.clear();
271 } else if (xmlfiles.find(file) != xmlfiles.end()) {
272 xmlfiles[file].first->unlock();
273 delete xmlfiles[file].first;
274 xmlfiles.erase(file);
275 }
276}
277
278#ifdef _WIN32
279 long int Application::readStringRegistryKey(const std::string& keyName, const std::string& valueName,
280 std::string& value, const std::string& defaultValue)
281{
282 HKEY key;
283 long open_error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName.c_str(), 0, KEY_READ, &key);
284 if (open_error != ERROR_SUCCESS) {
285 return open_error;
286 }
287 value = defaultValue;
288 CHAR buffer[1024];
289 DWORD bufferSize = sizeof(buffer);
290 ULONG error;
291 error = RegQueryValueEx(key, valueName.c_str(), 0, NULL, (LPBYTE) buffer, &bufferSize);
292 if (ERROR_SUCCESS == error) {
293 value = buffer;
294 }
295 RegCloseKey(key);
296 return error;
297}
298#endif
299
300 void Application::Messages::popError()
301{
302 if (!errorMessage.empty()) {
303 errorMessage.pop_back();
304 }
305}
306
307std::string Application::Messages::lastErrorMessage()
308{
309 if (!errorMessage.empty()) {
310 return errorMessage.back();
311 } else {
312 return "<no Cantera error>";
313 }
314}
315
316 void Application::Messages::getErrors(std::ostream& f)
317{
318 for (size_t j = 0; j < errorMessage.size(); j++) {
319 f << errorMessage[j] << endl;
320 }
321 errorMessage.clear();
322}
323
324 void Application::Messages::logErrors()
325{
326 for (size_t j = 0; j < errorMessage.size(); j++) {
327 writelog(errorMessage[j]);
328 writelogendl();
329 }
330 errorMessage.clear();
331}
332
333 void Application::setDefaultDirectories()
334{
335 // always look in the local directory first
336 inputDirs.push_back(".");
337
338 // if environment variable CANTERA_DATA is defined, then add it to the
339 // search path. CANTERA_DATA may include multiple directory, separated by
340 // the OS-dependent path separator (in the same manner as the PATH
341 // environment variable).
342#ifdef _WIN32
343 std::string pathsep = ";";
344#else
345 std::string pathsep = ":";
346#endif
347
348 if (getenv("CANTERA_DATA") != 0) {
349 string s = string(getenv("CANTERA_DATA"));
350 size_t start = 0;
351 size_t end = s.find(pathsep);
352 while(end != npos) {
353 inputDirs.push_back(s.substr(start, end-start));
354 start = end + 1;
355 end = s.find(pathsep, start);
356 }
357 inputDirs.push_back(s.substr(start,end));
358 }
359
360#ifdef _WIN32
361 // Under Windows, the Cantera setup utility records the installation
362 // directory in the registry. Data files are stored in the 'data'
363 // subdirectory of the main installation directory.
364 std::string installDir;
365 readStringRegistryKey("SOFTWARE\\Cantera\\Cantera " CANTERA_SHORT_VERSION,
366 "InstallDir", installDir, "");
367 if (installDir != "") {
368 inputDirs.push_back(installDir + "data");
369
370 // Scripts for converting mechanisms to CTI and CMTL are installed in
371 // the 'bin' subdirectory. Add that directory to the PYTHONPATH.
372 const char* old_pythonpath = getenv("PYTHONPATH");
373 std::string pythonpath = "PYTHONPATH=" + installDir + "\\bin";
374 if (old_pythonpath) {
375 pythonpath += ";";
376 pythonpath.append(old_pythonpath);
377 }
378 _putenv(pythonpath.c_str());
379 }
380
381#endif
382
383#ifdef DARWIN
384 // add a default data location for Mac OS X
385 inputDirs.push_back("/Applications/Cantera/data");
386#endif
387
388 // CANTERA_DATA is defined in file config.h. This file is written during the
389 // build process (unix), and points to the directory specified by the
390 // 'prefix' option to 'configure', or else to /usr/local/cantera.
391#ifdef CANTERA_DATA
392 string datadir = string(CANTERA_DATA);
393 inputDirs.push_back(datadir);
394#endif
395}
396
397 void Application::addDataDirectory(const std::string& dir)
398{
399 std::unique_lock<std::mutex> dirLock(dir_mutex);
400 if (inputDirs.empty()) {
401 setDefaultDirectories();
402 }
403 string d = stripnonprint(dir);
404
405 // Expand "~/" to user's home directory, if possible
406 if (d.find("~/") == 0 || d.find("~\\") == 0) {
407 char* home = getenv("HOME"); // POSIX systems
408 if (!home) {
409 home = getenv("USERPROFILE"); // Windows systems
410 }
411 if (home) {
412 d = home + d.substr(1, npos);
413 }
414 }
415
416 // Remove any existing entry for this directory
417 auto iter = std::find(inputDirs.begin(), inputDirs.end(), d);
418 if (iter != inputDirs.end()) {
419 inputDirs.erase(iter);
420 }
421
422 // Insert this directory at the beginning of the search path
423 inputDirs.insert(inputDirs.begin(), d);
424}
425
426std::string Application::findInputFile(const std::string& name)
427{
428 std::unique_lock<std::mutex> dirLock(dir_mutex);
429 string::size_type islash = name.find('/');
430 string::size_type ibslash = name.find('\\');
431 string::size_type icolon = name.find(':');
432 std::vector<string>& dirs = inputDirs;
433
434 // Expand "~/" to user's home directory, if possible
435 if (name.find("~/") == 0 || name.find("~\\") == 0) {
436 char* home = getenv("HOME"); // POSIX systems
437 if (!home) {
438 home = getenv("USERPROFILE"); // Windows systems
439 }
440 if (home) {
441 string full_name = home + name.substr(1, npos);
442 std::ifstream fin(full_name);
443 if (fin) {
444 return full_name;
445 } else {
446 throw CanteraError("Application::findInputFile",
447 "Input file '{}' not found", name);
448 }
449 }
450 }
451
452 // If this is an absolute path, just look for the file there
453 if (islash == 0 || ibslash == 0
454 || (icolon == 1 && (ibslash == 2 || islash == 2)))
455 {
456 std::ifstream fin(name);
457 if (fin) {
458 return name;
459 } else {
460 throw CanteraError("Application::findInputFile",
461 "Input file '{}' not found", name);
462 }
463 }
464
465 // Search the Cantera data directories for the input file, and return
466 // the full path if a match is found
467 size_t nd = dirs.size();
468 for (size_t i = 0; i < nd; i++) {
469 string full_name = dirs[i] + "/" + name;
470 std::ifstream fin(full_name);
471 if (fin) {
472 return full_name;
473 }
474 }
475 string msg = "\nInput file " + name + " not found in director";
476 msg += (nd == 1 ? "y " : "ies ");
477 for (size_t i = 0; i < nd; i++) {
478 msg += "\n'" + dirs[i] + "'";
479 if (i+1 < nd) {
480 msg += ", ";
481 }
482 }
483 msg += "\n\n";
484 msg += "To fix this problem, either:\n";
485 msg += " a) move the missing files into the local directory;\n";
486 msg += " b) define environment variable CANTERA_DATA to\n";
487 msg += " point to the directory containing the file.";
488 throw CanteraError("Application::findInputFile", msg);
489}
490
491 Application* Application::s_app = 0;
492
493} // namespace Cantera
application.h
Cantera::Application::Messages
Class to carry out messages.
Definition: application.h:51
Cantera::Application::Messages::errorMessage
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:161
Cantera::Application::Messages::logwriter
std::unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:164
Cantera::Application::Messages::writelogendl
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:95
Cantera::Application::ThreadMessages::operator->
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
Definition: application.cpp:108
Cantera::Application::ThreadMessages::removeThreadMessages
void removeThreadMessages()
Remove a local thread message.
Definition: application.cpp:121
Cantera::Application
Class to hold global data.
Definition: application.h:47
Cantera::Application::warn
void warn(const std::string &warning, const std::string &method, const std::string &extra="")
Generate a general purpose warning; repeated warnings are not suppressed.
Definition: application.cpp:188
Cantera::Application::warnings
std::set< std::string > warnings
Vector of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:485
Cantera::Application::get_XML_File
XML_Node * get_XML_File(const std::string &file, int debug=0)
Return a pointer to the XML tree for a Cantera input file.
Definition: application.cpp:205
Cantera::Application::s_app
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:498
Cantera::Application::writelog
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:333
Cantera::Application::inputDirs
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:474
Cantera::Application::warnlog
void warnlog(const std::string &warning, const std::string &msg)
Write a warning message to the screen.
Definition: application.h:343
Cantera::Application::Instance
static Application * Instance()
Return a pointer to the one and only instance of class Application.
Definition: application.cpp:149
Cantera::Application::~Application
virtual ~Application()
Destructor for class deletes global data.
Definition: application.cpp:158
Cantera::Application::get_XML_from_string
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
Definition: application.cpp:242
Cantera::Application::close_XML_File
void close_XML_File(const std::string &file)
Close an XML File.
Definition: application.cpp:262
Cantera::Application::thread_complete
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
Definition: application.cpp:200
Cantera::Application::xmlfiles
std::map< std::string, std::pair< XML_Node *, int > > xmlfiles
Current vector of XML file trees that have been previously parsed The second element of the value is ...
Definition: application.h:482
Cantera::Application::pMessages_t
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:168
Cantera::Application::warn_deprecated
void warn_deprecated(const std::string &method, const std::string &extra="")
Print a warning indicating that method is deprecated.
Definition: application.cpp:176
Cantera::Application::Application
Application()
Constructor for class sets up the initial conditions Protected ctor access thru static member functio...
Definition: application.cpp:131
Cantera::Application::writelogendl
void writelogendl()
Write an endl to the screen and flush output.
Definition: application.h:338
Cantera::Application::ApplicationDestroy
static void ApplicationDestroy()
Static function that destroys the application class's data.
Definition: application.cpp:167
Cantera::CanteraError
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Cantera::Logger
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:41
Cantera::Unit::units
static Unit * units()
Initialize the static Unit class.
Definition: units.h:30
Cantera::XML_Node
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
Cantera::XML_Node::build
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:763
Cantera::XML_Node::lock
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:900
ctml.h
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
Cantera::Application::Messages::addError
void addError(const std::string &r, const std::string &msg="")
Set an error condition in the application class without throwing an exception.
Definition: application.cpp:66
Cantera::Application::Messages::logErrors
void logErrors()
Prints all of the error messages using writelog.
Definition: application.cpp:324
Cantera::Application::Messages::lastErrorMessage
std::string lastErrorMessage()
Retrieve the last error message in a string.
Definition: application.cpp:307
Cantera::Application::Messages::getErrors
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
Definition: application.cpp:316
Cantera::Application::Messages::getErrorCount
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:80
Cantera::Application::Messages::popError
void popError()
Discard the last error message.
Definition: application.cpp:300
Cantera::Application::findInputFile
std::string findInputFile(const std::string &name)
Find an input file.
Definition: application.cpp:426
Cantera::ct2ctml_string
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:175
Cantera::Application::addDataDirectory
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
Definition: application.cpp:397
Cantera::ct_string2ctml_string
std::string ct_string2ctml_string(const std::string &cti)
Get a string with the ctml representation of a cti input string.
Definition: ct2ctml.cpp:180
Cantera::Application::setDefaultDirectories
void setDefaultDirectories()
Set the default directories for input files.
Definition: application.cpp:333
Cantera::Application::Messages::writelog
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.cpp:90
Cantera::Application::Messages::setLogger
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:85
Cantera::Application::Messages::warnlog
void warnlog(const std::string &warning, const std::string &msg)
Write a warning message to the screen.
Definition: application.cpp:100
Cantera
Namespace for the Cantera kernel.
Definition: AnyMap.cpp:557
Cantera::npos
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
Cantera::app_mutex
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
Cantera::xml_mutex
static std::mutex xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:39
Cantera::dir_mutex
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
Cantera::stripnonprint
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Definition: stringUtils.cpp:48
Cantera::msg_mutex
static std::mutex msg_mutex
Mutex for access to string messages.
Definition: application.cpp:106
stringUtils.h
Contains declarations for string manipulation functions within Cantera.
units.h
Header for units conversion utilities, which are used to translate user input from input files (See I...