diff --git a/include/cantera/base/ctml.h b/include/cantera/base/ctml.h index 1a5becdca..11ddb16eb 100644 --- a/include/cantera/base/ctml.h +++ b/include/cantera/base/ctml.h @@ -785,6 +785,15 @@ void ct2ctml(const char* file, const int debug = 0); */ std::string ct2ctml_string(const std::string& file); +//! Get a string with the ctml representation of a cti input string. +/*! + * @param cti String containing the cti representation + * @return String containing the xml representation of the input + * + * @ingroup inputfiles + */ +std::string ct_string2ctml_string(const std::string& cti); + //! Convert a Chemkin-format mechanism into a CTI file. /*! * @param in_file input file containing species and reactions diff --git a/include/cantera/base/global.h b/include/cantera/base/global.h index beb2901e3..ad35c290b 100644 --- a/include/cantera/base/global.h +++ b/include/cantera/base/global.h @@ -203,6 +203,9 @@ doublereal actEnergyToSI(const std::string& unit); //! @copydoc Application::get_XML_File XML_Node* get_XML_File(const std::string& file, int debug = 0); +//! @copydoc Application::get_XML_from_string +XML_Node* get_XML_from_string(const std::string& text); + //! @copydoc Application::close_XML_File void close_XML_File(const std::string& file); diff --git a/interfaces/cython/cantera/_cantera.pxd b/interfaces/cython/cantera/_cantera.pxd index 42c5990b1..3e1ca50bf 100644 --- a/interfaces/cython/cantera/_cantera.pxd +++ b/interfaces/cython/cantera/_cantera.pxd @@ -18,6 +18,7 @@ cdef extern from "cantera/base/global.h" namespace "Cantera": cdef size_t CxxNpos "Cantera::npos" cdef void CxxAppdelete "Cantera::appdelete" () cdef XML_Node* CxxGetXmlFile "Cantera::get_XML_File" (string) except + + cdef XML_Node* CxxGetXmlFromString "Cantera::get_XML_from_string" (string) except + cdef extern from "cantera/thermo/mix_defs.h": cdef int thermo_type_ideal_gas "Cantera::cIdealGas" diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index 608394fd7..1868cabab 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -1,9 +1,10 @@ cdef class _SolutionBase: - def __cinit__(self, infile='', phaseid='', phases=(), source=None): + def __cinit__(self, infile='', phaseid='', phases=(), origin=None, + source=None): # Shallow copy of an existing Solution (for slicing support) cdef _SolutionBase other - if source is not None: - other = <_SolutionBase?>source + if origin is not None: + other = <_SolutionBase?>origin # keep a reference to the parent to prevent the underlying # C++ objects from being deleted @@ -18,7 +19,12 @@ cdef class _SolutionBase: return # Instantiate a set of new Cantera C++ objects - rootNode = CxxGetXmlFile(stringify(infile)) + if infile: + rootNode = CxxGetXmlFile(stringify(infile)) + elif source: + rootNode = CxxGetXmlFromString(stringify(source)) + else: + raise ValueError('No phase definition provided') # Get XML data cdef XML_Node* phaseNode @@ -58,7 +64,7 @@ cdef class _SolutionBase: assert self.transport is not NULL def __getitem__(self, selection): - copy = self.__class__(source=self) + copy = self.__class__(origin=self) if isinstance(selection, slice): selection = range(selection.start or 0, selection.stop or self.n_species, diff --git a/interfaces/cython/cantera/ctml_writer.py b/interfaces/cython/cantera/ctml_writer.py index a5fc5f1f9..716c1786a 100644 --- a/interfaces/cython/cantera/ctml_writer.py +++ b/interfaces/cython/cantera/ctml_writer.py @@ -2617,21 +2617,30 @@ class Lindemann(object): #get_atomic_wts() validate() -def convert(filename, outName=None): - +def convert(filename=None, outName=None, text=None): import os - filename = os.path.expanduser(filename) - base = os.path.basename(filename) - root, _ = os.path.splitext(base) - dataset(root) + if filename is not None: + filename = os.path.expanduser(filename) + base = os.path.basename(filename) + root, _ = os.path.splitext(base) + dataset(root) + elif outName is None: + outName = 'STDOUT' + try: - with open(filename, 'rU') as f: - code = compile(f.read(), filename, 'exec') - exec(code) + if filename is not None: + with open(filename, 'rU') as f: + code = compile(f.read(), filename, 'exec') + else: + code = compile(text, '', 'exec') + exec(code) except SyntaxError as err: # Show more context than the default SyntaxError message # to help see problems in multi-line statements - text = open(filename, 'rU').readlines() + if filename: + text = open(filename, 'rU').readlines() + else: + text = text.split('\n') _printerr('%s in "%s" on line %i:\n' % (err.__class__.__name__, err.filename, err.lineno)) @@ -2646,7 +2655,11 @@ def convert(filename, outName=None): except Exception as err: import traceback - text = open(filename, 'rU').readlines() + if filename: + text = open(filename, 'rU').readlines() + else: + text = text.split('\n') + filename = '' tb = traceback.extract_tb(sys.exc_info()[2]) lineno = tb[-1][1] if tb[-1][0] == filename: diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 3c12e8a69..33bef9515 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -544,6 +544,39 @@ class ImportTest(utilities.CanteraTest): gas2 = ct.Solution('../data/air-no-reactions.xml', 'notair') self.check(gas2, 'notair', 900, 5*101325, 7, 2) + def test_import_phase_cti_text(self): + cti_def = """ +ideal_gas(name='spam', elements='O H', + species='gri30: all', + options='skip_undeclared_elements', + initial_state=state(temperature=350, pressure=2e6)) +""" + gas = ct.Solution(source=cti_def) + self.check(gas, 'spam', 350, 2e6, 8, 2) + + def test_import_phase_xml_text(self): + xml_def = """ + + + + + O + all + + + + 350.0 + 2000000.0 + + + + + +""" + gas = ct.Solution(source=xml_def) + self.check(gas, 'spam', 350, 2e6, 2, 1) + + def test_checkReactionBalance(self): with self.assertRaises(Exception): ct.Solution('../data/h2o2_unbalancedReaction.xml') diff --git a/src/base/application.cpp b/src/base/application.cpp index 171d4d38c..8f158594b 100644 --- a/src/base/application.cpp +++ b/src/base/application.cpp @@ -285,6 +285,26 @@ XML_Node* Application::get_XML_File(const std::string& file, int debug) return x; } +XML_Node* Application::get_XML_from_string(const std::string& text) +{ + ScopedLock xmlLock(xml_mutex); + std::pair& entry = xmlfiles[text]; + if (entry.first) { + // Return existing cached XML tree + return entry.first; + } + std::stringstream s; + size_t start = text.find_first_not_of(" \t\r\n"); + if (text.substr(start,5) == "build(s); + return entry.first; +} + void Application::close_XML_File(const std::string& file) { ScopedLock xmlLock(xml_mutex); diff --git a/src/base/application.h b/src/base/application.h index 6425522aa..fac5cda6e 100644 --- a/src/base/application.h +++ b/src/base/application.h @@ -300,6 +300,17 @@ public: */ XML_Node* get_XML_File(const std::string& file, int debug=0) ; + //! Read a CTI or CTML string and fill up an XML tree. + /*! + * Return a pointer to the XML tree corresponding to the specified + * CTI or XML string. If the given string has been processed before, + * the cached XML tree will be returned. Otherwise, the XML tree + * will be generated and stored in the cache. + * @param text CTI or CTML string + * @return Root of the corresponding XML tree + */ + XML_Node* get_XML_from_string(const std::string& text); + //! Close an XML File /*! * Close a file that is opened by this application object diff --git a/src/base/ct2ctml.cpp b/src/base/ct2ctml.cpp index 02f69a224..addd70dd1 100644 --- a/src/base/ct2ctml.cpp +++ b/src/base/ct2ctml.cpp @@ -80,8 +80,17 @@ void ct2ctml(const char* file, const int debug) out << xml; } -std::string ct2ctml_string(const std::string& file) +static std::string call_ctml_writer(const std::string& text, bool isfile) { + std::string file, arg; + if (isfile) { + file = text; + arg = "r'" + text + "'"; + } else { + file = ""; + arg = "text=r'''" + text + "'''"; + } + #ifdef HAS_NO_PYTHON /* * Section to bomb out if python is not @@ -109,7 +118,7 @@ std::string ct2ctml_string(const std::string& file) "except ImportError:\n" " print('sys.path: ' + repr(sys.path) + '\\n', file=sys.stderr)\n" " raise\n" - "ctml_writer.convert(r'" + file + "', 'STDOUT')\n" + "ctml_writer.convert(" + arg + ", outName='STDOUT')\n" "sys.exit(0)\n"); python.start(pypath(), args.begin(), args.end()); @@ -170,6 +179,15 @@ std::string ct2ctml_string(const std::string& file) return python_output; } +std::string ct2ctml_string(const std::string& file) +{ + return call_ctml_writer(file, true); +} + +std::string ct_string2ctml_string(const std::string& cti) +{ + return call_ctml_writer(cti, false); +} void ck2cti(const std::string& in_file, const std::string& thermo_file, const std::string& transport_file, const std::string& id_tag) diff --git a/src/base/global.cpp b/src/base/global.cpp index b724d0983..60fbd8b21 100644 --- a/src/base/global.cpp +++ b/src/base/global.cpp @@ -118,6 +118,11 @@ XML_Node* get_XML_File(const std::string& file, int debug) return xtmp; } +XML_Node* get_XML_from_string(const std::string& text) +{ + return app()->get_XML_from_string(text); +} + void close_XML_File(const std::string& file) { app()->close_XML_File(file) ;