[ck2cti] Allow conversion of standalone species thermo databases

This is useful when the species definitions are going to be used to define
phases in other input files.
This commit is contained in:
Ray Speth 2015-01-22 00:04:28 +00:00
parent efb80dfe05
commit 86393cda42
2 changed files with 108 additions and 61 deletions

View file

@ -1370,7 +1370,7 @@ class Parser(object):
return reaction, revReaction
def loadChemkinFile(self, path):
def loadChemkinFile(self, path, skipUndeclaredSpecies=True):
"""
Load a Chemkin-format input file to `path` on disk.
"""
@ -1490,18 +1490,27 @@ class Parser(object):
if entryPosition == entryLength-1:
label, thermo, comp, note = self.readNasa9Entry(entry)
try:
species = self.speciesDict[label]
# use the first set of thermo data found
if species.thermo is not None:
self.warn('Found additional thermo entry for species {0}. '
'If --permissive was given, the first entry is used.'.format(label))
if label not in self.speciesDict:
if skipUndeclaredSpecies:
logging.info('Skipping unexpected species "{0}" while reading thermodynamics entry.'.format(label))
thermo = []
continue
else:
species.thermo = thermo
species.composition = comp
species.note = note
except KeyError:
logging.info('Skipping unexpected species "{0}" while reading thermodynamics entry.'.format(label))
# Add a new species entry
species = Species(label=label)
self.speciesDict[label] = species
self.speciesList.append(species)
else:
species = self.speciesDict[label]
# use the first set of thermo data found
if species.thermo is not None:
self.warn('Found additional thermo entry for species {0}. '
'If --permissive was given, the first entry is used.'.format(label))
else:
species.thermo = thermo
species.composition = comp
species.note = note
entryPosition = -1
entry = []
@ -1528,18 +1537,29 @@ class Parser(object):
thermo.append(line)
if line[79] == '4':
label, thermo, comp, note = self.readThermoEntry(thermo, TintDefault)
try:
species = self.speciesDict[label]
# use the first set of thermo data found
if species.thermo is not None:
self.warn('Found additional thermo entry for species {0}. '
'If --permissive was given, the first entry is used.'.format(label))
if label not in self.speciesDict:
if skipUndeclaredSpecies:
logging.info('Skipping unexpected species "{0}" while reading thermodynamics entry.'.format(label))
thermo = []
line, comment = readline()
continue
else:
species.thermo = thermo
species.composition = comp
species.note = note
except KeyError:
logging.info('Skipping unexpected species "{0}" while reading thermodynamics entry.'.format(label))
# Add a new species entry
species = Species(label=label)
self.speciesDict[label] = species
self.speciesList.append(species)
else:
species = self.speciesDict[label]
# use the first set of thermo data found
if species.thermo is not None:
self.warn('Found additional thermo entry for species {0}. '
'If --permissive was given, the first entry is used.'.format(label))
else:
species.thermo = thermo
species.composition = comp
species.note = note
thermo = []
line, comment = readline()
@ -1740,19 +1760,20 @@ class Parser(object):
speciesNameLength = max(speciesNameLength, len(s.label))
# validate list of elements
missingElements = elementsFromSpecies - set(self.elements)
if missingElements:
raise InputParseError('Undefined elements: ' + str(missingElements))
if name is not None:
missingElements = elementsFromSpecies - set(self.elements)
if missingElements:
raise InputParseError('Undefined elements: ' + str(missingElements))
speciesNames = ['']
speciesPerLine = max(int((80-21)/(speciesNameLength + 2)), 1)
speciesNames = ['']
speciesPerLine = max(int((80-21)/(speciesNameLength + 2)), 1)
for i,s in enumerate(self.speciesList):
if i and not i % speciesPerLine:
speciesNames.append(' '*21)
speciesNames[-1] += '{0:{1}s}'.format(s.label, speciesNameLength+2)
for i,s in enumerate(self.speciesList):
if i and not i % speciesPerLine:
speciesNames.append(' '*21)
speciesNames[-1] += '{0:{1}s}'.format(s.label, speciesNameLength+2)
speciesNames = '\n'.join(line.rstrip() for line in speciesNames)
speciesNames = '\n'.join(line.rstrip() for line in speciesNames)
lines = []
@ -1766,18 +1787,19 @@ class Parser(object):
if header:
lines.extend(header)
# Write the gas definition
lines.append("units(length='cm', time='s', quantity={0!r}, act_energy={1!r})".format(self.quantity_units, self.energy_units))
lines.append('')
lines.append('ideal_gas(name={0!r},'.format(name))
lines.append(' elements="{0}",'.format(' '.join(self.elements)))
lines.append(' species="""{0}""",'.format(speciesNames))
if self.reactions:
lines.append(" reactions='all',")
if haveTransport:
lines.append(" transport={0!r},".format(transportModel))
lines.append(' initial_state=state(temperature=300.0, pressure=OneAtm))')
lines.append('')
if name is not None:
# Write the gas definition
lines.append("units(length='cm', time='s', quantity={0!r}, act_energy={1!r})".format(self.quantity_units, self.energy_units))
lines.append('')
lines.append('ideal_gas(name={0!r},'.format(name))
lines.append(' elements="{0}",'.format(' '.join(self.elements)))
lines.append(' species="""{0}""",'.format(speciesNames))
if self.reactions:
lines.append(" reactions='all',")
if haveTransport:
lines.append(" transport={0!r},".format(transportModel))
lines.append(' initial_state=state(temperature=300.0, pressure=OneAtm))')
lines.append('')
# Write the individual species data
lines.append(delimiterLine)
@ -1812,7 +1834,7 @@ class Parser(object):
ck2cti.py: Convert Chemkin-format mechanisms to Cantera input files (.cti)
Usage:
ck2cti --input=<filename>
ck2cti [--input=<filename>]
[--thermo=<filename>]
[--transport=<filename>]
[--id=<phase-id>]
@ -1826,6 +1848,10 @@ Example:
If the output file name is not given, an output file with the same name as the
input file, with the extension changed to '.cti'.
An input file containing only species definitions (which can be referenced from
phase definitions in other input files) can be created by specifying only a
thermo file.
The '--permissive' option allows certain recoverable parsing errors (e.g.
duplicate transport data) to be ignored.
@ -1834,7 +1860,8 @@ duplicate transport data) to be ignored.
def convertMech(self, inputFile, thermoFile=None,
transportFile=None, phaseName='gas',
outName=None, quiet=False, permissive=None):
inputFile = os.path.expanduser(inputFile)
if inputFile:
inputFile = os.path.expanduser(inputFile)
if thermoFile:
thermoFile = os.path.expanduser(thermoFile)
if transportFile:
@ -1850,21 +1877,25 @@ duplicate transport data) to be ignored.
if permissive is not None:
self.warning_as_error = not permissive
if not os.path.exists(inputFile):
raise IOError('Missing input file: {0!r}'.format(inputFile))
try:
# Read input mechanism files
self.loadChemkinFile(inputFile)
except Exception:
logging.warning("\nERROR: Unable to parse '{0}' near line {1}:\n".format(
inputFile, self.line_number))
raise
if inputFile:
if not os.path.exists(inputFile):
raise IOError('Missing input file: {0!r}'.format(inputFile))
try:
# Read input mechanism files
self.loadChemkinFile(inputFile)
except Exception:
logging.warning("\nERROR: Unable to parse '{0}' near line {1}:\n".format(
inputFile, self.line_number))
raise
else:
phaseName = None
if thermoFile:
if not os.path.exists(thermoFile):
raise IOError('Missing thermo file: {0!r}'.format(thermoFile))
try:
self.loadChemkinFile(thermoFile)
self.loadChemkinFile(thermoFile,
skipUndeclaredSpecies=bool(inputFile))
except Exception:
logging.warning("\nERROR: Unable to parse '{0}' near line {1}:\n".format(
thermoFile, self.line_number))
@ -1922,18 +1953,20 @@ def main(argv):
if '--input' in options:
inputFile = options['--input']
else:
print('Error: no mechanism input file specified')
sys.exit(1)
inputFile = None
thermoFile = options.get('--thermo')
if '--output' in options:
outName = options['--output']
if not outName.endswith('.cti'):
outName += '.cti'
else:
elif inputFile:
outName = os.path.splitext(inputFile)[0] + '.cti'
else:
outName = os.path.splitext(thermoFile)[0] + '.cti'
permissive = '--permissive' in options
thermoFile = options.get('--thermo')
transportFile = options.get('--transport')
phaseName = options.get('--id', 'gas')
@ -1941,6 +1974,10 @@ def main(argv):
outName, permissive=permissive)
# Do full validation by importing the resulting mechanism
if not inputFile:
# Can't validate input file that don't define a phase
return
try:
import cantera as ct
except ImportError:

View file

@ -87,6 +87,16 @@ class chemkinConverterTest(utilities.CanteraTest):
ref, gas = self.checkConversion('../data/pdep-test.xml', 'pdep_test.cti')
self.checkKinetics(ref, gas, [300, 800, 1450, 2800], [5e3, 1e5, 2e6])
def test_species_only(self):
convertMech(None,
thermoFile='../data/dummy-thermo.dat',
outName='dummy-thermo.cti', quiet=True)
cti = "ideal_gas(elements='C H', species='dummy-thermo:R1A R1B P1')"
gas = ct.Solution(source=cti)
self.assertEqual(gas.n_species, 3)
self.assertEqual(gas.n_reactions, 0)
def test_missingElement(self):
with self.assertRaises(ck2cti.InputParseError):
convertMech('../data/h2o2_missingElement.inp',