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"
10#include "units.h"
11
12#include <fstream>
13#include <sstream>
14#include <mutex>
15
16using std::string;
17using 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
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
38//! Mutex for controlling access to XML file storage
39static std::mutex xml_mutex;
40
41int 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
66void 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
81{
82 return static_cast<int>(errorMessage.size());
83}
84
86{
87 logwriter.reset(_logwriter);
88}
89
90void Application::Messages::writelog(const std::string& msg)
91{
92 logwriter->write(msg);
93}
94
96{
97 logwriter->writeendl();
98}
99
100void 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
106static std::mutex msg_mutex;
107
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
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
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
146 Unit::units();
147}
148
150{
151 std::unique_lock<std::mutex> appLock(app_mutex);
152 if (Application::s_app == 0) {
154 }
155 return s_app;
156}
157
159{
160 for (auto& f : xmlfiles) {
161 f.second.first->unlock();
162 delete f.second.first;
163 f.second.first = 0;
164 }
165}
166
168{
169 std::unique_lock<std::mutex> appLock(app_mutex);
170 if (Application::s_app != 0) {
171 delete Application::s_app;
173 }
174}
175
176void 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
188void 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
201{
202 pMessenger.removeThreadMessages();
203}
204
205XML_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
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
262void 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
279long 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
301{
302 if (!errorMessage.empty()) {
303 errorMessage.pop_back();
304 }
305}
306
308{
309 if (!errorMessage.empty()) {
310 return errorMessage.back();
311 } else {
312 return "<no Cantera error>";
313 }
314}
315
317{
318 for (size_t j = 0; j < errorMessage.size(); j++) {
319 f << errorMessage[j] << endl;
320 }
321 errorMessage.clear();
322}
323
325{
326 for (size_t j = 0; j < errorMessage.size(); j++) {
327 writelog(errorMessage[j]);
328 writelogendl();
329 }
330 errorMessage.clear();
331}
332
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
397void Application::addDataDirectory(const std::string& dir)
398{
399 std::unique_lock<std::mutex> dirLock(dir_mutex);
400 if (inputDirs.empty()) {
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
492
493} // namespace Cantera
Class to carry out messages.
Definition: application.h:51
std::vector< std::string > errorMessage
Current list of error messages.
Definition: application.h:161
std::unique_ptr< Logger > logwriter
Current pointer to the logwriter.
Definition: application.h:164
void writelogendl()
Write an end of line character to the screen and flush output.
Definition: application.cpp:95
Messages * operator->()
Provide a pointer dereferencing overloaded operator.
void removeThreadMessages()
Remove a local thread message.
Class to hold global data.
Definition: application.h:47
void warn(const std::string &warning, const std::string &method, const std::string &extra="")
Generate a general purpose warning; repeated warnings are not suppressed.
std::set< std::string > warnings
Vector of deprecation warnings that have been emitted (to suppress duplicates)
Definition: application.h:485
XML_Node * get_XML_File(const std::string &file, int debug=0)
Return a pointer to the XML tree for a Cantera input file.
static Application * s_app
Pointer to the single Application instance.
Definition: application.h:498
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.h:333
std::vector< std::string > inputDirs
Current vector of input directories to search for input files.
Definition: application.h:474
void warnlog(const std::string &warning, const std::string &msg)
Write a warning message to the screen.
Definition: application.h:343
static Application * Instance()
Return a pointer to the one and only instance of class Application.
virtual ~Application()
Destructor for class deletes global data.
XML_Node * get_XML_from_string(const std::string &text)
Read a CTI or CTML string and fill up an XML tree.
void close_XML_File(const std::string &file)
Close an XML File.
void thread_complete()
Delete and free memory allocated per thread in multithreaded applications.
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
shared_ptr< Messages > pMessages_t
Typedef for thread specific messages.
Definition: application.h:168
void warn_deprecated(const std::string &method, const std::string &extra="")
Print a warning indicating that method is deprecated.
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.
Definition: application.h:338
static void ApplicationDestroy()
Static function that destroys the application class's data.
Base class for exceptions thrown by Cantera classes.
Definition: ctexceptions.h:61
Base class for 'loggers' that write text messages to log files.
Definition: logger.h:41
static Unit * units()
Initialize the static Unit class.
Definition: units.h:30
Class XML_Node is a tree-based representation of the contents of an XML file.
Definition: xml.h:103
void build(const std::string &filename)
Populate the XML tree from an input file.
Definition: xml.cpp:763
void lock()
Set the lock for this node and all of its children.
Definition: xml.cpp:900
CTML ("Cantera Markup Language") is the variant of XML that Cantera uses to store data.
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
void logErrors()
Prints all of the error messages using writelog.
std::string lastErrorMessage()
Retrieve the last error message in a string.
void getErrors(std::ostream &f)
Prints all of the error messages to an ostream.
int getErrorCount()
Return the number of errors that have been encountered so far.
Definition: application.cpp:80
void popError()
Discard the last error message.
std::string findInputFile(const std::string &name)
Find an input file.
std::string ct2ctml_string(const std::string &file)
Get a string with the ctml representation of a cti file.
Definition: ct2ctml.cpp:175
void addDataDirectory(const std::string &dir)
Add a directory to the data file search path.
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
void setDefaultDirectories()
Set the default directories for input files.
void writelog(const std::string &msg)
Write a message to the screen.
Definition: application.cpp:90
void setLogger(Logger *logwriter)
Install a logger.
Definition: application.cpp:85
void warnlog(const std::string &warning, const std::string &msg)
Write a warning message to the screen.
Namespace for the Cantera kernel.
Definition: AnyMap.h:29
const size_t npos
index returned by functions to indicate "no position"
Definition: ct_defs.h:192
static std::mutex app_mutex
Mutex for creating singletons within the application object.
Definition: application.cpp:36
static std::mutex xml_mutex
Mutex for controlling access to XML file storage.
Definition: application.cpp:39
static std::mutex dir_mutex
Mutex for input directory access.
Definition: application.cpp:33
std::string stripnonprint(const std::string &s)
Strip non-printing characters wherever they are.
Definition: stringUtils.cpp:48
static std::mutex msg_mutex
Mutex for access to string messages.
Contains declarations for string manipulation functions within Cantera.
Header for units conversion utilities, which are used to translate user input from input files (See I...