From f96da9f15e9c48cfb33f9795f01ce0b7725fff51 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Sat, 27 Jun 2020 14:46:18 -0400 Subject: [PATCH] [Input] Allow use of subdirectories within Cantera data directories This provides the ability to organize data files included with Cantera and differentiate data which is useful for different purposes, e.g. sample data which is only meant to demonstrate capabilities and may not be physically meaningful. See Cantera/enhancements#22. Fixes #774 --- SConstruct | 14 +++- .../sample-data}/LiC6_electrodebulk.yaml | 0 interfaces/cython/.gitignore | 14 +--- interfaces/cython/setup.py.in | 2 +- samples/cxx/LiC6_electrode/LiC6_electrode.cpp | 2 +- src/base/application.cpp | 67 ++++++++++++------- src/base/application.h | 17 ++--- 7 files changed, 66 insertions(+), 50 deletions(-) rename {samples/cxx/LiC6_electrode => data/sample-data}/LiC6_electrodebulk.yaml (100%) diff --git a/SConstruct b/SConstruct index 027df1dcee..276f9b1e0b 100644 --- a/SConstruct +++ b/SConstruct @@ -79,7 +79,9 @@ if 'clean' in COMMAND_LINE_TARGETS: removeDirectory('interfaces/cython/Cantera.egg-info') removeDirectory('interfaces/python_minimal/Cantera_minimal.egg-info') for name in os.listdir('interfaces/cython/cantera/data/'): - if name != '__init__.py': + if os.path.isdir('interfaces/cython/cantera/data/' + name): + removeDirectory('interfaces/cython/cantera/data/' + name) + elif name != '__init__.py': removeFile('interfaces/cython/cantera/data/' + name) removeDirectory('interfaces/cython/cantera/test/data/test_subdir') for name in os.listdir('interfaces/cython/cantera/test/data/'): @@ -1614,6 +1616,14 @@ for xml in mglob(env, 'data/inputs', 'xml'): for yaml in mglob(env, "data", "yaml"): dest = pjoin("build", "data", yaml.name) build(env.Command(dest, yaml.path, Copy("$TARGET", "$SOURCE"))) +for subdir in os.listdir('data'): + if os.path.isdir(pjoin('data', subdir)): + for yaml in mglob(env, pjoin("data", subdir), "yaml"): + dest = pjoin("build", "data", subdir, yaml.name) + if not os.path.exists(pjoin("build", "data", subdir)): + os.makedirs(pjoin("build", "data", subdir)) + build(env.Command(dest, yaml.path, Copy("$TARGET", "$SOURCE"))) + if addInstallActions: # Put headers in place @@ -1621,7 +1631,7 @@ if addInstallActions: install(env.RecursiveInstall, '$inst_incdir', 'include/cantera') # Data files - install('$inst_datadir', mglob(env, 'build/data', 'cti', 'xml', 'yaml')) + install(env.RecursiveInstall, '$inst_datadir', 'build/data') ### List of libraries needed to link to Cantera ### diff --git a/samples/cxx/LiC6_electrode/LiC6_electrodebulk.yaml b/data/sample-data/LiC6_electrodebulk.yaml similarity index 100% rename from samples/cxx/LiC6_electrode/LiC6_electrodebulk.yaml rename to data/sample-data/LiC6_electrodebulk.yaml diff --git a/interfaces/cython/.gitignore b/interfaces/cython/.gitignore index 0601b64abf..c76ec86cfa 100644 --- a/interfaces/cython/.gitignore +++ b/interfaces/cython/.gitignore @@ -1,17 +1,7 @@ cantera/*.cpp cantera/*.c -cantera/data/*.cti -cantera/data/*.xml -cantera/data/*.yaml -cantera/data/*.yml -cantera/test/data/*.cti -cantera/test/data/*.xml -cantera/test/data/*.inp -cantera/test/data/*.dat -cantera/test/data/*.csv -cantera/test/data/*.yaml -cantera/test/data/*.yml -cantera/test/data/test_subdir +cantera/data +cantera/test/data setup.py scripts/ctml_writer.py scripts/ctml_writer diff --git a/interfaces/cython/setup.py.in b/interfaces/cython/setup.py.in index e2ba11053b..59d6304160 100644 --- a/interfaces/cython/setup.py.in +++ b/interfaces/cython/setup.py.in @@ -93,7 +93,7 @@ setup( 'Topic :: Scientific/Engineering :: Chemistry', ], package_data = { - 'cantera.data': ['*.*'], + 'cantera.data': ['*.*', '*/*.*'], 'cantera.test.data': ['*.*', '*/*.*'], 'cantera.examples': ['*/*.*'], 'cantera': ["@py_extension@", '*.pxd'], diff --git a/samples/cxx/LiC6_electrode/LiC6_electrode.cpp b/samples/cxx/LiC6_electrode/LiC6_electrode.cpp index 95f0f1c1ff..14f3cdd316 100644 --- a/samples/cxx/LiC6_electrode/LiC6_electrode.cpp +++ b/samples/cxx/LiC6_electrode/LiC6_electrode.cpp @@ -9,7 +9,7 @@ void calc_potentials() suppress_deprecation_warnings(); double Tk = 273.15 + 25.0; - std::string filename = "LiC6_electrodebulk.yaml"; + std::string filename = "sample-data/LiC6_electrodebulk.yaml"; std::string phasename = "LiC6_and_Vacancies"; std::unique_ptr electrodebulk(newPhase(filename,phasename)); std::string intercalatingSpeciesName("Li(C6)"); diff --git a/src/base/application.cpp b/src/base/application.cpp index 5aee1c39c0..4a8a869dc2 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -412,6 +412,7 @@ std::string Application::findInputFile(const std::string& name) std::unique_lock dirLock(dir_mutex); string::size_type islash = name.find('/'); string::size_type ibslash = name.find('\\'); + string::size_type icolon = name.find(':'); std::vector& dirs = inputDirs; // Expand "~/" to user's home directory, if possible @@ -421,36 +422,54 @@ std::string Application::findInputFile(const std::string& name) home = getenv("USERPROFILE"); // Windows systems } if (home) { - return home + name.substr(1, npos); - } - } - - if (islash == string::npos && ibslash == string::npos) { - size_t nd = dirs.size(); - for (size_t i = 0; i < nd; i++) { - string inname = dirs[i] + "/" + name; - std::ifstream fin(inname); + string full_name = home + name.substr(1, npos); + std::ifstream fin(full_name); if (fin) { - return inname; + return full_name; + } else { + throw CanteraError("Application::findInputFile", + "Input file '{}' not found", 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 += ", "; - } + } + + // 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); } - 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); } - return 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); } Application* Application::s_app = 0; diff --git a/src/base/application.h b/src/base/application.h index 87b62cd4c2..bad1eb18c3 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -247,23 +247,20 @@ class Application /*! * This routine will search for a file in the default locations specified * for the application. See the routine setDefaultDirectories() listed - * above. + * above. The first directory searched is usually the current working + * directory. * - * The default set of directories specified for the application will be - * searched if a '/' or an '\\' is not found in the name. If either is found - * then a relative path name is presumed, and the default directories are - * not searched. + * The default set of directories will not be searched if an absolute path + * (for example, one starting with `/` or `C:\`) or a path relative to the + * user's home directory (for example, starting with `~/`) is specified. * * The presence of the file is determined by whether the file can be * opened for reading by the current user. * * @param name Name of the input file to be searched for - * @return The absolute path name of the first matching file is - * returned. If a relative path name is indicated, the relative path - * name is returned. + * @return The absolute path name of the first matching file * - * If the file is not found, a message is written to stdout and a - * CanteraError exception is thrown. + * If the file is not found a CanteraError exception is thrown. * * @ingroup inputfiles */